SMACC2
Loading...
Searching...
No Matches
backward_local_planner.cpp
Go to the documentation of this file.
1// Copyright 2025 Robosoft Inc.
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/*****************************************************************************************************************
16 *
17 * Authors: Pablo Inigo Blasco, Brett Aldrich
18 *
19 ******************************************************************************************************************/
20
21#include <angles/angles.h>
24
25#include <boost/intrusive_ptr.hpp>
26#include <chrono>
27#include <nav_2d_utils/tf_help.hpp>
28#include <pluginlib/class_list_macros.hpp>
29#include <visualization_msgs/msg/marker_array.hpp>
30
31// register this planner as a BaseLocalPlanner plugin
32PLUGINLIB_EXPORT_CLASS(
34
35using namespace std::literals::chrono_literals;
36
37namespace cl_nav2z
38{
39namespace backward_local_planner
40{
47
54
56{
57 RCLCPP_INFO_STREAM(nh_->get_logger(), "activating controller BackwardLocalPlanner");
59
60 goalMarkerPublisher_->on_activate();
61 planPub_->on_activate();
62 backwardsPlanPath_.clear();
63}
64
66{
67 this->clearMarkers();
68 RCLCPP_WARN_STREAM(nh_->get_logger(), "[BackwardLocalPlanner] deactivated");
69 planPub_->on_deactivate();
70 goalMarkerPublisher_->on_deactivate();
71}
72
74{
75 this->clearMarkers();
76 RCLCPP_WARN_STREAM(nh_->get_logger(), "[BackwardLocalPlanner] cleanup");
77 this->backwardsPlanPath_.clear();
79}
80
87template <typename T>
88void tryGetOrSet(rclcpp_lifecycle::LifecycleNode::SharedPtr & node, std::string param, T & value)
89{
90 if (!node->get_parameter(param, value))
91 {
92 node->set_parameter(rclcpp::Parameter(param, value));
93 }
94}
95
97 const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent, std::string name,
98 const std::shared_ptr<tf2_ros::Buffer> tf,
99 const std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
100{
101 this->costmapRos_ = costmap_ros;
102 this->nh_ = parent.lock();
103 this->name_ = name;
104 this->tf_ = tf;
105
106 k_rho_ = -1.0;
107 k_alpha_ = 0.5;
108 k_betta_ = -1.0; // set to zero means that orientation is not important
109 carrot_distance_ = 0.4;
119 waitingTimeout_ = rclcpp::Duration(10s);
120
121 this->currentCarrotPoseIndex_ = 0;
122
124 nh_, name_ + ".pure_spinning_straight_line_mode", straightBackwardsAndPureSpinningMode_);
125
126 declareOrSet(nh_, name_ + ".k_rho", k_rho_);
127 declareOrSet(nh_, name_ + ".k_alpha", k_alpha_);
128 declareOrSet(nh_, name_ + ".k_betta", k_betta_);
129 declareOrSet(nh_, name_ + ".linear_mode_rho_error_threshold", linear_mode_rho_error_threshold_);
131 nh_, name_ + ".initial_rotation_alpha_error_threshold",
133
134 declareOrSet(nh_, name_ + ".carrot_distance", carrot_distance_);
135 declareOrSet(nh_, name_ + ".carrot_angular_distance", carrot_angular_distance_);
136 declareOrSet(nh_, name_ + ".enable_obstacle_checking", enable_obstacle_checking_);
137
138 declareOrSet(nh_, name_ + ".max_linear_x_speed", max_linear_x_speed_);
139 declareOrSet(nh_, name_ + ".max_angular_z_speed", max_angular_z_speed_);
140 declareOrSet(nh_, name_ + ".transform_tolerance", transform_tolerance_);
141
142 // we have to do this, for example for the case we are refining the final orientation.
143 // check at some point if the carrot is reached in "goal linear distance", then we go into
144 // some automatic pure-spinning mode where we only update the orientation
145 // This means that if we reach the carrot with precision we go into pure spinning mode but we cannot
146 // leave that point (maybe this could be improved)
147
149 {
150 RCLCPP_WARN_STREAM(
151 nh_->get_logger(), "[BackwardLocalPlanner] carrot_angular_distance ("
153 << ") cannot be lower than yaw_goal_tolerance (" << yaw_goal_tolerance_
154 << ") setting carrot_angular_distance = " << yaw_goal_tolerance_);
156 }
157
159 {
160 RCLCPP_WARN_STREAM(
161 nh_->get_logger(), "[BackwardLocalPlanner] carrot_linear_distance ("
162 << carrot_distance_ << ") cannot be lower than xy_goal_tolerance_ ("
164 << ") setting carrot_angular_distance = " << xy_goal_tolerance_);
166 }
167
168 goalMarkerPublisher_ = nh_->create_publisher<visualization_msgs::msg::MarkerArray>(
169 "backward_local_planner/goal_marker", rclcpp::QoS(1));
170
171 planPub_ =
172 nh_->create_publisher<nav_msgs::msg::Path>("backward_local_planner/path", rclcpp::QoS(1));
173}
174
176{
177 RCLCPP_INFO_STREAM(nh_->get_logger(), "--- parameters ---");
178 tryGetOrSet(nh_, name_ + ".k_rho", k_rho_);
179 RCLCPP_INFO_STREAM(nh_->get_logger(), name_ + ".k_rho:" << k_rho_);
180 tryGetOrSet(nh_, name_ + ".k_alpha", k_alpha_);
181 RCLCPP_INFO_STREAM(nh_->get_logger(), name_ + ".k_alpha:" << k_alpha_);
182 tryGetOrSet(nh_, name_ + ".k_betta", k_betta_);
183 RCLCPP_INFO_STREAM(nh_->get_logger(), name_ + ".k_betta:" << k_betta_);
185 nh_, name_ + ".initial_rotation_alpha_error_threshold",
187 RCLCPP_INFO_STREAM(
188 nh_->get_logger(),
189 name_ + ".initial_rotation_alpha_error_threshold: " << initial_rotation_alpha_error_threshold_);
190
191 tryGetOrSet(nh_, name_ + ".enable_obstacle_checking", enable_obstacle_checking_);
192 RCLCPP_INFO_STREAM(
193 nh_->get_logger(), name_ + ".enable_obstacle_checking: " << enable_obstacle_checking_);
194
195 tryGetOrSet(nh_, name_ + ".carrot_distance", carrot_distance_);
196 RCLCPP_INFO_STREAM(nh_->get_logger(), name_ + ".carrot_distance:" << carrot_distance_);
197 tryGetOrSet(nh_, name_ + ".carrot_angular_distance", carrot_angular_distance_);
198 RCLCPP_INFO_STREAM(
199 nh_->get_logger(), name_ + ".carrot_angular_distance: " << carrot_angular_distance_);
200
202 nh_, name_ + ".pure_spinning_straight_line_mode", straightBackwardsAndPureSpinningMode_);
203 RCLCPP_INFO_STREAM(
204 nh_->get_logger(),
205 name_ + ".pure_spinning_straight_line_mode: " << straightBackwardsAndPureSpinningMode_);
206
207 tryGetOrSet(nh_, name_ + ".linear_mode_rho_error_threshold", linear_mode_rho_error_threshold_);
208 RCLCPP_INFO_STREAM(
209 nh_->get_logger(),
210 name_ + ".linear_mode_rho_error_threshold: " << linear_mode_rho_error_threshold_);
211 tryGetOrSet(nh_, name_ + ".max_linear_x_speed", max_linear_x_speed_);
212 RCLCPP_INFO_STREAM(nh_->get_logger(), name_ + ".max_linear_x_speed: " << max_linear_x_speed_);
213 tryGetOrSet(nh_, name_ + ".max_angular_z_speed", max_angular_z_speed_);
214 RCLCPP_INFO_STREAM(nh_->get_logger(), name_ + ".max_angular_z_speed: " << max_angular_z_speed_);
215
217 {
218 RCLCPP_WARN_STREAM(
219 nh_->get_logger(), "[BackwardLocalPlanner] carrot_angular_distance ("
221 << ") cannot be lower than yaw_goal_tolerance (" << yaw_goal_tolerance_
222 << ") setting carrot_angular_distance = " << yaw_goal_tolerance_);
224 nh_->set_parameter(
225 rclcpp::Parameter(name_ + ".carrot_angular_distance", carrot_angular_distance_));
226 }
227 RCLCPP_INFO_STREAM(
228 nh_->get_logger(), name_ + ".carrot_angular_distance: " << carrot_angular_distance_);
229
231 {
232 RCLCPP_WARN_STREAM(
233 nh_->get_logger(), "[BackwardLocalPlanner] carrot_linear_distance ("
234 << carrot_distance_ << ") cannot be lower than xy_goal_tolerance_ ("
236 << ") setting carrot_angular_distance = " << xy_goal_tolerance_);
238 nh_->set_parameter(rclcpp::Parameter(name_ + ".carrot_distance", carrot_distance_));
239 }
240 RCLCPP_INFO_STREAM(nh_->get_logger(), name_ + ".carrot_distance:" << carrot_distance_);
241 RCLCPP_INFO_STREAM(nh_->get_logger(), "--- end params ---");
242}
243
245 const double & /*speed_limit*/, const bool & /*percentage*/)
246{
247 RCLCPP_WARN_STREAM(
248 nh_->get_logger(),
249 "BackwardLocalPlanner::setSpeedLimit invoked. Ignored, functionality not "
250 "implemented.");
251}
258 const geometry_msgs::msg::PoseStamped & tfpose, double & dist, double & angular_error)
259{
260 double angle = tf2::getYaw(tfpose.pose.orientation);
261 auto & carrot_pose = backwardsPlanPath_[currentCarrotPoseIndex_];
262 const geometry_msgs::msg::Point & carrot_point = carrot_pose.pose.position;
263
264 tf2::Quaternion carrot_orientation;
265 tf2::convert(carrot_pose.pose.orientation, carrot_orientation);
266 geometry_msgs::msg::Pose currentPoseDebugMsg = tfpose.pose;
267
268 // take error from the current position to the path point
269 double dx = carrot_point.x - tfpose.pose.position.x;
270 double dy = carrot_point.y - tfpose.pose.position.y;
271
272 dist = sqrt(dx * dx + dy * dy);
273
274 double pangle = tf2::getYaw(carrot_orientation);
275 angular_error = fabs(angles::shortest_angular_distance(pangle, angle));
276
277 RCLCPP_INFO_STREAM(
278 nh_->get_logger(), "[BackwardLocalPlanner] Compute carrot errors from current pose. (linear "
279 << dist << ")(angular " << angular_error << ")" << std::endl
280 << "Current carrot pose: " << std::endl
281 << carrot_pose << std::endl
282 << "Current actual pose:" << std::endl
283 << currentPoseDebugMsg);
284}
285
291bool BackwardLocalPlanner::updateCarrotGoal(const geometry_msgs::msg::PoseStamped & tfpose)
292{
293 RCLCPP_INFO_STREAM(nh_->get_logger(), "[BackwardsLocalPlanner] --- Carrot update ---");
294 double disterr = 0, angleerr = 0;
295 // iterate the point from the current position and backward until reaching a new goal point in the path
296 // this algorithm among other advantages has that skip the looping with an eager global planner
297 // that recalls the same plan (the already performed part of the plan in the current pose is skipped)
298 while (currentCarrotPoseIndex_ < (long)backwardsPlanPath_.size() - 1)
299 {
301
302 RCLCPP_INFO_STREAM(
303 nh_->get_logger(), "[BackwardsLocalPlanner] update carrot goal: Current index: "
304 << currentCarrotPoseIndex_ << "/" << backwardsPlanPath_.size());
305 RCLCPP_INFO(
306 nh_->get_logger(),
307 "[BackwardsLocalPlanner] update carrot goal: linear error %lf, angular error: %lf", disterr,
308 angleerr);
309
310 // target pose found, goal carrot tries to escape!
311 if (disterr < carrot_distance_ && angleerr < carrot_angular_distance_)
312 {
315 RCLCPP_INFO_STREAM(
316 nh_->get_logger(), "[BackwardsLocalPlanner] move carrot fw "
317 << currentCarrotPoseIndex_ << "/" << backwardsPlanPath_.size());
318 }
319 else
320 {
321 // carrot already escaped
322 break;
323 }
324 }
325 // RCLCPP_INFO(nh_->get_logger(),"[BackwardsLocalPlanner] computing angular error");
326 if (
327 currentCarrotPoseIndex_ >= (long)backwardsPlanPath_.size() - 1 && backwardsPlanPath_.size() > 0)
328 {
330 // reupdated errors
332 }
333
334 RCLCPP_INFO(
335 nh_->get_logger(), "[BackwardsLocalPlanner] Current index carrot goal: %d",
337 RCLCPP_INFO(
338 nh_->get_logger(),
339 "[BackwardsLocalPlanner] Update carrot goal: linear error %lf (xytol: %lf), angular error: "
340 "%lf",
341 disterr, xy_goal_tolerance_, angleerr);
342
343 bool carrotInGoalLinearRange = disterr < xy_goal_tolerance_;
344 RCLCPP_INFO(
345 nh_->get_logger(), "[BackwardsLocalPlanner] carrot in goal radius: %d",
346 carrotInGoalLinearRange);
347
348 RCLCPP_INFO(nh_->get_logger(), "[BackwardsLocalPlanner] ---End carrot update---");
349
350 return carrotInGoalLinearRange;
351}
352
354{
355 // this function should be called always the carrot is updated
356 divergenceDetectionLastCarrotLinearDistance_ = std::numeric_limits<double>::max();
357 return true;
358}
359
360bool BackwardLocalPlanner::divergenceDetectionUpdate(const geometry_msgs::msg::PoseStamped & tfpose)
361{
362 double disterr = 0, angleerr = 0;
364
365 RCLCPP_INFO_STREAM(
366 nh_->get_logger(), "[BackwardLocalPlanner] Divergence check. carrot goal distance. was: "
368 << ", now it is: " << disterr);
370 {
371 // candidate of divergence, we do not throw the divergence alarm yet
372 // but we neither update the distance since it is worse than the one
373 // we had previously with the same carrot.
374 const double MARGIN_FACTOR = 1.2;
375 if (disterr > MARGIN_FACTOR * divergenceDetectionLastCarrotLinearDistance_)
376 {
377 RCLCPP_ERROR_STREAM(
378 nh_->get_logger(),
379 "[BackwardLocalPlanner] Divergence detected. The same carrot goal distance was previously: "
380 << divergenceDetectionLastCarrotLinearDistance_ << "but now it is: " << disterr);
381 return true;
382 }
383 else
384 {
385 // divergence candidate
386 return false;
387 }
388 }
389 else
390 {
391 // update:
393 return false;
394 }
395}
396
398 const geometry_msgs::msg::PoseStamped & tfpose)
399{
400 // this function is specially useful when we want to reach the goal with a lot
401 // of precision. We may pass the goal and then the controller enters in some
402 // unstable state. With this, we are able to detect when stop moving.
403
404 // only apply if the carrot is in goal position and also if we are not in a pure spinning behavior v!=0
405
406 auto & carrot_pose = backwardsPlanPath_[currentCarrotPoseIndex_];
407 const geometry_msgs::msg::Point & carrot_point = carrot_pose.pose.position;
408 double yaw = tf2::getYaw(carrot_pose.pose.orientation);
409
410 // direction vector
411 double vx = cos(yaw);
412 double vy = sin(yaw);
413
414 // line implicit equation
415 // ax + by + c = 0
416 double c = -vx * carrot_point.x - vy * carrot_point.y;
417 const double C_OFFSET_METERS = 0.05; // 5 cm
418 double check = vx * tfpose.pose.position.x + vy * tfpose.pose.position.y + c + C_OFFSET_METERS;
419
420 RCLCPP_INFO_STREAM(
421 nh_->get_logger(),
422 "[BackwardLocalPlanner] half plane constraint:" << vx << "*" << carrot_point.x << " + " << vy
423 << "*" << carrot_point.y << " + " << c);
424 RCLCPP_INFO_STREAM(
425 nh_->get_logger(), "[BackwardLocalPlanner] constraint evaluation: "
426 << vx << "*" << tfpose.pose.position.x << " + " << vy << "*"
427 << tfpose.pose.position.y << " + " << c << " = " << check);
428
429 return check < 0;
430}
431
433 const geometry_msgs::msg::PoseStamped & tfpose,
434 const geometry_msgs::msg::Twist & /*currentTwist*/, double angle_error, bool & linearGoalReached,
435 nav2_core::GoalChecker * /*goal_checker*/)
436{
437 auto & finalgoal = backwardsPlanPath_.back();
438 double gdx = finalgoal.pose.position.x - tfpose.pose.position.x;
439 double gdy = finalgoal.pose.position.y - tfpose.pose.position.y;
440 double goaldist = sqrt(gdx * gdx + gdy * gdy);
441
442 auto abs_angle_error = fabs(angle_error);
443 RCLCPP_INFO_STREAM(
444 nh_->get_logger(), "[BackwardLocalPlanner] goal check. linear dist: "
445 << goaldist << "(" << this->xy_goal_tolerance_ << ")" << ", angular dist: "
446 << abs_angle_error << "(" << this->yaw_goal_tolerance_ << ")");
447
448 linearGoalReached = goaldist < this->xy_goal_tolerance_;
449
450 return linearGoalReached && abs_angle_error < this->yaw_goal_tolerance_;
451}
452
459 const geometry_msgs::msg::PoseStamped & /*tfpose*/, double & vetta, double & gamma,
460 double alpha_error, double betta_error, double rho_error)
461{
462 if (rho_error > linear_mode_rho_error_threshold_) // works in straight motion mode
463 {
464 if (fabs(alpha_error) > initial_rotation_alpha_error_threshold_)
465 {
466 // backward heading is misaligned with the carrot direction: spin in place to
467 // align before translating. Without this gate the robot orbits the goal on
468 // curved-path endgames: at full backward speed the angular authority
469 // (max_angular_z_speed) yields a minimum turning radius larger than the goal
470 // tolerance, so a misaligned approach can never converge.
471 vetta = 0;
472 gamma = k_alpha_ * alpha_error;
473 }
474 else
475 {
476 vetta = k_rho_ * rho_error;
477 gamma = k_alpha_ * alpha_error;
478 }
479 }
480 else if (fabs(betta_error) >= this->yaw_goal_tolerance_) // works in pure spinning mode
481 {
482 vetta = 0; // disable linear
483 gamma = k_betta_ * betta_error;
484 }
485}
486
492geometry_msgs::msg::TwistStamped BackwardLocalPlanner::computeVelocityCommands(
493 const geometry_msgs::msg::PoseStamped & pose, const geometry_msgs::msg::Twist & velocity,
494 nav2_core::GoalChecker * goal_checker)
495{
496 RCLCPP_INFO(
497 nh_->get_logger(),
498 "[BackwardLocalPlanner] ------------------- LOCAL PLANNER LOOP -----------------");
499 this->updateParameters();
500
501 // consistency check
502 if (this->backwardsPlanPath_.size() > 0)
503 {
504 RCLCPP_INFO_STREAM(
505 nh_->get_logger(), "[BackwardLocalPlanner] Current pose frame id: "
506 << backwardsPlanPath_.front().header.frame_id
507 << ", path pose frame id: " << pose.header.frame_id);
508
509 if (backwardsPlanPath_.front().header.frame_id != pose.header.frame_id)
510 {
511 RCLCPP_ERROR_STREAM(nh_->get_logger(), "[BackwardLocalPlanner] Inconsistent frames");
512 }
513 }
514
515 // xy_goal_tolerance and yaw_goal_tolerance are just used for logging proposes and clamping the carrot
516 // goal distance (parameter safety)
517 if (xy_goal_tolerance_ == -1 || yaw_goal_tolerance_ == -1)
518 {
519 geometry_msgs::msg::Pose posetol;
520 geometry_msgs::msg::Twist twistol;
521 if (goal_checker->getTolerances(posetol, twistol))
522 {
523 xy_goal_tolerance_ = posetol.position.x;
524 yaw_goal_tolerance_ = tf2::getYaw(posetol.orientation);
525
526 RCLCPP_INFO_STREAM(
527 nh_->get_logger(), "[BackwardLocalPlanner] xy_goal_tolerance_: "
529 << ", yaw_goal_tolerance_: " << yaw_goal_tolerance_);
530 }
531 else
532 {
533 RCLCPP_INFO_STREAM(
534 nh_->get_logger(), "[BackwardLocalPlanner] could not get tolerances from goal checker");
535 }
536 }
537
538 RCLCPP_INFO(
539 nh_->get_logger(),
540 "[BackwardLocalPlanner] ------------------- LOCAL PLANNER LOOP -----------------");
541
542 geometry_msgs::msg::TwistStamped cmd_vel;
543 RCLCPP_INFO(nh_->get_logger(), "[BackwardLocalPlanner] LOCAL PLANNER LOOP");
544 geometry_msgs::msg::PoseStamped paux;
545 geometry_msgs::msg::PoseStamped tfpose;
546
547 if (!costmapRos_->getRobotPose(tfpose))
548 {
549 RCLCPP_ERROR(
550 nh_->get_logger(),
551 "[BackwardLocalPlanner] missing robot pose, canceling compute Velocity Command");
552 } // it is not working in the pure spinning reel example, maybe the hyperplane check is enough
553 bool divergenceDetected = false;
554
555 bool emergency_stop = false;
556 if (divergenceDetected)
557 {
558 RCLCPP_ERROR(
559 nh_->get_logger(), "[BackwardLocalPlanner] Divergence detected. Sending emergency stop.");
560 emergency_stop = true;
561 }
562
563 bool carrotInLinearGoalRange = updateCarrotGoal(tfpose);
564 RCLCPP_INFO_STREAM(nh_->get_logger(), "[BackwardLocalPlanner] carrot goal created");
565
566 if (emergency_stop)
567 {
568 cmd_vel.twist.linear.x = 0;
569 cmd_vel.twist.angular.z = 0;
570 RCLCPP_INFO_STREAM(
571 nh_->get_logger(), "[BackwardLocalPlanner] emergency stop, exit compute commands");
572 return cmd_vel;
573 }
574
575 // ------ Evaluate the current context ----
576 double rho_error, betta_error, alpha_error;
577
578 // getting carrot goal information
579 tf2::Quaternion q;
580 tf2::convert(tfpose.pose.orientation, q);
581
582 RCLCPP_INFO_STREAM(
583 nh_->get_logger(), "[BackwardLocalPlanner] carrot goal: " << currentCarrotPoseIndex_ << "/"
584 << backwardsPlanPath_.size());
585 const geometry_msgs::msg::PoseStamped & carrotgoalpose =
587 RCLCPP_INFO_STREAM(
588 nh_->get_logger(), "[BackwardLocalPlanner] carrot goal pose current index: "
589 << currentCarrotPoseIndex_ << "/" << backwardsPlanPath_.size() << ": "
590 << carrotgoalpose);
591 const geometry_msgs::msg::Point & carrotGoalPosition = carrotgoalpose.pose.position;
592
593 tf2::Quaternion goalQ;
594 tf2::fromMsg(carrotgoalpose.pose.orientation, goalQ);
595 RCLCPP_INFO_STREAM(nh_->get_logger(), "[BackwardLocalPlanner] -- Control Policy --");
596 // goal orientation (global frame)
597 double betta = tf2::getYaw(goalQ);
598 RCLCPP_INFO_STREAM(nh_->get_logger(), "[BackwardLocalPlanner] goal orientation: " << betta);
599 betta = betta + betta_offset_;
600
601 double dx = carrotGoalPosition.x - tfpose.pose.position.x;
602 double dy = carrotGoalPosition.y - tfpose.pose.position.y;
603
604 // distance error to the targetpoint
605 rho_error = sqrt(dx * dx + dy * dy);
606
607 // heading to goal angle
608 double theta = tf2::getYaw(q);
609 double alpha = atan2(dy, dx);
610 alpha = alpha + alpha_offset_;
611
612 alpha_error = angles::shortest_angular_distance(alpha, theta);
613 betta_error = angles::shortest_angular_distance(betta, theta);
614 //------------- END CONTEXT EVAL ----------
615
616 bool linearGoalReached;
617 bool currentPoseInGoal =
618 checkCurrentPoseInGoalRange(tfpose, velocity, betta_error, linearGoalReached, goal_checker);
619
620 // Make sure the robot is very close to the goal and it is really in the the last goal point.
621 bool carrotInFinalGoalIndex = currentCarrotPoseIndex_ == (int)backwardsPlanPath_.size() - 1;
622
623 // checking if we are really in the end goal pose
624 if (currentPoseInGoal && carrotInFinalGoalIndex)
625 {
626 goalReached_ = true;
627 RCLCPP_INFO_STREAM(
628 nh_->get_logger(),
629 "[BackwardLocalPlanner] GOAL REACHED. Send stop command and skipping trajectory collision: "
630 << cmd_vel.twist);
631 cmd_vel.twist.linear.x = 0;
632 cmd_vel.twist.angular.z = 0;
633 return cmd_vel;
634 }
635 else if (
636 carrotInLinearGoalRange &&
637 linearGoalReached) // checking if we are in the end goal point but with incorrect
638 // orientation
639 {
640 // this means that we are not in the final angular distance, and we may even not be in the last carrot index
641 // (several intermediate angular poses until the last goal pose)
643 }
644
645 // --------------------
646 // zero-initialized: straightBackwardsAndPureSpinCmd leaves them untouched when the
647 // robot is within both the linear threshold and the yaw tolerance (safe stop)
648 double vetta = 0;
649 double gamma = 0;
651 {
652 // decorated control rule for this mode
654 tfpose, vetta, gamma, alpha_error, betta_error, rho_error);
655 }
656 else // default free navigation backward motion mode
657 {
658 // regular control rule
659 vetta = k_rho_ * rho_error;
660 gamma = k_alpha_ * alpha_error + k_betta_ * betta_error;
661
662 // Even if we are in free navigation, we can enter in the pure spinning state.
663 // then, the linear motion is deactivated.
665 {
666 RCLCPP_INFO(
667 nh_->get_logger(),
668 "[BackwardLocalPlanner] we entered in a pure spinning state even in not pure-spining "
669 "configuration, "
670 "carrotDistanceGoalReached: %d",
671 carrotInLinearGoalRange);
672 gamma = k_betta_ * betta_error;
673 vetta = 0;
674 }
675
676 // classical control to reach a goal backwards
677 }
678
679 // Apply command and Clamp to limits
680 cmd_vel.twist.linear.x = vetta;
681 cmd_vel.twist.angular.z = gamma;
682
683 if (cmd_vel.twist.linear.x > max_linear_x_speed_)
684 {
685 cmd_vel.twist.linear.x = max_linear_x_speed_;
686 }
687 else if (cmd_vel.twist.linear.x < -max_linear_x_speed_)
688 {
689 cmd_vel.twist.linear.x = -max_linear_x_speed_;
690 }
691
692 if (cmd_vel.twist.angular.z > max_angular_z_speed_)
693 {
694 cmd_vel.twist.angular.z = max_angular_z_speed_;
695 }
696 else if (cmd_vel.twist.angular.z < -max_angular_z_speed_)
697 {
698 cmd_vel.twist.angular.z = -max_angular_z_speed_;
699 }
700
701 publishGoalMarker(carrotGoalPosition.x, carrotGoalPosition.y, betta);
702
703 RCLCPP_INFO_STREAM(
704 nh_->get_logger(), "[BackwardLocalPlanner] local planner,"
705 << std::endl
706 << " current pose in goal: " << currentPoseInGoal << std::endl
707 << " carrot in final goal index: " << carrotInFinalGoalIndex << std::endl
708 << " carrot in linear goal range: " << carrotInLinearGoalRange << std::endl
709 << " straightAnPureSpiningMode: " << straightBackwardsAndPureSpinningMode_
710 << std::endl
711 << " inGoalPureSpinningState: " << inGoalPureSpinningState_ << std::endl
712 << " theta: " << theta << std::endl
713 << " betta: " << theta << std::endl
714 << " err_x: " << dx << std::endl
715 << " err_y:" << dy << std::endl
716 << " rho_error:" << rho_error << std::endl
717 << " alpha_error:" << alpha_error << std::endl
718 << " betta_error:" << betta_error << std::endl
719 << " vetta:" << vetta << std::endl
720 << " gamma:" << gamma << std::endl
721 << " cmd_vel.lin.x:" << cmd_vel.twist.linear.x << std::endl
722 << " cmd_vel.ang.z:" << cmd_vel.twist.angular.z);
723
725 {
726 bool carrotHalfPlaneConstraintFailure = checkCarrotHalfPlainConstraint(tfpose);
727
728 if (carrotHalfPlaneConstraintFailure)
729 {
730 RCLCPP_ERROR(
731 nh_->get_logger(),
732 "[BackwardLocalPlanner] CarrotHalfPlaneConstraintFailure detected. Sending "
733 "emergency stop and success to the planner.");
734 cmd_vel.twist.linear.x = 0;
735 }
736 }
737
738 // ---------------------- TRAJECTORY PREDICTION AND COLLISION AVOIDANCE ---------------------
739 geometry_msgs::msg::PoseStamped global_pose;
740 costmapRos_->getRobotPose(global_pose);
741
742 auto * costmap2d = costmapRos_->getCostmap();
743 auto yaw = tf2::getYaw(global_pose.pose.orientation);
744
745 auto & pos = global_pose.pose.position;
746
747 Eigen::Vector3f currentpose(pos.x, pos.y, yaw);
748 Eigen::Vector3f currentvel(
749 cmd_vel.twist.linear.x, cmd_vel.twist.linear.y, cmd_vel.twist.angular.z);
750 std::vector<Eigen::Vector3f> trajectory;
751 this->generateTrajectory(
752 currentpose, currentvel, 0.8 /*meters*/, M_PI / 8 /*rads*/, 3.0 /*seconds*/, 0.05 /*seconds*/,
753 trajectory);
754
755 // check plan rejection
756 bool acceptedLocalTrajectoryFreeOfObstacles = true;
757
758 unsigned int mx, my;
759
761 {
762 if (backwardsPlanPath_.size() > 0)
763 {
764 auto & finalgoalpose = backwardsPlanPath_.back();
765
766 int i = 0;
767 // RCLCPP_INFO_STREAM(nh_->get_logger(), "lplanner goal: " << finalgoalpose.pose.position);
768 geometry_msgs::msg::Twist mockzerospeed;
769
770 for (auto & p : trajectory)
771 {
772 float dx = p[0] - finalgoalpose.pose.position.x;
773 float dy = p[1] - finalgoalpose.pose.position.y;
774
775 float dst = sqrt(dx * dx + dy * dy);
776 if (dst < xy_goal_tolerance_)
777 {
778 RCLCPP_INFO(
779 nh_->get_logger(),
780 "[BackwardLocalPlanner] trajectory simulation for collision checking: goal "
781 "reached with no collision");
782 break;
783 }
784
785 costmap2d->worldToMap(p[0], p[1], mx, my);
786
787 if (costmap2d->getCost(mx, my) >= nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE)
788 {
789 acceptedLocalTrajectoryFreeOfObstacles = false;
790 RCLCPP_WARN_STREAM(
791 nh_->get_logger(),
792 "[BackwardLocalPlanner] ABORTED LOCAL PLAN BECAUSE OBSTACLE DETEDTED at point "
793 << i << "/" << trajectory.size() << std::endl
794 << p[0] << ", " << p[1]);
795 break;
796 }
797 i++;
798 }
799 }
800 else
801 {
802 RCLCPP_WARN(
803 nh_->get_logger(), "[BackwardLocalPlanner] Abort local - Backwards global plan size: %ld",
804 backwardsPlanPath_.size());
805 cmd_vel.twist.angular.z = 0;
806 cmd_vel.twist.linear.x = 0;
807 }
808 }
809
810 if (acceptedLocalTrajectoryFreeOfObstacles)
811 {
812 waiting_ = false;
813 RCLCPP_INFO(
814 nh_->get_logger(),
815 "[BackwardLocalPlanner] accepted local trajectory free of obstacle. Local planner "
816 "continues.");
817 return cmd_vel;
818 }
819 else // that is not appceted because existence of obstacles
820 {
821 // emergency stop for collision: waiting a while before sending error
822 cmd_vel.twist.linear.x = 0;
823 cmd_vel.twist.angular.z = 0;
824
825 if (waiting_ == false)
826 {
827 waiting_ = true;
828 waitingStamp_ = nh_->now();
829 RCLCPP_WARN(
830 nh_->get_logger(), "[BackwardLocalPlanner][Not accepted local plan] starting countdown");
831 }
832 else
833 {
834 auto waitingduration = nh_->now() - waitingStamp_;
835
836 if (waitingduration > this->waitingTimeout_)
837 {
838 RCLCPP_WARN(
839 nh_->get_logger(), "[BackwardLocalPlanner][Abort local] timeout! duration %lf/%f",
840 waitingduration.seconds(), waitingTimeout_.seconds());
841 cmd_vel.twist.linear.x = 0;
842 cmd_vel.twist.angular.z = 0;
843 return cmd_vel;
844 }
845 }
846
847 return cmd_vel;
848 }
849}
850
857{
858 RCLCPP_INFO(nh_->get_logger(), "[BackwardLocalPlanner] isGoalReached call");
859 return goalReached_;
860}
861
862bool BackwardLocalPlanner::findInitialCarrotGoal(geometry_msgs::msg::PoseStamped & tfpose)
863{
864 double lineardisterr, angleerr;
865 bool inCarrotRange = false;
866
867 // initial state check
868 computeCurrentEuclideanAndAngularErrorsToCarrotGoal(tfpose, lineardisterr, angleerr);
869
870 // lets set the carrot-goal in the correct place with this loop: advance through the
871 // contiguous in-range poses and stop at the last one before leaving the carrot range
872 while (currentCarrotPoseIndex_ < (int)backwardsPlanPath_.size())
873 {
874 computeCurrentEuclideanAndAngularErrorsToCarrotGoal(tfpose, lineardisterr, angleerr);
875
876 RCLCPP_INFO(
877 nh_->get_logger(),
878 "[BackwardLocalPlanner] Finding initial carrot goal i=%d - error to carrot, linear = %lf "
879 "(%lf), "
880 "angular : %lf (%lf)",
882
883 // current path point is inside the carrot distance range, goal carrot tries to escape!
884 if (lineardisterr < carrot_distance_ && angleerr < carrot_angular_distance_)
885 {
886 RCLCPP_INFO(
887 nh_->get_logger(),
888 "[BackwardLocalPlanner] Finding initial carrot goal i=%d - in carrot Range",
890 inCarrotRange = true;
891 // we are inside the goal range, keep advancing to find the last in-range pose
892 }
893 else if (inCarrotRange)
894 {
895 // we were inside the carrot range but not anymore, now we are just leaving.
896 // rollback last index increment (to go back inside the carrot goal scope) and
897 // start motion with that carrot goal we found
899 break;
900 }
901 else
902 {
903 RCLCPP_INFO(
904 nh_->get_logger(),
905 "[BackwardLocalPlanner] Finding initial carrot goal i=%d - carrot out of range, searching "
906 "coincidence...",
908 }
909
911 RCLCPP_INFO_STREAM(
912 nh_->get_logger(), "[BackwardLocalPlanner] setPlan: fw" << currentCarrotPoseIndex_);
913 }
914
915 // the whole remaining path was in range: the carrot is the final pose
917 {
919 }
920
921 RCLCPP_INFO_STREAM(
922 nh_->get_logger(), "[BackwardLocalPlanner] setPlan: (found first carrot:"
923 << inCarrotRange << ") initial carrot point index: "
924 << currentCarrotPoseIndex_ << "/" << backwardsPlanPath_.size());
925
926 return inCarrotRange;
927}
928
930{
931 // this algorithm is really important to have a precise carrot (linear or angular)
932 // and not being considered as a divergence from the path
933
934 RCLCPP_INFO(nh_->get_logger(), "[BackwardLocalPlanner] resample precise");
935 if (backwardsPlanPath_.size() <= 1)
936 {
937 RCLCPP_INFO_STREAM(
938 nh_->get_logger(),
939 "[BackwardLocalPlanner] resample precise skipping, size: " << backwardsPlanPath_.size());
940 return false;
941 }
942
943 int counter = 0;
944 double maxallowedAngularError = 0.45 * this->carrot_angular_distance_; // nyquist
945 double maxallowedLinearError = 0.45 * this->carrot_distance_; // nyquist
946
947 for (int i = 0; i < (int)backwardsPlanPath_.size() - 1; i++)
948 {
949 RCLCPP_INFO_STREAM(nh_->get_logger(), "[BackwardLocalPlanner] resample precise, check: " << i);
950 auto & currpose = backwardsPlanPath_[i];
951 auto & nextpose = backwardsPlanPath_[i + 1];
952
953 tf2::Quaternion qCurrent, qNext;
954 tf2::convert(currpose.pose.orientation, qCurrent);
955 tf2::convert(nextpose.pose.orientation, qNext);
956
957 double dx = nextpose.pose.position.x - currpose.pose.position.x;
958 double dy = nextpose.pose.position.y - currpose.pose.position.y;
959 double dist = sqrt(dx * dx + dy * dy);
960
961 bool resample = false;
962 if (dist > maxallowedLinearError)
963 {
964 RCLCPP_INFO_STREAM(
965 nh_->get_logger(), "[BackwardLocalPlanner] resampling point, linear distance:"
966 << dist << "(" << maxallowedLinearError << ")" << i);
967 resample = true;
968 }
969 else
970 {
971 double currentAngle = tf2::getYaw(qCurrent);
972 double nextAngle = tf2::getYaw(qNext);
973
974 double angularError = fabs(angles::shortest_angular_distance(currentAngle, nextAngle));
975 if (angularError > maxallowedAngularError)
976 {
977 resample = true;
978 RCLCPP_INFO_STREAM(
979 nh_->get_logger(), "[BackwardLocalPlanner] resampling point, angular distance:"
980 << angularError << "(" << maxallowedAngularError << ")" << i);
981 }
982 }
983
984 if (resample)
985 {
986 geometry_msgs::msg::PoseStamped pintermediate;
987 auto duration = rclcpp::Time(nextpose.header.stamp) - rclcpp::Time(currpose.header.stamp);
988
989 pintermediate.header.frame_id = currpose.header.frame_id;
990 pintermediate.header.stamp = rclcpp::Time(currpose.header.stamp) + duration * 0.5;
991
992 pintermediate.pose.position.x = 0.5 * (currpose.pose.position.x + nextpose.pose.position.x);
993 pintermediate.pose.position.y = 0.5 * (currpose.pose.position.y + nextpose.pose.position.y);
994 pintermediate.pose.position.z = 0.5 * (currpose.pose.position.z + nextpose.pose.position.z);
995 tf2::Quaternion intermediateQuat = tf2::slerp(qCurrent, qNext, 0.5);
996 pintermediate.pose.orientation = tf2::toMsg(intermediateQuat);
997
998 this->backwardsPlanPath_.insert(this->backwardsPlanPath_.begin() + i + 1, pintermediate);
999
1000 // retry this point
1001 i--;
1002 counter++;
1003 }
1004 }
1005
1006 RCLCPP_INFO_STREAM(
1007 nh_->get_logger(), "[BackwardLocalPlanner] End resampling. resampled:" << counter
1008 << " new inserted poses "
1009 "during precise "
1010 "resmapling.");
1011 return true;
1012}
1013
1019void BackwardLocalPlanner::setPlan(const nav_msgs::msg::Path & path)
1020{
1021 RCLCPP_INFO_STREAM(
1022 nh_->get_logger(),
1023 "[BackwardLocalPlanner] setPlan: new global plan received ( " << path.poses.size() << ")");
1024
1025 //------------- TRANSFORM TO LOCAL FRAME PATH ---------------------------
1026 nav_msgs::msg::Path transformedPlan;
1027 rclcpp::Duration ttol = rclcpp::Duration::from_seconds(transform_tolerance_);
1028 // transform global plan to the navigation reference frame
1029 for (auto & p : path.poses)
1030 {
1031 geometry_msgs::msg::PoseStamped transformedPose;
1032 nav_2d_utils::transformPose(tf_, costmapRos_->getGlobalFrameID(), p, transformedPose, ttol);
1033 transformedPose.header.frame_id = costmapRos_->getGlobalFrameID();
1034 transformedPlan.poses.push_back(transformedPose);
1035 }
1036
1037 backwardsPlanPath_ = transformedPlan.poses;
1038
1039 // --------- resampling path feature -----------
1040 geometry_msgs::msg::PoseStamped tfpose;
1041 if (!costmapRos_->getRobotPose(tfpose))
1042 {
1043 RCLCPP_ERROR(nh_->get_logger(), "Failure getting pose from Backward local planner");
1044 return;
1045 }
1046
1047 geometry_msgs::msg::PoseStamped posestamped = tfpose;
1048 backwardsPlanPath_.insert(backwardsPlanPath_.begin(), posestamped);
1049 this->resamplePrecisePlan();
1050
1051 nav_msgs::msg::Path planMsg;
1052 planMsg.poses = backwardsPlanPath_;
1053 planMsg.header.frame_id = costmapRos_->getGlobalFrameID();
1054 planMsg.header.stamp = nh_->now();
1055 planPub_->publish(planMsg);
1056
1057 // ------ reset controller state ----------------------
1058 goalReached_ = false;
1061 // re-read the tolerances from the goal checker on the next control cycle: the selected
1062 // goal checker may have changed since the previous navigation (goal_checker_selector)
1063 xy_goal_tolerance_ = -1;
1066
1067 if (path.poses.size() == 0)
1068 {
1069 RCLCPP_INFO_STREAM(nh_->get_logger(), "[BackwardLocalPlanner] received plan without any pose");
1070 return;
1071 }
1072
1073 // -------- initialize carrot ----------------
1074 bool foundInitialCarrotGoal = this->findInitialCarrotGoal(tfpose);
1075 if (!foundInitialCarrotGoal)
1076 {
1077 RCLCPP_ERROR(
1078 nh_->get_logger(),
1079 "[BackwardLocalPlanner] new plan rejected. The initial point in the global path is "
1080 "too much far away from the current state (according to carrot_distance "
1081 "parameter)");
1082 // return false; // in this case, the new plan broke the current execution
1083 return;
1084 }
1085 else
1086 {
1087 this->divergenceDetectionUpdate(tfpose);
1088 return;
1089 }
1090}
1091
1093 const Eigen::Vector3f & pos, const Eigen::Vector3f & vel, float maxdist, float maxanglediff,
1094 float maxtime, float dt, std::vector<Eigen::Vector3f> & outtraj)
1095{
1096 // simulate the trajectory and check for collisions, updating costs along the way
1097 bool end = false;
1098 float time = 0;
1099 Eigen::Vector3f currentpos = pos;
1100 int i = 0;
1101 while (!end)
1102 {
1103 auto loop_vel = vel;
1104 // update the position of the robot using the velocities passed in
1105 auto newpos = computeNewPositions(currentpos, loop_vel, dt);
1106
1107 auto dx = newpos[0] - currentpos[0];
1108 auto dy = newpos[1] - currentpos[1];
1109 float dist, angledist;
1110
1111 // RCLCPP_INFO(nh_->get_logger(), "traj point %d", i);
1112 dist = sqrt(dx * dx + dy * dy);
1113 if (dist > maxdist)
1114 {
1115 end = true;
1116 // RCLCPP_INFO(nh_->get_logger(), "dist break: %f", dist);
1117 }
1118 else
1119 {
1120 // ouble from, double to
1121 angledist = angles::shortest_angular_distance(currentpos[2], newpos[2]);
1122 if (angledist > maxanglediff)
1123 {
1124 end = true;
1125 // RCLCPP_INFO(nh_->get_logger(), "angle dist break: %f", angledist);
1126 }
1127 else
1128 {
1129 outtraj.push_back(newpos);
1130
1131 time += dt;
1132 if (time > maxtime)
1133 {
1134 end = true;
1135 // RCLCPP_INFO(nh_->get_logger(), "time break: %f", time);
1136 }
1137
1138 // RCLCPP_INFO(nh_->get_logger(), "dist: %f, angledist: %f, time: %f", dist, angledist, time);
1139 }
1140 }
1141
1142 currentpos = newpos;
1143 i++;
1144 } // end for simulation steps
1145}
1146
1148 const Eigen::Vector3f & pos, const Eigen::Vector3f & vel, double dt)
1149{
1150 Eigen::Vector3f new_pos = Eigen::Vector3f::Zero();
1151 new_pos[0] = pos[0] + (static_cast<double>(vel[0]) * cos(pos[2]) +
1152 static_cast<double>(vel[1]) * cos(M_PI_2 + pos[2])) *
1153 dt;
1154 new_pos[1] = pos[1] + (static_cast<double>(vel[0]) * sin(pos[2]) +
1155 static_cast<double>(vel[1]) * sin(M_PI_2 + pos[2])) *
1156 dt;
1157 new_pos[2] = pos[2] + vel[2] * dt;
1158 return new_pos;
1159}
1160
1162{
1163 visualization_msgs::msg::Marker marker;
1164 marker.header.frame_id = this->costmapRos_->getGlobalFrameID();
1165 marker.header.stamp = nh_->now();
1166
1167 marker.ns = "my_namespace2";
1168 marker.id = 0;
1169 marker.type = visualization_msgs::msg::Marker::ARROW;
1170 marker.action = visualization_msgs::msg::Marker::DELETEALL;
1171
1172 visualization_msgs::msg::MarkerArray ma;
1173 ma.markers.push_back(marker);
1174
1175 goalMarkerPublisher_->publish(ma);
1176}
1177
1183void BackwardLocalPlanner::publishGoalMarker(double x, double y, double phi)
1184{
1185 visualization_msgs::msg::Marker marker;
1186 marker.header.frame_id = this->costmapRos_->getGlobalFrameID();
1187 marker.header.stamp = nh_->now();
1188
1189 marker.ns = "my_namespace2";
1190 marker.id = 0;
1191 marker.type = visualization_msgs::msg::Marker::ARROW;
1192 marker.action = visualization_msgs::msg::Marker::ADD;
1193 marker.lifetime = rclcpp::Duration(1.0s);
1194
1195 marker.pose.orientation.w = 1;
1196
1197 marker.scale.x = 0.05;
1198 marker.scale.y = 0.15;
1199 marker.scale.z = 0.05;
1200 marker.color.a = 1.0;
1201
1202 // red marker
1203 marker.color.r = 1;
1204 marker.color.g = 0;
1205 marker.color.b = 0;
1206
1207 geometry_msgs::msg::Point start, end;
1208 start.x = x;
1209 start.y = y;
1210
1211 end.x = x + 0.5 * cos(phi);
1212 end.y = y + 0.5 * sin(phi);
1213
1214 marker.points.push_back(start);
1215 marker.points.push_back(end);
1216
1217 visualization_msgs::msg::MarkerArray ma;
1218 ma.markers.push_back(marker);
1219
1220 goalMarkerPublisher_->publish(ma);
1221}
1222} // namespace backward_local_planner
1223} // namespace cl_nav2z
bool updateCarrotGoal(const geometry_msgs::msg::PoseStamped &pose)
bool checkCarrotHalfPlainConstraint(const geometry_msgs::msg::PoseStamped &pose)
Eigen::Vector3f computeNewPositions(const Eigen::Vector3f &pos, const Eigen::Vector3f &vel, double dt)
virtual geometry_msgs::msg::TwistStamped computeVelocityCommands(const geometry_msgs::msg::PoseStamped &pose, const geometry_msgs::msg::Twist &velocity, nav2_core::GoalChecker *goal_checker) override
nav2_core computeVelocityCommands - calculates the best command given the current pose and velocity
void straightBackwardsAndPureSpinCmd(const geometry_msgs::msg::PoseStamped &pose, double &vetta, double &gamma, double alpha_error, double betta_error, double rho_error)
std::vector< geometry_msgs::msg::PoseStamped > backwardsPlanPath_
void generateTrajectory(const Eigen::Vector3f &pos, const Eigen::Vector3f &vel, float maxdist, float maxangle, float maxtime, float dt, std::vector< Eigen::Vector3f > &outtraj)
std::shared_ptr< rclcpp_lifecycle::LifecyclePublisher< visualization_msgs::msg::MarkerArray > > goalMarkerPublisher_
bool findInitialCarrotGoal(geometry_msgs::msg::PoseStamped &pose)
bool divergenceDetectionUpdate(const geometry_msgs::msg::PoseStamped &pose)
void setPlan(const nav_msgs::msg::Path &path) override
nav2_core setPlan - Sets the global plan
void computeCurrentEuclideanAndAngularErrorsToCarrotGoal(const geometry_msgs::msg::PoseStamped &pose, double &dist, double &angular_error)
bool checkCurrentPoseInGoalRange(const geometry_msgs::msg::PoseStamped &tfpose, const geometry_msgs::msg::Twist &currentTwist, double angle_error, bool &linearGoalReached, nav2_core::GoalChecker *goalChecker)
std::shared_ptr< rclcpp_lifecycle::LifecyclePublisher< nav_msgs::msg::Path > > planPub_
void configure(const rclcpp_lifecycle::LifecycleNode::WeakPtr &parent, std::string name, const std::shared_ptr< tf2_ros::Buffer > tf, const std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros) override
virtual void setSpeedLimit(const double &speed_limit, const bool &percentage) override
std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmapRos_
void declareOrSet(rclcpp_lifecycle::LifecycleNode::SharedPtr &node, std::string param, T &value)
Definition common.hpp:34
void tryGetOrSet(rclcpp_lifecycle::LifecycleNode::SharedPtr &node, std::string param, T &value)