18 #include "angles/angles.h"
19 #include "pluginlib/class_list_macros.hpp"
20 #include "nav2_controller/plugins/feasible_path_handler.hpp"
21 #include "nav2_util/path_utils.hpp"
22 #include "nav2_util/geometry_utils.hpp"
23 #include "nav2_core/controller_exceptions.hpp"
24 #include "nav2_ros_common/tf2_factories.hpp"
27 using rcl_interfaces::msg::ParameterType;
28 using std::placeholders::_1;
30 namespace nav2_controller
32 using nav2_util::geometry_utils::euclidean_distance;
36 auto node = node_.lock();
37 if (post_set_params_handler_ && node) {
38 node->remove_post_set_parameters_callback(post_set_params_handler_.get());
40 post_set_params_handler_.reset();
41 if (on_set_params_handler_ && node) {
42 node->remove_on_set_parameters_callback(on_set_params_handler_.get());
44 on_set_params_handler_.reset();
48 const nav2::LifecycleNode::WeakPtr & parent,
49 const rclcpp::Logger & logger,
50 const std::string & plugin_name,
51 const std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros,
52 nav2::TransformBuffer::SharedPtr tf)
55 plugin_name_ = plugin_name;
57 auto node = node_.lock();
58 costmap_ros_ = costmap_ros;
60 transform_tolerance_ = costmap_ros_->getTransformTolerance();
61 reject_unit_path_ = node->declare_or_get_parameter(
62 plugin_name +
".reject_unit_path",
false);
63 max_robot_pose_search_dist_ = node->declare_or_get_parameter(
65 prune_distance_ = node->declare_or_get_parameter(
66 plugin_name +
".prune_distance", 2.0);
67 enforce_path_inversion_ = node->declare_or_get_parameter(
68 plugin_name +
".enforce_path_inversion",
false);
69 enforce_path_rotation_ = node->declare_or_get_parameter(
70 plugin_name +
".enforce_path_rotation",
false);
71 inversion_xy_tolerance_ = node->declare_or_get_parameter(
72 plugin_name +
".inversion_xy_tolerance", 0.2);
73 inversion_yaw_tolerance_ = node->declare_or_get_parameter(
74 plugin_name +
".inversion_yaw_tolerance", 0.4);
75 minimum_rotation_angle_ = node->declare_or_get_parameter(
76 plugin_name +
".minimum_rotation_angle", 0.785);
77 if (max_robot_pose_search_dist_ < 0.0) {
79 logger_,
"Max robot search distance is negative, setting to max to search"
80 " every point on path for the closest value.");
81 max_robot_pose_search_dist_ = std::numeric_limits<double>::max();
83 constraint_locale_ = 0u;
84 if (!enforce_path_rotation_) {
85 minimum_rotation_angle_ = 0.0f;
89 post_set_params_handler_ = node->add_post_set_parameters_callback(
92 this, std::placeholders::_1));
93 on_set_params_handler_ = node->add_on_set_parameters_callback(
96 this, std::placeholders::_1));
101 const double max_costmap_dim_meters = std::max(
102 costmap_ros_->getCostmap()->getSizeInMetersX(),
103 costmap_ros_->getCostmap()->getSizeInMetersY());
104 return max_costmap_dim_meters / 2.0;
109 plan.poses.erase(plan.poses.begin(), end);
113 const geometry_msgs::msg::PoseStamped & robot_pose)
116 const auto last_pose = global_plan_up_to_constraint_.poses.back();
117 float distance = hypotf(
118 robot_pose.pose.position.x - last_pose.pose.position.x,
119 robot_pose.pose.position.y - last_pose.pose.position.y);
121 float angle_distance = angles::shortest_angular_distance(
122 tf2::getYaw(robot_pose.pose.orientation),
123 tf2::getYaw(last_pose.pose.orientation));
125 return distance <= inversion_xy_tolerance_ &&
126 fabs(angle_distance) <= inversion_yaw_tolerance_;
131 std::lock_guard<std::mutex> lock_reinit(mutex_);
133 global_plan_up_to_constraint_ = global_plan_;
134 if (enforce_path_inversion_ || enforce_path_rotation_) {
135 constraint_locale_ = nav2_util::removePosesAfterFirstConstraint(global_plan_up_to_constraint_,
136 enforce_path_inversion_, minimum_rotation_angle_);
141 const geometry_msgs::msg::PoseStamped & pose)
143 if (global_plan_up_to_constraint_.poses.empty()) {
147 if (reject_unit_path_ && global_plan_up_to_constraint_.poses.size() == 1) {
152 geometry_msgs::msg::PoseStamped robot_pose;
153 if (!nav2_util::transformPoseInTargetFrame(pose, robot_pose, *tf_,
154 global_plan_up_to_constraint_.header.frame_id,
155 transform_tolerance_))
164 const geometry_msgs::msg::PoseStamped & pose)
166 std::lock_guard<std::mutex> lock_reinit(mutex_);
170 auto closest_pose_upper_bound =
171 nav2_util::geometry_utils::first_after_integrated_distance(
172 global_plan_up_to_constraint_.poses.begin(), global_plan_up_to_constraint_.poses.end(),
173 max_robot_pose_search_dist_);
179 nav2_util::geometry_utils::min_by(
180 global_plan_up_to_constraint_.poses.begin(), closest_pose_upper_bound,
181 [
this](
const geometry_msgs::msg::PoseStamped & ps) {
182 return euclidean_distance(global_pose_, ps);
187 if (global_plan_up_to_constraint_.poses.size() > 1 &&
188 closest_point == std::prev(global_plan_up_to_constraint_.poses.end()))
190 closest_point = std::prev(closest_point);
193 auto pruned_plan_end =
194 nav2_util::geometry_utils::first_after_integrated_distance(
195 closest_point, global_plan_up_to_constraint_.poses.end(), prune_distance_);
197 return {closest_point, pruned_plan_end};
202 const nav2_core::PathIterator & closest_point,
203 const nav2_core::PathIterator & pruned_plan_end)
205 std::lock_guard<std::mutex> lock_reinit(mutex_);
206 nav_msgs::msg::Path transformed_plan;
207 transformed_plan.header.frame_id = costmap_ros_->getGlobalFrameID();
208 transformed_plan.header.stamp = global_pose_.header.stamp;
210 geometry_msgs::msg::TransformStamped plan_to_costmap;
213 plan_to_costmap = tf_->lookupTransform(
214 costmap_ros_->getGlobalFrameID(),
215 global_plan_.header.frame_id,
216 global_pose_.header.stamp,
217 tf2::durationFromSec(transform_tolerance_));
218 }
catch (
const tf2::TransformException & ex) {
220 std::string(
"Unable to transform global plan into costmap frame: ") +
227 std::lock_guard<nav2_costmap_2d::Costmap2D::mutex_t> costmap_lock(
228 *(costmap_ros_->getCostmap()->getMutex()));
229 for (
auto global_plan_pose = closest_point; global_plan_pose != pruned_plan_end;
232 geometry_msgs::msg::PoseStamped plan_pose = *global_plan_pose;
233 plan_pose.header = global_pose_.header;
234 geometry_msgs::msg::PoseStamped costmap_plan_pose;
235 tf2::doTransform(plan_pose, costmap_plan_pose, plan_to_costmap);
236 if (!costmap_ros_->getCostmap()->worldToMap(
237 costmap_plan_pose.pose.position.x,
238 costmap_plan_pose.pose.position.y, mx, my))
243 transformed_plan.poses.push_back(costmap_plan_pose);
248 prunePlan(global_plan_up_to_constraint_, closest_point);
250 if ((enforce_path_inversion_ || enforce_path_rotation_) && constraint_locale_ != 0u) {
252 prunePlan(global_plan_, global_plan_.poses.begin() + constraint_locale_);
253 global_plan_up_to_constraint_ = global_plan_;
254 constraint_locale_ = nav2_util::removePosesAfterFirstConstraint(global_plan_up_to_constraint_,
255 enforce_path_inversion_, minimum_rotation_angle_);
259 if (transformed_plan.poses.empty()) {
263 return transformed_plan;
267 const builtin_interfaces::msg::Time & stamp)
269 auto goal = global_plan_.poses.back();
270 goal.header.frame_id = global_plan_.header.frame_id;
271 goal.header.stamp = stamp;
272 if (goal.header.frame_id.empty()) {
275 geometry_msgs::msg::PoseStamped transformed_goal;
276 if (!nav2_util::transformPoseInTargetFrame(goal, transformed_goal, *costmap_ros_->getTfBuffer(),
277 costmap_ros_->getGlobalFrameID(), transform_tolerance_))
281 return transformed_goal;
284 rcl_interfaces::msg::SetParametersResult
286 const std::vector<rclcpp::Parameter> & parameters)
288 rcl_interfaces::msg::SetParametersResult result;
289 result.successful =
true;
290 for (
const auto & parameter : parameters) {
291 const auto & param_type = parameter.get_type();
292 const auto & param_name = parameter.get_name();
293 if (param_name.find(plugin_name_ +
".") != 0) {
296 if (param_type == ParameterType::PARAMETER_DOUBLE) {
297 if (parameter.as_double() < 0.0) {
299 logger_,
"The value of parameter '%s' is incorrectly set to %f, "
300 "it should be >=0. Ignoring parameter update.",
301 param_name.c_str(), parameter.as_double());
302 result.successful =
false;
311 const std::vector<rclcpp::Parameter> & parameters)
313 std::lock_guard<std::mutex> lock_reinit(mutex_);
314 rcl_interfaces::msg::SetParametersResult result;
315 for (
const auto & parameter : parameters) {
316 const auto & param_type = parameter.get_type();
317 const auto & param_name = parameter.get_name();
318 if (param_name.find(plugin_name_ +
".") != 0) {
321 if (param_type == ParameterType::PARAMETER_DOUBLE) {
322 if (param_name == plugin_name_ +
".max_robot_pose_search_dist") {
323 max_robot_pose_search_dist_ = parameter.as_double();
324 }
else if (param_name == plugin_name_ +
".inversion_xy_tolerance") {
325 inversion_xy_tolerance_ = parameter.as_double();
326 }
else if (param_name == plugin_name_ +
".inversion_yaw_tolerance") {
327 inversion_yaw_tolerance_ = parameter.as_double();
328 }
else if (param_name == plugin_name_ +
".prune_distance") {
329 prune_distance_ = parameter.as_double();
330 }
else if (param_name == plugin_name_ +
".minimum_rotation_angle") {
331 minimum_rotation_angle_ = parameter.as_double();
333 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
334 if (param_name == plugin_name_ +
".enforce_path_inversion") {
335 enforce_path_inversion_ = parameter.as_bool();
336 }
else if (param_name == plugin_name_ +
".enforce_path_rotation") {
337 enforce_path_rotation_ = parameter.as_bool();
338 if (!enforce_path_rotation_) {
339 minimum_rotation_angle_ = 0.0f;
This plugin manages the global plan by clipping it to the local segment, typically bounded by the loc...
~FeasiblePathHandler()
Destroy the Feasible Path Handler object.
double getCostmapMaxExtent() const
void prunePlan(nav_msgs::msg::Path &plan, const nav2_core::PathIterator end)
Prune a path to only interesting portions.
nav_msgs::msg::Path transformLocalPlan(const nav2_core::PathIterator &closest_point, const nav2_core::PathIterator &pruned_plan_end) override
Transforms a predefined segment of the global plan into the costmap global frame.
bool isWithinInversionTolerances(const geometry_msgs::msg::PoseStamped &robot_pose)
Check if the robot pose is within the set inversion tolerances.
geometry_msgs::msg::PoseStamped transformToGlobalPlanFrame(const geometry_msgs::msg::PoseStamped &pose)
Transform a pose to the global reference frame.
void setPlan(const nav_msgs::msg::Path &path) override
Set new reference plan.
void updateParametersCallback(const std::vector< rclcpp::Parameter > ¶meters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
geometry_msgs::msg::PoseStamped getTransformedGoal(const builtin_interfaces::msg::Time &stamp) override
Get the global goal pose transformed to the costmap global frame.
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > ¶meters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
nav2_core::PathSegment findPlanSegment(const geometry_msgs::msg::PoseStamped &pose) override
Determines the portion of the global plan to be used for local control. This function locates the sta...
void initialize(const nav2::LifecycleNode::WeakPtr &parent, const rclcpp::Logger &logger, const std::string &plugin_name, const std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros, nav2::TransformBuffer::SharedPtr tf) override
Initialize parameters.
Function-object for handling the path from Planner Server.