SMACC
Loading...
Searching...
No Matches
backward_local_planner.cpp
Go to the documentation of this file.
1#include <angles/angles.h>
2#include <pluginlib/class_list_macros.h>
4#include <visualization_msgs/MarkerArray.h>
5#include <boost/intrusive_ptr.hpp>
6
7//register this planner as a BaseLocalPlanner plugin
9
10namespace cl_move_base_z
11{
12 namespace backward_local_planner
13 {
14
21 : paramServer_(ros::NodeHandle("~BackwardLocalPlanner"))
22 {
23 }
24
31 {
32 }
33
34 void BackwardLocalPlanner::initialize(std::string /*name*/, tf2_ros::Buffer */*tf*/, costmap_2d::Costmap2DROS *costmap_ros)
35 {
36 this->costmapRos_ = costmap_ros;
37 this->initialize();
38 }
39
41 {
42 k_rho_ = -1.0;
43 k_alpha_ = 0.5;
44 k_betta_ = -1.0; // set to zero means that orientation is not important
47
48 f = boost::bind(&BackwardLocalPlanner::reconfigCB, this, _1, _2);
49 paramServer_.setCallback(f);
51
52 ros::NodeHandle nh("~/BackwardLocalPlanner");
53 nh.param("pure_spinning_straight_line_mode", straightBackwardsAndPureSpinningMode_, true);
54 nh.param("yaw_goal_tolerance", yaw_goal_tolerance_, 0.05);
55 nh.param("xy_goal_tolerance", xy_goal_tolerance_, 0.10);
56 nh.param("k_rho", k_rho_, k_rho_);
57 nh.param("k_betta", k_betta_, k_betta_);
58 nh.param("linear_mode_rho_error_threshold", linear_mode_rho_error_threshold_, linear_mode_rho_error_threshold_);
59
60 nh.param("carrot_distance", carrot_distance_, carrot_distance_);
61 nh.param("carrot_angular_distance", carrot_angular_distance_, carrot_angular_distance_);
62 nh.param("enable_obstacle_checking", enable_obstacle_checking_, enable_obstacle_checking_);
63
64 nh.param("max_linear_x_speed", max_linear_x_speed_, 1.0);
65 nh.param("max_angular_z_speed", max_angular_z_speed_, 2.0);
66
67 // we have to do this, for example for the case we are refining the final orientation.
68 // se check at some point if the carrot is reached in "goal linear distance", then we go into
69 // some automatic pure-spinning mode where we only update the orientation
70 // This means that if we reach the carrot with precision we go into pure spinning mode but we cannot
71 // leave that point (maybe this could be improved)
73 {
74 ROS_WARN_STREAM("[BackwardLocalPlanner] carrot_angular_distance (" << carrot_angular_distance_ << ") cannot be lower than yaw_goal_tolerance (" << yaw_goal_tolerance_ << ") setting carrot_angular_distance = " << yaw_goal_tolerance_);
76 }
77
79 {
80 ROS_WARN_STREAM("[BackwardLocalPlanner] carrot_linear_distance (" << carrot_distance_ << ") cannot be lower than xy_goal_tolerance_ (" << yaw_goal_tolerance_ << ") setting carrot_angular_distance = " << xy_goal_tolerance_);
82 }
83
84 goalMarkerPublisher_ = nh.advertise<visualization_msgs::MarkerArray>("goal_marker", 1);
85 waitingTimeout_ = ros::Duration(10);
86 }
87
93 void BackwardLocalPlanner::initialize(std::string /*name*/, tf::TransformListener */*tf*/, costmap_2d::Costmap2DROS *costmap_ros)
94 {
95 this->costmapRos_ = costmap_ros;
96 this->initialize();
97 }
98
99 void BackwardLocalPlanner::computeCurrentEuclideanAndAngularErrorsToCarrotGoal(const tf::Stamped<tf::Pose> &tfpose, double &dist, double &angular_error)
100 {
101 double angle = tf::getYaw(tfpose.getRotation());
102 auto &carrot_pose = backwardsPlanPath_[currentCarrotPoseIndex_];
103 const geometry_msgs::Point &carrot_point = carrot_pose.pose.position;
104
105 tf::Quaternion carrot_orientation;
106 tf::quaternionMsgToTF(carrot_pose.pose.orientation, carrot_orientation);
107
108 geometry_msgs::Pose currentPoseDebugMsg;
109 tf::poseTFToMsg(tfpose, currentPoseDebugMsg);
110
111 // take error from the current position to the path point
112 double dx = carrot_point.x - tfpose.getOrigin().x();
113 double dy = carrot_point.y - tfpose.getOrigin().y();
114 dist = sqrt(dx * dx + dy * dy);
115
116 double pangle = tf::getYaw(carrot_orientation);
117 angular_error = fabs(angles::shortest_angular_distance(pangle, angle));
118
119 ROS_DEBUG_STREAM("[BackwardLocalPlanner] Compute carrot errors. (linear " << dist << ")(angular " << angular_error << ")" << std::endl
120 << "Current carrot pose: " << std::endl
121 << carrot_pose << std::endl
122 << "Current actual pose:" << std::endl
123 << currentPoseDebugMsg);
124 }
125
131 bool BackwardLocalPlanner::updateCarrotGoal(const tf::Stamped<tf::Pose> &tfpose)
132 {
133 ROS_DEBUG("[BackwardsLocalPlanner] --- Computing carrot pose ---");
134 double disterr = 0, angleerr = 0;
135 // iterate the point from the current position and backward until reaching a new goal point in the path
136 // this algorithm among other advantages has that skip the looping with an eager global planner
137 // that recalls the same plan (the already performed part of the plan in the current pose is skipped)
139 {
141
142 ROS_DEBUG_STREAM("[BackwardsLocalPlanner] update carrot goal: Current index: " << currentCarrotPoseIndex_ << "/" << backwardsPlanPath_.size());
143 ROS_DEBUG("[BackwardsLocalPlanner] update carrot goal: linear error %lf, angular error: %lf", disterr, angleerr);
144
145 // target pose found, goal carrot tries to escape!
146 if (disterr < carrot_distance_ && angleerr < carrot_angular_distance_)
147 {
150 ROS_DEBUG_STREAM("[BackwardsLocalPlanner] fw " << currentCarrotPoseIndex_ << "/" << backwardsPlanPath_.size());
151 }
152 else
153 {
154 break;
155 }
156 }
157 //ROS_DEBUG("[BackwardsLocalPlanner] computing angular error");
159 {
162 }
163
164 ROS_DEBUG("[BackwardsLocalPlanner] Current index carrot goal: %ld", currentCarrotPoseIndex_);
165 ROS_DEBUG("[BackwardsLocalPlanner] Update carrot goal: linear error %lf, angular error: %lf", disterr, angleerr);
166 bool carrotInGoalLinear = disterr < xy_goal_tolerance_;
167 ROS_DEBUG("[BackwardsLocalPlanner] carrot in goal radius: %d", carrotInGoalLinear);
168
169 ROS_DEBUG("[BackwardsLocalPlanner] --- Computing carrot pose ---");
170
171 return carrotInGoalLinear;
172 }
173
175 {
176 // this function should be called always the carrot is updated
177 divergenceDetectionLastCarrotLinearDistance_ = std::numeric_limits<double>::max();
178 return true;
179 }
180
181 bool BackwardLocalPlanner::divergenceDetectionUpdate(const tf::Stamped<tf::Pose> &tfpose)
182 {
183 double disterr = 0, angleerr = 0;
185
186 ROS_DEBUG_STREAM("[BackwardLocalPlanner] Divergence check. carrot goal distance. was: " << divergenceDetectionLastCarrotLinearDistance_ << ", now it is: " << disterr);
188 {
189 // candidate of divergence, we do not throw the divergence alarm yet
190 // but we neither update the distance since it is worse than the one
191 // we had previously with the same carrot.
192 const double MARGIN_FACTOR = 1.2;
193 if (disterr > MARGIN_FACTOR * divergenceDetectionLastCarrotLinearDistance_)
194 {
195 ROS_ERROR_STREAM("[BackwardLocalPlanner] Divergence detected. The same carrot goal distance was previously: " << divergenceDetectionLastCarrotLinearDistance_ << "but now it is: " << disterr);
196 return true;
197 }
198 else
199 {
200 // divergence candidate
201 return false;
202 }
203 }
204 else
205 {
206 //update:
208 return false;
209 }
210 }
211
212 bool BackwardLocalPlanner::checkCarrotHalfPlainConstraint(const tf::Stamped<tf::Pose> &tfpose)
213 {
214 // this function is specially useful when we want to reach the goal with a lot
215 // of precision. We may pass the goal and then the controller enters in some
216 // unstable state. With this, we are able to detect when stop moving.
217
218 // only apply if the carrot is in goal position and also if we are not in a pure spinning behavior v!=0
219
220 auto &carrot_pose = backwardsPlanPath_[currentCarrotPoseIndex_];
221 const geometry_msgs::Point &carrot_point = carrot_pose.pose.position;
222 double yaw = tf::getYaw(carrot_pose.pose.orientation);
223
224 // direction vector
225 double vx = cos(yaw);
226 double vy = sin(yaw);
227
228 // line implicit equation
229 // ax + by + c = 0
230 double c = -vx * carrot_point.x - vy * carrot_point.y;
231 const double C_OFFSET_METERS = 0.05; // 5 cm
232 double check = vx * tfpose.getOrigin().x() + vy * tfpose.getOrigin().y() + c + C_OFFSET_METERS;
233
234 ROS_DEBUG_STREAM("[BackwardLocalPlanner] half plane constraint:" << vx << "*" << carrot_point.x << " + " << vy << "*" << carrot_point.y << " + " << c);
235 ROS_DEBUG_STREAM("[BackwardLocalPlanner] constraint evaluation: " << vx << "*" << tfpose.getOrigin().x() << " + " << vy << "*" << tfpose.getOrigin().y() << " + " << c << " = " << check);
236
237 return check < 0;
238 }
239
240 bool BackwardLocalPlanner::checkCurrentPoseInGoalRange(const tf::Stamped<tf::Pose> &tfpose, double angle_error, bool& linearGoalReached)
241 {
242 auto &finalgoal = backwardsPlanPath_.back();
243 double gdx = finalgoal.pose.position.x - tfpose.getOrigin().x();
244 double gdy = finalgoal.pose.position.y - tfpose.getOrigin().y();
245 double goaldist = sqrt(gdx * gdx + gdy * gdy);
246
247 auto abs_angle_error = fabs(angle_error);
248 // final_alpha_error =
249 ROS_DEBUG_STREAM("[BackwardLocalPlanner] goal check. linear dist: " << goaldist << "(" << this->xy_goal_tolerance_ << ")"
250 << ", angular dist: " << abs_angle_error << "(" << this->yaw_goal_tolerance_ << ")");
251
252 linearGoalReached = goaldist < this->xy_goal_tolerance_;
253
254 if (abs_angle_error < this->yaw_goal_tolerance_) // 5cm
255 {
256 return true;
257 }
258
259 return false;
260 }
266 void BackwardLocalPlanner::defaultBackwardCmd(const tf::Stamped<tf::Pose> &/*tfpose*/, double vetta, double gamma, double /*alpha_error*/, double /*betta_error*/, geometry_msgs::Twist &cmd_vel)
267 {
268 cmd_vel.linear.x = vetta;
269 cmd_vel.angular.z = gamma;
270 }
276 void BackwardLocalPlanner::straightBackwardsAndPureSpinCmd(const tf::Stamped<tf::Pose> &/*tfpose*/, double vetta, double gamma, double alpha_error, double betta_error, double rho_error, geometry_msgs::Twist &cmd_vel)
277 {
278 if (rho_error > linear_mode_rho_error_threshold_) // works in straight motion mode
279 {
280 vetta = k_rho_ * rho_error;
281 gamma = k_alpha_ * alpha_error;
282 }
283 else if (fabs(betta_error) >= this->yaw_goal_tolerance_) // works in pure spinning mode
284 {
285 vetta = 0;
286 gamma = k_betta_ * betta_error;
287 }
288 /*
289 // THIS IS NOT TRUE, IF THE CARROT REACHES THE GOAL DOES NOT MEAN THE ROBOT IS
290 // IN THE CORRECT POSITION
291 else if (currentCarrotPoseIndex_ >= backwardsPlanPath_.size() - 1)
292 {
293 vetta = 0;
294 gamma = 0;
295 goalReached_ = true;
296 backwardsPlanPath_.clear();
297
298 ROS_INFO_STREAM("BACKWARD LOCAL PLANNER END: Goal Reached. Stop action [rhoerror: " << rho_error<<"]");
299 }*/
300
301 cmd_vel.linear.x = vetta;
302 cmd_vel.angular.z = gamma;
303 }
304
305// MELODIC
306#if ROS_VERSION_MINIMUM(1, 13, 0)
307 tf::Stamped<tf::Pose> optionalRobotPose(costmap_2d::Costmap2DROS *costmapRos)
308 {
309 geometry_msgs::PoseStamped paux;
310 costmapRos->getRobotPose(paux);
311 tf::Stamped<tf::Pose> tfpose;
312 tf::poseStampedMsgToTF(paux, tfpose);
313 return tfpose;
314 }
315#else
316 // INDIGO AND PREVIOUS
317 tf::Stamped<tf::Pose> optionalRobotPose(costmap_2d::Costmap2DROS *costmapRos)
318 {
319 tf::Stamped<tf::Pose> tfpose;
320 costmapRos->getRobotPose(tfpose);
321 return tfpose;
322 }
323
324#endif
325
331 bool BackwardLocalPlanner::computeVelocityCommands(geometry_msgs::Twist &cmd_vel)
332 {
333 ROS_DEBUG("[BackwardLocalPlanner] ------------------- LOCAL PLANNER LOOP -----------------");
334 geometry_msgs::PoseStamped paux;
335 tf::Stamped<tf::Pose> tfpose = optionalRobotPose(costmapRos_);
336
337 //bool divergenceDetected = this->divergenceDetectionUpdate(tfpose);
338 // it is not working in the pure spinning reel example, maybe the hyperplane check is enough
339 bool divergenceDetected = false;
340
341 bool emergency_stop = false;
342 if (divergenceDetected)
343 {
344 ROS_ERROR("[BackwardLocalPlanner] Divergence detected. Sending emergency stop.");
345 emergency_stop = true;
346 }
347
348 bool carrotInLinearGoalRange = updateCarrotGoal(tfpose);
349 ROS_DEBUG_STREAM("[BackwardLocalPlanner] carrot goal created");
350
351 if (emergency_stop)
352 {
353 cmd_vel.linear.x = 0;
354 cmd_vel.angular.z = 0;
355 ROS_DEBUG_STREAM("[BackwardLocalPlanner] emergency stop, exit compute commands");
356 return false;
357 }
358
359 // ------ Evaluate the current context ----
360 double rho_error, betta_error, alpha_error;
361
362
363 // getting carrot goal information
364 tf::Quaternion q = tfpose.getRotation();
365 ROS_DEBUG_STREAM("[BackwardLocalPlanner] carrot goal: " << currentCarrotPoseIndex_ << "/" << backwardsPlanPath_.size());
366 const geometry_msgs::PoseStamped &carrotgoalpose = backwardsPlanPath_[currentCarrotPoseIndex_];
367 ROS_DEBUG_STREAM("[BackwardLocalPlanner] carrot goal pose current index: " << currentCarrotPoseIndex_ << "/" << backwardsPlanPath_.size() << ": " << carrotgoalpose);
368 const geometry_msgs::Point &carrotGoalPosition = carrotgoalpose.pose.position;
369
370 tf::Quaternion goalQ;
371 tf::quaternionMsgToTF(carrotgoalpose.pose.orientation, goalQ);
372 ROS_DEBUG_STREAM("[BackwardLocalPlanner] goal orientation: " << goalQ);
373
374 //goal orientation (global frame)
375 double betta = tf::getYaw(goalQ);
376 betta = betta + betta_offset_;
377
378 double dx = carrotGoalPosition.x - tfpose.getOrigin().x();
379 double dy = carrotGoalPosition.y - tfpose.getOrigin().y();
380
381 //distance error to the targetpoint
382 rho_error = sqrt(dx * dx + dy * dy);
383
384 //heading to goal angle
385 double theta = tf::getYaw(q);
386 double alpha = atan2(dy, dx);
387 alpha = alpha + alpha_offset_;
388
389 alpha_error = angles::shortest_angular_distance(alpha, theta);
390 betta_error = angles::shortest_angular_distance(betta, theta);
391 //------------- END CONTEXT EVAL ----------
392
393 bool linearGoalReached;
394 bool currentPoseInGoal = checkCurrentPoseInGoalRange(tfpose,betta_error, linearGoalReached);
395
396 bool carrotInFinalGoal = carrotInLinearGoalRange && currentCarrotPoseIndex_ == backwardsPlanPath_.size() - 1;
397
398 if(currentPoseInGoal && carrotInFinalGoal)
399 {
400 goalReached_ = true;
401 backwardsPlanPath_.clear();
402 ROS_INFO_STREAM(" [BackwardLocalPlanner] GOAL REACHED. Send stop command and skipping trajectory collision: " << cmd_vel);
403 cmd_vel.linear.x =0;
404 cmd_vel.angular.z = 0;
405 return true;
406 }
407
408 if (carrotInLinearGoalRange && linearGoalReached) // we miss here and not any carrot ahead is outside goal, replacing carrotInLinearGoalRange to carrotInFinalLinearGoalRange
409 {
411 }
412
413
414 double vetta = k_rho_ * rho_error;
415 double gamma = k_alpha_ * alpha_error + k_betta_ * betta_error;
416 // --------------------
417
419 {
420 this->straightBackwardsAndPureSpinCmd(tfpose, vetta, gamma, alpha_error, betta_error, rho_error, cmd_vel);
421 }
422 else // default curved backward-free motion mode
423 {
424 // case B: goal position reached but orientation not yet reached. deactivate linear motion.
426 {
427 ROS_DEBUG("[BackwardLocalPlanner] we entered in a pure spinning state even in not pure-spining configuration, carrotDistanceGoalReached: %d", carrotInLinearGoalRange);
428 gamma = k_betta_ * betta_error;
429 vetta = 0;
430 }
431
432 //classical control to reach a goal backwards
433 this->defaultBackwardCmd(tfpose, vetta, gamma, alpha_error, betta_error, cmd_vel);
434 }
435
436 if (cmd_vel.linear.x > max_linear_x_speed_)
437 {
438 cmd_vel.linear.x = max_linear_x_speed_;
439 }
440 else if (cmd_vel.linear.x < -max_linear_x_speed_)
441 {
442 cmd_vel.linear.x = -max_linear_x_speed_;
443 }
444
445 if (cmd_vel.angular.z > max_angular_z_speed_)
446 {
447 cmd_vel.angular.z = max_angular_z_speed_;
448 }
449 else if (cmd_vel.angular.z < -max_angular_z_speed_)
450 {
451 cmd_vel.angular.z = -max_angular_z_speed_;
452 }
453
454 publishGoalMarker(carrotGoalPosition.x, carrotGoalPosition.y, betta);
455
456 ROS_DEBUG_STREAM("[BackwardLocalPlanner] local planner," << std::endl
457 << " straightAnPureSpiningMode: " << straightBackwardsAndPureSpinningMode_ << std::endl
458 << " inGoalPureSpinningState: " << inGoalPureSpinningState_ << std::endl
459 << "carrotInLinearGoalRange: " << carrotInLinearGoalRange << std::endl
460 << " theta: " << theta << std::endl
461 << " betta: " << theta << std::endl
462 << " err_x: " << dx << std::endl
463 << " err_y:" << dy << std::endl
464 << " rho_error:" << rho_error << std::endl
465 << " alpha_error:" << alpha_error << std::endl
466 << " betta_error:" << betta_error << std::endl
467 << " vetta:" << vetta << std::endl
468 << " gamma:" << gamma << std::endl
469 << " cmd_vel.lin.x:" << cmd_vel.linear.x << std::endl
470 << " cmd_vel.ang.z:" << cmd_vel.angular.z);
471
473 {
474 bool carrotHalfPlaneConstraintFailure = checkCarrotHalfPlainConstraint(tfpose);
475
476 if (carrotHalfPlaneConstraintFailure)
477 {
478 ROS_ERROR("[BackwardLocalPlanner] CarrotHalfPlaneConstraintFailure detected. Sending emergency stop and success to the planner.");
479 cmd_vel.linear.x = 0;
480 }
481 }
482
483
484 // ---------------------- TRAJECTORY PREDICTION AND COLLISION AVOIDANCE ---------------------
485 //cmd_vel.linear.x=0;
486 //cmd_vel.angular.z = 0;
487 tf::Stamped<tf::Pose> global_pose = optionalRobotPose(costmapRos_);
488
489 auto *costmap2d = costmapRos_->getCostmap();
490 auto yaw = tf::getYaw(global_pose.getRotation());
491
492 auto &pos = global_pose.getOrigin();
493
494 Eigen::Vector3f currentpose(pos.x(), pos.y(), yaw);
495 Eigen::Vector3f currentvel(cmd_vel.linear.x, cmd_vel.linear.y, cmd_vel.angular.z);
496 std::vector<Eigen::Vector3f> trajectory;
497 this->generateTrajectory(currentpose, currentvel, 0.8 /*meters*/, M_PI / 8 /*rads*/, 3.0 /*seconds*/, 0.05 /*seconds*/, trajectory);
498
499 // check plan rejection
500 bool acceptedLocalTrajectoryFreeOfObstacles = true;
501
502 uint32_t mx, my;
503
505 {
506 if (backwardsPlanPath_.size() > 0)
507 {
508 auto &finalgoalpose = backwardsPlanPath_.back();
509
510 int i = 0;
511 // ROS_INFO_STREAM("lplanner goal: " << finalgoalpose.pose.position);
512 for (auto &p : trajectory)
513 {
514 float dx = p[0] - finalgoalpose.pose.position.x;
515 float dy = p[1] - finalgoalpose.pose.position.y;
516
517 float dst = sqrt(dx * dx + dy * dy);
518 if (dst < xy_goal_tolerance_)
519 {
520 ROS_DEBUG("[BackwardLocalPlanner] trajectory simulation for collision checking: goal reached with no collision");
521 break;
522 }
523
524 costmap2d->worldToMap(p[0], p[1], mx, my);
525 /*uint64_t cost =*/ costmap2d->getCost(mx, my);
526
527 // ROS_DEBUG("[BackwardLocalPlanner] checking cost pt %d [%lf, %lf] cell[%d,%d] = %d", i, p[0], p[1], mx, my, cost);
528 // ROS_DEBUG_STREAM("[BackwardLocalPlanner] cost: " << cost);
529
530 // static const unsigned char NO_INFORMATION = 255;
531 // static const unsigned char LETHAL_OBSTACLE = 254;
532 // static const unsigned char INSCRIBED_INFLATED_OBSTACLE = 253;
533 // static const unsigned char FREE_SPACE = 0;
534
535 if (costmap2d->getCost(mx, my) >= costmap_2d::INSCRIBED_INFLATED_OBSTACLE)
536 {
537 acceptedLocalTrajectoryFreeOfObstacles = false;
538 ROS_WARN_STREAM("[BackwardLocalPlanner] ABORTED LOCAL PLAN BECAUSE OBSTACLE DETEDTED at point " << i << "/" << trajectory.size() << std::endl
539 << p[0] << ", " << p[1]);
540 break;
541 }
542 i++;
543 }
544 }
545 else
546 {
547 ROS_WARN("[BackwardLocalPlanner] Abort local - Backwards global plan size: %ld", backwardsPlanPath_.size());
548 return false;
549 }
550 }
551
552 if (acceptedLocalTrajectoryFreeOfObstacles)
553 {
554 waiting_ = false;
555 ROS_DEBUG("[BackwardLocalPlanner] accepted local trajectory free of obstacle. Local planner continues.");
556 return true;
557 }
558 else // that is not appceted because existence of obstacles
559 {
560 // emergency stop for collision: waiting a while before sending error
561 cmd_vel.linear.x = 0;
562 cmd_vel.angular.z = 0;
563
564 if (waiting_ == false)
565 {
566 waiting_ = true;
567 waitingStamp_ = ros::Time::now();
568 ROS_WARN("[BackwardLocalPlanner][Not accepted local plan] starting countdown");
569 }
570 else
571 {
572 auto waitingduration = ros::Time::now() - waitingStamp_;
573
574 if (waitingduration > this->waitingTimeout_)
575 {
576 ROS_WARN("[BackwardLocalPlanner][Abort local] timeout! duration %lf/%f", waitingduration.toSec(), waitingTimeout_.toSec());
577 return false;
578 }
579 }
580
581 return true;
582 }
583 }
584
590 void BackwardLocalPlanner::reconfigCB(::backward_local_planner::BackwardLocalPlannerConfig &config, uint32_t /*level*/)
591 {
592 ROS_INFO("[BackwardLocalPlanner] reconfigure Request");
593 k_alpha_ = config.k_alpha;
594 k_betta_ = config.k_betta;
595 k_rho_ = config.k_rho;
596 enable_obstacle_checking_ = config.enable_obstacle_checking;
597
598 //alpha_offset_ = config.alpha_offset;
599 //betta_offset_ = config.betta_offset;
600
601 carrot_distance_ = config.carrot_distance;
602 carrot_angular_distance_ = config.carrot_angular_distance;
603 xy_goal_tolerance_ = config.xy_goal_tolerance;
604 yaw_goal_tolerance_ = config.yaw_goal_tolerance;
605 straightBackwardsAndPureSpinningMode_ = config.pure_spinning_straight_line_mode;
606
608 {
609 ROS_WARN_STREAM("[BackwardLocalPlanner] carrot_angular_distance (" << carrot_angular_distance_ << ") cannot be lower than yaw_goal_tolerance (" << yaw_goal_tolerance_ << ") setting carrot_angular_distance = " << yaw_goal_tolerance_);
611 config.carrot_angular_distance = yaw_goal_tolerance_;
612 }
613
615 {
616 ROS_WARN_STREAM("[BackwardLocalPlanner] carrot_linear_distance (" << carrot_distance_ << ") cannot be lower than xy_goal_tolerance_ (" << yaw_goal_tolerance_ << ") setting carrot_angular_distance = " << xy_goal_tolerance_);
618 config.carrot_distance = xy_goal_tolerance_;
619 }
620 }
621
628 {
629 ROS_DEBUG_STREAM("[BackwardLocalPlanner] isGoalReached call: " << goalReached_);
630 return goalReached_;
631 }
632
633 bool BackwardLocalPlanner::findInitialCarrotGoal(tf::Stamped<tf::Pose> &tfpose)
634 {
635 double lineardisterr, angleerr;
636 bool inCarrotRange = false;
637
638 // initial state check
639 computeCurrentEuclideanAndAngularErrorsToCarrotGoal(tfpose, lineardisterr, angleerr);
640
641 // int closestIndex = -1;
642 // double minpointdist = std::numeric_limits<double>::max();
643
644 // lets set the carrot-goal in the correct place with this loop
645 while (currentCarrotPoseIndex_ < backwardsPlanPath_.size() && !inCarrotRange)
646 {
647 computeCurrentEuclideanAndAngularErrorsToCarrotGoal(tfpose, lineardisterr, angleerr);
648
649 ROS_DEBUG("[BackwardLocalPlanner] Finding initial carrot goal i=%ld - error to carrot, linear = %lf (%lf), angular : %lf (%lf)", currentCarrotPoseIndex_, lineardisterr, carrot_distance_, angleerr, carrot_angular_distance_);
650
651 // current path point is inside the carrot distance range, goal carrot tries to escape!
652 if (lineardisterr < carrot_distance_ && angleerr < carrot_angular_distance_)
653 {
654 ROS_DEBUG("[BackwardLocalPlanner] Finding initial carrot goal i=%ld - in carrot Range", currentCarrotPoseIndex_);
655 inCarrotRange = true;
656 // we are inside the goal range
657 }
658 else if (inCarrotRange && (lineardisterr > carrot_distance_ || angleerr > carrot_angular_distance_))
659 {
660 // we were inside the carrot range but not anymore, now we are just leaving. we want to continue forward (currentCarrotPoseIndex_++)
661 // unless we go out of the carrot range
662
663 // but we rollback last index increment (to go back inside the carrot goal scope) and start motion with that carrot goal we found
665 break;
666 }
667 else
668 {
669 ROS_DEBUG("[BackwardLocalPlanner] Finding initial carrot goal i=%ld - carrot out of range, searching coincidence...", currentCarrotPoseIndex_);
670 }
671
673 ROS_DEBUG_STREAM("[BackwardLocalPlanner] setPlan: fw" << currentCarrotPoseIndex_);
674 }
675
676 ROS_INFO_STREAM("[BackwardLocalPlanner] setPlan: (found first carrot:" << inCarrotRange << ") initial carrot point index: " << currentCarrotPoseIndex_ << "/" << backwardsPlanPath_.size());
677
678 return inCarrotRange;
679 }
680
682 {
683 // this algorithm is really important to have a precise carrot (linear or angular)
684 // and not being considered as a divergence from the path
685
686 ROS_DEBUG("[BackwardLocalPlanner] resample precise");
687 if(backwardsPlanPath_.size() <=1)
688 {
689 ROS_DEBUG_STREAM("[BackwardLocalPlanner] resample precise skipping, size: " << backwardsPlanPath_.size());
690 return false;
691 }
692
693 int counter = 0;
694 double maxallowedAngularError = 0.45 * this->carrot_angular_distance_; // nyquist
695 double maxallowedLinearError = 0.45 * this->carrot_distance_; // nyquist
696
697 for (uint64_t i = 0; i < backwardsPlanPath_.size() - 1; i++)
698 {
699 ROS_DEBUG_STREAM("[BackwardLocalPlanner] resample precise, check: " << i);
700 auto &currpose = backwardsPlanPath_[i];
701 auto &nextpose = backwardsPlanPath_[i + 1];
702
703 tf::Quaternion qCurrent, qNext;
704 tf::quaternionMsgToTF(currpose.pose.orientation, qCurrent);
705 tf::quaternionMsgToTF(nextpose.pose.orientation, qNext);
706
707 double dx = nextpose.pose.position.x - currpose.pose.position.x;
708 double dy = nextpose.pose.position.y - currpose.pose.position.y;
709 double dist = sqrt(dx * dx + dy * dy);
710
711 bool resample = false;
712 if (dist > maxallowedLinearError)
713 {
714 ROS_DEBUG_STREAM("[BackwardLocalPlanner] resampling point, linear distance:" << dist << "(" << maxallowedLinearError << ")" << i);
715 resample = true;
716 }
717 else
718 {
719 double currentAngle = tf::getYaw(qCurrent);
720 double nextAngle = tf::getYaw(qNext);
721
722 double angularError = fabs(angles::shortest_angular_distance(currentAngle, nextAngle));
723 if (angularError > maxallowedAngularError)
724 {
725 resample = true;
726 ROS_DEBUG_STREAM("[BackwardLocalPlanner] resampling point, angular distance:" << angularError << "(" << maxallowedAngularError << ")" << i);
727 }
728 }
729
730 if (resample)
731 {
732 geometry_msgs::PoseStamped pintermediate;
733 auto duration = nextpose.header.stamp - currpose.header.stamp;
734 pintermediate.header.frame_id = currpose.header.frame_id;
735 pintermediate.header.stamp = currpose.header.stamp + duration *0.5;
736
737 pintermediate.pose.position.x = 0.5 * (currpose.pose.position.x + nextpose.pose.position.x);
738 pintermediate.pose.position.y = 0.5 * (currpose.pose.position.y + nextpose.pose.position.y);
739 pintermediate.pose.position.z = 0.5 * (currpose.pose.position.z + nextpose.pose.position.z);
740 tf::Quaternion intermediateQuat = tf::slerp(qCurrent, qNext, 0.5);
741 tf::quaternionTFToMsg(intermediateQuat, pintermediate.pose.orientation);
742
743 this->backwardsPlanPath_.insert(this->backwardsPlanPath_.begin() + i + 1, pintermediate);
744
745 // retry this point
746 i--;
747 counter++;
748 }
749 }
750
751 ROS_DEBUG_STREAM("[BackwardLocalPlanner] End resampling. resampled:" << counter << " new inserted poses during precise resmapling.");
752 return true;
753 }
754
760 bool BackwardLocalPlanner::setPlan(const std::vector<geometry_msgs::PoseStamped> &plan)
761 {
762 ROS_INFO_STREAM("[BackwardLocalPlanner] setPlan: new global plan received (" << plan.size() << ")");
764 goalReached_ = false;
765 backwardsPlanPath_ = plan;
767
768 // find again the new carrot goal, from the destiny direction
769 tf::Stamped<tf::Pose> tfpose = optionalRobotPose(costmapRos_);
770
771 geometry_msgs::PoseStamped posestamped;
772 tf::poseStampedTFToMsg(tfpose,posestamped);
773 backwardsPlanPath_.insert(backwardsPlanPath_.begin(), posestamped);
774
775 this->resamplePrecisePlan();
776
779
780 if (plan.size() == 0)
781 {
782 ROS_WARN("[BackwardLocalPlanner] received plan without any pose");
783 return true;
784 }
785
786 bool foundInitialCarrotGoal = this->findInitialCarrotGoal(tfpose);
787 if (!foundInitialCarrotGoal)
788 {
789 ROS_ERROR("[BackwardLocalPlanner] new plan rejected. The initial point in the global path is too much far away from the current state (according to carrot_distance parameter)");
790 return false; // in this case, the new plan broke the current execution
791 }
792 else
793 {
794 this->divergenceDetectionUpdate(tfpose);
795 // SANDARD AND PREFERRED CASE ON NEW PLAN
796 return true;
797 }
798 }
799
800 void BackwardLocalPlanner::generateTrajectory(const Eigen::Vector3f &pos, const Eigen::Vector3f &vel, float maxdist, float maxanglediff, float maxtime, float dt, std::vector<Eigen::Vector3f> &outtraj)
801 {
802 //simulate the trajectory and check for collisions, updating costs along the way
803 bool end = false;
804 float time = 0;
805 Eigen::Vector3f currentpos = pos;
806 int i = 0;
807 while (!end)
808 {
809 //add the point to the trajectory so we can draw it later if we want
810 //traj.addPoint(pos[0], pos[1], pos[2]);
811
812 // if (continued_acceleration_) {
813 // //calculate velocities
814 // loop_vel = computeNewVelocities(sample_target_vel, loop_vel, limits_->getAccLimits(), dt);
815 // //ROS_WARN_NAMED("Generator", "Flag: %d, Loop_Vel %f, %f, %f", continued_acceleration_, loop_vel[0], loop_vel[1], loop_vel[2]);
816 // }
817
818 auto loop_vel = vel;
819 //update the position of the robot using the velocities passed in
820 auto newpos = computeNewPositions(currentpos, loop_vel, dt);
821
822 auto dx = newpos[0] - currentpos[0];
823 auto dy = newpos[1] - currentpos[1];
824 float dist, angledist;
825
826 //ROS_INFO("traj point %d", i);
827 dist = sqrt(dx * dx + dy * dy);
828 if (dist > maxdist)
829 {
830 end = true;
831 //ROS_INFO("dist break: %f", dist);
832 }
833 else
834 {
835 // ouble from, double to
836 angledist = angles::shortest_angular_distance(currentpos[2], newpos[2]);
837 if (angledist > maxanglediff)
838 {
839 end = true;
840 //ROS_INFO("angle dist break: %f", angledist);
841 }
842 else
843 {
844 outtraj.push_back(newpos);
845
846 time += dt;
847 if (time > maxtime)
848 {
849 end = true;
850 //ROS_INFO("time break: %f", time);
851 }
852
853 //ROS_INFO("dist: %f, angledist: %f, time: %f", dist, angledist, time);
854 }
855 }
856
857 currentpos = newpos;
858 i++;
859 } // end for simulation steps
860 }
861
862 Eigen::Vector3f BackwardLocalPlanner::computeNewPositions(const Eigen::Vector3f &pos, const Eigen::Vector3f &vel, double dt)
863 {
864 Eigen::Vector3f new_pos = Eigen::Vector3f::Zero();
865 new_pos[0] = pos[0] + (vel[0] * cos(pos[2]) + vel[1] * cos(M_PI_2 + pos[2])) * dt;
866 new_pos[1] = pos[1] + (vel[0] * sin(pos[2]) + vel[1] * sin(M_PI_2 + pos[2])) * dt;
867 new_pos[2] = pos[2] + vel[2] * dt;
868 return new_pos;
869 }
870
876 void BackwardLocalPlanner::publishGoalMarker(double x, double y, double phi)
877 {
878 visualization_msgs::Marker marker;
879 marker.header.frame_id = this->costmapRos_->getGlobalFrameID();
880 marker.header.stamp = ros::Time::now();
881
882 marker.ns = "my_namespace2";
883 marker.id = 0;
884 marker.type = visualization_msgs::Marker::ARROW;
885 marker.action = visualization_msgs::Marker::ADD;
886 marker.pose.orientation.w = 1;
887
888 marker.scale.x = 0.05;
889 marker.scale.y = 0.15;
890 marker.scale.z = 0.05;
891 marker.color.a = 1.0;
892
893 marker.color.r = 1;
894 marker.color.g = 0;
895 marker.color.b = 0;
896
897 geometry_msgs::Point start, end;
898 start.x = x;
899 start.y = y;
900
901 end.x = x + 0.5 * cos(phi);
902 end.y = y + 0.5 * sin(phi);
903
904 marker.points.push_back(start);
905 marker.points.push_back(end);
906
907 visualization_msgs::MarkerArray ma;
908 ma.markers.push_back(marker);
909
910 goalMarkerPublisher_.publish(ma);
911 }
912 } // namespace backward_local_planner
913} // namespace cl_move_base_z
PLUGINLIB_EXPORT_CLASS(cl_move_base_z::backward_global_planner::BackwardGlobalPlanner, nav_core::BaseGlobalPlanner)
virtual bool isGoalReached() override
Check if the goal pose has been achieved by the local planner.
Eigen::Vector3f computeNewPositions(const Eigen::Vector3f &pos, const Eigen::Vector3f &vel, double dt)
bool updateCarrotGoal(const tf::Stamped< tf::Pose > &tfpose)
void defaultBackwardCmd(const tf::Stamped< tf::Pose > &tfpose, double vetta, double gamma, double alpha_error, double betta_error, geometry_msgs::Twist &cmd_vel)
bool checkCarrotHalfPlainConstraint(const tf::Stamped< tf::Pose > &tfpose)
virtual bool computeVelocityCommands(geometry_msgs::Twist &cmd_vel) override
Given the current position, orientation, and velocity of the robot: compute velocity commands to send...
void straightBackwardsAndPureSpinCmd(const tf::Stamped< tf::Pose > &tfpose, double vetta, double gamma, double alpha_error, double betta_error, double rho_error, geometry_msgs::Twist &cmd_vel)
void reconfigCB(::backward_local_planner::BackwardLocalPlannerConfig &config, uint32_t level)
bool checkCurrentPoseInGoalRange(const tf::Stamped< tf::Pose > &tfpose, double angle_error, bool &inLinearGoal)
void generateTrajectory(const Eigen::Vector3f &pos, const Eigen::Vector3f &vel, float maxdist, float maxangle, float maxtime, float dt, std::vector< Eigen::Vector3f > &outtraj)
virtual bool setPlan(const std::vector< geometry_msgs::PoseStamped > &plan) override
Set the plan that the local planner is following.
bool divergenceDetectionUpdate(const tf::Stamped< tf::Pose > &tfpose)
void computeCurrentEuclideanAndAngularErrorsToCarrotGoal(const tf::Stamped< tf::Pose > &tfpose, double &dist, double &angular_error)
std::vector< geometry_msgs::PoseStamped > backwardsPlanPath_
dynamic_reconfigure::Server<::backward_local_planner::BackwardLocalPlannerConfig >::CallbackType f
dynamic_reconfigure::Server<::backward_local_planner::BackwardLocalPlannerConfig > paramServer_
tf::Stamped< tf::Pose > optionalRobotPose(costmap_2d::Costmap2DROS *costmapRos)