15 #include "nav2_util/path_utils.hpp"
21 #include "nav2_util/geometry_utils.hpp"
22 #include "nav2_ros_common/tf2_factories.hpp"
26 PathSearchResult distance_from_path(
27 const nav_msgs::msg::Path & path,
28 const geometry_msgs::msg::Pose & robot_pose,
29 const size_t start_index,
30 const double search_window_length)
32 PathSearchResult result;
33 result.closest_segment_index = start_index;
34 result.distance = std::numeric_limits<double>::max();
36 if (path.poses.empty()) {
40 if (path.poses.size() == 1) {
41 result.distance = nav2_util::geometry_utils::euclidean_distance(
42 robot_pose, path.poses.front().pose);
43 result.closest_segment_index = 0;
47 if (start_index >= path.poses.size()) {
48 throw std::runtime_error(
49 "Requested start index (" + std::to_string(start_index) +
50 ") is greater than or equal to path size (" + std::to_string(path.poses.size()) +
54 double distance_traversed = 0.0;
55 for (
size_t i = start_index; i < path.poses.size() - 1; ++i) {
56 if (distance_traversed > search_window_length) {
60 const double current_distance = geometry_utils::distance_to_path_segment(
63 path.poses[i + 1].pose);
65 if (current_distance < result.distance) {
66 result.distance = current_distance;
67 result.closest_segment_index = i;
70 distance_traversed += geometry_utils::euclidean_distance(
75 const auto & segment_start = path.poses[result.closest_segment_index];
76 const auto & segment_end = path.poses[result.closest_segment_index + 1];
79 const double cross_product = geometry_utils::cross_product_2d(
80 robot_pose.position, segment_start.pose, segment_end.pose);
81 result.distance *= (cross_product >= 0.0 ? 1.0 : -1.0);
86 bool transformPathInTargetFrame(
87 const nav_msgs::msg::Path & input_path,
88 nav_msgs::msg::Path & transformed_path,
89 nav2::TransformBuffer & tf_buffer,
const std::string target_frame,
90 const double transform_timeout)
92 static rclcpp::Logger logger = rclcpp::get_logger(
"transformPathInTargetFrame");
94 if (input_path.header.frame_id == target_frame) {
95 transformed_path = input_path;
99 transformed_path.header.frame_id = target_frame;
100 transformed_path.header.stamp = input_path.header.stamp;
101 transformed_path.poses.reserve(input_path.poses.size());
103 for (
const auto & input_pose : input_path.poses) {
104 geometry_msgs::msg::PoseStamped source_pose, transformed_pose;
105 source_pose.header.frame_id = input_path.header.frame_id;
106 source_pose.header.stamp = input_path.header.stamp;
107 source_pose.pose = input_pose.pose;
109 if (!nav2_util::transformPoseInTargetFrame(
110 source_pose, transformed_pose, tf_buffer, target_frame, transform_timeout))
114 "Failed to transform path from '%s' to '%s'.",
115 input_path.header.frame_id.c_str(), target_frame.c_str());
119 transformed_pose.pose.position.z = 0.0;
120 transformed_path.poses.push_back(std::move(transformed_pose));
126 unsigned int findFirstPathConstraint(
127 nav_msgs::msg::Path & path,
128 bool enforce_path_inversion,
129 float rotation_threshold)
132 if (path.poses.size() < 3) {
133 return path.poses.size();
136 const bool check_rotation = fabs(rotation_threshold) < 1e-6f ? false :
true;
137 unsigned int rotation_idx = path.poses.size();
138 unsigned int inversion_idx = path.poses.size();
139 float prev_dx = 0.0f;
140 float prev_dy = 0.0f;
143 for (
unsigned int idx = 0; idx < path.poses.size() - 1; ++idx) {
144 float dx = path.poses[idx + 1].pose.position.x -
145 path.poses[idx].pose.position.x;
146 float dy = path.poses[idx + 1].pose.position.y -
147 path.poses[idx].pose.position.y;
148 float trans = hypot(dx, dy);
151 if (rotation_idx <= idx + 1) {
156 if (enforce_path_inversion && trans > 1e-4) {
159 float dot_product = prev_dx * dx + prev_dy * dy;
160 if (dot_product < 0.0f) {
161 inversion_idx = idx + 1;
170 if (check_rotation && trans < 1e-4 && rotation_idx == path.poses.size()) {
171 float accumulated_rotation = 0.0f;
172 unsigned int end_idx = idx;
176 while (end_idx < path.poses.size() - 1) {
177 float current_yaw = tf2::getYaw(path.poses[end_idx].pose.orientation);
178 float next_yaw = tf2::getYaw(path.poses[end_idx + 1].pose.orientation);
179 accumulated_rotation += fabs(angles::shortest_angular_distance(current_yaw, next_yaw));
180 if (accumulated_rotation > rotation_threshold) {
181 rotation_idx = end_idx + 1;
184 if (end_idx + 2 < path.poses.size()) {
185 float ndx = path.poses[end_idx + 2].pose.position.x -
186 path.poses[end_idx + 1].pose.position.x;
187 float ndy = path.poses[end_idx + 2].pose.position.y -
188 path.poses[end_idx + 1].pose.position.y;
190 if (hypot(ndx, ndy) > 1e-4) {
202 return std::min(rotation_idx, inversion_idx);
205 unsigned int removePosesAfterFirstConstraint(
206 nav_msgs::msg::Path & path,
207 bool enforce_path_inversion,
208 float rotation_threshold)
210 nav_msgs::msg::Path cropped_path = path;
211 const unsigned int first_after_constraint = findFirstPathConstraint(cropped_path,
212 enforce_path_inversion, rotation_threshold);
213 if (first_after_constraint == path.poses.size()) {
217 cropped_path.poses.erase(
218 cropped_path.poses.begin() + first_after_constraint, cropped_path.poses.end());
220 return first_after_constraint;
224 nav_msgs::msg::Path & new_path,
225 nav_msgs::msg::Path & old_path)
227 return old_path.poses.size() != 0 &&
228 new_path.poses.size() != 0 &&
229 new_path.poses.size() != old_path.poses.size() &&
230 old_path.poses.back().pose.position == new_path.poses.back().pose.position;
234 nav_msgs::msg::Path & new_path,
235 nav_msgs::msg::Path & old_path)
237 return old_path.poses.size() != 0 &&
238 new_path.poses.size() != 0 &&
239 new_path.poses.back().pose.position != old_path.poses.back().pose.position;