15 #include "nav2_util/path_utils.hpp"
21 #include "nav2_util/geometry_utils.hpp"
22 #include "nav2_ros_common/tf2_factories.hpp"
23 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
27 PathSearchResult distance_from_path(
28 const nav_msgs::msg::Path & path,
29 const geometry_msgs::msg::Pose & robot_pose,
30 const size_t start_index,
31 const double search_window_length)
33 PathSearchResult result;
34 result.closest_segment_index = start_index;
35 result.distance = std::numeric_limits<double>::max();
37 if (path.poses.empty()) {
41 if (path.poses.size() == 1) {
42 result.distance = nav2_util::geometry_utils::euclidean_distance(
43 robot_pose, path.poses.front().pose);
44 result.closest_segment_index = 0;
48 if (start_index >= path.poses.size()) {
49 throw std::runtime_error(
50 "Requested start index (" + std::to_string(start_index) +
51 ") is greater than or equal to path size (" + std::to_string(path.poses.size()) +
55 double distance_traversed = 0.0;
56 for (
size_t i = start_index; i < path.poses.size() - 1; ++i) {
57 if (distance_traversed > search_window_length) {
61 const double current_distance = geometry_utils::distance_to_path_segment(
64 path.poses[i + 1].pose);
66 if (current_distance < result.distance) {
67 result.distance = current_distance;
68 result.closest_segment_index = i;
71 distance_traversed += geometry_utils::euclidean_distance(
76 const auto & segment_start = path.poses[result.closest_segment_index];
77 const auto & segment_end = path.poses[result.closest_segment_index + 1];
80 const double cross_product = geometry_utils::cross_product_2d(
81 robot_pose.position, segment_start.pose, segment_end.pose);
82 result.distance *= (cross_product >= 0.0 ? 1.0 : -1.0);
87 bool transformPathInTargetFrame(
88 const nav_msgs::msg::Path & input_path,
89 nav_msgs::msg::Path & transformed_path,
90 nav2::TransformBuffer & tf_buffer,
const std::string target_frame,
91 const double transform_timeout)
93 static rclcpp::Logger logger = rclcpp::get_logger(
"transformPathInTargetFrame");
95 if (input_path.header.frame_id == target_frame) {
96 transformed_path = input_path;
100 transformed_path.header.frame_id = target_frame;
101 transformed_path.header.stamp = input_path.header.stamp;
102 transformed_path.poses.reserve(input_path.poses.size());
104 if (input_path.poses.empty()) {
108 geometry_msgs::msg::TransformStamped transform;
110 transform = tf_buffer.lookupTransform(
111 target_frame, input_path.header.frame_id,
112 tf2_ros::fromMsg(input_path.header.stamp),
113 tf2::durationFromSec(transform_timeout));
114 }
catch (
const tf2::TransformException & ex) {
117 "Failed to transform path from '%s' to '%s': %s",
118 input_path.header.frame_id.c_str(), target_frame.c_str(), ex.what());
122 for (
const auto & input_pose : input_path.poses) {
123 geometry_msgs::msg::PoseStamped source_pose, transformed_pose;
124 source_pose.header.frame_id = input_path.header.frame_id;
125 source_pose.header.stamp = input_path.header.stamp;
126 source_pose.pose = input_pose.pose;
128 tf2::doTransform(source_pose, transformed_pose, transform);
129 transformed_pose.pose.position.z = 0.0;
130 transformed_path.poses.push_back(std::move(transformed_pose));
136 unsigned int findFirstPathConstraint(
137 nav_msgs::msg::Path & path,
138 bool enforce_path_inversion,
139 float rotation_threshold)
142 if (path.poses.size() < 3) {
143 return path.poses.size();
146 const bool check_rotation = fabs(rotation_threshold) < 1e-6f ? false :
true;
147 unsigned int rotation_idx = path.poses.size();
148 unsigned int inversion_idx = path.poses.size();
149 float prev_dx = 0.0f;
150 float prev_dy = 0.0f;
153 for (
unsigned int idx = 0; idx < path.poses.size() - 1; ++idx) {
154 float dx = path.poses[idx + 1].pose.position.x -
155 path.poses[idx].pose.position.x;
156 float dy = path.poses[idx + 1].pose.position.y -
157 path.poses[idx].pose.position.y;
158 float trans = hypot(dx, dy);
161 if (rotation_idx <= idx + 1) {
166 if (enforce_path_inversion && trans > 1e-4) {
169 float dot_product = prev_dx * dx + prev_dy * dy;
170 if (dot_product < 0.0f) {
171 inversion_idx = idx + 1;
180 if (check_rotation && trans < 1e-4 && rotation_idx == path.poses.size()) {
181 float accumulated_rotation = 0.0f;
182 unsigned int end_idx = idx;
186 while (end_idx < path.poses.size() - 1) {
187 float current_yaw = tf2::getYaw(path.poses[end_idx].pose.orientation);
188 float next_yaw = tf2::getYaw(path.poses[end_idx + 1].pose.orientation);
189 accumulated_rotation += fabs(angles::shortest_angular_distance(current_yaw, next_yaw));
190 if (accumulated_rotation > rotation_threshold) {
191 rotation_idx = end_idx + 1;
194 if (end_idx + 2 < path.poses.size()) {
195 float ndx = path.poses[end_idx + 2].pose.position.x -
196 path.poses[end_idx + 1].pose.position.x;
197 float ndy = path.poses[end_idx + 2].pose.position.y -
198 path.poses[end_idx + 1].pose.position.y;
200 if (hypot(ndx, ndy) > 1e-4) {
212 return std::min(rotation_idx, inversion_idx);
215 unsigned int removePosesAfterFirstConstraint(
216 nav_msgs::msg::Path & path,
217 bool enforce_path_inversion,
218 float rotation_threshold)
220 nav_msgs::msg::Path cropped_path = path;
221 const unsigned int first_after_constraint = findFirstPathConstraint(cropped_path,
222 enforce_path_inversion, rotation_threshold);
223 if (first_after_constraint == path.poses.size()) {
227 cropped_path.poses.erase(
228 cropped_path.poses.begin() + first_after_constraint, cropped_path.poses.end());
230 return first_after_constraint;
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.size() != old_path.poses.size() &&
240 old_path.poses.back().pose.position == new_path.poses.back().pose.position;
244 nav_msgs::msg::Path & new_path,
245 nav_msgs::msg::Path & old_path)
247 return old_path.poses.size() != 0 &&
248 new_path.poses.size() != 0 &&
249 new_path.poses.back().pose.position != old_path.poses.back().pose.position;