15#include <boost/graph/detail/adjacency_list.hpp>
16#include <boost/graph/dijkstra_shortest_paths.hpp>
17#include <boost/graph/named_function_params.hpp>
18#include <boost/graph/properties.hpp>
19#include <boost/property_map/property_map.hpp>
21#include <Eigen/Geometry>
23#include <range/v3/algorithm/sort.hpp>
24#include <range/v3/range/conversion.hpp>
25#include <range/v3/view/filter.hpp>
26#include <range/v3/view/reverse.hpp>
28#include <SimoxUtility/algorithm/string/string_tools.h>
29#include <VirtualRobot/Robot.h>
63#include <SemanticObjectRelations/Shapes/Shape.h>
80 return hasLocalPlanner;
86 return segmentLocalPlanner;
95 return plan.trajectory;
106 return plan.trajectory;
112 config{config}, srv{services}
135 return diff.translation().norm();
139 for (
auto edge :
graph.edges())
141 const core::Pose start = resolveGraphVertex(edge.source());
142 const core::Pose goal = resolveGraphVertex(edge.target());
144 switch (edge.attrib().strategy)
148 ARMARX_VERBOSE <<
"Global planning from " << start.translation() <<
" to "
149 << goal.translation();
155 if (globalPlan.has_value())
159 edge.attrib().trajectory = globalPlan->trajectory;
161 << globalPlan->trajectory.length();
162 edge.attrib().cost() = globalPlan->trajectory.length();
167 edge.attrib().cost() = std::numeric_limits<float>::max();
174 edge.attrib().cost() = std::numeric_limits<float>::max();
182 edge.attrib().cost() = cost(start, goal);
197 std::vector<core::Pose> globalWaypoints;
198 switch (navigationFrame)
201 globalWaypoints = waypoints;
204 globalWaypoints.reserve(waypoints.size());
208 const core::Pose global_T_robot(srv.sceneProvider->scene().robot->getGlobalPose());
209 ARMARX_VERBOSE <<
"Initial robot pose: " << global_T_robot.matrix();
211 std::transform(std::begin(waypoints),
213 std::back_inserter(globalWaypoints),
214 [&](
const core::Pose& p) {
return global_T_robot * p; });
219 moveToAbsolute(globalWaypoints);
226 ARMARX_INFO <<
"Received moveToAlternatives() request.";
228 std::vector<core::TargetAlternative> globalTargets;
229 switch (navigationFrame)
235 globalTargets.reserve(
targets.size());
239 const core::Pose global_T_robot(srv.sceneProvider->scene().robot->getGlobalPose());
240 ARMARX_VERBOSE <<
"Initial robot pose: " << global_T_robot.matrix();
242 std::transform(std::begin(
targets),
244 std::back_inserter(globalTargets),
253 moveToAbsoluteAlternatives(globalTargets);
262 std::vector<core::Pose> globalWaypoints;
263 switch (navigationFrame)
266 globalWaypoints = waypoints;
269 globalWaypoints.reserve(waypoints.size());
271 std::begin(waypoints),
273 std::back_inserter(globalWaypoints),
275 {
return core::Pose(srv.sceneProvider->scene().robot->getGlobalPose()) * p; });
280 updateAbsolute(globalWaypoints);
287 const core::Graph::ConstVertex& startVertex,
288 const core::Graph::ConstVertex& goalVertex)
291 <<
graph.numEdges() <<
" edges.";
293 std::vector<core::Graph::VertexDescriptor> predecessors(
graph.numVertices());
294 std::vector<int> d(num_vertices(
graph));
300 core::Graph::VertexDescriptor start = startVertex.descriptor();
302 auto predecessorMap = boost::make_iterator_property_map(
303 predecessors.begin(), boost::get(boost::vertex_index,
graph));
305 auto params = boost::predecessor_map(predecessorMap)
306 .distance_map(boost::make_iterator_property_map(
307 d.begin(), boost::get(boost::vertex_index,
graph)))
308 .weight_map(weightMap);
315 for (
const auto& edge :
graph.edges())
317 ARMARX_VERBOSE << edge.sourceObjectID() <<
" -> " << edge.targetObjectID() <<
": "
318 << edge.attrib().m_value;
321 std::vector<core::Graph::EdgeDescriptor> edgesToBeRemoved;
322 for (
const auto edge :
graph.edges())
324 if (edge.attrib().m_value == std::numeric_limits<float>::max())
326 edgesToBeRemoved.push_back(edge.descriptor());
330 for (
const auto edge : edgesToBeRemoved)
332 boost::remove_edge(edge,
graph);
336 for (
const auto& edge :
graph.edges())
338 ARMARX_VERBOSE << edge.sourceObjectID() <<
" -> " << edge.targetObjectID() <<
": "
339 << edge.attrib().m_value;
340 ARMARX_VERBOSE <<
"Edge: " << edge <<
"cost: " << edge.attrib().cost()
341 <<
", type: " <<
static_cast<int>(edge.attrib().strategy)
342 <<
" , has traj " << edge.attrib().trajectory.has_value();
346 <<
graph.vertex(start).objectID() <<
"` to vertex `"
347 <<
graph.vertex(goalVertex.descriptor()).objectID() <<
"`";
349 boost::dijkstra_shortest_paths(
graph, start, params);
357 core::Graph::VertexDescriptor currentVertex = goalVertex.descriptor();
358 while (currentVertex != startVertex.descriptor())
360 shortestPath.push_back(
graph.vertex(currentVertex).objectID());
364 auto parent = predecessorMap[currentVertex];
370 auto outEdges =
graph.vertex(parent).outEdges();
371 ARMARX_VERBOSE <<
"Parent has " << std::distance(outEdges.begin(), outEdges.end())
375 <<
"Cannot reach another vertex from vertex `"
376 <<
graph.vertex(parent).objectID();
378 auto edgeIt = std::find_if(outEdges.begin(),
380 [¤tVertex](
const auto& edge) ->
bool
381 { return edge.target().descriptor() == currentVertex; });
386 currentVertex = parent;
389 shortestPath.push_back(startVertex.objectID());
395 shortestPath = shortestPath | ranges::views::reverse | ranges::to_vector;
408 <<
"At least start and goal vertices must be available";
412 std::vector<core::GlobalTrajectoryPoint> trajectoryPoints;
419 ARMARX_VERBOSE <<
"Shortest path with " << shortestPath.size() <<
" vertices";
422 for (
size_t i = 0; i < shortestPath.size() - 1; i++)
428 const core::Graph::ConstEdge edge =
429 graph.edge(
graph.vertex(shortestPath.at(i)),
graph.vertex(shortestPath.at(i + 1)));
435 switch (edge.attrib().strategy)
445 ARMARX_INFO <<
"Length: " << edge.attrib().trajectory->length();
450 << edge.attrib().trajectory->points().size() <<
" waypoints";
455 const std::vector<core::GlobalTrajectoryPoint> edgeTrajectoryPoints =
456 edge.attrib().trajectory->points();
459 const int offset = trajectoryPoints.empty() ? 0 : 1;
461 if (edgeTrajectoryPoints.size() > 2)
465 trajectoryPoints.insert(trajectoryPoints.end(),
466 edgeTrajectoryPoints.begin() + offset,
467 edgeTrajectoryPoints.end());
492 const float point2pointVelocity = 400;
494 const core::GlobalTrajectoryPoint currentTrajPt = {
495 .waypoint = {.pose = resolveGraphVertex(graph.vertex(shortestPath.at(i)))},
496 .velocity = point2pointVelocity};
498 const core::GlobalTrajectoryPoint nextTrajPt{
500 resolveGraphVertex(graph.vertex(shortestPath.at(i + 1)))},
524 trajectoryPoints.push_back(currentTrajPt);
525 trajectoryPoints.push_back(nextTrajPt);
536 ARMARX_INFO <<
"Trajectory consists of " << trajectoryPoints.size() <<
" points";
538 for (
const auto& pt : trajectoryPoints)
543 return {trajectoryPoints};
547 Navigator::convertToGraph(
const std::vector<client::WaypointTarget>& targets)
const
550 GraphBuilder graphBuilder;
554 for (
const auto& target : targets)
591 if (
target.pose.has_value())
593 graphBuilder.connect(
target.pose.value(),
target.strategy);
600 if (not
target.locationId->empty())
603 target.locationId.value(), srv.sceneProvider->scene().graph->subgraphs);
608 << vertex.attrib().getPose();
613 ARMARX_INFO <<
"Found " << routes.size() <<
" routes to location `"
614 <<
target.locationId.value();
617 <<
"` is not a reachable vertex on the graph!";
620 graphBuilder.connect(routes,
626 ARMARX_ERROR <<
"Either `location_id` or `pose` has to be provided!";
629 const auto goalVertex = graphBuilder.getGraph().vertex(graphBuilder.goalVertex());
630 ARMARX_INFO <<
"Goal vertex is " <<
QUOTED(goalVertex.attrib().getLocationName());
636 Navigator::resolveGraphVertex(
const core::Graph::ConstVertex& vertex)
const
639 srv.sceneProvider->scene().staticScene->objectInfo,
640 vertex.attrib().getPose());
642 <<
"The location of vertex " << vertex.attrib().getLocationName()
643 <<
" couldn't be resolved (" << goal.errorMsg <<
")";
644 return goal.pose.value();
648 Navigator::setupRamping(core::GlobalTrajectory& trajectory,
float startVelocity)
const
650 std::vector<std::pair<std::size_t, float>> fixedVelocities;
652 if (config.stack.generalConfig.enableRampingStart)
654 fixedVelocities.emplace_back(0, startVelocity);
656 if (config.stack.generalConfig.enableRampingEnd)
658 fixedVelocities.emplace_back(trajectory.points().size() - 1,
659 config.stack.generalConfig.boundaryVelocity);
661 if (config.stack.generalConfig.enableRampingCorners)
663 const auto& corners =
664 trajectory.calculateCorners(config.stack.generalConfig.cornerLimit);
666 for (
const auto& [idx,
angle] : corners)
668 fixedVelocities.emplace_back(idx, config.stack.generalConfig.cornerVelocity);
672 trajectory.applyRamping(fixedVelocities, config.stack.generalConfig.rampLength);
681 <<
"only absolute movement implemented atm.";
693 auto graphBuilder = convertToGraph(
targets);
698 auto startVertex = graphBuilder.startVertex;
699 auto goalVertex = graphBuilder.getGraph().vertex(graphBuilder.goalVertex());
701 ARMARX_INFO <<
"Goal pose according to graph is " << graphBuilder.goalPose().matrix();
707 goalReachedMonitor = std::nullopt;
709 graphBuilder.goalPose(), srv.sceneProvider->scene(), config.goalReachedConfig);
711 if (goalReachedMonitor->goalReached(
false))
714 << goalReachedMonitor->goal().translation().head<2>()
715 <<
". Robot won't move.";
719 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())});
725 setGraphEdgeCosts(
graph);
729 srv.introspector->onGlobalGraph(
graph);
737 std::vector<core::Pose> vertexPoses;
738 vertexPoses.emplace_back(srv.sceneProvider->scene().robot->getGlobalPose());
740 ARMARX_INFO <<
"Navigating along the following nodes:";
741 for (
const semrel::ShapeID& vertex : shortestPath)
744 vertexPoses.push_back(resolveGraphVertex(
graph.vertex(vertex)));
747 srv.introspector->onGlobalShortestPath(vertexPoses);
762 .helperTrajectory = std::nullopt};
767 srv.executor->execute(globalPlan->currentGlobalSegment());
774 srv.introspector->onGlobalPlannerResult(globalPlan->plan);
776 ARMARX_INFO <<
"Global planning completed. Will now start all required threads";
783 Navigator::startStack()
792 const std::scoped_lock<std::mutex> lock{runningTaskMtx};
805 runningTask->start();
807 else if (not runningTask->isRunning())
809 runningTask->start();
814 if (globalPlan->useLocalPlanner(hasLocalPlanner()))
816 if (srv.executor !=
nullptr)
823 if (srv.executor !=
nullptr)
831 srv.publisher->movementStarted(core::MovementStartedEvent{
833 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())});
837 Navigator::moveToAbsolute(
const std::vector<core::Pose>& waypoints,
bool sceneUpdate)
857 ARMARX_INFO <<
"Request to move from " << srv.sceneProvider->scene().robot->getGlobalPose()
858 <<
" to " << waypoints.back().matrix();
861 goalReachedMonitor = std::nullopt;
862 goalReachedMonitor = GoalReachedMonitor(
863 waypoints.back(), srv.sceneProvider->scene(), config.goalReachedConfig);
865 if (goalReachedMonitor->goalReached(
false))
867 ARMARX_INFO <<
"Already at goal position. Robot won't move.";
871 srv.publisher->goalReached(core::GoalReachedEvent{
873 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())});
885 srv.introspector->onGoal(waypoints.back());
886 globalPlan = config.stack.globalPlanner->plan(waypoints.back());
888 if (srv.drawer !=
nullptr)
890 srv.drawer->callGenericDrawFunction(
891 [&](viz::Client& client)
892 { config.stack.globalPlanner->visualizeDebugInfo(client); });
896 ARMARX_WARNING <<
"No drawer available. Cannot visualize global planner debug info.";
902 if (not globalPlan.has_value())
905 srv.publisher->globalPlanningFailed(core::GlobalPlanningFailedEvent{
908 srv.introspector->failure();
913 setupGlobalPlanSubvidision();
915 startGlobalPathSegment(
false,
false);
919 Navigator::updateAbsolute(
const std::vector<core::Pose>& waypoints)
933 srv.introspector->onGoal(waypoints.back());
934 globalPlan = config.stack.globalPlanner->plan(waypoints.back());
938 if (not globalPlan.has_value())
941 srv.publisher->globalPlanningFailed(core::GlobalPlanningFailedEvent{
944 srv.introspector->failure();
949 setupGlobalPlanSubvidision();
951 startGlobalPathSegment(
false,
true);
955 Navigator::moveToAbsoluteAlternatives(
const std::vector<core::TargetAlternative>& targets)
969 ARMARX_INFO <<
"Request to move from " << srv.sceneProvider->scene().robot->getGlobalPose()
970 <<
" to " << targets.size() <<
" alternatives";
972 lastAllAlternativesImpossible = std::nullopt;
975 for (
const auto& t : targets)
977 GoalReachedMonitor monitor(
978 t.target, srv.sceneProvider->scene(), config.goalReachedConfig);
980 if (monitor.goalReached(
false))
982 ARMARX_INFO <<
"Already at a possible goal position. Robot won't move.";
984 srv.publisher->goalReached(core::GoalReachedEvent{
986 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())});
993 targetAlternatives = targets;
996 if (setupTargetAlternatives())
999 setupGlobalPlanSubvidision();
1001 startGlobalPathSegment(
false,
false);
1006 Navigator::setupTargetAlternatives()
1010 globalPlan = std::nullopt;
1016 const auto spfaPlanner =
1017 std::dynamic_pointer_cast<global_planning::SPFA>(config.stack.globalPlanner);
1018 if (spfaPlanner !=
nullptr)
1020 const core::Pose start(srv.sceneProvider->scene().robot->getGlobalPose());
1022 const auto planningResult = spfaPlanner->executePlanner(start);
1024 for (
const auto& target : targetAlternatives)
1027 const auto path = spfaPlanner->calculatePath(planningResult,
target.target);
1028 if (path.has_value() and verifyGlobalPathPossible(path->trajectory))
1031 srv.introspector->onGoal(
target.target);
1032 goalReachedMonitor = std::nullopt;
1033 goalReachedMonitor = GoalReachedMonitor(
1034 target.target, srv.sceneProvider->scene(), config.goalReachedConfig);
1037 <<
" with priority " <<
target.priority;
1043 <<
" not reachable";
1049 const auto goal = targetAlternatives.front().target;
1051 <<
"Current global planner is not an SPFA planner, discarding alternatives!";
1053 globalPlan = config.stack.globalPlanner->plan(goal);
1054 srv.introspector->onGoal(goal);
1055 goalReachedMonitor = std::nullopt;
1056 goalReachedMonitor =
1057 GoalReachedMonitor(goal, srv.sceneProvider->scene(), config.goalReachedConfig);
1060 targetAlternatives.clear();
1063 if (srv.drawer !=
nullptr)
1065 srv.drawer->callGenericDrawFunction(
1066 [&](viz::Client& client)
1067 { config.stack.globalPlanner->visualizeDebugInfo(client); });
1071 ARMARX_WARNING <<
"No drawer available. Cannot visualize global planner debug info.";
1077 if (not globalPlan.has_value())
1080 srv.publisher->globalPlanningFailed(core::GlobalPlanningFailedEvent{
1083 srv.introspector->failure();
1091 Navigator::setupGlobalPlanSubvidision()
1095 const auto& globalPlanPoses = globalPlan->plan.trajectory.poses();
1098 if (not globalPlan->plan.trajectory.points().empty())
1100 ARMARX_INFO <<
"Trajectory final pose " << globalPlanPoses.back().matrix();
1102 goalReachedMonitor->updateGoal(globalPlanPoses.back());
1104 srv.publisher->globalTrajectoryUpdated(core::GlobalTrajectoryUpdatedEvent{
1111 if ((not config.general.subdivision.enable) or (not hasLocalPlanner()) or
1112 globalPlan->plan.trajectory.points().empty())
1122 ARMARX_INFO <<
"Starting subdividing global path.";
1124 ARMARX_CHECK(srv.sceneProvider->scene().staticScene.has_value());
1126 srv.sceneProvider->scene().staticScene->distanceToObstaclesCostmap.has_value());
1127 const auto& costmap = srv.sceneProvider->scene().staticScene->distanceToObstaclesCostmap;
1129 std::vector<bool> localPlanEligibility;
1130 localPlanEligibility.reserve(globalPlan->plan.trajectory.points().size());
1131 for (
const auto& pose : globalPlanPoses)
1133 Eigen::Vector2f pt =
conv::to2D(pose.translation());
1134 localPlanEligibility.push_back(costmap->value(pt).value_or(0) >
1135 config.general.subdivision.localPlannerCostmapThreshold);
1139 bool falseSequence =
false;
1140 const int n = localPlanEligibility.size();
1141 const float expansion = config.general.subdivision.globalPlanExpansionDistance;
1144 for (
int i = 0; i <
n;)
1148 if (localPlanEligibility[i])
1151 falseSequence =
false;
1154 float totalDistance = 0;
1157 for (; (totalDistance < expansion) and (i < n); i++)
1159 localPlanEligibility[i] =
false;
1160 totalDistance += (globalPlanPoses[i - 1].translation() -
1161 globalPlanPoses[i].translation())
1171 if (not localPlanEligibility[i])
1174 falseSequence =
true;
1177 float totalDistance = 0;
1178 for (
int j = i - 1; (totalDistance < expansion) and (j >= 0); j--)
1180 localPlanEligibility[j] =
false;
1181 totalDistance += (globalPlanPoses[j].translation() -
1182 globalPlanPoses[j + 1].translation())
1198 GlobalPathSubdivision::Subdivision segment;
1200 segment.useLocalPlanner = localPlanEligibility[0];
1203 const auto checkSegmentLength = [&]()
1206 if (not segment.useLocalPlanner)
1211 const auto& subTrajectory =
1212 globalPlan->plan.trajectory.getSubTrajectory(segment.s, segment.t);
1213 const float segmentLength = subTrajectory.length();
1214 if (segmentLength >= config.general.subdivision.minSegmentDistance)
1219 if (globalPlan->subdivision.empty() and
static_cast<int>(segment.t) >= n)
1222 ARMARX_VERBOSE <<
"Segment " << globalPlan->subdivision.size() <<
": [" << segment.s
1223 <<
", " << segment.t
1224 <<
"); is the only segment. length=" << segmentLength;
1228 ARMARX_VERBOSE <<
"Segment " << globalPlan->subdivision.size() <<
": [" << segment.s
1229 <<
", " << segment.t
1230 <<
"); was too short for local planner: " << segmentLength;
1232 if (globalPlan->subdivision.empty())
1235 segment.useLocalPlanner =
false;
1237 <<
"First segment, convert to global planner segment and grow it further.";
1241 if (
static_cast<int>(segment.t) < n)
1244 segment = globalPlan->subdivision.back();
1245 globalPlan->subdivision.pop_back();
1246 ARMARX_VERBOSE <<
"Removing preceding segment and grow it further.";
1251 globalPlan->subdivision.back().t = segment.t;
1252 ARMARX_VERBOSE <<
"Last segment; extent previous segment to the end.";
1258 for (
int i = 0; i <
n; i++)
1261 while (i < n and localPlanEligibility[i] == segment.useLocalPlanner)
1267 if (checkSegmentLength())
1269 globalPlan->subdivision.emplace_back(segment);
1270 ARMARX_VERBOSE <<
"Segment " << globalPlan->subdivision.size() <<
": [" << segment.s
1271 <<
", " << segment.t
1272 <<
"); localPlanner=" << segment.useLocalPlanner;
1276 segment.useLocalPlanner = localPlanEligibility[i];
1283 std::size_t lastSegmentEnd = 0;
1284 for (
const auto& segment : globalPlan->subdivision)
1287 const std::ptrdiff_t segmentLength = segment.t - segment.s;
1289 lastSegmentEnd = segment.t;
1293 globalPlan->currentSegment = 0;
1294 srv.introspector->onGlobalPlannerSubdivision(globalPlan.value());
1296 ARMARX_INFO <<
"Divided global path into " << globalPlan->subdivision.size() <<
" segments";
1300 Navigator::startGlobalPathSegment(
bool incrementSegment,
bool rampFromCurrentVelocity)
1302 if (not globalPlan->subdivision.empty())
1306 if (incrementSegment)
1308 globalPlan->currentSegment++;
1311 if (globalPlan->currentSegment >= globalPlan->subdivision.size())
1317 ARMARX_INFO <<
"Starting segment " << globalPlan->currentSegment;
1318 const auto& currentSegment = globalPlan->subdivision[globalPlan->currentSegment];
1320 globalPlan->currentSegmentTrajectory =
1321 globalPlan->plan.trajectory.getSubTrajectory(currentSegment.s, currentSegment.t);
1323 setupRamping(globalPlan->currentGlobalSegment(),
1324 rampFromCurrentVelocity
1325 ? srv.sceneProvider->scene().platformVelocity.linear.norm()
1326 : config.stack.generalConfig.boundaryVelocity);
1328 goalReachedMonitor->updateGoal(
1329 globalPlan->currentGlobalSegment().points().back().waypoint.pose);
1333 if (incrementSegment)
1338 setupRamping(globalPlan->currentGlobalSegment(),
1339 rampFromCurrentVelocity
1340 ? srv.sceneProvider->scene().platformVelocity.linear.norm()
1341 : config.stack.generalConfig.boundaryVelocity);
1343 goalReachedMonitor->updateGoal(
1344 globalPlan->currentGlobalSegment().points().back().waypoint.pose);
1347 srv.introspector->onGlobalPlannerResult(globalPlan->plan);
1350 if (globalPlan->useLocalPlanner(hasLocalPlanner()))
1352 const auto localPlannerResult = updateLocalPlanner();
1353 updateExecutor(localPlannerResult);
1354 updateIntrospector(localPlannerResult);
1358 updateExecutor(globalPlan->currentGlobalSegment());
1361 ARMARX_INFO <<
"Start executing global plan segment (local planner="
1362 << globalPlan->useLocalPlanner(hasLocalPlanner())
1363 <<
"). Will now start all required threads.";
1379 const std::optional<std::string>& providerName)
1384 const auto resolveLocation =
1386 const std::optional<std::string>& providerName) -> std::vector<core::Location>
1388 const auto locations = srv.sceneProvider->scene().staticScene->locations;
1391 for (
const auto&
location : locations)
1397 const auto matchingLocs =
1402 <<
QUOTED(providerName.value_or(
"~unset~")) <<
".";
1403 return matchingLocs;
1408 <<
"The given location does not match the format <location>(:<instance-id>)? '"
1411 std::string instanceID;
1412 if (
split.size() == 2)
1415 instanceID =
split.back();
1418 const auto matchingLocations = resolveLocation(
split.front(), providerName);
1421 if (matchingLocations.empty())
1424 <<
QUOTED(providerName.value_or(
"~unset~"));
1427 if (matchingLocations.size() > 1)
1430 <<
" from provider " <<
QUOTED(providerName.value_or(
"~unset~"));
1431 for (
const auto&
location : matchingLocations)
1439 srv.sceneProvider->scene().staticScene->objectInfo,
1440 matchingLocations.front().framedPose,
1443 if (goal.pose.has_value())
1445 moveToAbsolute({goal.pose.value()},
false);
1465 const std::scoped_lock<std::mutex> lock{runMtx};
1470 <<
"Called Navigator::run() although shouldRun is false. Directly returning.";
1483 [&]() { updateScene(
true ); });
1488 srv.debugObserverHelper->setDebugObserverDatafield(
"scene update [ms]",
1489 duration.toMilliSecondsDouble());
1496 if (globalPlan.has_value() and not targetAlternatives.empty())
1498 checkGlobalPathAlternatives();
1503 if ((srv.introspector !=
nullptr) and (srv.sceneProvider !=
nullptr) and
1504 srv.sceneProvider->scene().robot)
1507 srv.introspector->onRobotPose(
1508 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose()));
1581 if (globalPlan->useLocalPlanner(hasLocalPlanner()))
1583 const auto localPlannerResult = updateLocalPlanner();
1584 updateExecutor(localPlannerResult);
1585 updateIntrospector(localPlannerResult);
1587 if (srv.executor !=
nullptr && localPlannerResult.has_value())
1589 srv.executor->ensureIsActive(
1593 else if (srv.executor !=
nullptr)
1595 srv.executor->ensureIsActive(
1602 srv.debugObserverHelper->setDebugObserverDatafield(
"local planner update [ms]",
1603 duration.toMilliSecondsDouble());
1610 if (hasSafetyGuard())
1613 const auto result = updateSafetyGuard();
1614 srv.debugObserverHelper->setDebugObserverDatafield(
"safety_guard.limit_linear",
1615 result.twistLimits.linear);
1616 srv.debugObserverHelper->setDebugObserverDatafield(
"safety_guard.limit_angular",
1617 result.twistLimits.angular);
1619 if (srv.executor !=
nullptr)
1621 srv.executor->updateVelocityLimits(result.twistLimits);
1627 if (srv.executor !=
nullptr)
1629 srv.executor->updateVelocityLimits(config.stack.generalConfig.maxVel);
1637 srv.debugObserverHelper->setDebugObserverDatafield(
"velocity_factor",
1638 velocityFactor.load());
1641 if (srv.executor !=
nullptr)
1643 srv.executor->updateVelocityFactor(velocityFactor);
1658 srv.debugObserverHelper->setDebugObserverDatafield(
"monitor update [ms]",
1659 duration.toMilliSecondsDouble());
1663 safety_guard::SafetyGuardResult
1664 Navigator::updateSafetyGuard()
1668 const core::Pose global_T_robot(srv.sceneProvider->scene().robot->getGlobalPose());
1669 const auto proj = globalPlan->currentGlobalSegment().getProjection(
1671 const Eigen::Vector3f global_V_movement = proj.wayPointAfter.waypoint.pose.translation() -
1672 proj.projection.waypoint.pose.translation();
1674 return config.stack.safetyGuard->computeSafetyLimits(global_V_movement.head<2>());
1678 Navigator::hasSafetyGuard()
const
1680 return config.stack.safetyGuard !=
nullptr;
1684 Navigator::hasLocalPlanner() const noexcept
1686 return config.stack.localPlanner !=
nullptr;
1690 Navigator::updateScene(
const bool fullUpdate)
1697 Navigator::checkGlobalPathAlternatives()
1701 const auto replanAlternatives = [&]()
1703 lastAllAlternativesImpossible = std::nullopt;
1708 const auto previousGlobalPlan = globalPlan;
1710 if (setupTargetAlternatives())
1714 setupGlobalPlanSubvidision();
1718 ARMARX_INFO <<
"No possible alternatives, restoring previous global path.";
1719 globalPlan = std::move(previousGlobalPlan);
1724 startGlobalPathSegment(
false,
true);
1727 if (not verifyGlobalPathPossible(globalPlan->currentGlobalSegment()))
1731 if (lastAllAlternativesImpossible.has_value() and
1734 config.general.targetAlternativesFilterTimeSeconds)))
1737 <<
"Current globalPlan invalid, waiting for timeout before replanning.";
1741 ARMARX_INFO <<
"Global path no longer possible, trying other alternatives!";
1743 replanAlternatives();
1746 else if (lastAllAlternativesImpossible.has_value())
1751 <<
"Previously invalid global plan valid again -> replanning all alternatives";
1753 replanAlternatives();
1758 Navigator::verifyGlobalPathPossible(
const core::GlobalTrajectory& plan)
1760 const auto& costmap = srv.sceneProvider->scene().staticScene->distanceToObstaclesCostmap;
1763 for (
const auto& pt : plan.points())
1765 const auto& pose = pt.waypoint.pose;
1766 const Eigen::Vector2f position =
conv::to2D(pose.translation());
1767 const auto vertex = costmap->toVertex(position);
1768 if (not costmap->isValid(vertex.index) or costmap->isInCollision(vertex.position))
1777 std::optional<local_planning::LocalPlannerResult>
1778 Navigator::updateLocalPlanner()
1781 const std::scoped_lock<std::mutex> lock{updateLocalPlannerMtx};
1789 const auto& globalTrajectory = globalPlan->currentGlobalSegment();
1790 ARMARX_VERBOSE << globalTrajectory.points().size() <<
" points in global plan";
1791 localPlan = config.stack.localPlanner->plan(globalTrajectory);
1793 if (localPlan.has_value())
1795 srv.publisher->localTrajectoryUpdated(core::LocalTrajectoryUpdatedEvent{
1805 srv.publisher->localPlanningFailed(core::LocalPlanningFailedEvent{
1808 return std::nullopt;
1811 srv.publisher->localPlanningFailed(core::LocalPlanningFailedEvent{
1814 return std::nullopt;
1818 Navigator::updateExecutor(
const std::optional<local_planning::LocalPlannerResult>& localPlan)
1820 if (srv.executor ==
nullptr)
1833 if (not localPlan.has_value())
1837 srv.executor->execute(core::LocalTrajectory{},
false);
1838 srv.executor->stop();
1859 srv.executor->execute(localPlan->trajectory,
true);
1863 Navigator::updateExecutor(
const core::GlobalTrajectory& globalTrajectory)
1865 if (srv.executor ==
nullptr)
1871 << globalTrajectory.points().size() <<
" points.";
1881 if (globalTrajectory.points().empty())
1885 srv.executor->stop();
1889 ARMARX_IMPORTANT <<
"Executing global plan with " << globalTrajectory.points().size()
1892 srv.executor->execute(globalTrajectory,
false);
1896 Navigator::updateIntrospector(
1897 const std::optional<local_planning::LocalPlannerResult>& localPlan)
1901 srv.introspector->onLocalPlannerResult(localPlan);
1905 Navigator::updateMonitor()
1909 if (not goalReachedMonitor.has_value())
1917 const auto status = goalReachedMonitor->status();
1918 armarx::DebugObserverHelper* debugObserver = srv.debugObserverHelper;
1920 if (debugObserver !=
nullptr)
1925 goalReachedMonitor->getConfig().posTh);
1927 goalReachedMonitor->getConfig().oriTh);
1930 if (not
isPaused() and goalReachedMonitor->goalReached())
1933 ARMARX_INFO <<
"Current global path segment finished!";
1936 if (startGlobalPathSegment(
true,
true))
1939 ARMARX_INFO <<
"Goal " << goalReachedMonitor->goal().translation().head<2>()
1943 srv.publisher->goalReached(core::GoalReachedEvent{
1945 {
core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())}});
1947 srv.introspector->success();
1953 Navigator::stopAllThreads()
1955 const std::scoped_lock<std::mutex> lock{runningTaskMtx};
1959 runningTask->stop();
2009 <<
"Scaling factor for velocity may not be negative, but is " << velocityFactor;
2011 <<
"Scaling factor for velocity may not be > 1, but is " << velocityFactor;
2013 this->velocityFactor = velocityFactor;
2021 executorEnabled.store(
false);
2023 if (srv.executor !=
nullptr)
2026 srv.executor->stop();
2035 executorEnabled.store(
true);
2037 if (srv.executor !=
nullptr)
2039 if (hasLocalPlanner())
2060 goalReachedMonitor.reset();
2061 goalReachedMonitor = std::nullopt;
2067 return not executorEnabled.load();
2073 return (not shouldRun) or (not runningTask) or (not runningTask->isRunning());
2077 config{std::move(other.config)},
2078 srv{std::move(other.srv)},
2079 executorEnabled{other.executorEnabled.load()}
#define ARMARX_CHECK_NOT_EMPTY(c)
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
static DateTime Now()
Current time on the virtual clock.
void setDebugObserverDatafield(const std::string &channelName, const std::string &datafieldName, const TimedVariantPtr &value) const
static Duration SecondsDouble(double seconds)
Constructs a duration in seconds.
The periodic task executes one thread method repeatedly using the time period specified in the constr...
double toMilliSecondsDouble() const
Returns the amount of milliseconds.
static Duration measure(std::function< void(void)> subjectToMeasure, ClockType clockType=ClockType::Virtual)
Measures the duration needed to execute the given lambda and returns it.
void initialize(const core::Pose &pose)
void moveToAlternatives(const std::vector< core::TargetAlternative > &targets, core::NavigationFrame navigationFrame) override
void moveTowards(const core::Direction &direction, core::NavigationFrame navigationFrame) override
void update(const std::vector< core::Pose > &waypoints, core::NavigationFrame navigationFrame) override
Navigator(const Config &config, const InjectedServices &services)
void setVelocityFactor(float velocityFactor) override
void moveToLocation(const std::string &location, const std::optional< std::string > &providerName) override
bool isPaused() const noexcept override
void moveTo(const std::vector< core::Pose > &waypoints, core::NavigationFrame navigationFrame) override
bool isStopped() const noexcept override
Brief description of class targets.
#define ARMARX_CHECK_GREATER(lhs, rhs)
This macro evaluates whether lhs is greater (>) than rhs and if it turns out to be false it will thro...
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_POSITIVE(number)
This macro evaluates whether number is positive (> 0) and if it turns out to be false it will throw a...
#define ARMARX_CHECK_LESS_EQUAL(lhs, rhs)
This macro evaluates whether lhs is less or equal (<=) rhs and if it turns out to be false it will th...
#define ARMARX_CHECK_GREATER_EQUAL(lhs, rhs)
This macro evaluates whether lhs is greater or equal (>=) rhs and if it turns out to be false it will...
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_INFO
The normal logging level.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
armarx::core::time::Duration Duration
std::vector< Eigen::Vector2f > to2D(const std::vector< Eigen::Vector3f > &v)
std::vector< core::Location > findMatchingLocations(const std::vector< core::Location > &locations, const std::string &locationName, const std::optional< std::string > &provider)
Graph::ConstVertex getVertexByName(const std::string &vertexName, const Graph &graph)
Eigen::Vector3f Direction
std::vector< GraphPath > findPathsTo(Graph::ConstVertex vertex, const Graph &graph)
void resolveLocation(Graph::Vertex &vertex, const aron::data::DictPtr &locationData)
const core::Graph & getSubgraph(const std::string &vertexName, const Graphs &graphs)
This file is part of ArmarX.
This file is part of ArmarX.
This file is part of ArmarX.
std::vector< semrel::ShapeID > GraphPath
GraphPath findShortestPath(core::Graph graph, const core::Graph::ConstVertex &startVertex, const core::Graph::ConstVertex &goalVertex)
std::vector< std::string > split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
std::string GetHandledExceptionString()
Vertex target(const detail::edge_base< Directed, Vertex > &e, const PCG &)
constexpr auto n() noexcept
double angle(const Point &a, const Point &b, const Point &c)
Event describing that the global trajectory was updated.
Event describing that the targeted goal was successfully reached.
GlobalPathSubdivision(const global_planning::GlobalPlannerResult &globalPlan)
global_planning::GlobalPlannerResult plan
const core::GlobalTrajectory & currentGlobalSegment() const
bool useLocalPlanner(bool hasLocalPlanner)
core::GlobalTrajectory currentSegmentTrajectory
std::size_t currentSegment
std::vector< Subdivision > subdivision
global_planning::GlobalPlannerPtr globalPlanner
int replanningUpdatePeriod
struct armarx::navigation::server::Navigator::Config::General::@344207070160307005105315017266073311106207125254 tasks
server::NavigationStack stack
EventPublishingInterface * publisher