21 #include "nav2_controller/plugins/adaptive_tolerance_goal_checker.hpp"
22 #include "pluginlib/class_list_macros.hpp"
23 #include "angles/angles.h"
24 #include "nav2_ros_common/node_utils.hpp"
25 #include "nav2_util/geometry_utils.hpp"
26 #include "tf2/utils.hpp"
28 using rcl_interfaces::msg::ParameterType;
29 using std::placeholders::_1;
31 namespace nav2_controller
35 : fine_xy_goal_tolerance_(0.10),
36 fine_xy_goal_tolerance_sq_(0.01),
37 coarse_xy_goal_tolerance_(0.25),
38 coarse_xy_goal_tolerance_sq_(0.0625),
39 xy_goal_tolerance_buffer_(0.0),
40 fine_xy_goal_tolerance_reset_sq_(0.01),
41 coarse_xy_goal_tolerance_reset_sq_(0.0625),
42 yaw_goal_tolerance_(0.25),
43 path_length_tolerance_(1.0),
45 symmetric_yaw_tolerance_(false),
46 trans_stopped_velocity_(0.10),
47 rot_stopped_velocity_(0.10),
48 required_stagnation_cycles_(15),
50 in_tolerance_zone_(false),
51 stopped_stagnation_count_(0),
52 distance_stagnation_count_(0),
53 best_distance_sq_(std::numeric_limits<double>::max()),
62 auto node = node_.lock();
63 if (post_set_params_handler_ && node) {
64 node->remove_post_set_parameters_callback(post_set_params_handler_.get());
66 post_set_params_handler_.reset();
67 if (on_set_params_handler_ && node) {
68 node->remove_on_set_parameters_callback(on_set_params_handler_.get());
70 on_set_params_handler_.reset();
74 const nav2::LifecycleNode::WeakPtr & parent,
75 const std::string & plugin_name,
76 const std::shared_ptr<nav2_costmap_2d::Costmap2DROS>)
78 plugin_name_ = plugin_name;
80 auto node = node_.lock();
81 logger_ = node->get_logger();
83 fine_xy_goal_tolerance_ = node->declare_or_get_parameter(
84 plugin_name +
".fine_xy_goal_tolerance", 0.10);
85 coarse_xy_goal_tolerance_ = node->declare_or_get_parameter(
86 plugin_name +
".coarse_xy_goal_tolerance", 0.25);
87 xy_goal_tolerance_buffer_ = node->declare_or_get_parameter(
88 plugin_name +
".xy_goal_tolerance_buffer", 0.0);
89 yaw_goal_tolerance_ = node->declare_or_get_parameter(
90 plugin_name +
".yaw_goal_tolerance", 0.25);
91 path_length_tolerance_ = node->declare_or_get_parameter(
92 plugin_name +
".path_length_tolerance", 1.0);
93 stateful_ = node->declare_or_get_parameter(plugin_name +
".stateful",
true);
94 symmetric_yaw_tolerance_ = node->declare_or_get_parameter(
95 plugin_name +
".symmetric_yaw_tolerance",
false);
96 trans_stopped_velocity_ = node->declare_or_get_parameter(
97 plugin_name +
".trans_stopped_velocity", 0.10);
98 rot_stopped_velocity_ = node->declare_or_get_parameter(
99 plugin_name +
".rot_stopped_velocity", 0.10);
100 required_stagnation_cycles_ = node->declare_or_get_parameter(
101 plugin_name +
".required_stagnation_cycles", 15);
103 fine_xy_goal_tolerance_sq_ = fine_xy_goal_tolerance_ * fine_xy_goal_tolerance_;
104 coarse_xy_goal_tolerance_sq_ = coarse_xy_goal_tolerance_ * coarse_xy_goal_tolerance_;
105 fine_xy_goal_tolerance_reset_sq_ = (fine_xy_goal_tolerance_ + xy_goal_tolerance_buffer_) *
106 (fine_xy_goal_tolerance_ + xy_goal_tolerance_buffer_);
107 coarse_xy_goal_tolerance_reset_sq_ = (coarse_xy_goal_tolerance_ + xy_goal_tolerance_buffer_) *
108 (coarse_xy_goal_tolerance_ + xy_goal_tolerance_buffer_);
110 if (fine_xy_goal_tolerance_ >= coarse_xy_goal_tolerance_) {
112 logger_,
"Fine XY goal tolerance (%.3f) is greater or equal to coarse XY goal "
113 "tolerance (%.3f). This may lead to unintended behavior (when fine >= coarse the "
114 "checker will act as a simple goal checker). Consider setting "
115 "fine_xy_goal_tolerance < coarse_xy_goal_tolerance.",
116 fine_xy_goal_tolerance_, coarse_xy_goal_tolerance_);
119 post_set_params_handler_ = node->add_post_set_parameters_callback(
122 this, std::placeholders::_1));
123 on_set_params_handler_ = node->add_on_set_parameters_callback(
126 this, std::placeholders::_1));
132 in_tolerance_zone_ =
false;
133 stopped_stagnation_count_ = 0;
134 distance_stagnation_count_ = 0;
135 best_distance_sq_ = std::numeric_limits<double>::max();
138 xy_acceptance_reason_ = XyAcceptanceReason::NONE;
142 const geometry_msgs::msg::Pose & query_pose,
143 const geometry_msgs::msg::Pose & goal_pose,
144 const geometry_msgs::msg::Twist & velocity,
145 const nav_msgs::msg::Path & transformed_global_plan)
147 if (!
isGoalXYReached(query_pose, goal_pose, velocity, transformed_global_plan)) {
151 std::lock_guard<std::mutex> lock_reinit(mutex_);
154 const double query_yaw = tf2::getYaw(query_pose.orientation);
155 const double goal_yaw = tf2::getYaw(goal_pose.orientation);
156 bool yaw_reached =
false;
158 if (symmetric_yaw_tolerance_) {
159 const double dyaw_forward = angles::shortest_angular_distance(query_yaw, goal_yaw);
160 const double dyaw_backward = angles::shortest_angular_distance(
161 query_yaw, angles::normalize_angle(goal_yaw + M_PI));
162 yaw_reached = std::fabs(dyaw_forward) <= yaw_goal_tolerance_ ||
163 std::fabs(dyaw_backward) <= yaw_goal_tolerance_;
165 const double dyaw = angles::shortest_angular_distance(query_yaw, goal_yaw);
166 yaw_reached = std::fabs(dyaw) <= yaw_goal_tolerance_;
169 std::string xy_acceptance_reason =
toString(xy_acceptance_reason_);
173 "AdaptiveToleranceGoalChecker: goal reached via %s "
174 "(fine: %.3f m, coarse: %.3f m)",
175 xy_acceptance_reason.c_str(),
176 fine_xy_goal_tolerance_, coarse_xy_goal_tolerance_);
183 const geometry_msgs::msg::Pose & query_pose,
184 const geometry_msgs::msg::Pose & goal_pose,
185 const geometry_msgs::msg::Twist & velocity,
186 const nav_msgs::msg::Path & transformed_global_plan)
188 std::lock_guard<std::mutex> lock_reinit(mutex_);
191 if (nav2_util::geometry_utils::calculate_path_length(transformed_global_plan) >
192 path_length_tolerance_)
198 const double dx = query_pose.position.x - goal_pose.position.x;
199 const double dy = query_pose.position.y - goal_pose.position.y;
200 const double dist_sq = dx * dx + dy * dy;
203 if (dist_sq <= fine_xy_goal_tolerance_sq_) {
204 xy_acceptance_reason_ = XyAcceptanceReason::FINE_TOLERANCE;
211 }
else if (dist_sq <= coarse_xy_goal_tolerance_sq_) {
213 if (!in_tolerance_zone_) {
214 in_tolerance_zone_ =
true;
215 stopped_stagnation_count_ = 0;
216 distance_stagnation_count_ = 0;
217 best_distance_sq_ = dist_sq;
224 if (dist_sq < best_distance_sq_) {
225 best_distance_sq_ = dist_sq;
226 distance_stagnation_count_ = 0;
228 distance_stagnation_count_++;
233 std::hypot(velocity.linear.x, velocity.linear.y) <= trans_stopped_velocity_ &&
234 std::fabs(velocity.angular.z) <= rot_stopped_velocity_)
236 stopped_stagnation_count_++;
238 stopped_stagnation_count_ = 0;
244 const bool crossed_finish_line = dx * approach_dx_ + dy * approach_dy_ >= 0.0;
246 if (!crossed_finish_line &&
247 stopped_stagnation_count_ < required_stagnation_cycles_ &&
248 distance_stagnation_count_ < required_stagnation_cycles_)
254 if (crossed_finish_line) {
255 xy_acceptance_reason_ = XyAcceptanceReason::COARSE_TOLERANCE_FINISH_LINE;
256 }
else if (stopped_stagnation_count_ >= required_stagnation_cycles_) {
257 xy_acceptance_reason_ = XyAcceptanceReason::COARSE_TOLERANCE_STOPPED_STAGNATION;
259 xy_acceptance_reason_ = XyAcceptanceReason::COARSE_TOLERANCE_DISTANCE_STAGNATION;
267 in_tolerance_zone_ =
false;
268 stopped_stagnation_count_ = 0;
269 distance_stagnation_count_ = 0;
272 }
else if (stateful_ && xy_goal_tolerance_buffer_ > 0.0) {
273 const double dx = query_pose.position.x - goal_pose.position.x;
274 const double dy = query_pose.position.y - goal_pose.position.y;
275 const double dist_sq = dx * dx + dy * dy;
278 const double reset_threshold_sq =
279 xy_acceptance_reason_ == XyAcceptanceReason::FINE_TOLERANCE ?
280 fine_xy_goal_tolerance_reset_sq_ : coarse_xy_goal_tolerance_reset_sq_;
282 if (dist_sq > reset_threshold_sq) {
284 in_tolerance_zone_ =
false;
285 stopped_stagnation_count_ = 0;
286 distance_stagnation_count_ = 0;
295 geometry_msgs::msg::Pose & pose_tolerance,
296 geometry_msgs::msg::Twist & vel_tolerance,
297 double & path_length_tolerance)
299 std::lock_guard<std::mutex> lock_reinit(mutex_);
300 const double invalid_field = std::numeric_limits<double>::lowest();
303 pose_tolerance.position.x = coarse_xy_goal_tolerance_;
304 pose_tolerance.position.y = coarse_xy_goal_tolerance_;
305 pose_tolerance.position.z = invalid_field;
306 pose_tolerance.orientation =
307 nav2_util::geometry_utils::orientationAroundZAxis(yaw_goal_tolerance_);
309 vel_tolerance.linear.x = trans_stopped_velocity_;
310 vel_tolerance.linear.y = trans_stopped_velocity_;
311 vel_tolerance.linear.z = invalid_field;
313 vel_tolerance.angular.x = invalid_field;
314 vel_tolerance.angular.y = invalid_field;
315 vel_tolerance.angular.z = rot_stopped_velocity_;
317 path_length_tolerance = path_length_tolerance_;
325 case XyAcceptanceReason::FINE_TOLERANCE:
326 return "fine tolerance";
327 case XyAcceptanceReason::COARSE_TOLERANCE_FINISH_LINE:
328 return "coarse tolerance / finish line";
329 case XyAcceptanceReason::COARSE_TOLERANCE_STOPPED_STAGNATION:
330 return "coarse tolerance / stopped stagnation";
331 case XyAcceptanceReason::COARSE_TOLERANCE_DISTANCE_STAGNATION:
332 return "coarse tolerance / distance stagnation";
333 case XyAcceptanceReason::NONE:
339 rcl_interfaces::msg::SetParametersResult
341 const std::vector<rclcpp::Parameter> & parameters)
343 rcl_interfaces::msg::SetParametersResult result;
344 result.successful =
true;
345 for (
const auto & parameter : parameters) {
346 const auto & param_type = parameter.get_type();
347 const auto & param_name = parameter.get_name();
348 if (param_name.find(plugin_name_ +
".") != 0) {
351 if (param_type == ParameterType::PARAMETER_DOUBLE) {
352 if (parameter.as_double() < 0.0) {
354 logger_,
"The value of parameter '%s' is incorrectly set to %f, "
355 "it should be >=0. Ignoring parameter update.",
356 param_name.c_str(), parameter.as_double());
357 result.successful =
false;
359 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
360 if (param_name == plugin_name_ +
".required_stagnation_cycles" &&
361 parameter.as_int() < 1)
364 logger_,
"The value of parameter '%s' is incorrectly set to %ld, "
365 "it should be >= 1. Ignoring parameter update.",
366 param_name.c_str(), parameter.as_int());
367 result.successful =
false;
376 const std::vector<rclcpp::Parameter> & parameters)
378 std::lock_guard<std::mutex> lock_reinit(mutex_);
379 for (
const auto & parameter : parameters) {
380 const auto & param_type = parameter.get_type();
381 const auto & param_name = parameter.get_name();
382 if (param_name.find(plugin_name_ +
".") != 0) {
385 if (param_type == ParameterType::PARAMETER_DOUBLE) {
386 if (param_name == plugin_name_ +
".fine_xy_goal_tolerance") {
387 fine_xy_goal_tolerance_ = parameter.as_double();
388 fine_xy_goal_tolerance_sq_ = fine_xy_goal_tolerance_ * fine_xy_goal_tolerance_;
389 fine_xy_goal_tolerance_reset_sq_ =
390 (fine_xy_goal_tolerance_ + xy_goal_tolerance_buffer_) *
391 (fine_xy_goal_tolerance_ + xy_goal_tolerance_buffer_);
392 if (fine_xy_goal_tolerance_ >= coarse_xy_goal_tolerance_) {
394 logger_,
"Fine XY goal tolerance (%.3f) is greater or equal to coarse XY goal "
395 "tolerance (%.3f). This may lead to unintended behavior (when fine >= coarse the "
396 "checker will act as a simple goal checker). Consider setting "
397 "fine_xy_goal_tolerance < coarse_xy_goal_tolerance.",
398 fine_xy_goal_tolerance_, coarse_xy_goal_tolerance_);
400 }
else if (param_name == plugin_name_ +
".coarse_xy_goal_tolerance") {
401 coarse_xy_goal_tolerance_ = parameter.as_double();
402 coarse_xy_goal_tolerance_sq_ = coarse_xy_goal_tolerance_ * coarse_xy_goal_tolerance_;
403 coarse_xy_goal_tolerance_reset_sq_ =
404 (coarse_xy_goal_tolerance_ + xy_goal_tolerance_buffer_) *
405 (coarse_xy_goal_tolerance_ + xy_goal_tolerance_buffer_);
406 if (fine_xy_goal_tolerance_ >= coarse_xy_goal_tolerance_) {
408 logger_,
"Fine XY goal tolerance (%.3f) is greater or equal to coarse XY goal "
409 "tolerance (%.3f). This may lead to unintended behavior (when fine >= coarse the "
410 "checker will act as a simple goal checker). Consider setting "
411 "fine_xy_goal_tolerance < coarse_xy_goal_tolerance.",
412 fine_xy_goal_tolerance_, coarse_xy_goal_tolerance_);
414 }
else if (param_name == plugin_name_ +
".xy_goal_tolerance_buffer") {
415 xy_goal_tolerance_buffer_ = parameter.as_double();
416 fine_xy_goal_tolerance_reset_sq_ =
417 (fine_xy_goal_tolerance_ + xy_goal_tolerance_buffer_) *
418 (fine_xy_goal_tolerance_ + xy_goal_tolerance_buffer_);
419 coarse_xy_goal_tolerance_reset_sq_ =
420 (coarse_xy_goal_tolerance_ + xy_goal_tolerance_buffer_) *
421 (coarse_xy_goal_tolerance_ + xy_goal_tolerance_buffer_);
422 }
else if (param_name == plugin_name_ +
".yaw_goal_tolerance") {
423 yaw_goal_tolerance_ = parameter.as_double();
424 }
else if (param_name == plugin_name_ +
".path_length_tolerance") {
425 path_length_tolerance_ = parameter.as_double();
426 }
else if (param_name == plugin_name_ +
".trans_stopped_velocity") {
427 trans_stopped_velocity_ = parameter.as_double();
428 }
else if (param_name == plugin_name_ +
".rot_stopped_velocity") {
429 rot_stopped_velocity_ = parameter.as_double();
431 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
432 if (param_name == plugin_name_ +
".stateful") {
433 stateful_ = parameter.as_bool();
434 }
else if (param_name == plugin_name_ +
".symmetric_yaw_tolerance") {
435 symmetric_yaw_tolerance_ = parameter.as_bool();
437 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
438 if (param_name == plugin_name_ +
".required_stagnation_cycles") {
439 required_stagnation_cycles_ =
static_cast<int>(parameter.as_int());
Goal Checker plugin with two tolerance tiers: a tight desired tolerance and a looser coarse tolerance...
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...
bool isGoalXYReached(const geometry_msgs::msg::Pose &query_pose, const geometry_msgs::msg::Pose &goal_pose, const geometry_msgs::msg::Twist &velocity, const nav_msgs::msg::Path &transformed_global_plan) override
Check if XY goal position has been reached (without considering yaw)
XyAcceptanceReason
Reason that the XY component of the goal was accepted.
bool getTolerances(geometry_msgs::msg::Pose &pose_tolerance, geometry_msgs::msg::Twist &vel_tolerance, double &path_length_tolerance) override
Get the position and velocity tolerances.
void updateParametersCallback(const std::vector< rclcpp::Parameter > ¶meters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
void initialize(const nav2::LifecycleNode::WeakPtr &parent, const std::string &plugin_name, const std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros) override
Initialize the goal checker.
~AdaptiveToleranceGoalChecker()
Destroy the Progress Goal Checker object.
void reset() override
Reset the goal checker state.
AdaptiveToleranceGoalChecker()
Construct a new Progress Goal Checker object.
static std::string toString(XyAcceptanceReason reason)
Convert XY acceptance reason to string.
bool isGoalReached(const geometry_msgs::msg::Pose &query_pose, const geometry_msgs::msg::Pose &goal_pose, const geometry_msgs::msg::Twist &velocity, const nav_msgs::msg::Path &transformed_global_plan) override
Check if the goal is reached.
Function-object for checking whether a goal has been reached.