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