Nav2 Navigation Stack - kilted  kilted
ROS 2 Navigation Stack
goal_intent_search.hpp
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 #ifndef NAV2_ROUTE__GOAL_INTENT_SEARCH_HPP_
16 #define NAV2_ROUTE__GOAL_INTENT_SEARCH_HPP_
17 
18 #include <limits>
19 #include <string>
20 #include <vector>
21 #include <queue>
22 #include <unordered_set>
23 #include <memory>
24 
25 #include "std_msgs/msg/color_rgba.hpp"
26 #include "visualization_msgs/msg/marker_array.hpp"
27 #include "visualization_msgs/msg/marker.hpp"
28 #include "geometry_msgs/msg/vector3.hpp"
29 #include "nav2_util/geometry_utils.hpp"
30 #include "nav2_msgs/msg/route.hpp"
31 #include "nav2_route/types.hpp"
32 #include "nav2_costmap_2d/costmap_2d.hpp"
33 #include "nav2_costmap_2d/cost_values.hpp"
34 #include "nav2_util/line_iterator.hpp"
35 
36 namespace nav2_route
37 {
38 
39 namespace GoalIntentSearch
40 {
41 
46 {
47 public:
52  explicit BreadthFirstSearch(std::shared_ptr<nav2_costmap_2d::Costmap2D> costmap)
53  : costmap_(costmap)
54  {
55  }
56 
64  bool search(
65  const geometry_msgs::msg::PoseStamped & reference_node,
66  const std::vector<geometry_msgs::msg::PoseStamped> & candidate_nodes,
67  const int max_iterations = std::numeric_limits<int>::max())
68  {
69  closest_node_idx_ = 0u;
70 
71  // Convert target to costmap space
72  unsigned int goal_x, goal_y;
73  if (!costmap_->worldToMap(reference_node.pose.position.x, reference_node.pose.position.y,
74  goal_x, goal_y))
75  {
76  return false;
77  }
78  unsigned int goal_id = costmap_->getIndex(goal_x, goal_y);
79 
80  // Convert candidates to costmap space
81  std::vector<unsigned int> candidate_ids;
82  candidate_ids.reserve(candidate_nodes.size());
83  for (const auto & node : candidate_nodes) {
84  unsigned int node_x, node_y;
85  if (!costmap_->worldToMap(node.pose.position.x, node.pose.position.y, node_x, node_y)) {
86  // Off the costmap, consider this candidate invalid
87  continue;
88  }
89  candidate_ids.push_back(costmap_->getIndex(node_x, node_y));
90  }
91 
92  if (candidate_ids.empty()) {
93  return false;
94  }
95 
96  // Main Breadth-First Search
97  std::queue<unsigned int> search_queue;
98  std::unordered_set<unsigned int> visited;
99  visited.insert(goal_id);
100  search_queue.push(goal_id);
101  int iterations = 0;
102  while (!search_queue.empty() && iterations < max_iterations) {
103  unsigned int current_id = search_queue.front();
104  search_queue.pop();
105  iterations++;
106 
107  // Check if the current node is a candidate
108  auto iter = std::find(candidate_ids.begin(), candidate_ids.end(), current_id);
109  if (iter != candidate_ids.end()) {
110  closest_node_idx_ = std::distance(candidate_ids.begin(), iter);
111  return true;
112  }
113 
114  // Get neighbors of the current node
115  std::vector<unsigned int> neighbors = getNeighbors(current_id);
116 
117  // Add unvisited neighbors to the queue
118  for (const auto & neighbor : neighbors) {
119  if (visited.find(neighbor) == visited.end()) {
120  visited.insert(neighbor);
121  search_queue.push(neighbor);
122  }
123  }
124  }
125 
126  // No solution found with full expansion or iterations exceeded
127  return false;
128  }
129 
135  std::vector<unsigned int> getNeighbors(const unsigned int current_id)
136  {
137  int size_x = static_cast<int>(costmap_->getSizeInCellsX());
138  const std::vector<int> offsets = {
139  -1, 1, -size_x, size_x,
140  -size_x - 1, -size_x + 1,
141  size_x - 1, size_x + 1};
142  int costmap_size = static_cast<int>(costmap_->getSizeInCellsX() * costmap_->getSizeInCellsY());
143 
144  unsigned int p_mx, p_my;
145  costmap_->indexToCells(current_id, p_mx, p_my);
146 
147  std::vector<unsigned int> neighbors;
148  for (const auto & offset : offsets) {
149  // If out of bounds of costmap
150  int signed_neighbor_id = static_cast<int>(current_id) + offset;
151  if (signed_neighbor_id < 0 || signed_neighbor_id >= costmap_size) {
152  continue;
153  }
154  unsigned int neighbor_id = static_cast<unsigned int>(signed_neighbor_id);
155 
156  // If wrapping around the costmap
157  unsigned int n_mx, n_my;
158  costmap_->indexToCells(neighbor_id, n_mx, n_my);
159  if (std::fabs(static_cast<float>(p_mx) - static_cast<float>(n_mx)) > 1 ||
160  std::fabs(static_cast<float>(p_my) - static_cast<float>(n_my)) > 1)
161  {
162  continue;
163  }
164 
165  // Add if not in collision
166  if (!inCollision(neighbor_id)) {
167  neighbors.push_back(neighbor_id);
168  }
169  }
170 
171  return neighbors;
172  }
173 
179  bool inCollision(const unsigned int id)
180  {
181  // Check if the node is in collision
182  float cost = static_cast<float>(costmap_->getCost(id));
183  return cost >= 253.0f && cost != 255.0f;
184  }
185 
190  unsigned int getClosestNodeIdx()
191  {
192  return closest_node_idx_;
193  }
194 
198  ~BreadthFirstSearch() = default;
199 
200 protected:
201  std::shared_ptr<nav2_costmap_2d::Costmap2D> costmap_;
202  unsigned int closest_node_idx_{0u};
203 };
204 
209 {
210 public:
215  explicit LoSCollisionChecker(std::shared_ptr<nav2_costmap_2d::Costmap2D> costmap)
216  : costmap_(costmap)
217  {
218  }
219 
223  ~LoSCollisionChecker() = default;
224 
231  bool worldToMap(const geometry_msgs::msg::Point & start, const geometry_msgs::msg::Point & end)
232  {
233  if (!costmap_->worldToMap(start.x, start.y, x0_, y0_) ||
234  !costmap_->worldToMap(end.x, end.y, x1_, y1_))
235  {
236  return false;
237  }
238  return true;
239  }
240 
246  {
247  nav2_util::LineIterator iter(x0_, y0_, x1_, y1_);
248  for (; iter.isValid(); iter.advance()) {
249  float cost = static_cast<float>(costmap_->getCost(iter.getX(), iter.getY()));
250  if (cost >= 253.0f && cost != 255.0 /*unknown*/) {
251  return true;
252  }
253  }
254  return false;
255  }
256 
257 protected:
258  std::shared_ptr<nav2_costmap_2d::Costmap2D> costmap_;
259  unsigned int x0_, x1_, y0_, y1_;
260 };
261 
262 } // namespace GoalIntentSearch
263 
264 } // namespace nav2_route
265 
266 #endif // NAV2_ROUTE__GOAL_INTENT_SEARCH_HPP_
BreadthFirstSearch(std::shared_ptr< nav2_costmap_2d::Costmap2D > costmap)
Constructor.
bool search(const geometry_msgs::msg::PoseStamped &reference_node, const std::vector< geometry_msgs::msg::PoseStamped > &candidate_nodes, const int max_iterations=std::numeric_limits< int >::max())
Search for the closest node to the given reference node.
std::vector< unsigned int > getNeighbors(const unsigned int current_id)
Get the neighbors of a given node.
unsigned int getClosestNodeIdx()
Get the output closest node in candidate indices.
bool inCollision(const unsigned int id)
Check if a node is in collision with the costmap.
bool isInCollision()
Check if the line segment is in collision with the costmap.
bool worldToMap(const geometry_msgs::msg::Point &start, const geometry_msgs::msg::Point &end)
Find the line segment in cosmap frame.
LoSCollisionChecker(std::shared_ptr< nav2_costmap_2d::Costmap2D > costmap)
Constructor.
An iterator implementing Bresenham Ray-Tracing.
bool isValid() const
If the iterator is valid.
int getX() const
Get current X value.
void advance()
Advance iteration along the line.
int getY() const
Get current Y value.