Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
path_converter.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/path_converter.hpp"
23 
24 namespace nav2_route
25 {
26 
27 void PathConverter::configure(nav2::LifecycleNode::SharedPtr node)
28 {
29  // Density to make path points
30  density_ = static_cast<float>(node->declare_or_get_parameter("path_density", 0.05));
31  smoothing_radius_ = static_cast<float>(
32  node->declare_or_get_parameter("smoothing_radius", 1.0));
33  smoothing_angle_threshold_ = static_cast<float>(
34  node->declare_or_get_parameter("smoothing_angle_threshold", 2.9));
35  smooth_corners_ = node->declare_or_get_parameter("smooth_corners", false);
36 
37  path_pub_ = node->create_publisher<nav_msgs::msg::Path>("plan");
38  path_pub_->on_activate();
39  logger_ = node->get_logger();
40 }
41 
42 nav_msgs::msg::Path PathConverter::densify(
43  const Route & route,
44  const ReroutingState & rerouting_info,
45  const std::string & frame,
46  const rclcpp::Time & now)
47 {
48  nav_msgs::msg::Path path;
49  path.header.stamp = now;
50  path.header.frame_id = frame;
51 
52  // If we're rerouting and covering the same previous edge to start,
53  // the path should contain the relevant partial information along edge
54  // to avoid unnecessary free-space planning where state is retained
55  if (rerouting_info.curr_edge) {
56  const Coordinates & start = rerouting_info.closest_pt_on_edge;
57  const Coordinates & end = rerouting_info.curr_edge->end->coords;
58  interpolateEdge(start.x, start.y, end.x, end.y, path.poses);
59  }
60 
61  Coordinates start;
62  Coordinates end;
63 
64  if (!route.edges.empty()) {
65  start = route.edges[0]->start->coords;
66 
67  // Fill in path via route edges
68  for (unsigned int i = 0; i < route.edges.size() - 1; i++) {
69  const EdgePtr edge = route.edges[i];
70  const EdgePtr & next_edge = route.edges[i + 1];
71  end = edge->end->coords;
72 
73  CornerArc corner_arc(start, end, next_edge->end->coords, smoothing_radius_,
74  smoothing_angle_threshold_);
75  if (corner_arc.isCornerValid() && smooth_corners_) {
76  // if an arc exists, end of the first edge is the start of the arc
77  end = corner_arc.getCornerStart();
78 
79  // interpolate to start of arc
80  interpolateEdge(start.x, start.y, end.x, end.y, path.poses);
81 
82  // interpolate arc
83  corner_arc.interpolateArc(density_ / smoothing_radius_, path.poses);
84 
85  // new start of next edge is end of smoothing arc
86  start = corner_arc.getCornerEnd();
87  } else {
88  if (smooth_corners_) {
89  RCLCPP_WARN(
90  logger_, "Unable to smooth corner between edge %i and edge %i", edge->edgeid,
91  next_edge->edgeid);
92  }
93  interpolateEdge(start.x, start.y, end.x, end.y, path.poses);
94  start = end;
95  }
96  }
97  }
98 
99  if (route.edges.empty()) {
100  path.poses.push_back(utils::toMsg(route.start_node->coords.x, route.start_node->coords.y));
101  } else {
103  start.x, start.y, route.edges.back()->end->coords.x,
104  route.edges.back()->end->coords.y, path.poses);
105 
106  path.poses.push_back(
107  utils::toMsg(route.edges.back()->end->coords.x, route.edges.back()->end->coords.y));
108  }
109 
110  // Set path poses orientations for each point
111  for (size_t i = 0; i < path.poses.size() - 1; ++i) {
112  const auto & pose = path.poses[i];
113  const auto & next_pose = path.poses[i + 1];
114  const double dx = next_pose.pose.position.x - pose.pose.position.x;
115  const double dy = next_pose.pose.position.y - pose.pose.position.y;
116  const double yaw = atan2(dy, dx);
117  path.poses[i].pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(yaw);
118  }
119 
120  // Set the last pose orientation to the last edge
121  if (!route.edges.empty()) {
122  const auto & last_edge = route.edges.back();
123  const double dx = last_edge->end->coords.x - last_edge->start->coords.x;
124  const double dy = last_edge->end->coords.y - last_edge->start->coords.y;
125  path.poses.back().pose.orientation =
126  nav2_util::geometry_utils::orientationAroundZAxis(atan2(dy, dx));
127  }
128 
129  // publish path similar to planner server
130  path_pub_->publish(std::make_unique<nav_msgs::msg::Path>(path));
131 
132  return path;
133 }
134 
136  float x0, float y0, float x1, float y1,
137  std::vector<geometry_msgs::msg::PoseStamped> & poses)
138 {
139  // Find number of points to populate by given density
140  const float mag = hypotf(x1 - x0, y1 - y0);
141  const unsigned int num_pts = ceil(mag / density_);
142  // For zero-length edges, we can just push the start point and return
143  if (num_pts < 1) {
144  return;
145  }
146 
147  const float iterpolated_dist = mag / num_pts;
148 
149  // Find unit vector direction
150  float ux = (x1 - x0) / mag;
151  float uy = (y1 - y0) / mag;
152 
153  // March along it until dist
154  float x = x0;
155  float y = y0;
156  poses.push_back(utils::toMsg(x, y));
157 
158  unsigned int pt_ctr = 0;
159  while (pt_ctr < num_pts - 1) {
160  x += ux * iterpolated_dist;
161  y += uy * iterpolated_dist;
162  pt_ctr++;
163  poses.push_back(utils::toMsg(x, y));
164  }
165 }
166 
167 } // namespace nav2_route
A class used to smooth corners defined by the edges and nodes of the route graph. Used with path conv...
bool isCornerValid() const
return if a valid corner arc (one that doesn't overrun the edge lengths) is generated
void interpolateArc(const float &max_angle_resolution, std::vector< geometry_msgs::msg::PoseStamped > &poses)
interpolates the arc for a path of certain density
Coordinates getCornerStart() const
return the start coordinate of the corner arc
Coordinates getCornerEnd() const
return the end coordinate of the corner arc
void interpolateEdge(float x0, float y0, float x1, float y1, std::vector< geometry_msgs::msg::PoseStamped > &poses)
Convert an individual edge into a dense line.
void configure(nav2::LifecycleNode::SharedPtr node)
Configure the object.
nav_msgs::msg::Path densify(const Route &route, const ReroutingState &rerouting_info, const std::string &frame, const rclcpp::Time &now)
Convert a Route into a dense path.
An object to store Node coordinates in different frames.
Definition: types.hpp:173
An object representing edges between nodes.
Definition: types.hpp:134
State shared to objects to communicate important rerouting data to avoid rerouting over blocked edges...
Definition: types.hpp:264
An ordered set of nodes and edges corresponding to the planned route.
Definition: types.hpp:211