Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
geometry_utils.hpp
1 // Copyright (c) 2019 Intel Corporation
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_UTIL__GEOMETRY_UTILS_HPP_
16 #define NAV2_UTIL__GEOMETRY_UTILS_HPP_
17 
18 #include <cmath>
19 #include <vector>
20 
21 #include "geometry_msgs/msg/pose.hpp"
22 #include "geometry_msgs/msg/pose_stamped.hpp"
23 #include "geometry_msgs/msg/point.hpp"
24 #include "geometry_msgs/msg/quaternion.hpp"
25 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
26 #include "nav_msgs/msg/path.hpp"
27 #include "nav2_msgs/msg/waypoint_status.hpp"
28 
29 namespace nav2_util
30 {
31 namespace geometry_utils
32 {
33 
39 inline geometry_msgs::msg::Quaternion orientationAroundZAxis(double angle)
40 {
41  tf2::Quaternion q;
42  q.setRPY(0, 0, angle); // void returning function
43  return tf2::toMsg(q);
44 }
45 
53 inline double euclidean_distance(
54  const geometry_msgs::msg::Point & pos1,
55  const geometry_msgs::msg::Point & pos2,
56  const bool is_3d = false)
57 {
58  double dx = pos1.x - pos2.x;
59  double dy = pos1.y - pos2.y;
60 
61  if (is_3d) {
62  double dz = pos1.z - pos2.z;
63  return std::hypot(dx, dy, dz);
64  }
65 
66  return std::hypot(dx, dy);
67 }
68 
76 inline double euclidean_distance(
77  const geometry_msgs::msg::Pose & pos1,
78  const geometry_msgs::msg::Pose & pos2,
79  const bool is_3d = false)
80 {
81  double dx = pos1.position.x - pos2.position.x;
82  double dy = pos1.position.y - pos2.position.y;
83 
84  if (is_3d) {
85  double dz = pos1.position.z - pos2.position.z;
86  return std::hypot(dx, dy, dz);
87  }
88 
89  return std::hypot(dx, dy);
90 }
91 
99 inline double euclidean_distance(
100  const geometry_msgs::msg::PoseStamped & pos1,
101  const geometry_msgs::msg::PoseStamped & pos2,
102  const bool is_3d = false)
103 {
104  return euclidean_distance(pos1.pose, pos2.pose, is_3d);
105 }
106 
110 template<typename Iter, typename Getter>
111 inline Iter min_by(Iter begin, Iter end, Getter getCompareVal)
112 {
113  if (begin == end) {
114  return end;
115  }
116  auto lowest = getCompareVal(*begin);
117  Iter lowest_it = begin;
118  for (Iter it = ++begin; it != end; ++it) {
119  auto comp = getCompareVal(*it);
120  if (comp <= lowest) {
121  lowest = comp;
122  lowest_it = it;
123  }
124  }
125  return lowest_it;
126 }
127 
131 template<typename Iter, typename Getter>
132 inline Iter first_after_integrated_distance(Iter begin, Iter end, Getter getCompareVal)
133 {
134  if (begin == end) {
135  return end;
136  }
137  Getter dist = 0.0;
138  for (Iter it = begin; it != end - 1; it++) {
139  dist += euclidean_distance(*it, *(it + 1));
140  if (dist > getCompareVal) {
141  return it + 1;
142  }
143  }
144  return end;
145 }
146 
155 inline double calculate_path_length(const nav_msgs::msg::Path & path, size_t start_index = 0)
156 {
157  if (start_index + 1 >= path.poses.size()) {
158  return 0.0;
159  }
160  double path_length = 0.0;
161  for (size_t idx = start_index; idx < path.poses.size() - 1; ++idx) {
162  path_length += euclidean_distance(path.poses[idx].pose, path.poses[idx + 1].pose);
163  }
164  return path_length;
165 }
166 
174 inline int find_next_matching_goal_in_waypoint_statuses(
175  const std::vector<nav2_msgs::msg::WaypointStatus> & waypoint_statuses,
176  const geometry_msgs::msg::PoseStamped & goal)
177 {
178  auto itr = std::find_if(waypoint_statuses.begin(), waypoint_statuses.end(),
179  [&goal](const nav2_msgs::msg::WaypointStatus & status){
180  return status.waypoint_pose == goal &&
181  status.waypoint_status == nav2_msgs::msg::WaypointStatus::PENDING;
182  });
183 
184  if (itr == waypoint_statuses.end()) {
185  return -1;
186  }
187 
188  return itr - waypoint_statuses.begin();
189 }
190 
198 template<class PointT>
199 inline bool isPointInsidePolygon(
200  const double px, const double py, const std::vector<PointT> & polygon)
201 {
202  // Adaptation of Shimrat, Moshe. "Algorithm 112: position of point relative to polygon."
203  // Communications of the ACM 5.8 (1962): 434.
204  // Implementation of ray crossings algorithm for point in polygon task solving.
205  // Y coordinate is fixed. Moving the ray on X+ axis starting from given point.
206  // Odd number of intersections with polygon boundaries means the point is inside polygon.
207  const int points_num = polygon.size();
208  int i, j; // Polygon vertex iterators
209  bool res = false; // Final result, initialized with already inverted value
210 
211  // Starting from the edge where the last point of polygon is connected to the first
212  i = points_num - 1;
213  for (j = 0; j < points_num; j++) {
214  // Checking the edge only if given point is between edge boundaries by Y coordinates.
215  // One of the condition should contain equality in order to exclude the edges
216  // parallel to X+ ray.
217  if ((py <= polygon[i].y) == (py > polygon[j].y)) {
218  // Calculating the intersection coordinate of X+ ray
219  const double x_inter = polygon[i].x +
220  (py - polygon[i].y) *
221  (polygon[j].x - polygon[i].x) /
222  (polygon[j].y - polygon[i].y);
223  // If intersection with checked edge is greater than point x coordinate,
224  // inverting the result
225  if (x_inter > px) {
226  res = !res;
227  }
228  }
229  i = j;
230  }
231  return res;
232 }
233 
248 inline double distance_to_path_segment(
249  const geometry_msgs::msg::Point & point,
250  const geometry_msgs::msg::Pose & start,
251  const geometry_msgs::msg::Pose & end)
252 {
253  const auto & p = point;
254  const auto & a = start.position;
255  const auto & b = end.position;
256 
257  const double dx_seg = b.x - a.x;
258  const double dy_seg = b.y - a.y;
259 
260  const double seg_len_sq = (dx_seg * dx_seg) + (dy_seg * dy_seg);
261 
262  if (seg_len_sq <= 1e-9) {
263  return euclidean_distance(point, a);
264  }
265 
266  const double dot = ((p.x - a.x) * dx_seg) + ((p.y - a.y) * dy_seg);
267  const double t = std::clamp(dot / seg_len_sq, 0.0, 1.0);
268 
269  const double proj_x = a.x + t * dx_seg;
270  const double proj_y = a.y + t * dy_seg;
271 
272  const double dx_proj = p.x - proj_x;
273  const double dy_proj = p.y - proj_y;
274  return std::hypot(dx_proj, dy_proj);
275 }
276 
288 inline double cross_product_2d(
289  const geometry_msgs::msg::Point & point,
290  const geometry_msgs::msg::Pose & start,
291  const geometry_msgs::msg::Pose & end)
292 {
293  const auto & p = point;
294  const auto & a = start.position;
295  const auto & b = end.position;
296 
297  const double path_vec_x = b.x - a.x;
298  const double path_vec_y = b.y - a.y;
299 
300  const double robot_vec_x = p.x - a.x;
301  const double robot_vec_y = p.y - a.y;
302 
303  return (path_vec_x * robot_vec_y) - (path_vec_y * robot_vec_x);
304 }
305 
306 } // namespace geometry_utils
307 } // namespace nav2_util
308 
309 #endif // NAV2_UTIL__GEOMETRY_UTILS_HPP_