Nav2 Navigation Stack - lyrical  lyrical
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(
179  waypoint_statuses.begin(), waypoint_statuses.end(),
180  [&goal](const nav2_msgs::msg::WaypointStatus & status) {
181  return status.waypoint_pose == goal &&
182  status.waypoint_status == nav2_msgs::msg::WaypointStatus::PENDING;
183  });
184 
185  if (itr == waypoint_statuses.end()) {
186  return -1;
187  }
188 
189  return itr - waypoint_statuses.begin();
190 }
191 
199 template<class PointT>
200 inline bool isPointInsidePolygon(
201  const double px, const double py, const std::vector<PointT> & polygon)
202 {
203  // Adaptation of Shimrat, Moshe. "Algorithm 112: position of point relative to polygon."
204  // Communications of the ACM 5.8 (1962): 434.
205  // Implementation of ray crossings algorithm for point in polygon task solving.
206  // Y coordinate is fixed. Moving the ray on X+ axis starting from given point.
207  // Odd number of intersections with polygon boundaries means the point is inside polygon.
208  const int points_num = polygon.size();
209  int i, j; // Polygon vertex iterators
210  bool res = false; // Final result, initialized with already inverted value
211 
212  // Starting from the edge where the last point of polygon is connected to the first
213  i = points_num - 1;
214  for (j = 0; j < points_num; j++) {
215  // Checking the edge only if given point is between edge boundaries by Y coordinates.
216  // One of the condition should contain equality in order to exclude the edges
217  // parallel to X+ ray.
218  if ((py <= polygon[i].y) == (py > polygon[j].y)) {
219  // Calculating the intersection coordinate of X+ ray
220  const double x_inter = polygon[i].x +
221  (py - polygon[i].y) *
222  (polygon[j].x - polygon[i].x) /
223  (polygon[j].y - polygon[i].y);
224  // If intersection with checked edge is greater than point x coordinate,
225  // inverting the result
226  if (x_inter > px) {
227  res = !res;
228  }
229  }
230  i = j;
231  }
232  return res;
233 }
234 
249 inline double distance_to_path_segment(
250  const geometry_msgs::msg::Point & point,
251  const geometry_msgs::msg::Pose & start,
252  const geometry_msgs::msg::Pose & end)
253 {
254  const auto & p = point;
255  const auto & a = start.position;
256  const auto & b = end.position;
257 
258  const double dx_seg = b.x - a.x;
259  const double dy_seg = b.y - a.y;
260 
261  const double seg_len_sq = (dx_seg * dx_seg) + (dy_seg * dy_seg);
262 
263  if (seg_len_sq <= 1e-9) {
264  return euclidean_distance(point, a);
265  }
266 
267  const double dot = ((p.x - a.x) * dx_seg) + ((p.y - a.y) * dy_seg);
268  const double t = std::clamp(dot / seg_len_sq, 0.0, 1.0);
269 
270  const double proj_x = a.x + t * dx_seg;
271  const double proj_y = a.y + t * dy_seg;
272 
273  const double dx_proj = p.x - proj_x;
274  const double dy_proj = p.y - proj_y;
275  return std::hypot(dx_proj, dy_proj);
276 }
277 
289 inline double cross_product_2d(
290  const geometry_msgs::msg::Point & point,
291  const geometry_msgs::msg::Pose & start,
292  const geometry_msgs::msg::Pose & end)
293 {
294  const auto & p = point;
295  const auto & a = start.position;
296  const auto & b = end.position;
297 
298  const double path_vec_x = b.x - a.x;
299  const double path_vec_y = b.y - a.y;
300 
301  const double robot_vec_x = p.x - a.x;
302  const double robot_vec_y = p.y - a.y;
303 
304  return (path_vec_x * robot_vec_y) - (path_vec_y * robot_vec_x);
305 }
306 
307 } // namespace geometry_utils
308 } // namespace nav2_util
309 
310 #endif // NAV2_UTIL__GEOMETRY_UTILS_HPP_