homotopy_class_planner.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2016,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of the institute nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  * Author: Christoph Rösmann
37  *********************************************************************/
38 
40 
41 #include <limits>
42 
43 namespace teb_local_planner
44 {
45 
46 HomotopyClassPlanner::HomotopyClassPlanner() : cfg_(NULL), obstacles_(NULL), via_points_(NULL), robot_model_(new PointRobotFootprint()), initial_plan_(NULL), initialized_(false)
47 {
48 }
49 
52 {
53  initialize(cfg, obstacles, robot_model, visual, via_points);
54 }
55 
57 {
58 }
59 
62 {
63  cfg_ = &cfg;
66  robot_model_ = robot_model;
67 
70  else
72 
73  initialized_ = true;
74 
75  setVisualization(visual);
76 }
77 
78 
80 {
81  visualization_ = visualization;
82 }
83 
84 
85 
86 bool HomotopyClassPlanner::plan(const std::vector<geometry_msgs::PoseStamped>& initial_plan, const geometry_msgs::Twist* start_vel, bool free_goal_vel)
87 {
88  ROS_ASSERT_MSG(initialized_, "Call initialize() first.");
89 
90  // store initial plan for further initializations (must be valid for the lifetime of this object or clearPlanner() is called!)
91  initial_plan_ = &initial_plan;
92 
93  PoseSE2 start(initial_plan.front().pose);
94  PoseSE2 goal(initial_plan.back().pose);
95 
96  return plan(start, goal, start_vel, free_goal_vel);
97 }
98 
99 
100 bool HomotopyClassPlanner::plan(const tf::Pose& start, const tf::Pose& goal, const geometry_msgs::Twist* start_vel, bool free_goal_vel)
101 {
102  ROS_ASSERT_MSG(initialized_, "Call initialize() first.");
103  PoseSE2 start_pose(start);
104  PoseSE2 goal_pose(goal);
105  return plan(start_pose, goal_pose, start_vel, free_goal_vel);
106 }
107 
108 bool HomotopyClassPlanner::plan(const PoseSE2& start, const PoseSE2& goal, const geometry_msgs::Twist* start_vel, bool free_goal_vel)
109 {
110  ROS_ASSERT_MSG(initialized_, "Call initialize() first.");
111 
112  // Update old TEBs with new start, goal and velocity
113  updateAllTEBs(&start, &goal, start_vel);
114 
115  // Init new TEBs based on newly explored homotopy classes
117  // update via-points if activated
119  // Optimize all trajectories in alternative homotopy classes
121  // Select which candidate (based on alternative homotopy classes) should be used
122  selectBestTeb();
123 
124  initial_plan_ = nullptr; // clear pointer to any previous initial plan (any previous plan is useless regarding the h-signature);
125  return true;
126 }
127 
128 bool HomotopyClassPlanner::getVelocityCommand(double& vx, double& vy, double& omega, int look_ahead_poses) const
129 {
130  TebOptimalPlannerConstPtr best_teb = bestTeb();
131  if (!best_teb)
132  {
133  vx = 0;
134  vy = 0;
135  omega = 0;
136  return false;
137  }
138 
139  return best_teb->getVelocityCommand(vx, vy, omega, look_ahead_poses);
140 }
141 
142 
143 
144 
146 {
147  if (visualization_)
148  {
149  // Visualize graph
151  visualization_->publishGraph(graph_search_->graph_);
152 
153  // Visualize active tebs as marker
154  visualization_->publishTebContainer(tebs_);
155 
156  // Visualize best teb and feedback message if desired
157  TebOptimalPlannerConstPtr best_teb = bestTeb();
158  if (best_teb)
159  {
160  visualization_->publishLocalPlanAndPoses(best_teb->teb());
161 
162  if (best_teb->teb().sizePoses() > 0) //TODO maybe store current pose (start) within plan method as class field.
163  visualization_->publishRobotFootprintModel(best_teb->teb().Pose(0), *robot_model_);
164 
165  // feedback message
167  {
168  int best_idx = bestTebIdx();
169  if (best_idx>=0)
170  visualization_->publishFeedbackMessage(tebs_, (unsigned int) best_idx, *obstacles_);
171  }
172  }
173  }
174  else ROS_DEBUG("Ignoring HomotopyClassPlanner::visualize() call, since no visualization class was instantiated before.");
175 }
176 
177 
178 
180 {
181  // iterate existing h-signatures and check if there is an existing H-Signature similar the candidate
182  for (const std::pair<EquivalenceClassPtr, bool>& eqrel : equivalence_classes_)
183  {
184  if (eq_class->isEqual(*eqrel.first))
185  return true; // Found! Homotopy class already exists, therefore nothing added
186  }
187  return false;
188 }
189 
191 {
192  if (!eq_class)
193  return false;
194 
195  if (!eq_class->isValid())
196  {
197  ROS_WARN("HomotopyClassPlanner: Ignoring invalid H-signature");
198  return false;
199  }
200 
201  if (hasEquivalenceClass(eq_class))
202  return false;
203 
204  // Homotopy class not found -> Add to class-list, return that the h-signature is new
205  equivalence_classes_.push_back(std::make_pair(eq_class,lock));
206  return true;
207 }
208 
209 
211 {
212  // clear old h-signatures (since they could be changed due to new obstacle positions.
213  equivalence_classes_.clear();
214 
215  // Adding the equivalence class of the latest best_teb_ first
216  TebOptPlannerContainer::iterator it_best_teb = best_teb_ ? std::find(tebs_.begin(), tebs_.end(), best_teb_) : tebs_.end();
217  bool has_best_teb = it_best_teb != tebs_.end();
218  if (has_best_teb)
219  {
220  std::iter_swap(tebs_.begin(), it_best_teb); // Putting the last best teb at the beginning of the container
222  best_teb_->teb().poses().end(), getCplxFromVertexPosePtr , obstacles_,
223  best_teb_->teb().timediffs().begin(), best_teb_->teb().timediffs().end()));
224  }
225  // Collect h-signatures for all existing TEBs and store them together with the corresponding iterator / pointer:
226 // typedef std::list< std::pair<TebOptPlannerContainer::iterator, std::complex<long double> > > TebCandidateType;
227 // TebCandidateType teb_candidates;
228 
229  // get new homotopy classes and delete multiple TEBs per homotopy class. Skips the best teb if available (added before).
230  TebOptPlannerContainer::iterator it_teb = has_best_teb ? std::next(tebs_.begin(), 1) : tebs_.begin();
231  while(it_teb != tebs_.end())
232  {
233  // calculate equivalence class for the current candidate
234  EquivalenceClassPtr equivalence_class = calculateEquivalenceClass(it_teb->get()->teb().poses().begin(), it_teb->get()->teb().poses().end(), getCplxFromVertexPosePtr , obstacles_,
235  it_teb->get()->teb().timediffs().begin(), it_teb->get()->teb().timediffs().end());
236 
237 // teb_candidates.push_back(std::make_pair(it_teb,H));
238 
239  // WORKAROUND until the commented code below works
240  // Here we do not compare cost values. Just first come first serve...
241  bool new_flag = addEquivalenceClassIfNew(equivalence_class);
242  if (!new_flag)
243  {
244  it_teb = tebs_.erase(it_teb);
245  continue;
246  }
247 
248  ++it_teb;
249  }
250  if(delete_detours)
252 
253  // Find multiple candidates and delete the one with higher cost
254  // TODO: this code needs to be adpated. Erasing tebs from the teb container_ could make iteratores stored in the candidate list invalid!
255 // TebCandidateType::reverse_iterator cand_i = teb_candidates.rbegin();
256 // int test_idx = 0;
257 // while (cand_i != teb_candidates.rend())
258 // {
259 //
260 // TebCandidateType::reverse_iterator cand_j = std::find_if(boost::next(cand_i),teb_candidates.rend(), boost::bind(compareH,_1,cand_i->second));
261 // if (cand_j != teb_candidates.rend() && cand_j != cand_i)
262 // {
263 // TebOptimalPlannerPtr pt1 = *(cand_j->first);
264 // TebOptimalPlannerPtr pt2 = *(cand_i->first);
265 // assert(pt1);
266 // assert(pt2);
267 // if ( cand_j->first->get()->getCurrentCost().sum() > cand_i->first->get()->getCurrentCost().sum() )
268 // {
269 // // found one that has higher cost, therefore erase cand_j
270 // tebs_.erase(cand_j->first);
271 // teb_candidates.erase(cand_j);
272 // }
273 // else // otherwise erase cand_i
274 // {
275 // tebs_.erase(cand_i->first);
276 // cand_i = teb_candidates.erase(cand_i);
277 // }
278 // }
279 // else
280 // {
281 // ROS_WARN_STREAM("increase cand_i");
282 // ++cand_i;
283 // }
284 // }
285 
286  // now add the h-signatures to the internal lookup-table (but only if there is no existing duplicate)
287 // for (TebCandidateType::iterator cand=teb_candidates.begin(); cand!=teb_candidates.end(); ++cand)
288 // {
289 // bool new_flag = addNewHSignatureIfNew(cand->second, cfg_->hcp.h_signature_threshold);
290 // if (!new_flag)
291 // {
292 // // ROS_ERROR_STREAM("getAndFilterHomotopyClassesTEB() - This schould not be happen.");
293 // tebs_.erase(cand->first);
294 // }
295 // }
296 
297 }
298 
300 {
301  if ( (!all_trajectories && !initial_plan_) || !via_points_ || via_points_->empty() || cfg_->optim.weight_viapoint <= 0)
302  return;
303 
304  if(equivalence_classes_.size() < tebs_.size())
305  {
306  ROS_ERROR("HomotopyClassPlanner::updateReferenceTrajectoryWithViaPoints(): Number of h-signatures does not match number of trajectories.");
307  return;
308  }
309 
310  if (all_trajectories)
311  {
312  // enable via-points for all tebs
313  for (std::size_t i=0; i < equivalence_classes_.size(); ++i)
314  {
315  tebs_[i]->setViaPoints(via_points_);
316  }
317  }
318  else
319  {
320  // enable via-points for teb in the same hommotopy class as the initial_plan and deactivate it for all other ones
321  for (std::size_t i=0; i < equivalence_classes_.size(); ++i)
322  {
323  if(initial_plan_eq_class_->isEqual(*equivalence_classes_[i].first))
324  tebs_[i]->setViaPoints(via_points_);
325  else
326  tebs_[i]->setViaPoints(NULL);
327  }
328  }
329 }
330 
331 
332 void HomotopyClassPlanner::exploreEquivalenceClassesAndInitTebs(const PoseSE2& start, const PoseSE2& goal, double dist_to_obst, const geometry_msgs::Twist* start_vel)
333 {
334  // first process old trajectories
336 
337  // inject initial plan if available and not yet captured
338  if (initial_plan_)
339  {
341  }
342  else
343  {
344  initial_plan_teb_.reset();
345  initial_plan_teb_ = getInitialPlanTEB(); // this method searches for initial_plan_eq_class_ in the teb container (-> if !initial_plan_teb_)
346  }
347 
348  // now explore new homotopy classes and initialize tebs if new ones are found. The appropriate createGraph method is chosen via polymorphism.
349  graph_search_->createGraph(start,goal,dist_to_obst,cfg_->hcp.obstacle_heading_threshold, start_vel);
350 }
351 
352 
354 {
355  if(tebs_.size() >= cfg_->hcp.max_number_classes)
356  return TebOptimalPlannerPtr();
358 
359  candidate->teb().initTrajectoryToGoal(start, goal, 0, cfg_->robot.max_vel_x, cfg_->trajectory.min_samples, cfg_->trajectory.allow_init_with_backwards_motion);
360 
361  if (start_velocity)
362  candidate->setVelocityStart(*start_velocity);
363 
364  EquivalenceClassPtr H = calculateEquivalenceClass(candidate->teb().poses().begin(), candidate->teb().poses().end(), getCplxFromVertexPosePtr, obstacles_,
365  candidate->teb().timediffs().begin(), candidate->teb().timediffs().end());
366 
368  {
369  tebs_.push_back(candidate);
370  return tebs_.back();
371  }
372 
373  // If the candidate constitutes no new equivalence class, return a null pointer
374  return TebOptimalPlannerPtr();
375 }
376 
377 
378 TebOptimalPlannerPtr HomotopyClassPlanner::addAndInitNewTeb(const std::vector<geometry_msgs::PoseStamped>& initial_plan, const geometry_msgs::Twist* start_velocity)
379 {
380  if(tebs_.size() >= cfg_->hcp.max_number_classes)
381  return TebOptimalPlannerPtr();
383 
384  candidate->teb().initTrajectoryToGoal(initial_plan, cfg_->robot.max_vel_x,
386 
387  if (start_velocity)
388  candidate->setVelocityStart(*start_velocity);
389 
390  // store the h signature of the initial plan to enable searching a matching teb later.
391  initial_plan_eq_class_ = calculateEquivalenceClass(candidate->teb().poses().begin(), candidate->teb().poses().end(), getCplxFromVertexPosePtr, obstacles_,
392  candidate->teb().timediffs().begin(), candidate->teb().timediffs().end());
393 
394  if(addEquivalenceClassIfNew(initial_plan_eq_class_, true)) // also prevent candidate from deletion
395  {
396  tebs_.push_back(candidate);
397  return tebs_.back();
398  }
399 
400  // If the candidate constitutes no new equivalence class, return a null pointer
401  return TebOptimalPlannerPtr();
402 }
403 
404 void HomotopyClassPlanner::updateAllTEBs(const PoseSE2* start, const PoseSE2* goal, const geometry_msgs::Twist* start_velocity)
405 {
406  // If new goal is too far away, clear all existing trajectories to let them reinitialize later.
407  // Since all Tebs are sharing the same fixed goal pose, just take the first candidate:
408  if (!tebs_.empty()
409  && ((goal->position() - tebs_.front()->teb().BackPose().position()).norm() >= cfg_->trajectory.force_reinit_new_goal_dist
410  || fabs(g2o::normalize_theta(goal->theta() - tebs_.front()->teb().BackPose().theta())) >= cfg_->trajectory.force_reinit_new_goal_angular))
411  {
412  ROS_DEBUG("New goal: distance to existing goal is higher than the specified threshold. Reinitalizing trajectories.");
413  tebs_.clear();
414  equivalence_classes_.clear();
415  }
416 
417  // hot-start from previous solutions
418  for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
419  {
420  it_teb->get()->teb().updateAndPruneTEB(*start, *goal);
421  if (start_velocity)
422  it_teb->get()->setVelocityStart(*start_velocity);
423  }
424 }
425 
426 
427 void HomotopyClassPlanner::optimizeAllTEBs(int iter_innerloop, int iter_outerloop)
428 {
429  // optimize TEBs in parallel since they are independend of each other
431  {
432  // Must prevent .join_all() from throwing exception if interruption was
433  // requested, as this can lead to multiple threads operating on the same
434  // TEB, which leads to SIGSEGV
435  boost::this_thread::disable_interruption di;
436 
437  boost::thread_group teb_threads;
438  for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
439  {
440  teb_threads.create_thread( boost::bind(&TebOptimalPlanner::optimizeTEB, it_teb->get(), iter_innerloop, iter_outerloop,
443  }
444  teb_threads.join_all();
445  }
446  else
447  {
448  for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
449  {
450  it_teb->get()->optimizeTEB(iter_innerloop,iter_outerloop, true, cfg_->hcp.selection_obst_cost_scale,
451  cfg_->hcp.selection_viapoint_cost_scale, cfg_->hcp.selection_alternative_time_cost); // compute cost as well inside optimizeTEB (last argument = true)
452  }
453  }
454 }
455 
457 {
458  // first check stored teb object
459  if (initial_plan_teb_)
460  {
461  // check if the teb is still part of the teb container
462  if ( std::find(tebs_.begin(), tebs_.end(), initial_plan_teb_ ) != tebs_.end() )
463  return initial_plan_teb_;
464  else
465  {
466  initial_plan_teb_.reset(); // reset pointer for next call
467  ROS_DEBUG("initial teb not found, trying to find a match according to the cached equivalence class");
468  }
469  }
470 
471  // reset the locked state for equivalence classes // TODO: this might be adapted if not only the plan containing the initial plan is locked!
472  for (int i=0; i<equivalence_classes_.size(); ++i)
473  {
474  equivalence_classes_[i].second = false;
475  }
476 
477  // otherwise check if the stored reference equivalence class exist in the list of known classes
479  {
480  if (equivalence_classes_.size() == tebs_.size())
481  {
482  for (int i=0; i<equivalence_classes_.size(); ++i)
483  {
484  if (equivalence_classes_[i].first->isEqual(*initial_plan_eq_class_))
485  {
486  equivalence_classes_[i].second = true;
487  return tebs_[i];
488  }
489  }
490  }
491  else
492  ROS_ERROR("HomotopyClassPlanner::getInitialPlanTEB(): number of equivalence classes (%lu) and number of trajectories (%lu) does not match.", equivalence_classes_.size(), tebs_.size());
493  }
494  else
495  ROS_DEBUG("HomotopyClassPlanner::getInitialPlanTEB(): initial TEB not found in the set of available trajectories.");
496 
497  return TebOptimalPlannerPtr();
498 }
499 
501 {
502  double min_cost = std::numeric_limits<double>::max(); // maximum cost
503  double min_cost_last_best = std::numeric_limits<double>::max();
504  double min_cost_initial_plan_teb = std::numeric_limits<double>::max();
505  TebOptimalPlannerPtr initial_plan_teb = getInitialPlanTEB();
506 
507  // check if last best_teb is still a valid candidate
508  if (best_teb_ && std::find(tebs_.begin(), tebs_.end(), best_teb_) != tebs_.end())
509  {
510  // get cost of this candidate
511  min_cost_last_best = best_teb_->getCurrentCost() * cfg_->hcp.selection_cost_hysteresis; // small hysteresis
513  }
514  else
515  {
516  last_best_teb_.reset();
517  }
518 
519  if (initial_plan_teb) // the validity was already checked in getInitialPlanTEB()
520  {
521  // get cost of this candidate
522  min_cost_initial_plan_teb = initial_plan_teb->getCurrentCost() * cfg_->hcp.selection_prefer_initial_plan; // small hysteresis
523  }
524 
525 
526  best_teb_.reset(); // reset pointer
527 
528  for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
529  {
530  // check if the related TEB leaves the local costmap region
531 // if (tebs_.size()>1 && !(*it_teb)->teb().isTrajectoryInsideRegion(20, -1, 1))
532 // {
533 // ROS_INFO("HomotopyClassPlanner::selectBestTeb(): skipping trajectories that are not inside the local costmap");
534 // continue;
535 // }
536 
537  double teb_cost;
538 
539  if (*it_teb == last_best_teb_)
540  teb_cost = min_cost_last_best; // skip already known cost value of the last best_teb
541  else if (*it_teb == initial_plan_teb)
542  teb_cost = min_cost_initial_plan_teb;
543  else
544  teb_cost = it_teb->get()->getCurrentCost();
545 
546  if (teb_cost < min_cost)
547  {
548  // check if this candidate is currently not selected
549  best_teb_ = *it_teb;
550  min_cost = teb_cost;
551  }
552  }
553 
554 
555  // in case we haven't found any teb due to some previous checks, investigate list again
556 // if (!best_teb_ && !tebs_.empty())
557 // {
558 // ROS_DEBUG("all " << tebs_.size() << " tebs rejected previously");
559 // if (tebs_.size()==1)
560 // best_teb_ = tebs_.front();
561 // else // if multiple TEBs are available:
562 // {
563 // // try to use the one that relates to the initial plan
564 // TebOptimalPlannerPtr initial_plan_teb = getInitialPlanTEB();
565 // if (initial_plan_teb)
566 // best_teb_ = initial_plan_teb;
567 // else
568 // {
569 // // now compute the cost for the rest (we haven't computed it before)
570 // for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
571 // {
572 // double teb_cost = it_teb->get()->getCurrentCost();
573 // if (teb_cost < min_cost)
574 // {
575 // // check if this candidate is currently not selected
576 // best_teb_ = *it_teb;
577 // min_cost = teb_cost;
578 // }
579 // }
580 // }
581 // }
582 // }
583 
584  // check if we are allowed to change
586  {
587  ros::Time now = ros::Time::now();
589  {
591  }
592  else
593  {
594  ROS_DEBUG("HomotopyClassPlanner::selectBestTeb(): Switching equivalence classes blocked (check parameter switching_blocking_period.");
595  // block switching, so revert best_teb_
597  }
598 
599  }
600 
601 
602  return best_teb_;
603 }
604 
606 {
607  if (tebs_.size() == 1)
608  return 0;
609 
610  if (!best_teb_)
611  return -1;
612 
613  int idx = 0;
614  for (TebOptPlannerContainer::const_iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb, ++idx)
615  {
616  if (*it_teb == best_teb_)
617  return idx;
618  }
619  return -1;
620 }
621 
622 bool HomotopyClassPlanner::isTrajectoryFeasible(base_local_planner::CostmapModel* costmap_model, const std::vector<geometry_msgs::Point>& footprint_spec,
623  double inscribed_radius, double circumscribed_radius, int look_ahead_idx)
624 {
625  bool feasible = false;
626  while(ros::ok() && !feasible && tebs_.size() > 0)
627  {
629  if (!best)
630  {
631  ROS_ERROR("Couldn't retrieve the best plan");
632  return false;
633  }
634  feasible = best->isTrajectoryFeasible(costmap_model, footprint_spec, inscribed_radius, circumscribed_radius, look_ahead_idx);
635  if(!feasible)
636  {
637  removeTeb(best);
638  if(last_best_teb_ && (last_best_teb_ == best)) // Same plan as before.
639  return feasible; // Not failing could result in oscillations between trajectories.
640  }
641  }
642  return feasible;
643 }
644 
646 {
647  if(tebs_.empty())
648  return TebOptimalPlannerPtr();
649  if (!best_teb_ || std::find(tebs_.begin(), tebs_.end(), best_teb_) == tebs_.end())
651  return best_teb_;
652 }
653 
654 TebOptPlannerContainer::iterator HomotopyClassPlanner::removeTeb(TebOptimalPlannerPtr& teb)
655 {
656  TebOptPlannerContainer::iterator return_iterator = tebs_.end();
657  if(equivalence_classes_.size() != tebs_.size())
658  {
659  ROS_ERROR("removeTeb: size of eq classes != size of tebs");
660  return return_iterator;
661  }
662  auto it_eq_classes = equivalence_classes_.begin();
663  for(auto it = tebs_.begin(); it != tebs_.end(); ++it)
664  {
665  if(*it == teb)
666  {
667  return_iterator = tebs_.erase(it);
668  equivalence_classes_.erase(it_eq_classes);
669  break;
670  }
671  ++it_eq_classes;
672  }
673  return return_iterator;
674 }
675 
677 {
678  // set preferred turning dir for all TEBs
679  for (TebOptPlannerContainer::const_iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
680  {
681  (*it_teb)->setPreferredTurningDir(dir);
682  }
683 }
684 
685 void HomotopyClassPlanner::computeCurrentCost(std::vector<double>& cost, double obst_cost_scale, double viapoint_cost_scale, bool alternative_time_cost)
686 {
687  for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
688  {
689  it_teb->get()->computeCurrentCost(cost, obst_cost_scale, viapoint_cost_scale, alternative_time_cost);
690  }
691 }
692 
693 void HomotopyClassPlanner::deletePlansDetouringBackwards(const double orient_threshold,
694  const double len_orientation_vector)
695 {
696  if (tebs_.size() < 2 || !best_teb_ || std::find(tebs_.begin(), tebs_.end(), best_teb_) == tebs_.end() ||
697  best_teb_->teb().sizePoses() < 2)
698  {
699  return; // A moving direction wasn't chosen yet
700  }
701  double current_movement_orientation;
702  double best_plan_duration = std::max(best_teb_->teb().getSumOfAllTimeDiffs(), 1.0);
703  if(!computeStartOrientation(best_teb_, len_orientation_vector, current_movement_orientation))
704  return; // The plan is shorter than len_orientation_vector
705  for(auto it_teb = tebs_.begin(); it_teb != tebs_.end();)
706  {
707  if(*it_teb == best_teb_) // The current plan should not be considered a detour
708  {
709  ++it_teb;
710  continue;
711  }
712  if((*it_teb)->teb().sizePoses() < 2)
713  {
714  ROS_DEBUG("Discarding a plan with less than 2 poses");
715  it_teb = removeTeb(*it_teb);
716  continue;
717  }
718  double plan_orientation;
719  if(!computeStartOrientation(*it_teb, len_orientation_vector, plan_orientation))
720  {
721  ROS_DEBUG("Failed to compute the start orientation for one of the tebs, likely close to the target");
722  it_teb = removeTeb(*it_teb);
723  continue;
724  }
725  if(fabs(g2o::normalize_theta(plan_orientation - current_movement_orientation)) > orient_threshold)
726  {
727  it_teb = removeTeb(*it_teb); // Plan detouring backwards
728  continue;
729  }
730  if(!it_teb->get()->isOptimized())
731  {
732  ROS_DEBUG("Removing a teb because it's not optimized");
733  it_teb = removeTeb(*it_teb); // Deletes tebs that cannot be optimized (last optim call failed)
734  continue;
735  }
736  if(it_teb->get()->teb().getSumOfAllTimeDiffs() / best_plan_duration > cfg_->hcp.max_ratio_detours_duration_best_duration)
737  {
738  ROS_DEBUG("Removing a teb because it's duration is much longer than that of the best teb");
739  it_teb = removeTeb(*it_teb);
740  continue;
741  }
742  ++it_teb;
743  }
744 }
745 
746 bool HomotopyClassPlanner::computeStartOrientation(const TebOptimalPlannerPtr plan, const double len_orientation_vector,
747  double& orientation)
748 {
749  VertexPose start_pose = plan->teb().Pose(0);
750  bool second_pose_found = false;
751  Eigen::Vector2d start_vector;
752  for(auto& pose : plan->teb().poses())
753  {
754  start_vector = start_pose.position() - pose->position();
755  if(start_vector.norm() > len_orientation_vector)
756  {
757  second_pose_found = true;
758  break;
759  }
760  }
761  if(!second_pose_found) // The current plan is too short to make assumptions on the start orientation
762  return false;
763  orientation = std::atan2(start_vector[1], start_vector[0]);
764  return true;
765 }
766 
767 
768 } // end namespace
boost::shared_ptr< GraphSearchInterface > graph_search_
virtual bool isTrajectoryFeasible(base_local_planner::CostmapModel *costmap_model, const std::vector< geometry_msgs::Point > &footprint_spec, double inscribed_radius=0.0, double circumscribed_radius=0.0, int look_ahead_idx=-1)
Check whether the planned trajectory is feasible or not.
TebVisualizationPtr visualization_
Instance of the visualization class (local/global plan, obstacles, ...)
bool allow_init_with_backwards_motion
If true, the underlying trajectories might be initialized with backwards motions in case the goal is ...
Definition: teb_config.h:77
double min_obstacle_dist
Minimum desired separation from obstacles.
Definition: teb_config.h:119
bool computeStartOrientation(const TebOptimalPlannerPtr plan, const double len_orientation_vector, double &orientation)
Given a plan, computes its start orientation using a vector of length >= len_orientation_vector start...
Eigen::Vector2d & position()
Access the 2D position part.
Definition: pose_se2.h:145
double force_reinit_new_goal_angular
Reinitialize the trajectory if a previous goal is updated with an angular difference of more than the...
Definition: teb_config.h:84
void exploreEquivalenceClassesAndInitTebs(const PoseSE2 &start, const PoseSE2 &goal, double dist_to_obst, const geometry_msgs::Twist *start_vel)
Explore paths in new equivalence classes (e.g. homotopy classes) and initialize TEBs from them...
const ObstContainer * obstacles() const
Access current obstacle container (read-only)
Config class for the teb_local_planner and its components.
Definition: teb_config.h:61
const std::vector< geometry_msgs::PoseStamped > * initial_plan_
Store the initial plan if available for a better trajectory initialization.
EquivalenceClassPtr initial_plan_eq_class_
Store the equivalence class of the initial plan.
TebOptimalPlannerPtr bestTeb() const
Access current best trajectory candidate (that relates to the "best" homotopy class).
bool selection_alternative_time_cost
If true, time cost is replaced by the total transition time.
Definition: teb_config.h:179
bool initialized_
Keeps track about the correct initialization of this class.
virtual void computeCurrentCost(std::vector< double > &cost, double obst_cost_scale=1.0, double viapoint_cost_scale=1.0, bool alternative_time_cost=false)
double obstacle_heading_threshold
Specify the value of the normalized scalar product between obstacle heading and goal heading in order...
Definition: teb_config.h:189
struct teb_local_planner::TebConfig::Robot robot
Robot related parameters.
void deletePlansDetouringBackwards(const double orient_threshold, const double len_orientation_vector)
Checks if the orientation of the computed trajectories differs from that of the best plan of more tha...
TebOptimalPlannerPtr selectBestTeb()
In case of multiple, internally stored, alternative trajectories, select the best one according to th...
double detours_orientation_tolerance
A plan is considered a detour if its start orientation differs more than this from the best plan...
Definition: teb_config.h:196
TebOptimalPlannerPtr initial_plan_teb_
Store pointer to the TEB related to the initial plan (use method getInitialPlanTEB() since it checks ...
TebOptimalPlannerPtr findBestTeb()
In case of empty best teb, scores again the available plans to find the best one. The best_teb_ varia...
TebOptimalPlannerPtr addAndInitNewTeb(BidirIter path_start, BidirIter path_end, Fun fun_position, double start_orientation, double goal_orientation, const geometry_msgs::Twist *start_velocity)
Add a new Teb to the internal trajectory container, if this teb constitutes a new equivalence class...
void updateReferenceTrajectoryViaPoints(bool all_trajectories)
Associate trajectories with via-points.
struct teb_local_planner::TebConfig::Trajectory trajectory
Trajectory related parameters.
EquivalenceClassPtr calculateEquivalenceClass(BidirIter path_start, BidirIter path_end, Fun fun_cplx_point, const ObstContainer *obstacles=NULL, boost::optional< TimeDiffSequence::iterator > timediff_start=boost::none, boost::optional< TimeDiffSequence::iterator > timediff_end=boost::none)
Calculate the equivalence class of a path.
TebVisualizationPtr visual
#define ROS_WARN(...)
boost::shared_ptr< TebOptimalPlanner > TebOptimalPlannerPtr
Abbrev. for shared instances of the TebOptimalPlanner.
virtual bool plan(const std::vector< geometry_msgs::PoseStamped > &initial_plan, const geometry_msgs::Twist *start_vel=NULL, bool free_goal_vel=false)
Plan a trajectory based on an initial reference plan.
RobotFootprintModelPtr robot_model_
Robot model shared instance.
ObstContainer * obstacles_
Store obstacles that are relevant for planning.
double force_reinit_new_goal_dist
Reinitialize the trajectory if a previous goal is updated with a seperation of more than the specifie...
Definition: teb_config.h:83
bool enable_multithreading
Activate multiple threading for planning multiple trajectories in parallel.
Definition: teb_config.h:172
RotType
Symbols for left/none/right rotations.
Definition: misc.h:53
const TebConfig * cfg_
Config class that stores and manages all related parameters.
double switching_blocking_period
Specify a time duration in seconds that needs to be expired before a switch to new equivalence class ...
Definition: teb_config.h:180
const ViaPointContainer * via_points_
Store the current list of via-points.
virtual ~HomotopyClassPlanner()
Destruct the HomotopyClassPlanner.
struct teb_local_planner::TebConfig::HomotopyClasses hcp
virtual bool getVelocityCommand(double &vx, double &vy, double &omega, int look_ahead_poses) const
Get the velocity command from a previously optimized plan to control the robot at the current samplin...
void initialize(const TebConfig &cfg, ObstContainer *obstacles=NULL, RobotFootprintModelPtr robot_model=boost::make_shared< PointRobotFootprint >(), TebVisualizationPtr visualization=TebVisualizationPtr(), const ViaPointContainer *via_points=NULL)
Initialize the HomotopyClassPlanner.
#define ROS_ASSERT_MSG(cond,...)
TebOptimalPlannerPtr getInitialPlanTEB()
Returns a shared pointer to the TEB related to the initial plan.
bool visualize_hc_graph
Visualize the graph that is created for exploring new homotopy classes.
Definition: teb_config.h:193
virtual void setPreferredTurningDir(RotType dir)
Prefer a desired initial turning direction (by penalizing the opposing one)
std::vector< ObstaclePtr > ObstContainer
Abbrev. for containers storing multiple obstacles.
Definition: obstacles.h:297
bool simple_exploration
If true, distinctive trajectories are explored using a simple left-right approach (pass each obstacle...
Definition: teb_config.h:173
ROSCPP_DECL bool ok()
double selection_cost_hysteresis
Specify how much trajectory cost must a new candidate have w.r.t. a previously selected trajectory in...
Definition: teb_config.h:175
std::complex< long double > getCplxFromVertexPosePtr(const VertexPose *pose)
< Inline function used for calculateHSignature() in combination with VertexPose pointers ...
TebOptPlannerContainer::iterator removeTeb(TebOptimalPlannerPtr &teb)
Removes the specified teb and the corresponding homotopy class from the list of available ones...
bool delete_detours_backwards
If enabled, the planner will discard the plans detouring backwards with respect to the best plan...
Definition: teb_config.h:195
bool hasEquivalenceClass(const EquivalenceClassPtr &eq_class) const
Check if a h-signature exists already.
double length_start_orientation_vector
Length of the vector used to compute the start orientation of a plan.
Definition: teb_config.h:197
TebOptPlannerContainer tebs_
Container that stores multiple local teb planners (for alternative equivalence classes) and their cor...
TebOptimalPlannerPtr last_best_teb_
Points to the plan used in the previous control cycle.
This class implements a pose in the domain SE2: The pose consist of the position x and y and an orie...
Definition: pose_se2.h:57
double & theta()
Access the orientation part (yaw angle) of the pose.
Definition: pose_se2.h:182
double selection_viapoint_cost_scale
Extra scaling of via-point cost terms just for selecting the &#39;best&#39; candidate.
Definition: teb_config.h:178
Eigen::Vector2d & position()
Access the 2D position part.
Definition: vertex_pose.h:141
int no_inner_iterations
Number of solver iterations called in each outerloop iteration.
Definition: teb_config.h:138
void renewAndAnalyzeOldTebs(bool delete_detours)
Renew all found h-signatures for the new planning step based on existing TEBs. Optionally detours can...
virtual void visualize()
Publish the local plan, pose sequence and additional information via ros topics (e.g. subscribe with rviz).
EquivalenceClassContainer equivalence_classes_
Store all known quivalence classes (e.g. h-signatures) to allow checking for duplicates after finding...
bool viapoints_all_candidates
If true, all trajectories of different topologies are attached to the current set of via-points...
Definition: teb_config.h:191
This class optimizes an internal Timed Elastic Band trajectory using the g2o-framework.
bool global_plan_overwrite_orientation
Overwrite orientation of local subgoals provided by the global planner.
Definition: teb_config.h:76
This class stores and wraps a SE2 pose (position and orientation) into a vertex that can be optimized...
Definition: vertex_pose.h:63
static Time now()
bool addEquivalenceClassIfNew(const EquivalenceClassPtr &eq_class, bool lock=false)
Internal helper function that adds a new equivalence class to the list of known classes only if it is...
double weight_viapoint
Optimization weight for minimizing the distance to via-points.
Definition: teb_config.h:161
void setVisualization(TebVisualizationPtr visualization)
Register a TebVisualization class to enable visiualization routines (e.g. publish the local plan and ...
void optimizeAllTEBs(int iter_innerloop, int iter_outerloop)
Optimize all available trajectories by invoking the optimizer on each one.
ViaPointContainer via_points
int no_outer_iterations
Each outerloop iteration automatically resizes the trajectory and invokes the internal optimizer with...
Definition: teb_config.h:139
TebOptimalPlannerPtr best_teb_
Store the current best teb.
struct teb_local_planner::TebConfig::Optimization optim
Optimization related parameters.
double selection_obst_cost_scale
Extra scaling of obstacle cost terms just for selecting the &#39;best&#39; candidate.
Definition: teb_config.h:177
bool publish_feedback
Publish planner feedback containing the full trajectory and a list of active obstacles (should be ena...
Definition: teb_config.h:86
void updateAllTEBs(const PoseSE2 *start, const PoseSE2 *goal, const geometry_msgs::Twist *start_velocity)
Update TEBs with new pose, goal and current velocity.
ros::Time last_eq_class_switching_time_
Store the time at which the equivalence class changed recently.
#define ROS_ERROR(...)
int max_number_classes
Specify the maximum number of allowed alternative homotopy classes (limits computational effort) ...
Definition: teb_config.h:174
double max_ratio_detours_duration_best_duration
Detours are discarted if their execution time / the execution time of the best teb is > this...
Definition: teb_config.h:198
struct teb_local_planner::TebConfig::Obstacles obstacles
Obstacle related parameters.
int min_samples
Minimum number of samples (should be always greater than 2)
Definition: teb_config.h:74
std::vector< Eigen::Vector2d, Eigen::aligned_allocator< Eigen::Vector2d > > ViaPointContainer
Typedef for a container storing via-points.
bool optimizeTEB(int iterations_innerloop, int iterations_outerloop, bool compute_cost_afterwards=false, double obst_cost_scale=1.0, double viapoint_cost_scale=1.0, bool alternative_time_cost=false)
Optimize a previously initialized trajectory (actual TEB optimization loop).
int bestTebIdx() const
find the index of the currently best TEB in the container
double max_vel_x
Maximum translational velocity of the robot.
Definition: teb_config.h:94
double selection_prefer_initial_plan
Specify a cost reduction in the interval (0,1) for the trajectory in the equivalence class of the ini...
Definition: teb_config.h:176
#define ROS_DEBUG(...)


teb_local_planner
Author(s): Christoph Rösmann
autogenerated on Wed Jun 3 2020 04:03:08