10 #include <boost/graph/dijkstra_shortest_paths.hpp>
11 #include <boost/graph/named_function_params.hpp>
12 #include <boost/graph/properties.hpp>
13 #include <boost/graph/subgraph.hpp>
15 #include <Eigen/Geometry>
17 #include <SimoxUtility/math/convert/mat3f_to_rpy.h>
18 #include <VirtualRobot/Robot.h>
47 #include "range/v3/algorithm/reverse.hpp"
48 #include "range/v3/range/conversion.hpp"
49 #include <SemanticObjectRelations/Shapes/Shape.h>
50 #include <range/v3/view.hpp>
51 #include <range/v3/view/reverse.hpp>
65 config{config}, srv{services}
81 Navigator::setGraphEdgeCosts(
core::Graph& graph)
const
88 return diff.translation().norm();
92 for (
auto edge : graph.edges())
94 const core::Pose start = resolveGraphVertex(edge.source());
95 const core::Pose goal = resolveGraphVertex(edge.target());
97 switch (edge.attrib().strategy)
101 ARMARX_VERBOSE <<
"Global planning from " << start.translation() <<
" to "
102 << goal.translation();
108 if (globalPlan.has_value())
112 edge.attrib().trajectory = globalPlan->trajectory;
114 << globalPlan->trajectory.length();
115 edge.attrib().cost() = globalPlan->trajectory.length();
135 edge.attrib().cost() = cost(start, goal);
150 std::vector<core::Pose> globalWaypoints;
151 switch (navigationFrame)
154 globalWaypoints = waypoints;
157 globalWaypoints.reserve(waypoints.size());
162 ARMARX_VERBOSE <<
"Initial robot pose: " << global_T_robot.matrix();
166 std::back_inserter(globalWaypoints),
167 [&](
const core::Pose& p) {
return global_T_robot * p; });
172 moveToAbsolute(globalWaypoints);
181 std::vector<core::Pose> globalWaypoints;
182 switch (navigationFrame)
185 globalWaypoints = waypoints;
188 globalWaypoints.reserve(waypoints.size());
190 std::begin(waypoints),
192 std::back_inserter(globalWaypoints),
199 updateAbsolute(globalWaypoints);
206 const core::Graph::ConstVertex& startVertex,
207 const core::Graph::ConstVertex& goalVertex)
209 ARMARX_VERBOSE <<
"Graph consists of " << graph.numVertices() <<
" vertices and "
210 << graph.numEdges() <<
" edges.";
212 std::vector<core::Graph::VertexDescriptor> predecessors(graph.numVertices());
213 std::vector<int> d(num_vertices(graph));
219 core::Graph::VertexDescriptor start = startVertex.descriptor();
221 auto predecessorMap = boost::make_iterator_property_map(
222 predecessors.begin(), boost::get(boost::vertex_index, graph));
224 auto params = boost::predecessor_map(predecessorMap)
225 .distance_map(boost::make_iterator_property_map(
226 d.begin(), boost::get(boost::vertex_index, graph)))
227 .weight_map(weightMap);
234 for (
const auto& edge : graph.edges())
236 ARMARX_VERBOSE << edge.sourceObjectID() <<
" -> " << edge.targetObjectID() <<
": "
237 << edge.attrib().m_value;
240 std::vector<core::Graph::EdgeDescriptor> edgesToBeRemoved;
241 for (
const auto edge : graph.edges())
245 edgesToBeRemoved.push_back(edge.descriptor());
249 for (
const auto edge : edgesToBeRemoved)
251 boost::remove_edge(edge, graph);
255 for (
const auto& edge : graph.edges())
257 ARMARX_VERBOSE << edge.sourceObjectID() <<
" -> " << edge.targetObjectID() <<
": "
258 << edge.attrib().m_value;
259 ARMARX_VERBOSE <<
"Edge: " << edge <<
"cost: " << edge.attrib().cost()
260 <<
", type: " <<
static_cast<int>(edge.attrib().strategy)
261 <<
" , has traj " << edge.attrib().trajectory.has_value();
265 << graph.vertex(start).objectID() <<
"` to vertex `"
266 << graph.vertex(goalVertex.descriptor()).objectID() <<
"`";
268 boost::dijkstra_shortest_paths(graph, start, params);
276 core::Graph::VertexDescriptor currentVertex = goalVertex.descriptor();
277 while (currentVertex != startVertex.descriptor())
279 shortestPath.push_back(graph.vertex(currentVertex).objectID());
283 auto parent = predecessorMap[currentVertex];
289 auto outEdges = graph.vertex(parent).outEdges();
294 <<
"Cannot reach another vertex from vertex `"
295 << graph.vertex(parent).objectID();
297 auto edgeIt = std::find_if(outEdges.begin(),
299 [¤tVertex](
const auto& edge) ->
bool
300 { return edge.target().descriptor() == currentVertex; });
305 currentVertex = parent;
308 shortestPath.push_back(startVertex.objectID());
327 <<
"At least start and goal vertices must be available";
331 std::vector<core::GlobalTrajectoryPoint> trajectoryPoints;
338 ARMARX_VERBOSE <<
"Shortest path with " << shortestPath.size() <<
" vertices";
341 for (
size_t i = 0; i < shortestPath.size() - 1; i++)
347 const core::Graph::ConstEdge edge =
348 graph.edge(graph.vertex(shortestPath.at(i)), graph.vertex(shortestPath.at(i + 1)));
352 ARMARX_VERBOSE << static_cast<int>(edge.attrib().strategy);
354 switch (edge.attrib().strategy)
364 ARMARX_INFO <<
"Length: " << edge.attrib().trajectory->length();
369 << edge.attrib().trajectory->points().size() <<
" waypoints";
374 const std::vector<core::GlobalTrajectoryPoint> edgeTrajectoryPoints =
375 edge.attrib().trajectory->points();
378 const int offset = trajectoryPoints.empty() ? 0 : 1;
380 if (edgeTrajectoryPoints.size() > 2)
384 trajectoryPoints.insert(trajectoryPoints.end(),
385 edgeTrajectoryPoints.begin() + offset,
386 edgeTrajectoryPoints.end());
411 const float point2pointVelocity = 400;
413 const core::GlobalTrajectoryPoint currentTrajPt = {
414 .waypoint = {.pose = resolveGraphVertex(graph.vertex(shortestPath.at(i)))},
415 .velocity = point2pointVelocity};
417 const core::GlobalTrajectoryPoint nextTrajPt{
419 resolveGraphVertex(graph.vertex(shortestPath.at(i + 1)))},
443 trajectoryPoints.push_back(currentTrajPt);
444 trajectoryPoints.push_back(nextTrajPt);
455 ARMARX_INFO <<
"Trajectory consists of " << trajectoryPoints.size() <<
" points";
457 for (
const auto& pt : trajectoryPoints)
462 return {trajectoryPoints};
466 Navigator::convertToGraph(
const std::vector<client::WaypointTarget>& targets)
const
469 GraphBuilder graphBuilder;
473 for (
const auto&
target : targets)
510 if (
target.pose.has_value())
512 graphBuilder.connect(
target.pose.value(),
target.strategy);
519 if (not
target.locationId->empty())
527 << vertex.attrib().getPose();
532 ARMARX_INFO <<
"Found " << routes.size() <<
" routes to location `"
533 <<
target.locationId.value();
536 <<
"` is not a reachable vertex on the graph!";
539 graphBuilder.connect(routes,
545 ARMARX_ERROR <<
"Either `location_id` or `pose` has to be provided!";
548 const auto goalVertex = graphBuilder.getGraph().vertex(graphBuilder.goalVertex());
549 ARMARX_INFO <<
"Goal vertex is `" << goalVertex.attrib().getName() <<
"`";
555 Navigator::resolveGraphVertex(
const core::Graph::ConstVertex& vertex)
const
559 vertex.attrib().getPose());
561 <<
"The location of vertex " << vertex.attrib().getName() <<
" couldn't be resolved ("
562 << goal.errorMsg <<
")";
563 return goal.pose.value();
571 <<
"only absolute movement implemented atm.";
583 auto graphBuilder = convertToGraph(targets);
588 auto startVertex = graphBuilder.startVertex;
589 auto goalVertex = graphBuilder.getGraph().vertex(graphBuilder.goalVertex());
591 ARMARX_INFO <<
"Goal pose according to graph is " << graphBuilder.goalPose().matrix();
597 goalReachedMonitor = std::nullopt;
601 if (goalReachedMonitor->goalReached(
false))
604 << goalReachedMonitor->goal().translation().head<2>()
605 <<
". Robot won't move.";
615 setGraphEdgeCosts(graph);
622 const auto shortestPath =
findShortestPath(graph, startVertex.value(), goalVertex);
627 std::vector<core::Pose> vertexPoses;
630 ARMARX_INFO <<
"Navigating along the following nodes:";
631 for (
const semrel::ShapeID& vertex : shortestPath)
633 ARMARX_INFO <<
" - " << graph.vertex(vertex).attrib().getName();
634 vertexPoses.push_back(resolveGraphVertex(graph.vertex(vertex)));
664 ARMARX_INFO <<
"Global planning completed. Will now start all required threads";
671 Navigator::startStack()
684 runningTask->start();
687 if (hasLocalPlanner())
703 Navigator::moveToAbsolute(
const std::vector<core::Pose>& waypoints)
721 <<
" to " << waypoints.back().matrix();
724 goalReachedMonitor = std::nullopt;
725 goalReachedMonitor = GoalReachedMonitor(
728 if (goalReachedMonitor->goalReached(
false))
730 ARMARX_INFO <<
"Already at goal position. Robot won't move.";
750 if (not globalPlan.has_value())
764 if (not hasLocalPlanner())
766 updateExecutor(globalPlan.value());
769 ARMARX_INFO <<
"Global planning completed. Will now start all required threads";
778 Navigator::updateAbsolute(
const std::vector<core::Pose>& waypoints)
795 if (not globalPlan.has_value())
810 ARMARX_INFO <<
"Global planning completed. Will now start all required threads";
832 ARMARX_CHECK(locations.count(location) > 0) <<
"Unknown location `" << location <<
"`.";
833 return locations.at(location);
838 <<
"The given location does not match the format <location>(:<instance-id>)? '"
841 std::string instanceID;
842 if (
split.size() == 2)
845 instanceID =
split[1];
852 if (goal.pose.has_value())
854 moveToAbsolute({goal.pose.value()});
896 std::lock_guard g{globalPlanningRequestMtx};
898 if (globalPlanningRequest.has_value())
900 const auto& waypoints = globalPlanningRequest.value();
905 goalReachedMonitor = std::nullopt;
906 goalReachedMonitor = GoalReachedMonitor(
909 if (goalReachedMonitor->goalReached(
false))
911 ARMARX_INFO <<
"Already at goal position. Robot won't move.";
924 ARMARX_INFO <<
"Update/Planning global trajectory";
934 if (not globalPlan.has_value())
948 if (not hasLocalPlanner())
950 updateExecutor(globalPlan.value());
963 if (hasLocalPlanner())
965 const auto localPlannerResult = updateLocalPlanner();
966 updateExecutor(localPlannerResult);
967 updateIntrospector(localPlannerResult);
982 <<
"Local planner update: " << duration.toMilliSecondsDouble() <<
"ms.";
985 duration.toMilliSecondsDouble());
996 <<
"Monitor update: " << duration.toMilliSecondsDouble() <<
"ms.";
999 duration.toMilliSecondsDouble());
1004 Navigator::updateScene(
const bool fullUpdate)
1010 std::optional<local_planning::LocalPlannerResult>
1011 Navigator::updateLocalPlanner()
1020 if (localPlan.has_value())
1032 return std::nullopt;
1038 return std::nullopt;
1042 Navigator::updateExecutor(
const std::optional<local_planning::LocalPlannerResult>& localPlan)
1057 if (not localPlan.has_value())
1084 Navigator::updateExecutor(
const std::optional<global_planning::GlobalPlannerResult>& globalPlan)
1092 << globalPlan->trajectory.points().size() <<
" points.";
1102 if (not globalPlan.has_value())
1109 ARMARX_IMPORTANT <<
"Executing global plan with " << globalPlan->trajectory.points().size()
1115 Navigator::updateIntrospector(
1116 const std::optional<local_planning::LocalPlannerResult>& localPlan)
1124 Navigator::updateMonitor()
1129 if (not
isPaused() and goalReachedMonitor->goalReached())
1133 ARMARX_INFO <<
"Goal " << goalReachedMonitor->goal().translation().head<2>()
1148 goalReachedMonitor.reset();
1149 goalReachedMonitor = std::nullopt;
1154 Navigator::stopAllThreads()
1204 executorEnabled.store(
false);
1219 executorEnabled.store(
true);
1223 if (hasLocalPlanner())
1244 goalReachedMonitor.reset();
1245 goalReachedMonitor = std::nullopt;
1251 return not executorEnabled.load();
1257 return (not runningTask) or (not runningTask->isRunning());
1261 config{std::move(other.config)},
1262 srv{std::move(other.srv)},
1263 executorEnabled{other.executorEnabled.load()}