14 #include <boost/graph/detail/adjacency_list.hpp>
15 #include <boost/graph/dijkstra_shortest_paths.hpp>
16 #include <boost/graph/named_function_params.hpp>
17 #include <boost/graph/properties.hpp>
18 #include <boost/property_map/property_map.hpp>
20 #include <Eigen/Geometry>
22 #include <SimoxUtility/algorithm/string/string_tools.h>
23 #include <VirtualRobot/Robot.h>
50 #include <SemanticObjectRelations/Shapes/Shape.h>
51 #include <range/v3/range/conversion.hpp>
52 #include <range/v3/view/reverse.hpp>
66 config{config}, srv{services}
84 Navigator::setGraphEdgeCosts(
core::Graph& graph)
const
91 return diff.translation().norm();
95 for (
auto edge : graph.edges())
97 const core::Pose start = resolveGraphVertex(edge.source());
98 const core::Pose goal = resolveGraphVertex(edge.target());
100 switch (edge.attrib().strategy)
104 ARMARX_VERBOSE <<
"Global planning from " << start.translation() <<
" to "
105 << goal.translation();
111 if (globalPlan.has_value())
115 edge.attrib().trajectory = globalPlan->trajectory;
117 << globalPlan->trajectory.length();
118 edge.attrib().cost() = globalPlan->trajectory.length();
138 edge.attrib().cost() = cost(start, goal);
153 std::vector<core::Pose> globalWaypoints;
154 switch (navigationFrame)
157 globalWaypoints = waypoints;
160 globalWaypoints.reserve(waypoints.size());
165 ARMARX_VERBOSE <<
"Initial robot pose: " << global_T_robot.matrix();
169 std::back_inserter(globalWaypoints),
170 [&](
const core::Pose& p) {
return global_T_robot * p; });
175 moveToAbsolute(globalWaypoints);
184 std::vector<core::Pose> globalWaypoints;
185 switch (navigationFrame)
188 globalWaypoints = waypoints;
191 globalWaypoints.reserve(waypoints.size());
193 std::begin(waypoints),
195 std::back_inserter(globalWaypoints),
202 updateAbsolute(globalWaypoints);
209 const core::Graph::ConstVertex& startVertex,
210 const core::Graph::ConstVertex& goalVertex)
212 ARMARX_VERBOSE <<
"Graph consists of " << graph.numVertices() <<
" vertices and "
213 << graph.numEdges() <<
" edges.";
215 std::vector<core::Graph::VertexDescriptor> predecessors(graph.numVertices());
216 std::vector<int> d(num_vertices(graph));
222 core::Graph::VertexDescriptor start = startVertex.descriptor();
224 auto predecessorMap = boost::make_iterator_property_map(
225 predecessors.begin(), boost::get(boost::vertex_index, graph));
227 auto params = boost::predecessor_map(predecessorMap)
228 .distance_map(boost::make_iterator_property_map(
229 d.begin(), boost::get(boost::vertex_index, graph)))
230 .weight_map(weightMap);
237 for (
const auto& edge : graph.edges())
239 ARMARX_VERBOSE << edge.sourceObjectID() <<
" -> " << edge.targetObjectID() <<
": "
240 << edge.attrib().m_value;
243 std::vector<core::Graph::EdgeDescriptor> edgesToBeRemoved;
244 for (
const auto edge : graph.edges())
248 edgesToBeRemoved.push_back(edge.descriptor());
252 for (
const auto edge : edgesToBeRemoved)
254 boost::remove_edge(edge, graph);
258 for (
const auto& edge : graph.edges())
260 ARMARX_VERBOSE << edge.sourceObjectID() <<
" -> " << edge.targetObjectID() <<
": "
261 << edge.attrib().m_value;
262 ARMARX_VERBOSE <<
"Edge: " << edge <<
"cost: " << edge.attrib().cost()
263 <<
", type: " <<
static_cast<int>(edge.attrib().strategy)
264 <<
" , has traj " << edge.attrib().trajectory.has_value();
268 << graph.vertex(start).objectID() <<
"` to vertex `"
269 << graph.vertex(goalVertex.descriptor()).objectID() <<
"`";
271 boost::dijkstra_shortest_paths(graph, start, params);
279 core::Graph::VertexDescriptor currentVertex = goalVertex.descriptor();
280 while (currentVertex != startVertex.descriptor())
282 shortestPath.push_back(graph.vertex(currentVertex).objectID());
286 auto parent = predecessorMap[currentVertex];
292 auto outEdges = graph.vertex(parent).outEdges();
297 <<
"Cannot reach another vertex from vertex `"
298 << graph.vertex(parent).objectID();
300 auto edgeIt = std::find_if(outEdges.begin(),
302 [¤tVertex](
const auto& edge) ->
bool
303 { return edge.target().descriptor() == currentVertex; });
308 currentVertex = parent;
311 shortestPath.push_back(startVertex.objectID());
330 <<
"At least start and goal vertices must be available";
334 std::vector<core::GlobalTrajectoryPoint> trajectoryPoints;
341 ARMARX_VERBOSE <<
"Shortest path with " << shortestPath.size() <<
" vertices";
344 for (
size_t i = 0; i < shortestPath.size() - 1; i++)
350 const core::Graph::ConstEdge edge =
351 graph.edge(graph.vertex(shortestPath.at(i)), graph.vertex(shortestPath.at(i + 1)));
355 ARMARX_VERBOSE << static_cast<int>(edge.attrib().strategy);
357 switch (edge.attrib().strategy)
367 ARMARX_INFO <<
"Length: " << edge.attrib().trajectory->length();
372 << edge.attrib().trajectory->points().size() <<
" waypoints";
377 const std::vector<core::GlobalTrajectoryPoint> edgeTrajectoryPoints =
378 edge.attrib().trajectory->points();
381 const int offset = trajectoryPoints.empty() ? 0 : 1;
383 if (edgeTrajectoryPoints.size() > 2)
387 trajectoryPoints.insert(trajectoryPoints.end(),
388 edgeTrajectoryPoints.begin() + offset,
389 edgeTrajectoryPoints.end());
414 const float point2pointVelocity = 400;
416 const core::GlobalTrajectoryPoint currentTrajPt = {
417 .waypoint = {.pose = resolveGraphVertex(graph.vertex(shortestPath.at(i)))},
418 .velocity = point2pointVelocity};
420 const core::GlobalTrajectoryPoint nextTrajPt{
422 resolveGraphVertex(graph.vertex(shortestPath.at(i + 1)))},
446 trajectoryPoints.push_back(currentTrajPt);
447 trajectoryPoints.push_back(nextTrajPt);
458 ARMARX_INFO <<
"Trajectory consists of " << trajectoryPoints.size() <<
" points";
460 for (
const auto& pt : trajectoryPoints)
465 return {trajectoryPoints};
469 Navigator::convertToGraph(
const std::vector<client::WaypointTarget>& targets)
const
472 GraphBuilder graphBuilder;
476 for (
const auto&
target : targets)
513 if (
target.pose.has_value())
515 graphBuilder.connect(
target.pose.value(),
target.strategy);
522 if (not
target.locationId->empty())
530 << vertex.attrib().getPose();
535 ARMARX_INFO <<
"Found " << routes.size() <<
" routes to location `"
536 <<
target.locationId.value();
539 <<
"` is not a reachable vertex on the graph!";
542 graphBuilder.connect(routes,
548 ARMARX_ERROR <<
"Either `location_id` or `pose` has to be provided!";
551 const auto goalVertex = graphBuilder.getGraph().vertex(graphBuilder.goalVertex());
552 ARMARX_INFO <<
"Goal vertex is `" << goalVertex.attrib().getName() <<
"`";
558 Navigator::resolveGraphVertex(
const core::Graph::ConstVertex& vertex)
const
562 vertex.attrib().getPose());
564 <<
"The location of vertex " << vertex.attrib().getName() <<
" couldn't be resolved ("
565 << goal.errorMsg <<
")";
566 return goal.pose.value();
574 <<
"only absolute movement implemented atm.";
586 auto graphBuilder = convertToGraph(targets);
591 auto startVertex = graphBuilder.startVertex;
592 auto goalVertex = graphBuilder.getGraph().vertex(graphBuilder.goalVertex());
594 ARMARX_INFO <<
"Goal pose according to graph is " << graphBuilder.goalPose().matrix();
600 goalReachedMonitor = std::nullopt;
604 if (goalReachedMonitor->goalReached(
false))
607 << goalReachedMonitor->goal().translation().head<2>()
608 <<
". Robot won't move.";
618 setGraphEdgeCosts(graph);
625 const auto shortestPath =
findShortestPath(graph, startVertex.value(), goalVertex);
630 std::vector<core::Pose> vertexPoses;
633 ARMARX_INFO <<
"Navigating along the following nodes:";
634 for (
const semrel::ShapeID& vertex : shortestPath)
636 ARMARX_INFO <<
" - " << graph.vertex(vertex).attrib().getName();
637 vertexPoses.push_back(resolveGraphVertex(graph.vertex(vertex)));
668 ARMARX_INFO <<
"Global planning completed. Will now start all required threads";
675 Navigator::startStack()
690 runningTask->start();
695 if (hasLocalPlanner())
718 Navigator::moveToAbsolute(
const std::vector<core::Pose>& waypoints)
736 <<
" to " << waypoints.back().matrix();
739 goalReachedMonitor = std::nullopt;
740 goalReachedMonitor = GoalReachedMonitor(
743 if (goalReachedMonitor->goalReached(
false))
745 ARMARX_INFO <<
"Already at goal position. Robot won't move.";
765 if (not globalPlan.has_value())
780 if (not hasLocalPlanner())
782 updateExecutor(globalPlan.value());
786 const auto localPlannerResult = updateLocalPlanner();
787 updateExecutor(localPlannerResult);
788 updateIntrospector(localPlannerResult);
791 ARMARX_INFO <<
"Global planning completed. Will now start all required threads";
800 Navigator::updateAbsolute(
const std::vector<core::Pose>& waypoints)
817 if (not globalPlan.has_value())
833 ARMARX_INFO <<
"Global planning completed. Will now start all required threads";
855 ARMARX_CHECK(locations.count(location) > 0) <<
"Unknown location `" << location <<
"`.";
856 return locations.at(location);
861 <<
"The given location does not match the format <location>(:<instance-id>)? '"
864 std::string instanceID;
865 if (
split.size() == 2)
868 instanceID =
split[1];
875 if (goal.pose.has_value())
877 moveToAbsolute({goal.pose.value()});
897 const std::scoped_lock<std::mutex> lock{runMtx};
995 if (hasLocalPlanner())
997 const auto localPlannerResult = updateLocalPlanner();
998 updateExecutor(localPlannerResult);
999 updateIntrospector(localPlannerResult);
1001 if (srv.
executor !=
nullptr && localPlannerResult.has_value())
1014 <<
"Local planner update: " << duration.toMilliSecondsDouble() <<
"ms.";
1017 duration.toMilliSecondsDouble());
1022 if (hasSafetyGuard())
1025 const auto result = updateSafetyGuard();
1027 result.twistLimits.linear);
1029 result.twistLimits.angular);
1046 <<
"Monitor update: " << duration.toMilliSecondsDouble() <<
"ms.";
1049 duration.toMilliSecondsDouble());
1053 safety_guard::SafetyGuardResult
1054 Navigator::updateSafetyGuard()
1062 Navigator::hasSafetyGuard()
const
1068 Navigator::hasLocalPlanner() const noexcept
1074 Navigator::updateScene(
const bool fullUpdate)
1080 std::optional<local_planning::LocalPlannerResult>
1081 Navigator::updateLocalPlanner()
1084 const std::scoped_lock<std::mutex> lock{updateLocalPlannerMtx};
1092 ARMARX_VERBOSE << globalPlan->trajectory.points().size() <<
" points in global plan";
1095 if (localPlan.has_value())
1110 return std::nullopt;
1116 return std::nullopt;
1120 Navigator::updateExecutor(
const std::optional<local_planning::LocalPlannerResult>& localPlan)
1135 if (not localPlan.has_value())
1165 Navigator::updateExecutor(
const std::optional<global_planning::GlobalPlannerResult>& globalPlan)
1173 << globalPlan->trajectory.points().size() <<
" points.";
1183 if (not globalPlan.has_value())
1191 ARMARX_IMPORTANT <<
"Executing global plan with " << globalPlan->trajectory.points().size()
1198 Navigator::updateIntrospector(
1199 const std::optional<local_planning::LocalPlannerResult>& localPlan)
1207 Navigator::updateMonitor()
1212 if (not
isPaused() and goalReachedMonitor->goalReached())
1216 ARMARX_INFO <<
"Goal " << goalReachedMonitor->goal().translation().head<2>()
1238 Navigator::stopAllThreads()
1241 runningTask =
nullptr;
1289 executorEnabled.store(
false);
1303 executorEnabled.store(
true);
1307 if (hasLocalPlanner())
1328 goalReachedMonitor.reset();
1329 goalReachedMonitor = std::nullopt;
1335 return not executorEnabled.load();
1341 return (not runningTask) or (not runningTask->isRunning());
1345 config{std::move(other.config)},
1346 srv{std::move(other.srv)},
1347 executorEnabled{other.executorEnabled.load()}