Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
corner_smoothing.hpp
1 // Copyright (c) 2025, Polymath Robotics
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__CORNER_SMOOTHING_HPP_
16 #define NAV2_ROUTE__CORNER_SMOOTHING_HPP_
17 
18 #include <vector>
19 #include <cmath>
20 #include <algorithm>
21 
22 #include "nav2_route/types.hpp"
23 #include "nav2_route/utils.hpp"
24 
25 namespace nav2_route
26 {
32 class CornerArc
33 {
34 public:
44  const Coordinates & start, const Coordinates & corner, const Coordinates & end,
45  float minimum_radius, float angle_threshold)
46  {
47  start_edge_length_ = hypotf(corner.x - start.x, corner.y - start.y);
48  end_edge_length_ = hypotf(end.x - corner.x, end.y - corner.y);
49 
50  // invalid scenario would cause equations to blow up
51  if (start_edge_length_ == 0.0 || end_edge_length_ == 0.0) {return;}
52 
53  float angle = getAngleBetweenEdges(start, corner, end);
54 
55  // Cannot smooth a 0° angle (u-turn/back and forth) or
56  // angles greater than angle_threshold (nearly straight line)
57  if (std::abs(angle) < 1E-6 || std::abs(angle) > angle_threshold) {return;}
58 
59  float tangent_length = minimum_radius / (std::tan(std::fabs(angle) / 2));
60 
61  if (tangent_length < start_edge_length_ && tangent_length < end_edge_length_) {
62  std::vector<float> start_edge_unit_tangent =
63  {(start.x - corner.x) / start_edge_length_, (start.y - corner.y) / start_edge_length_};
64  std::vector<float> end_edge_unit_tangent =
65  {(end.x - corner.x) / end_edge_length_, (end.y - corner.y) / end_edge_length_};
66 
67  float bisector_x = start_edge_unit_tangent[0] + end_edge_unit_tangent[0];
68  float bisector_y = start_edge_unit_tangent[1] + end_edge_unit_tangent[1];
69  float bisector_magnitude = std::sqrt(bisector_x * bisector_x + bisector_y * bisector_y);
70 
71  std::vector<float> unit_bisector =
72  {bisector_x / bisector_magnitude, bisector_y / bisector_magnitude};
73 
74  start_coordinate_.x = corner.x + start_edge_unit_tangent[0] * tangent_length;
75  start_coordinate_.y = corner.y + start_edge_unit_tangent[1] * tangent_length;
76 
77  end_coordinate_.x = corner.x + end_edge_unit_tangent[0] * tangent_length;
78  end_coordinate_.y = corner.y + end_edge_unit_tangent[1] * tangent_length;
79 
80  float bisector_length = minimum_radius / std::sin(angle / 2);
81 
82  circle_center_coordinate_.x = corner.x + unit_bisector[0] * bisector_length;
83  circle_center_coordinate_.y = corner.y + unit_bisector[1] * bisector_length;
84 
85  valid_corner_ = true;
86  }
87  }
88 
92  ~CornerArc() = default;
93 
100  const float & max_angle_resolution,
101  std::vector<geometry_msgs::msg::PoseStamped> & poses)
102  {
103  std::vector<float> r_start{start_coordinate_.x - circle_center_coordinate_.x,
104  start_coordinate_.y - circle_center_coordinate_.y};
105  std::vector<float> r_end{end_coordinate_.x - circle_center_coordinate_.x,
106  end_coordinate_.y - circle_center_coordinate_.y};
107  float cross = r_start[0] * r_end[1] - r_start[1] * r_end[0];
108  float dot = r_start[0] * r_end[0] + r_start[1] * r_end[1];
109  float signed_angle = std::atan2(cross, dot);
110  // lower limit for N is 1 to protect against divide by 0
111  int N = std::max(1, static_cast<int>(std::ceil(std::abs(signed_angle) / max_angle_resolution)));
112  float angle_resolution = signed_angle / N;
113 
114  float x, y;
115  for (int i = 0; i < N; i++) {
116  float angle = i * angle_resolution;
117  x = circle_center_coordinate_.x +
118  (r_start[0] * std::cos(angle) - r_start[1] * std::sin(angle));
119  y = circle_center_coordinate_.y +
120  (r_start[0] * std::sin(angle) + r_start[1] * std::cos(angle));
121  poses.push_back(utils::toMsg(x, y));
122  }
123  }
124 
129  bool isCornerValid() const {return valid_corner_;}
130 
135  Coordinates getCornerStart() const {return start_coordinate_;}
136 
141  Coordinates getCornerEnd() const {return end_coordinate_;}
142 
143 protected:
152  const Coordinates & start, const Coordinates & corner,
153  const Coordinates & end)
154  {
155  float start_dx = start.x - corner.x;
156  float start_dy = start.y - corner.y;
157 
158  float end_dx = end.x - corner.x;
159  float end_dy = end.y - corner.y;
160 
161  float angle =
162  acos((start_dx * end_dx + start_dy * end_dy) / (start_edge_length_ * end_edge_length_));
163 
164  return angle;
165  }
166 
167 private:
168  bool valid_corner_{false};
169  float start_edge_length_;
170  float end_edge_length_;
171  Coordinates start_coordinate_;
172  Coordinates end_coordinate_;
173  Coordinates circle_center_coordinate_;
174 };
175 
176 } // namespace nav2_route
177 
178 #endif // NAV2_ROUTE__CORNER_SMOOTHING_HPP_
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
~CornerArc()=default
A destructor for nav2_route::CornerArc.
float getAngleBetweenEdges(const Coordinates &start, const Coordinates &corner, const Coordinates &end)
find the unsigned angle between a corner generated by 3 points
CornerArc(const Coordinates &start, const Coordinates &corner, const Coordinates &end, float minimum_radius, float angle_threshold)
A constructor for nav2_route::CornerArc.
Coordinates getCornerEnd() const
return the end coordinate of the corner arc
An object to store Node coordinates in different frames.
Definition: types.hpp:173