Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
route_planner.cpp
1 // Copyright (c) 2025, Open Navigation LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <string>
16 #include <limits>
17 #include <memory>
18 #include <vector>
19 #include <mutex>
20 #include <algorithm>
21 
22 #include "nav2_route/route_planner.hpp"
23 #include "nav2_ros_common/tf2_factories.hpp"
24 
25 namespace nav2_route
26 {
27 
29  nav2::LifecycleNode::SharedPtr node,
30  const nav2::TransformBuffer::SharedPtr tf_buffer,
31  const std::shared_ptr<nav2_costmap_2d::CostmapSubscriber> costmap_subscriber)
32 {
33  max_iterations_ = node->declare_or_get_parameter("max_iterations", 0);
34 
35  if (max_iterations_ <= 0) {
36  max_iterations_ = std::numeric_limits<int>::max();
37  }
38 
39  edge_scorer_ = std::make_unique<EdgeScorer>(node, tf_buffer, costmap_subscriber);
40 }
41 
43  Graph & graph, unsigned int start_index, unsigned int goal_index,
44  const std::vector<unsigned int> & blocked_ids,
45  const RouteRequest & route_request)
46 {
47  if (graph.empty()) {
48  throw nav2_core::NoValidGraph("Graph is invalid for routing!");
49  }
50 
51  // Find the start and goal pointers, it is important in this function
52  // that the start node is the underlying pointer, so that the address
53  // is valid when this function goes out of scope
54  const NodePtr & start_node = &graph.at(start_index);
55  const NodePtr & goal_node = &graph.at(goal_index);
56  findShortestGraphTraversal(graph, start_node, goal_node, blocked_ids, route_request);
57 
58  EdgePtr & parent_edge = goal_node->search_state.parent_edge;
59  if (!parent_edge) {
60  throw nav2_core::NoValidRouteCouldBeFound("Could not find a route to the requested goal!");
61  }
62 
63  // Convert graph traversal into a meaningful route
64  Route route;
65  while (parent_edge) {
66  route.edges.push_back(parent_edge);
67  parent_edge = parent_edge->start->search_state.parent_edge;
68  }
69 
70  std::reverse(route.edges.begin(), route.edges.end());
71  route.start_node = start_node;
72  route.route_cost = goal_node->search_state.integrated_cost;
73  return route;
74 }
75 
77 {
78  // For graphs < 75,000 nodes, iterating through one time on initialization to reset the state
79  // is neglibably different to allocating & deallocating the complimentary blocks of memory
80  for (unsigned int i = 0; i != graph.size(); i++) {
81  graph[i].search_state.reset();
82  }
83 }
84 
86  Graph & graph, const NodePtr start_node, const NodePtr goal_node,
87  const std::vector<unsigned int> & blocked_ids,
88  const RouteRequest & route_request)
89 {
90  // Setup the Dijkstra's search
91  resetSearchStates(graph);
92  start_id_ = start_node->nodeid;
93  goal_id_ = goal_node->nodeid;
94  start_node->search_state.integrated_cost = 0.0;
95  addNode(0.0, start_node);
96 
97  NodePtr neighbor{nullptr};
98  EdgePtr edge{nullptr};
99  float potential_cost = 0.0, traversal_cost = 0.0;
100  int iterations = 0;
101  while (!queue_.empty() && iterations < max_iterations_) {
102  iterations++;
103 
104  // Get the next lowest cost node
105  auto [curr_cost, node] = getNextNode();
106 
107  // This has been visited, thus already lowest cost
108  if (curr_cost != node->search_state.integrated_cost) {
109  continue;
110  }
111 
112  // We have the shortest path
113  if (isGoal(node)) {
114  // Reset states
115  clearQueue();
116  return;
117  }
118 
119  // Expand to connected nodes
120  EdgeVector & edges = getEdges(node);
121  for (unsigned int edge_num = 0; edge_num != edges.size(); edge_num++) {
122  edge = &edges[edge_num];
123  neighbor = edge->end;
124 
125  // If edge is invalid (lane closed, occupied, etc), don't expand
126  if (!getTraversalCost(edge, traversal_cost, blocked_ids, route_request)) {
127  continue;
128  }
129 
130  potential_cost = curr_cost + traversal_cost;
131  if (potential_cost < neighbor->search_state.integrated_cost) {
132  neighbor->search_state.parent_edge = edge;
133  neighbor->search_state.integrated_cost = potential_cost;
134  neighbor->search_state.traversal_cost = traversal_cost;
135  addNode(potential_cost, neighbor);
136  }
137  }
138  }
139 
140  if (iterations == max_iterations_) {
141  // Reset states
142  clearQueue();
143  throw nav2_core::TimedOut("Maximum iterations was exceeded!");
144  }
145 }
146 
148  const EdgePtr edge, float & score, const std::vector<unsigned int> & blocked_ids,
149  const RouteRequest & route_request)
150 {
151  // If edge or node is in the blocked list, don't expand
152  auto is_blocked = std::find_if(
153  blocked_ids.begin(), blocked_ids.end(),
154  [&](unsigned int id) {return id == edge->edgeid || id == edge->end->nodeid;});
155  if (is_blocked != blocked_ids.end()) {
156  return false;
157  }
158 
159  // If an edge's cost is marked as not to be overridden by scoring plugins
160  // Or there are no scoring plugins, use the edge's cost, if it is valid (positive)
161  if (!edge->edge_cost.overridable || edge_scorer_->numPlugins() == 0) {
162  if (edge->edge_cost.cost <= 0.0) {
164  "Edge " + std::to_string(edge->edgeid) +
165  " doesn't contain and cannot compute a valid edge cost!");
166  }
167  score = edge->edge_cost.cost;
168  return true;
169  }
170 
171  return edge_scorer_->score(edge, route_request, classifyEdge(edge), score);
172 }
173 
175 {
176  NodeElement data = queue_.top();
177  queue_.pop();
178  return data;
179 }
180 
181 void RoutePlanner::addNode(const float cost, const NodePtr node)
182 {
183  queue_.emplace(cost, node);
184 }
185 
186 EdgeVector & RoutePlanner::getEdges(const NodePtr node)
187 {
188  return node->neighbors;
189 }
190 
192 {
193  NodeQueue q;
194  std::swap(queue_, q);
195 }
196 
198 {
199  return node->nodeid == goal_id_;
200 }
201 
203 {
204  return node->nodeid == start_id_;
205 }
206 
207 nav2_route::EdgeType RoutePlanner::classifyEdge(const EdgePtr edge)
208 {
209  if (isStart(edge->start)) {
210  return EdgeType::START;
211  } else if (isGoal(edge->end)) {
212  return EdgeType::END;
213  }
214  return nav2_route::EdgeType::NONE;
215 }
216 
217 } // namespace nav2_route
void findShortestGraphTraversal(Graph &graph, const NodePtr start_node, const NodePtr goal_node, const std::vector< unsigned int > &blocked_ids, const RouteRequest &route_request)
Dikstra's algorithm search on the graph.
void configure(nav2::LifecycleNode::SharedPtr node, const nav2::TransformBuffer::SharedPtr tf_buffer, const std::shared_ptr< nav2_costmap_2d::CostmapSubscriber > costmap_subscriber)
Configure the route planner, get parameters.
nav2_route::EdgeType classifyEdge(const EdgePtr edge)
Checks edge is a start or end edge.
bool getTraversalCost(const EdgePtr edge, float &score, const std::vector< unsigned int > &blocked_ids, const RouteRequest &route_request)
Gets the traversal cost for an edge using edge scorers.
bool isGoal(const NodePtr node)
Checks if a given node is the goal node.
NodeElement getNextNode()
Gets the next node in the priority queue for search.
virtual Route findRoute(Graph &graph, unsigned int start_index, unsigned int goal_index, const std::vector< unsigned int > &blocked_ids, const RouteRequest &route_request)
Find the route from start to goal on the graph.
EdgeVector & getEdges(const NodePtr node)
Gets the edges from a given node.
void addNode(const float cost, const NodePtr node)
Adds a node to the priority queue for search.
void resetSearchStates(Graph &graph)
Reset the search state of the graph nodes.
bool isStart(const NodePtr node)
Checks if a given node is the start node.
void clearQueue()
Clears the priority queue.
An object representing edges between nodes.
Definition: types.hpp:134
An object to store the nodes in the graph file.
Definition: types.hpp:183
An object to store salient features of the route request including its start and goal node ids,...
Definition: types.hpp:224
An ordered set of nodes and edges corresponding to the planned route.
Definition: types.hpp:211