19#include <boost/graph/detail/adjacency_list.hpp>
20#include <boost/graph/dijkstra_shortest_paths.hpp>
21#include <boost/graph/named_function_params.hpp>
22#include <boost/graph/properties.hpp>
23#include <boost/property_map/property_map.hpp>
25#include <Eigen/Geometry>
27#include <range/v3/algorithm/sort.hpp>
28#include <range/v3/range/conversion.hpp>
29#include <range/v3/view/filter.hpp>
30#include <range/v3/view/reverse.hpp>
32#include <SimoxUtility/algorithm/string/string_tools.h>
33#include <VirtualRobot/Robot.h>
67#include <SemanticObjectRelations/Shapes/Shape.h>
84 return hasLocalPlanner;
90 return segmentLocalPlanner;
99 return plan.trajectory;
110 return plan.trajectory;
131 std::optional<algorithms::Toppra::DriveParams>
150 catch (
const std::exception& e)
152 ARMARX_WARNING <<
"Cannot read the platform dynamics config for platform "
161 const std::optional<algorithms::Toppra::DriveParams>& driveParams,
164 *applied = generalConfig.parametrization;
166 const auto rampingInstead = [&](
const std::string& reason)
168 ARMARX_WARNING <<
"TOPP-RA trajectory parametrization was requested but " << reason
169 <<
". Falling back to ramping.";
175 return fac::TrajectoryParametrizationFactory::create(ramping);
180 return fac::TrajectoryParametrizationFactory::create(generalConfig);
185 return rampingInstead(
"it is not available in this build: " +
189 if (not general.platformDynamicsEnabled)
191 return rampingInstead(
"its drive parameters are disabled by "
192 "`p.navigator.general.platformDynamicsEnabled`");
195 if (not driveParams.has_value())
197 return rampingInstead(
198 "its drive parameters could not be read for platform '" + general.platform +
199 "'. Check `p.navigator.general.platform` against the files in "
200 "data/armarx_navigation/config/platform/");
205 return fac::TrajectoryParametrizationFactory::create(generalConfig, *driveParams);
207 catch (
const std::exception& e)
209 return rampingInstead(std::string{
"it could not be initialised: "} + e.what());
217 driveParams{loadDriveParams(config.stack.generalConfig, config.general)},
218 parametrization{createParametrization(config.stack.generalConfig,
221 ¶metrizationMode)},
222 rampingFallback{
fac::TrajectoryParametrizationFactory::create(
253 return diff.translation().norm();
257 for (
auto edge :
graph.edges())
259 const core::Pose start = resolveGraphVertex(edge.source());
260 const core::Pose goal = resolveGraphVertex(edge.target());
262 switch (edge.attrib().strategy)
266 ARMARX_VERBOSE <<
"Global planning from " << start.translation() <<
" to "
267 << goal.translation();
272 const auto globalPlan = config.stack.globalPlanner->plan(start, goal);
273 if (globalPlan.has_value())
277 edge.attrib().trajectory = globalPlan->trajectory;
279 << globalPlan->trajectory.length();
280 edge.attrib().cost() = globalPlan->trajectory.length();
285 edge.attrib().cost() = std::numeric_limits<float>::max();
292 edge.attrib().cost() = std::numeric_limits<float>::max();
300 edge.attrib().cost() = cost(start, goal);
315 std::vector<core::Pose> globalWaypoints;
316 switch (navigationFrame)
319 globalWaypoints = waypoints;
322 globalWaypoints.reserve(waypoints.size());
326 const core::Pose global_T_robot(srv.sceneProvider->scene().robot->getGlobalPose());
327 ARMARX_VERBOSE <<
"Initial robot pose: " << global_T_robot.matrix();
329 std::transform(std::begin(waypoints),
331 std::back_inserter(globalWaypoints),
332 [&](
const core::Pose& p) {
return global_T_robot * p; });
337 moveToAbsolute(globalWaypoints);
344 ARMARX_INFO <<
"Received moveToAlternatives() request.";
346 std::vector<core::TargetAlternative> globalTargets;
347 switch (navigationFrame)
353 globalTargets.reserve(
targets.size());
357 const core::Pose global_T_robot(srv.sceneProvider->scene().robot->getGlobalPose());
358 ARMARX_VERBOSE <<
"Initial robot pose: " << global_T_robot.matrix();
360 std::transform(std::begin(
targets),
362 std::back_inserter(globalTargets),
371 moveToAbsoluteAlternatives(globalTargets);
380 std::vector<core::Pose> globalWaypoints;
381 switch (navigationFrame)
384 globalWaypoints = waypoints;
387 globalWaypoints.reserve(waypoints.size());
389 std::begin(waypoints),
391 std::back_inserter(globalWaypoints),
393 {
return core::Pose(srv.sceneProvider->scene().robot->getGlobalPose()) * p; });
398 updateAbsolute(globalWaypoints);
405 const core::Graph::ConstVertex& startVertex,
406 const core::Graph::ConstVertex& goalVertex)
409 <<
graph.numEdges() <<
" edges.";
411 std::vector<core::Graph::VertexDescriptor> predecessors(
graph.numVertices());
412 std::vector<int> d(num_vertices(
graph));
418 core::Graph::VertexDescriptor start = startVertex.descriptor();
420 auto predecessorMap = boost::make_iterator_property_map(
421 predecessors.begin(), boost::get(boost::vertex_index,
graph));
423 auto params = boost::predecessor_map(predecessorMap)
424 .distance_map(boost::make_iterator_property_map(
425 d.begin(), boost::get(boost::vertex_index,
graph)))
426 .weight_map(weightMap);
433 for (
const auto& edge :
graph.edges())
435 ARMARX_VERBOSE << edge.sourceObjectID() <<
" -> " << edge.targetObjectID() <<
": "
436 << edge.attrib().m_value;
439 std::vector<core::Graph::EdgeDescriptor> edgesToBeRemoved;
440 for (
const auto edge :
graph.edges())
442 if (edge.attrib().m_value == std::numeric_limits<float>::max())
444 edgesToBeRemoved.push_back(edge.descriptor());
448 for (
const auto edge : edgesToBeRemoved)
450 boost::remove_edge(edge,
graph);
454 for (
const auto& edge :
graph.edges())
456 ARMARX_VERBOSE << edge.sourceObjectID() <<
" -> " << edge.targetObjectID() <<
": "
457 << edge.attrib().m_value;
458 ARMARX_VERBOSE <<
"Edge: " << edge <<
"cost: " << edge.attrib().cost()
459 <<
", type: " <<
static_cast<int>(edge.attrib().strategy)
460 <<
" , has traj " << edge.attrib().trajectory.has_value();
464 <<
graph.vertex(start).objectID() <<
"` to vertex `"
465 <<
graph.vertex(goalVertex.descriptor()).objectID() <<
"`";
467 boost::dijkstra_shortest_paths(
graph, start, params);
475 core::Graph::VertexDescriptor currentVertex = goalVertex.descriptor();
476 while (currentVertex != startVertex.descriptor())
478 shortestPath.push_back(
graph.vertex(currentVertex).objectID());
482 auto parent = predecessorMap[currentVertex];
488 auto outEdges =
graph.vertex(parent).outEdges();
489 ARMARX_VERBOSE <<
"Parent has " << std::distance(outEdges.begin(), outEdges.end())
493 <<
"Cannot reach another vertex from vertex `"
494 <<
graph.vertex(parent).objectID();
496 auto edgeIt = std::find_if(outEdges.begin(),
498 [¤tVertex](
const auto& edge) ->
bool
499 { return edge.target().descriptor() == currentVertex; });
504 currentVertex = parent;
507 shortestPath.push_back(startVertex.objectID());
513 shortestPath = shortestPath | ranges::views::reverse | ranges::to_vector;
521 Navigator::convertToTrajectory(
const GraphPath& shortestPath,
const core::Graph&
graph)
const
526 <<
"At least start and goal vertices must be available";
530 std::vector<core::GlobalTrajectoryPoint> trajectoryPoints;
537 ARMARX_VERBOSE <<
"Shortest path with " << shortestPath.size() <<
" vertices";
540 for (
size_t i = 0; i < shortestPath.size() - 1; i++)
546 const core::Graph::ConstEdge edge =
547 graph.edge(
graph.vertex(shortestPath.at(i)),
graph.vertex(shortestPath.at(i + 1)));
553 switch (edge.attrib().strategy)
563 ARMARX_INFO <<
"Length: " << edge.attrib().trajectory->length();
568 << edge.attrib().trajectory->points().size() <<
" waypoints";
573 const std::vector<core::GlobalTrajectoryPoint> edgeTrajectoryPoints =
574 edge.attrib().trajectory->points();
577 const int offset = trajectoryPoints.empty() ? 0 : 1;
579 if (edgeTrajectoryPoints.size() > 2)
583 trajectoryPoints.insert(trajectoryPoints.end(),
584 edgeTrajectoryPoints.begin() + offset,
585 edgeTrajectoryPoints.end());
610 const float point2pointVelocity = 400;
612 const core::GlobalTrajectoryPoint currentTrajPt = {
613 .waypoint = {.pose = resolveGraphVertex(graph.vertex(shortestPath.at(i)))},
614 .velocity = point2pointVelocity};
618 resolveGraphVertex(
graph.vertex(shortestPath.at(i + 1)))},
642 trajectoryPoints.push_back(currentTrajPt);
643 trajectoryPoints.push_back(nextTrajPt);
654 ARMARX_INFO <<
"Trajectory consists of " << trajectoryPoints.size() <<
" points";
656 for (
const auto& pt : trajectoryPoints)
661 return {trajectoryPoints};
665 Navigator::convertToGraph(
const std::vector<client::WaypointTarget>& targets)
const
668 GraphBuilder graphBuilder;
669 graphBuilder.initialize(
core::Pose(srv.sceneProvider->scene().robot->getGlobalPose()));
672 for (
const auto& target : targets)
709 if (
target.pose.has_value())
711 graphBuilder.connect(
target.pose.value(),
target.strategy);
718 if (not
target.locationId->empty())
721 target.locationId.value(), srv.sceneProvider->scene().graph->subgraphs);
726 << vertex.attrib().getPose();
731 ARMARX_INFO <<
"Found " << routes.size() <<
" routes to location `"
732 <<
target.locationId.value();
735 <<
"` is not a reachable vertex on the graph!";
738 graphBuilder.connect(routes,
744 ARMARX_ERROR <<
"Either `location_id` or `pose` has to be provided!";
747 const auto goalVertex = graphBuilder.getGraph().vertex(graphBuilder.goalVertex());
748 ARMARX_INFO <<
"Goal vertex is " <<
QUOTED(goalVertex.attrib().getLocationName());
754 Navigator::resolveGraphVertex(
const core::Graph::ConstVertex& vertex)
const
757 srv.sceneProvider->scene().staticScene->objectInfo,
758 vertex.attrib().getPose());
760 <<
"The location of vertex " << vertex.attrib().getLocationName()
761 <<
" couldn't be resolved (" << goal.errorMsg <<
")";
762 return goal.pose.value();
771 <<
"only absolute movement implemented atm.";
783 auto graphBuilder = convertToGraph(
targets);
788 auto startVertex = graphBuilder.startVertex;
789 auto goalVertex = graphBuilder.getGraph().vertex(graphBuilder.goalVertex());
791 ARMARX_INFO <<
"Goal pose according to graph is " << graphBuilder.goalPose().matrix();
797 goalReachedMonitor = std::nullopt;
799 graphBuilder.goalPose(), srv.sceneProvider->scene(), config.goalReachedConfig);
801 if (goalReachedMonitor->goalReached(
false))
804 << goalReachedMonitor->goal().translation().head<2>()
805 <<
". Robot won't move.";
809 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())});
815 setGraphEdgeCosts(
graph);
819 srv.introspector->onGlobalGraph(
graph);
827 std::vector<core::Pose> vertexPoses;
828 vertexPoses.emplace_back(srv.sceneProvider->scene().robot->getGlobalPose());
830 ARMARX_INFO <<
"Navigating along the following nodes:";
831 for (
const semrel::ShapeID& vertex : shortestPath)
834 vertexPoses.push_back(resolveGraphVertex(
graph.vertex(vertex)));
837 srv.introspector->onGlobalShortestPath(vertexPoses);
852 .helperTrajectory = std::nullopt};
857 srv.executor->execute(globalPlan->currentGlobalSegment());
864 srv.introspector->onGlobalPlannerResult(globalPlan->plan);
866 ARMARX_INFO <<
"Global planning completed. Will now start all required threads";
873 Navigator::startStack()
882 const std::scoped_lock<std::mutex> lock{runningTaskMtx};
892 config.general.tasks.replanningUpdatePeriod,
895 runningTask->start();
897 else if (not runningTask->isRunning())
899 runningTask->start();
904 if (globalPlan->useLocalPlanner(hasLocalPlanner()))
906 if (srv.executor !=
nullptr)
908 srv.executor->start(ExecutorInterface::ControllerType::LocalTrajectory);
913 if (srv.executor !=
nullptr)
915 srv.executor->start(ExecutorInterface::ControllerType::GlobalTrajectory);
923 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())});
927 Navigator::moveToAbsolute(
const std::vector<core::Pose>& waypoints,
bool sceneUpdate)
947 ARMARX_INFO <<
"Request to move from " << srv.sceneProvider->scene().robot->getGlobalPose()
948 <<
" to " << waypoints.back().matrix();
951 goalReachedMonitor = std::nullopt;
952 goalReachedMonitor = GoalReachedMonitor(
953 waypoints.back(), srv.sceneProvider->scene(), config.goalReachedConfig);
955 if (goalReachedMonitor->goalReached(
false))
957 ARMARX_INFO <<
"Already at goal position. Robot won't move.";
963 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())});
975 srv.introspector->onGoal(waypoints.back());
976 globalPlan = config.stack.globalPlanner->plan(waypoints.back());
978 if (srv.drawer !=
nullptr)
980 srv.drawer->callGenericDrawFunction(
982 { config.stack.globalPlanner->visualizeDebugInfo(
client); });
986 ARMARX_WARNING <<
"No drawer available. Cannot visualize global planner debug info.";
992 if (not globalPlan.has_value())
998 srv.introspector->failure();
1003 setupGlobalPlanSubvidision();
1005 startGlobalPathSegment(
false,
false);
1009 Navigator::updateAbsolute(
const std::vector<core::Pose>& waypoints)
1023 srv.introspector->onGoal(waypoints.back());
1024 globalPlan = config.stack.globalPlanner->plan(waypoints.back());
1028 if (not globalPlan.has_value())
1034 srv.introspector->failure();
1039 setupGlobalPlanSubvidision();
1041 startGlobalPathSegment(
false,
true);
1045 Navigator::moveToAbsoluteAlternatives(
const std::vector<core::TargetAlternative>& targets)
1059 ARMARX_INFO <<
"Request to move from " << srv.sceneProvider->scene().robot->getGlobalPose()
1060 <<
" to " << targets.size() <<
" alternatives";
1062 lastAllAlternativesImpossible = std::nullopt;
1065 for (
const auto& t : targets)
1067 GoalReachedMonitor monitor(
1068 t.target, srv.sceneProvider->scene(), config.goalReachedConfig);
1070 if (monitor.goalReached(
false))
1072 ARMARX_INFO <<
"Already at a possible goal position. Robot won't move.";
1076 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())});
1083 targetAlternatives = targets;
1086 if (setupTargetAlternatives())
1089 setupGlobalPlanSubvidision();
1091 startGlobalPathSegment(
false,
false);
1096 Navigator::setupTargetAlternatives()
1100 globalPlan = std::nullopt;
1106 const auto spfaPlanner =
1107 std::dynamic_pointer_cast<global_planning::SPFA>(config.stack.globalPlanner);
1108 if (spfaPlanner !=
nullptr)
1110 const core::Pose start(srv.sceneProvider->scene().robot->getGlobalPose());
1112 const auto planningResult = spfaPlanner->executePlanner(start);
1114 for (
const auto& target : targetAlternatives)
1117 const auto path = spfaPlanner->calculatePath(planningResult,
target.target);
1118 if (path.has_value() and verifyGlobalPathPossible(path->trajectory))
1121 srv.introspector->onGoal(
target.target);
1122 goalReachedMonitor = std::nullopt;
1123 goalReachedMonitor = GoalReachedMonitor(
1124 target.target, srv.sceneProvider->scene(), config.goalReachedConfig);
1127 <<
" with priority " <<
target.priority;
1133 <<
" not reachable";
1139 const auto goal = targetAlternatives.front().target;
1141 <<
"Current global planner is not an SPFA planner, discarding alternatives!";
1143 globalPlan = config.stack.globalPlanner->plan(goal);
1144 srv.introspector->onGoal(goal);
1145 goalReachedMonitor = std::nullopt;
1146 goalReachedMonitor =
1147 GoalReachedMonitor(goal, srv.sceneProvider->scene(), config.goalReachedConfig);
1150 targetAlternatives.clear();
1153 if (srv.drawer !=
nullptr)
1155 srv.drawer->callGenericDrawFunction(
1157 { config.stack.globalPlanner->visualizeDebugInfo(
client); });
1161 ARMARX_WARNING <<
"No drawer available. Cannot visualize global planner debug info.";
1167 if (not globalPlan.has_value())
1173 srv.introspector->failure();
1181 Navigator::setupGlobalPlanSubvidision()
1185 const auto& globalPlanPoses = globalPlan->plan.trajectory.poses();
1188 if (not globalPlan->plan.trajectory.points().empty())
1190 ARMARX_INFO <<
"Trajectory final pose " << globalPlanPoses.back().matrix();
1192 goalReachedMonitor->updateGoal(globalPlanPoses.back());
1201 if ((not config.general.subdivision.enable) or (not hasLocalPlanner()) or
1202 globalPlan->plan.trajectory.points().empty())
1212 ARMARX_INFO <<
"Starting subdividing global path.";
1214 ARMARX_CHECK(srv.sceneProvider->scene().staticScene.has_value());
1216 srv.sceneProvider->scene().staticScene->distanceToObstaclesCostmap.has_value());
1217 const auto& costmap = srv.sceneProvider->scene().staticScene->distanceToObstaclesCostmap;
1219 std::vector<bool> localPlanEligibility;
1220 localPlanEligibility.reserve(globalPlan->plan.trajectory.points().size());
1221 for (
const auto& pose : globalPlanPoses)
1223 Eigen::Vector2f pt =
conv::to2D(pose.translation());
1224 localPlanEligibility.push_back(costmap->value(pt).value_or(0) >
1225 config.general.subdivision.localPlannerCostmapThreshold);
1229 bool falseSequence =
false;
1230 const int n = localPlanEligibility.size();
1231 const float expansion = config.general.subdivision.globalPlanExpansionDistance;
1234 for (
int i = 0; i <
n;)
1238 if (localPlanEligibility[i])
1241 falseSequence =
false;
1244 float totalDistance = 0;
1247 for (; (totalDistance < expansion) and (i < n); i++)
1249 localPlanEligibility[i] =
false;
1250 totalDistance += (globalPlanPoses[i - 1].translation() -
1251 globalPlanPoses[i].translation())
1261 if (not localPlanEligibility[i])
1264 falseSequence =
true;
1267 float totalDistance = 0;
1268 for (
int j = i - 1; (totalDistance < expansion) and (j >= 0); j--)
1270 localPlanEligibility[j] =
false;
1271 totalDistance += (globalPlanPoses[j].translation() -
1272 globalPlanPoses[j + 1].translation())
1288 GlobalPathSubdivision::Subdivision segment;
1290 segment.useLocalPlanner = localPlanEligibility[0];
1293 const auto checkSegmentLength = [&]()
1296 if (not segment.useLocalPlanner)
1301 const auto& subTrajectory =
1302 globalPlan->plan.trajectory.getSubTrajectory(segment.s, segment.t);
1303 const float segmentLength = subTrajectory.length();
1304 if (segmentLength >= config.general.subdivision.minSegmentDistance)
1309 if (globalPlan->subdivision.empty() and
static_cast<int>(segment.t) >= n)
1312 ARMARX_VERBOSE <<
"Segment " << globalPlan->subdivision.size() <<
": [" << segment.s
1313 <<
", " << segment.t
1314 <<
"); is the only segment. length=" << segmentLength;
1318 ARMARX_VERBOSE <<
"Segment " << globalPlan->subdivision.size() <<
": [" << segment.s
1319 <<
", " << segment.t
1320 <<
"); was too short for local planner: " << segmentLength;
1322 if (globalPlan->subdivision.empty())
1325 segment.useLocalPlanner =
false;
1327 <<
"First segment, convert to global planner segment and grow it further.";
1331 if (
static_cast<int>(segment.t) < n)
1334 segment = globalPlan->subdivision.back();
1335 globalPlan->subdivision.pop_back();
1336 ARMARX_VERBOSE <<
"Removing preceding segment and grow it further.";
1341 globalPlan->subdivision.back().t = segment.t;
1342 ARMARX_VERBOSE <<
"Last segment; extent previous segment to the end.";
1348 for (
int i = 0; i <
n; i++)
1351 while (i < n and localPlanEligibility[i] == segment.useLocalPlanner)
1357 if (checkSegmentLength())
1359 globalPlan->subdivision.emplace_back(segment);
1360 ARMARX_VERBOSE <<
"Segment " << globalPlan->subdivision.size() <<
": [" << segment.s
1361 <<
", " << segment.t
1362 <<
"); localPlanner=" << segment.useLocalPlanner;
1366 segment.useLocalPlanner = localPlanEligibility[i];
1373 std::size_t lastSegmentEnd = 0;
1374 for (
const auto& segment : globalPlan->subdivision)
1377 const std::ptrdiff_t segmentLength = segment.t - segment.s;
1379 lastSegmentEnd = segment.t;
1383 globalPlan->currentSegment = 0;
1384 srv.introspector->onGlobalPlannerSubdivision(globalPlan.value());
1386 ARMARX_INFO <<
"Divided global path into " << globalPlan->subdivision.size() <<
" segments";
1402 constexpr std::size_t minimumWaypointsForToppra = 4;
1405 trajectory.points().size() < minimumWaypointsForToppra)
1408 rampingFallback->apply(trajectory, startVelocity);
1414 parametrization->apply(trajectory, startVelocity);
1416 catch (
const std::exception& e)
1420 ARMARX_WARNING <<
"Trajectory parametrization failed (" << e.what()
1421 <<
"). Falling back to ramping for this request.";
1424 trajectory = planned;
1425 rampingFallback->apply(trajectory, startVelocity);
1433 ARMARX_INFO <<
"Parametrized " << trajectory.points().size() <<
" waypoints with "
1436 <<
" s (the planner's obstacle-aware velocities alone would take "
1440 if (config.general.parametrizationDump)
1443 {.requested = config.stack.generalConfig.parametrization,
1446 .parametrized = trajectory,
1448 .driveParams = driveParams,
1449 .maxVelocity = config.stack.generalConfig.maxVel.linear,
1450 .maxAngularVelocity = config.stack.generalConfig.maxVel.angular},
1451 config.general.parametrizationDumpPath);
1456 Navigator::resumeAfterEmergencyStop()
1458 if (not globalPlan.has_value() or isPaused() or isStopped())
1463 if (globalPlan->useLocalPlanner(hasLocalPlanner()))
1471 if (trajectory.points().empty())
1477 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose()).translation();
1481 auto [remaining, endsAtGoal] = trajectory.getSubTrajectory(
1482 robotPosition, std::numeric_limits<float>::infinity());
1484 if (remaining.points().size() < 2)
1486 ARMARX_WARNING <<
"Cannot re-anchor the trajectory after the emergency stop: only "
1487 << remaining.points().size()
1488 <<
" waypoints remain. Resuming on the trajectory as planned.";
1492 ARMARX_IMPORTANT <<
"Re-anchoring the trajectory at the robot's current pose after the "
1494 << remaining.points().size() <<
" of " << trajectory.points().size()
1495 <<
" waypoints remain.";
1497 applyParametrization(remaining, config.stack.generalConfig.boundaryVelocity);
1499 updateExecutor(remaining);
1503 Navigator::startGlobalPathSegment(
bool incrementSegment,
bool rampFromCurrentVelocity)
1505 if (not globalPlan->subdivision.empty())
1509 if (incrementSegment)
1511 globalPlan->currentSegment++;
1514 if (globalPlan->currentSegment >= globalPlan->subdivision.size())
1520 ARMARX_INFO <<
"Starting segment " << globalPlan->currentSegment;
1521 const auto& currentSegment = globalPlan->subdivision[globalPlan->currentSegment];
1523 globalPlan->currentSegmentTrajectory =
1524 globalPlan->plan.trajectory.getSubTrajectory(currentSegment.s, currentSegment.t);
1526 applyParametrization(globalPlan->currentGlobalSegment(),
1527 rampFromCurrentVelocity
1528 ? srv.sceneProvider->scene().platformVelocity.linear.norm()
1529 : config.stack.generalConfig.boundaryVelocity);
1531 goalReachedMonitor->updateGoal(
1532 globalPlan->currentGlobalSegment().points().back().waypoint.pose);
1536 if (incrementSegment)
1541 applyParametrization(globalPlan->currentGlobalSegment(),
1542 rampFromCurrentVelocity
1543 ? srv.sceneProvider->scene().platformVelocity.linear.norm()
1544 : config.stack.generalConfig.boundaryVelocity);
1546 goalReachedMonitor->updateGoal(
1547 globalPlan->currentGlobalSegment().points().back().waypoint.pose);
1550 srv.introspector->onGlobalPlannerResult(globalPlan->plan);
1553 if (globalPlan->useLocalPlanner(hasLocalPlanner()))
1555 const auto localPlannerResult = updateLocalPlanner();
1556 updateExecutor(localPlannerResult);
1557 updateIntrospector(localPlannerResult);
1561 updateExecutor(globalPlan->currentGlobalSegment());
1564 ARMARX_INFO <<
"Start executing global plan segment (local planner="
1565 << globalPlan->useLocalPlanner(hasLocalPlanner())
1566 <<
"). Will now start all required threads.";
1582 const std::optional<std::string>& providerName)
1587 const auto resolveLocation =
1589 const std::optional<std::string>& providerName) -> std::vector<core::Location>
1591 const auto locations = srv.sceneProvider->scene().staticScene->locations;
1594 for (
const auto&
location : locations)
1600 const auto matchingLocs =
1605 <<
QUOTED(providerName.value_or(
"~unset~")) <<
".";
1606 return matchingLocs;
1611 <<
"The given location does not match the format <location>(:<instance-id>)? '"
1614 std::string instanceID;
1615 if (
split.size() == 2)
1618 instanceID =
split.back();
1621 const auto matchingLocations = resolveLocation(
split.front(), providerName);
1624 if (matchingLocations.empty())
1627 <<
QUOTED(providerName.value_or(
"~unset~"));
1630 if (matchingLocations.size() > 1)
1633 <<
" from provider " <<
QUOTED(providerName.value_or(
"~unset~"));
1634 for (
const auto&
location : matchingLocations)
1642 srv.sceneProvider->scene().staticScene->objectInfo,
1643 matchingLocations.front().framedPose,
1646 if (goal.pose.has_value())
1648 moveToAbsolute({goal.pose.value()},
false);
1668 const std::scoped_lock<std::mutex> lock{runMtx};
1673 <<
"Called Navigator::run() although shouldRun is false. Directly returning.";
1686 [&]() { updateScene(
true ); });
1691 srv.debugObserverHelper->setDebugObserverDatafield(
"scene update [ms]",
1692 duration.toMilliSecondsDouble());
1699 if (globalPlan.has_value() and not targetAlternatives.empty())
1701 checkGlobalPathAlternatives();
1706 if ((srv.introspector !=
nullptr) and (srv.sceneProvider !=
nullptr) and
1707 srv.sceneProvider->scene().robot)
1710 srv.introspector->onRobotPose(
1711 core::Pose(srv.sceneProvider->scene().robot->getGlobalPose()));
1780 if (srv.executor !=
nullptr and srv.executor->consumeEmergencyStopRelease())
1782 resumeAfterEmergencyStop();
1792 if (globalPlan->useLocalPlanner(hasLocalPlanner()))
1794 const auto localPlannerResult = updateLocalPlanner();
1795 updateExecutor(localPlannerResult);
1796 updateIntrospector(localPlannerResult);
1798 if (srv.executor !=
nullptr && localPlannerResult.has_value() &&
1799 not isPaused() && not isStopped())
1801 srv.executor->ensureIsActive(
1802 ExecutorInterface::ControllerType::LocalTrajectory);
1805 else if (srv.executor !=
nullptr && not isPaused() && not isStopped())
1807 srv.executor->ensureIsActive(
1808 ExecutorInterface::ControllerType::GlobalTrajectory);
1814 srv.debugObserverHelper->setDebugObserverDatafield(
"local planner update [ms]",
1815 duration.toMilliSecondsDouble());
1822 if (hasSafetyGuard())
1825 const auto result = updateSafetyGuard();
1826 srv.debugObserverHelper->setDebugObserverDatafield(
"safety_guard.limit_linear",
1827 result.twistLimits.linear);
1828 srv.debugObserverHelper->setDebugObserverDatafield(
"safety_guard.limit_angular",
1829 result.twistLimits.angular);
1831 if (srv.executor !=
nullptr)
1833 srv.executor->updateVelocityLimits(result.twistLimits);
1838 checkRobotBlocked(result);
1843 if (srv.executor !=
nullptr)
1845 srv.executor->updateVelocityLimits(config.stack.generalConfig.maxVel);
1853 srv.debugObserverHelper->setDebugObserverDatafield(
"velocity_factor",
1854 velocityFactor.load());
1857 if (srv.executor !=
nullptr)
1859 srv.executor->updateVelocityFactor(velocityFactor);
1874 srv.debugObserverHelper->setDebugObserverDatafield(
"monitor update [ms]",
1875 duration.toMilliSecondsDouble());
1880 Navigator::updateSafetyGuard()
1884 const core::Pose global_T_robot(srv.sceneProvider->scene().robot->getGlobalPose());
1885 const auto proj = globalPlan->currentGlobalSegment().getProjection(
1887 const Eigen::Vector3f global_V_movement = proj.wayPointAfter.waypoint.pose.translation() -
1888 proj.projection.waypoint.pose.translation();
1890 return config.stack.safetyGuard->computeSafetyLimits(global_V_movement.head<2>());
1896 const auto& cfg = config.general.blockedReplanning;
1898 if (not cfg.enabled)
1904 if (isPaused() or isStopped() or not globalPlan.has_value() or
1905 not goalReachedMonitor.has_value())
1907 blockedSince = std::nullopt;
1913 const bool blocked = (result.twistLimits.linear < cfg.linearLimit) or
1914 (result.twistLimits.angular < cfg.angularLimit);
1919 blockedSince = std::nullopt;
1924 if (not blockedSince.has_value())
1931 const armarx::Duration blockedFor = now - blockedSince.value();
1932 srv.debugObserverHelper->setDebugObserverDatafield(
"safety_guard.blocked_for [ms]",
1942 <<
"ms (safety guard limits near zero). Re-running the global planner.";
1945 blockedSince = std::nullopt;
1948 updateAbsolute({goalReachedMonitor->goal()});
1952 Navigator::hasSafetyGuard()
const
1954 return config.stack.safetyGuard !=
nullptr;
1958 Navigator::hasLocalPlanner() const noexcept
1960 return config.stack.localPlanner !=
nullptr;
1964 Navigator::updateScene(
const bool fullUpdate)
1971 Navigator::checkGlobalPathAlternatives()
1975 const auto replanAlternatives = [&]()
1977 lastAllAlternativesImpossible = std::nullopt;
1982 const auto previousGlobalPlan = globalPlan;
1984 if (setupTargetAlternatives())
1988 setupGlobalPlanSubvidision();
1992 ARMARX_INFO <<
"No possible alternatives, restoring previous global path.";
1993 globalPlan = std::move(previousGlobalPlan);
1998 startGlobalPathSegment(
false,
true);
2001 if (not verifyGlobalPathPossible(globalPlan->currentGlobalSegment()))
2005 if (lastAllAlternativesImpossible.has_value() and
2008 config.general.targetAlternativesFilterTimeSeconds)))
2011 <<
"Current globalPlan invalid, waiting for timeout before replanning.";
2015 ARMARX_INFO <<
"Global path no longer possible, trying other alternatives!";
2017 replanAlternatives();
2020 else if (lastAllAlternativesImpossible.has_value())
2025 <<
"Previously invalid global plan valid again -> replanning all alternatives";
2027 replanAlternatives();
2034 const auto& costmap = srv.sceneProvider->scene().staticScene->distanceToObstaclesCostmap;
2037 for (
const auto& pt : plan.points())
2039 const auto& pose = pt.waypoint.pose;
2040 const Eigen::Vector2f position =
conv::to2D(pose.translation());
2041 const auto vertex = costmap->toVertex(position);
2042 if (not costmap->isValid(vertex.index) or costmap->isInCollision(vertex.position))
2051 std::optional<local_planning::LocalPlannerResult>
2052 Navigator::updateLocalPlanner()
2055 const std::scoped_lock<std::mutex> lock{updateLocalPlannerMtx};
2063 const auto& globalTrajectory = globalPlan->currentGlobalSegment();
2064 ARMARX_VERBOSE << globalTrajectory.points().size() <<
" points in global plan";
2065 localPlan = config.stack.localPlanner->plan(globalTrajectory);
2067 if (localPlan.has_value())
2082 return std::nullopt;
2088 return std::nullopt;
2092 Navigator::updateExecutor(
const std::optional<local_planning::LocalPlannerResult>& localPlan)
2094 if (srv.executor ==
nullptr)
2100 if (isPaused() or isStopped())
2107 if (not localPlan.has_value())
2112 srv.executor->stop();
2133 srv.executor->execute(localPlan->trajectory,
true);
2139 if (srv.executor ==
nullptr)
2145 << globalTrajectory.points().size() <<
" points.";
2155 if (globalTrajectory.points().empty())
2159 srv.executor->stop();
2163 ARMARX_IMPORTANT <<
"Executing global plan with " << globalTrajectory.points().size()
2166 srv.executor->execute(globalTrajectory,
false);
2170 Navigator::updateIntrospector(
2171 const std::optional<local_planning::LocalPlannerResult>& localPlan)
2175 srv.introspector->onLocalPlannerResult(localPlan);
2179 Navigator::updateMonitor()
2183 if (not goalReachedMonitor.has_value())
2191 const auto status = goalReachedMonitor->status();
2192 armarx::DebugObserverHelper* debugObserver = srv.debugObserverHelper;
2194 if (debugObserver !=
nullptr)
2199 goalReachedMonitor->getConfig().posTh);
2201 goalReachedMonitor->getConfig().oriTh);
2204 if (not isPaused() and goalReachedMonitor->goalReached())
2207 ARMARX_INFO <<
"Current global path segment finished!";
2210 if (startGlobalPathSegment(
true,
true))
2213 ARMARX_INFO <<
"Goal " << goalReachedMonitor->goal().translation().head<2>()
2220 if (srv.executor !=
nullptr)
2222 srv.executor->dumpDiagnostics();
2229 {
core::Pose(srv.sceneProvider->scene().robot->getGlobalPose())}});
2231 srv.introspector->success();
2237 Navigator::stopAllThreads()
2239 const std::scoped_lock<std::mutex> lock{runningTaskMtx};
2243 runningTask->stop();
2293 <<
"Scaling factor for velocity may not be negative, but is " << velocityFactor;
2295 <<
"Scaling factor for velocity may not be > 1, but is " << velocityFactor;
2297 this->velocityFactor = velocityFactor;
2305 executorEnabled.store(
false);
2307 if (srv.executor !=
nullptr)
2310 srv.executor->stop();
2319 executorEnabled.store(
true);
2321 if (srv.executor !=
nullptr)
2323 if (hasLocalPlanner())
2344 goalReachedMonitor.reset();
2345 goalReachedMonitor = std::nullopt;
2351 return not executorEnabled.load();
2357 return (not shouldRun) or (not runningTask) or (not runningTask->isRunning());
2361 config{std::move(other.config)},
2362 srv{std::move(other.srv)},
2363 parametrization{std::move(other.parametrization)},
2364 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.
Time-optimal reparametrization under per-motor torque and command-ramp limits.
static bool available()
Whether the python side was found at configure time. False means apply() throws.
static std::string unavailableReason()
Why it is unavailable, as recorded at configure time. Empty when available.
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
Toppra::DriveParams LoadDriveParams(const std::filesystem::path &configFile)
Read Toppra::DriveParams from a PlatformDynamics<Robot>.json.
std::shared_ptr< TrajectoryParametrization > TrajectoryParametrizationPtr
std::filesystem::path DriveParamsPath(const std::string &robot)
Resolve config/platform/PlatformDynamics<robot>.json inside the armarx_navigation package.
This file is part of ArmarX.
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
TrajectoryParametrization
How the velocities along a planned path are assigned.
@ Ramping
Ramp down at the start, the goal and every corner. The stack's historical behaviour.
@ Toppra
Time-optimal under per-motor torque and command-ramp limits.
std::string ToString(const TrajectoryParametrization parametrization)
The lowercase name used in configs, logs and the analysis application's results.
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.
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)
void writeParametrizationDump(const ParametrizationRecord &record, const std::string &path)
Write record as JSON, for offline rendering with plot-navigation-request.
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
TrajectoryParametrization parametrization
How the planned velocity profile is (re-)assigned before execution.
Event describing that the global trajectory was updated.
Event describing that the targeted goal was successfully reached.
Event describing that the local trajectory was updated.
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
bool platformDynamicsEnabled