Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
controller_utils.cpp
1 // Copyright (c) 2020 Shrijit Singh
2 // Copyright (c) 2020 Samsung Research America
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include <cmath>
17 #include "nav2_util/controller_utils.hpp"
18 #include "nav2_util/geometry_utils.hpp"
19 
20 namespace nav2_util
21 {
22 geometry_msgs::msg::Point circleSegmentIntersection(
23  const geometry_msgs::msg::Point & p1,
24  const geometry_msgs::msg::Point & p2,
25  double r)
26 {
27  // Formula for intersection of a line with a circle centered at the origin,
28  // modified to always return the point that is on the segment between the two points.
29  // https://mathworld.wolfram.com/Circle-LineIntersection.html
30  // This works because the poses are transformed into the robot frame.
31  // This can be derived from solving the system of equations of a line and a circle
32  // which results in something that is just a reformulation of the quadratic formula.
33  // Interactive illustration in doc/circle-segment-intersection.ipynb as well as at
34  // https://www.desmos.com/calculator/td5cwbuocd
35  double x1 = p1.x;
36  double x2 = p2.x;
37  double y1 = p1.y;
38  double y2 = p2.y;
39 
40  double dx = x2 - x1;
41  double dy = y2 - y1;
42  double dr2 = dx * dx + dy * dy;
43  double D = x1 * y2 - x2 * y1;
44 
45  // Augmentation to only return point within segment
46  double d1 = x1 * x1 + y1 * y1;
47  double d2 = x2 * x2 + y2 * y2;
48  double dd = d2 - d1;
49 
50  geometry_msgs::msg::Point p;
51  double sqrt_term = std::sqrt(r * r * dr2 - D * D);
52  p.x = (D * dy + std::copysign(1.0, dd) * dx * sqrt_term) / dr2;
53  p.y = (-D * dx + std::copysign(1.0, dd) * dy * sqrt_term) / dr2;
54  return p;
55 }
56 
57 geometry_msgs::msg::Point linearInterpolation(
58  const geometry_msgs::msg::Point & p1,
59  const geometry_msgs::msg::Point & p2,
60  const double target_dist)
61 {
62  geometry_msgs::msg::Point result;
63 
64  double dx = p2.x - p1.x;
65  double dy = p2.y - p1.y;
66  double d_dist = std::hypot(dx, dy);
67 
68  double target_ratio = target_dist / d_dist;
69 
70  result.x = p1.x + target_ratio * dx;
71  result.y = p1.y + target_ratio * dy;
72  return result;
73 }
74 
75 geometry_msgs::msg::PoseStamped getLookAheadPoint(
76  double & lookahead_dist,
77  const nav_msgs::msg::Path & transformed_plan,
78  const bool interpolate_after_goal)
79 {
80  // Find the first pose which is at a distance greater than the lookahead distance
81  // Using distance along the path
82  const auto & poses = transformed_plan.poses;
83  auto goal_pose_it = poses.begin();
84  double path_dist = 0.0;
85  double interpolation_dist = 0.0;
86 
87  bool pose_found = false;
88  for (size_t i = 1; i < poses.size(); i++) {
89  const auto & prev_pose = poses[i - 1].pose.position;
90  const auto & curr_pose = poses[i].pose.position;
91 
92  const double d = std::hypot(curr_pose.x - prev_pose.x, curr_pose.y - prev_pose.y);
93  if (path_dist + d >= lookahead_dist) {
94  goal_pose_it = poses.begin() + i;
95  pose_found = true;
96  break;
97  }
98 
99  path_dist += d;
100  }
101 
102  interpolation_dist = lookahead_dist - path_dist;
103  if (!pose_found) {
104  goal_pose_it = poses.end();
105  }
106 
107  // If the no pose is not far enough, take the last pose
108  if (goal_pose_it == transformed_plan.poses.end()) {
109  if (interpolate_after_goal) {
110  auto last_pose_it = std::prev(transformed_plan.poses.end());
111  auto prev_last_pose_it = std::prev(last_pose_it);
112 
113  double end_path_orientation = atan2(
114  last_pose_it->pose.position.y - prev_last_pose_it->pose.position.y,
115  last_pose_it->pose.position.x - prev_last_pose_it->pose.position.x);
116 
117  // Project the last segment out to guarantee it is beyond the look ahead
118  // distance
119  auto projected_position = last_pose_it->pose.position;
120  projected_position.x += cos(end_path_orientation) * lookahead_dist;
121  projected_position.y += sin(end_path_orientation) * lookahead_dist;
122 
123  // Use the linear interpolation to find the position at the correct look
124  // ahead distance
125  const auto interpolated_position = linearInterpolation(
126  last_pose_it->pose.position, projected_position, interpolation_dist);
127 
128  geometry_msgs::msg::PoseStamped interpolated_pose;
129  interpolated_pose.header = last_pose_it->header;
130  interpolated_pose.pose.position = interpolated_position;
131 
132  return interpolated_pose;
133  } else {
134  lookahead_dist = path_dist; // Updating lookahead distance since using the final point
135  goal_pose_it = std::prev(transformed_plan.poses.end());
136  }
137  } else if (goal_pose_it != transformed_plan.poses.begin()) {
138  // Find the point on the robot path
139  // that is exactly the lookahead distance away from the robot pose (the origin)
140  // This can be found with a linear interpolation between the prev_pose and
141  // the goal_pose, moving interpolation_dist starting from prev_pose in the
142  // direction of goal_pose.
143  auto prev_pose_it = std::prev(goal_pose_it);
144  auto point = linearInterpolation(
145  prev_pose_it->pose.position,
146  goal_pose_it->pose.position, interpolation_dist);
147 
148  // Calculate orientation towards interpolated position
149  // Convert yaw to quaternion
150  double yaw = atan2(
151  point.y - prev_pose_it->pose.position.y,
152  point.x - prev_pose_it->pose.position.x);
153 
154  geometry_msgs::msg::PoseStamped pose;
155  pose.header.frame_id = prev_pose_it->header.frame_id;
156  pose.header.stamp = goal_pose_it->header.stamp;
157  pose.pose.position = point;
158  pose.pose.orientation = geometry_utils::orientationAroundZAxis(yaw);
159  return pose;
160  }
161 
162  return *goal_pose_it;
163 }
164 } // namespace nav2_util