Nav2 Navigation Stack - rolling  main
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  visualization_msgs::msg::Marker curr_marker;
63  curr_marker.header.frame_id = frame;
64  curr_marker.header.stamp = now;
65  curr_marker.action = 0;
66 
67  auto getSphereSize = []() {
68  geometry_msgs::msg::Vector3 v_msg;
69  v_msg.x = 0.05;
70  v_msg.y = 0.05;
71  v_msg.z = 0.05;
72  return v_msg;
73  };
74 
75  auto getSphereColor = []() {
76  std_msgs::msg::ColorRGBA c_msg;
77  c_msg.r = 1.0;
78  c_msg.g = 0.0;
79  c_msg.b = 0.0;
80  c_msg.a = 1.0;
81  return c_msg;
82  };
83 
84  auto getLineColor = []() {
85  std_msgs::msg::ColorRGBA c_msg;
86  c_msg.r = 0.0;
87  c_msg.g = 1.0;
88  c_msg.b = 0.0;
89  c_msg.a = 0.5; // So bi-directional connections stand out overlapping
90  return c_msg;
91  };
92 
93  unsigned int marker_idx = 1;
94  for (unsigned int i = 0; i != graph.size(); i++) {
95  if (graph[i].nodeid == std::numeric_limits<int>::max()) {
96  continue; // Skip "deleted" nodes
97  }
98  curr_marker.ns = "route_graph";
99  curr_marker.id = marker_idx++;
100  curr_marker.type = visualization_msgs::msg::Marker::SPHERE;
101  curr_marker.pose.position.x = graph[i].coords.x;
102  curr_marker.pose.position.y = graph[i].coords.y;
103  curr_marker.scale = getSphereSize();
104  curr_marker.color = getSphereColor();
105  msg.markers.push_back(curr_marker);
106 
107  // Add text
108  curr_marker.ns = "route_graph_ids";
109  curr_marker.id = marker_idx++;
110  curr_marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
111  curr_marker.pose.position.x = graph[i].coords.x + 0.07;
112  curr_marker.pose.position.y = graph[i].coords.y;
113  curr_marker.text = std::to_string(graph[i].nodeid);
114  curr_marker.scale.z = 0.1;
115  msg.markers.push_back(curr_marker);
116 
117  for (unsigned int j = 0; j != graph[i].neighbors.size(); j++) {
118  curr_marker.ns = "route_graph";
119  curr_marker.id = marker_idx++;
120  curr_marker.type = visualization_msgs::msg::Marker::LINE_LIST;
121  curr_marker.pose.position.x = 0; // Set to 0 since points are relative to this frame
122  curr_marker.pose.position.y = 0; // Set to 0 since points are relative to this frame
123  curr_marker.points.resize(2);
124  curr_marker.points[0].x = graph[i].coords.x;
125  curr_marker.points[0].y = graph[i].coords.y;
126  curr_marker.points[1].x = graph[i].neighbors[j].end->coords.x;
127  curr_marker.points[1].y = graph[i].neighbors[j].end->coords.y;
128  curr_marker.scale.x = 0.03;
129  curr_marker.color = getLineColor();
130  msg.markers.push_back(curr_marker);
131  curr_marker.points.clear(); // Reset for next node marker
132 
133  // Add text
134  curr_marker.ns = "route_graph_ids";
135  curr_marker.id = marker_idx++;
136  curr_marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
137  curr_marker.pose.position.x =
138  graph[i].coords.x + ((graph[i].neighbors[j].end->coords.x - graph[i].coords.x) / 2.0) +
139  0.07;
140 
141  // Deal with overlapping bi-directional text markers by offsetting locations
142  float y_offset = 0.0;
143  if (graph[i].nodeid > graph[i].neighbors[j].end->nodeid) {
144  y_offset = 0.05;
145  } else {
146  y_offset = -0.05;
147  }
148 
149  curr_marker.pose.position.y =
150  graph[i].coords.y + ((graph[i].neighbors[j].end->coords.y - graph[i].coords.y) / 2.0) +
151  y_offset;
152  curr_marker.text = std::to_string(graph[i].neighbors[j].edgeid);
153  curr_marker.scale.z = 0.1;
154  msg.markers.push_back(curr_marker);
155  }
156  }
157 
158  return msg;
159 }
160 
168 inline nav2_msgs::msg::Route toMsg(
169  const nav2_route::Route & route, const std::string & frame, const rclcpp::Time & now)
170 {
171  nav2_msgs::msg::Route msg;
172  msg.header.frame_id = frame;
173  msg.header.stamp = now;
174  msg.route_cost = route.route_cost;
175 
176  nav2_msgs::msg::RouteNode route_node;
177  nav2_msgs::msg::RouteEdge route_edge;
178  route_node.nodeid = route.start_node->nodeid;
179  route_node.position.x = route.start_node->coords.x;
180  route_node.position.y = route.start_node->coords.y;
181  msg.nodes.push_back(route_node);
182 
183  // Provide the Node info and Edge IDs we're traversing through
184  for (unsigned int i = 0; i != route.edges.size(); i++) {
185  route_edge.edgeid = route.edges[i]->edgeid;
186  route_edge.start.x = route.edges[i]->start->coords.x;
187  route_edge.start.y = route.edges[i]->start->coords.y;
188  route_edge.end.x = route.edges[i]->end->coords.x;
189  route_edge.end.y = route.edges[i]->end->coords.y;
190  msg.edges.push_back(route_edge);
191 
192  route_node.nodeid = route.edges[i]->end->nodeid;
193  route_node.position.x = route.edges[i]->end->coords.x;
194  route_node.position.y = route.edges[i]->end->coords.y;
195  msg.nodes.push_back(route_node);
196  }
197 
198  return msg;
199 }
200 
209 inline float normalizedDot(
210  const float v1x, const float v1y,
211  const float v2x, const float v2y)
212 {
213  const float mag1 = std::hypotf(v1x, v1y);
214  const float mag2 = std::hypotf(v2x, v2y);
215  if (mag1 < 1e-6 || mag2 < 1e-6) {
216  return 0.0;
217  }
218  return (v1x / mag1) * (v2x / mag2) + (v1y / mag1) * (v2y / mag2);
219 }
220 
228 inline Coordinates findClosestPoint(
229  const geometry_msgs::msg::PoseStamped & pose,
230  const Coordinates & start, const Coordinates & end)
231 {
232  Coordinates pt;
233  const float vx = end.x - start.x;
234  const float vy = end.y - start.y;
235  const float ux = start.x - pose.pose.position.x;
236  const float uy = start.y - pose.pose.position.y;
237  const float uv = vx * ux + vy * uy;
238  const float vv = vx * vx + vy * vy;
239 
240  // They are the same point, so only one option
241  if (vv < 1e-6) {
242  return start;
243  }
244 
245  const float t = -uv / vv;
246  if (t > 0.0 && t < 1.0) {
247  pt.x = (1.0 - t) * start.x + t * end.x;
248  pt.y = (1.0 - t) * start.y + t * end.y;
249  } else if (t <= 0.0) {
250  pt = start;
251  } else {
252  pt = end;
253  }
254 
255  return pt;
256 }
257 
258 inline float distance(const Coordinates & coords, const geometry_msgs::msg::PoseStamped & pose)
259 {
260  return hypotf(coords.x - pose.pose.position.x, coords.y - pose.pose.position.y);
261 }
262 
263 } // namespace utils
264 
265 } // namespace nav2_route
266 
267 #endif // NAV2_ROUTE__UTILS_HPP_
An ordered set of nodes and edges corresponding to the planned route.
Definition: types.hpp:211