Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
utils.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 #include <limits>
16 #include <string>
17 #include <vector>
18 #include <memory>
19 #include "std_msgs/msg/color_rgba.hpp"
20 #include "visualization_msgs/msg/marker_array.hpp"
21 #include "visualization_msgs/msg/marker.hpp"
22 #include "geometry_msgs/msg/vector3.hpp"
23 #include "nav2_util/geometry_utils.hpp"
24 #include "nav2_msgs/msg/route.hpp"
25 #include "nav2_route/types.hpp"
26 #include "nav2_costmap_2d/costmap_2d.hpp"
27 #include "nav2_util/line_iterator.hpp"
28 
29 #ifndef NAV2_ROUTE__UTILS_HPP_
30 #define NAV2_ROUTE__UTILS_HPP_
31 
32 namespace nav2_route
33 {
34 
35 namespace utils
36 {
37 
44 inline geometry_msgs::msg::PoseStamped toMsg(const float x, const float y)
45 {
46  geometry_msgs::msg::PoseStamped pose;
47  pose.pose.position.x = x;
48  pose.pose.position.y = y;
49  return pose;
50 }
51 
59 inline visualization_msgs::msg::MarkerArray::UniquePtr toMsg(
60  const nav2_route::Graph & graph, const std::string & frame, const rclcpp::Time & now)
61 {
62  auto msg = std::make_unique<visualization_msgs::msg::MarkerArray>();
63 
64  visualization_msgs::msg::Marker nodes_marker;
65  nodes_marker.header.frame_id = frame;
66  nodes_marker.header.stamp = now;
67  nodes_marker.action = 0;
68  nodes_marker.ns = "route_graph_nodes";
69  nodes_marker.type = visualization_msgs::msg::Marker::SPHERE_LIST;
70  nodes_marker.scale.x = 0.1;
71  nodes_marker.scale.y = 0.1;
72  nodes_marker.scale.z = 0.1;
73  nodes_marker.color.r = 1.0;
74  nodes_marker.color.a = 1.0;
75  nodes_marker.points.reserve(graph.size());
76 
77  visualization_msgs::msg::Marker edges_marker;
78  edges_marker.header.frame_id = frame;
79  edges_marker.header.stamp = now;
80  edges_marker.action = 0;
81  edges_marker.ns = "route_graph_edges";
82  edges_marker.type = visualization_msgs::msg::Marker::LINE_LIST;
83  edges_marker.scale.x = 0.05; // Line width
84  edges_marker.color.g = 1.0;
85  edges_marker.color.a = 0.5; // Semi-transparent green so bidirectional connections stand out
86  constexpr size_t points_per_edge = 2;
87  // This probably under-reserves but saves some initial reallocations
88  constexpr size_t likely_min_edges_per_node = 2;
89  edges_marker.points.reserve(graph.size() * points_per_edge * likely_min_edges_per_node);
90 
91  geometry_msgs::msg::Point node_pos;
92  geometry_msgs::msg::Point edge_start;
93  geometry_msgs::msg::Point edge_end;
94 
95  visualization_msgs::msg::Marker node_id_marker;
96  node_id_marker.header.frame_id = frame;
97  node_id_marker.header.stamp = now;
98  node_id_marker.action = 0;
99  node_id_marker.ns = "route_graph_node_ids";
100  node_id_marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
101  node_id_marker.scale.x = 0.1;
102  node_id_marker.scale.y = 0.1;
103  node_id_marker.scale.z = 0.1;
104  node_id_marker.color.a = 1.0;
105  node_id_marker.color.r = 1.0;
106 
107  visualization_msgs::msg::Marker edge_id_marker;
108  edge_id_marker.header.frame_id = frame;
109  edge_id_marker.header.stamp = now;
110  edge_id_marker.action = 0;
111  edge_id_marker.ns = "route_graph_edge_ids";
112  edge_id_marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
113  edge_id_marker.scale.x = 0.1;
114  edge_id_marker.scale.y = 0.1;
115  edge_id_marker.scale.z = 0.1;
116  edge_id_marker.color.a = 1.0;
117  edge_id_marker.color.g = 1.0;
118 
119  for (const auto & node : graph) {
120  node_pos.x = node.coords.x;
121  node_pos.y = node.coords.y;
122  nodes_marker.points.push_back(node_pos);
123 
124  // Add text for Node ID
125  node_id_marker.id++;
126  node_id_marker.pose.position.x = node.coords.x + 0.07;
127  node_id_marker.pose.position.y = node.coords.y;
128  node_id_marker.text = std::to_string(node.nodeid);
129  msg->markers.push_back(node_id_marker);
130 
131  for (const auto & neighbor : node.neighbors) {
132  edge_start.x = node.coords.x;
133  edge_start.y = node.coords.y;
134  edge_end.x = neighbor.end->coords.x;
135  edge_end.y = neighbor.end->coords.y;
136  edges_marker.points.push_back(edge_start);
137  edges_marker.points.push_back(edge_end);
138 
139  // Deal with overlapping bi-directional text markers by offsetting locations
140  float y_offset = 0.0;
141  if (node.nodeid > neighbor.end->nodeid) {
142  y_offset = 0.05;
143  } else {
144  y_offset = -0.05;
145  }
146  const float x_offset = 0.07;
147 
148  // Add text for Edge ID
149  edge_id_marker.id++;
150  edge_id_marker.pose.position.x =
151  node.coords.x + ((neighbor.end->coords.x - node.coords.x) / 2.0) + x_offset;
152  edge_id_marker.pose.position.y =
153  node.coords.y + ((neighbor.end->coords.y - node.coords.y) / 2.0) + y_offset;
154  edge_id_marker.text = std::to_string(neighbor.edgeid);
155  msg->markers.push_back(edge_id_marker);
156  }
157  }
158 
159  msg->markers.push_back(edges_marker);
160  msg->markers.push_back(nodes_marker);
161  return msg;
162 }
163 
171 inline nav2_msgs::msg::Route toMsg(
172  const nav2_route::Route & route, const std::string & frame, const rclcpp::Time & now)
173 {
174  nav2_msgs::msg::Route msg;
175  msg.header.frame_id = frame;
176  msg.header.stamp = now;
177  msg.route_cost = route.route_cost;
178 
179  nav2_msgs::msg::RouteNode route_node;
180  nav2_msgs::msg::RouteEdge route_edge;
181  route_node.nodeid = route.start_node->nodeid;
182  route_node.position.x = route.start_node->coords.x;
183  route_node.position.y = route.start_node->coords.y;
184  msg.nodes.push_back(route_node);
185 
186  // Provide the Node info and Edge IDs we're traversing through
187  for (unsigned int i = 0; i != route.edges.size(); i++) {
188  route_edge.edgeid = route.edges[i]->edgeid;
189  route_edge.start.x = route.edges[i]->start->coords.x;
190  route_edge.start.y = route.edges[i]->start->coords.y;
191  route_edge.end.x = route.edges[i]->end->coords.x;
192  route_edge.end.y = route.edges[i]->end->coords.y;
193  msg.edges.push_back(route_edge);
194 
195  route_node.nodeid = route.edges[i]->end->nodeid;
196  route_node.position.x = route.edges[i]->end->coords.x;
197  route_node.position.y = route.edges[i]->end->coords.y;
198  msg.nodes.push_back(route_node);
199  }
200 
201  return msg;
202 }
203 
212 inline float normalizedDot(
213  const float v1x, const float v1y,
214  const float v2x, const float v2y)
215 {
216  const float mag1 = std::hypotf(v1x, v1y);
217  const float mag2 = std::hypotf(v2x, v2y);
218  if (mag1 < 1e-6 || mag2 < 1e-6) {
219  return 0.0;
220  }
221  return (v1x / mag1) * (v2x / mag2) + (v1y / mag1) * (v2y / mag2);
222 }
223 
231 inline Coordinates findClosestPoint(
232  const geometry_msgs::msg::PoseStamped & pose,
233  const Coordinates & start, const Coordinates & end)
234 {
235  Coordinates pt;
236  const float vx = end.x - start.x;
237  const float vy = end.y - start.y;
238  const float ux = start.x - pose.pose.position.x;
239  const float uy = start.y - pose.pose.position.y;
240  const float uv = vx * ux + vy * uy;
241  const float vv = vx * vx + vy * vy;
242 
243  // They are the same point, so only one option
244  if (vv < 1e-6) {
245  return start;
246  }
247 
248  const float t = -uv / vv;
249  if (t > 0.0 && t < 1.0) {
250  pt.x = (1.0 - t) * start.x + t * end.x;
251  pt.y = (1.0 - t) * start.y + t * end.y;
252  } else if (t <= 0.0) {
253  pt = start;
254  } else {
255  pt = end;
256  }
257 
258  return pt;
259 }
260 
261 inline float distance(const Coordinates & coords, const geometry_msgs::msg::PoseStamped & pose)
262 {
263  return hypotf(coords.x - pose.pose.position.x, coords.y - pose.pose.position.y);
264 }
265 
266 } // namespace utils
267 
268 } // namespace nav2_route
269 
270 #endif // NAV2_ROUTE__UTILS_HPP_
An ordered set of nodes and edges corresponding to the planned route.
Definition: types.hpp:211