optimal_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 #include <map>
41 #include <limits>
42 
43 
44 namespace teb_local_planner
45 {
46 
47 // ============== Implementation ===================
48 
49 TebOptimalPlanner::TebOptimalPlanner() : cfg_(NULL), obstacles_(NULL), via_points_(NULL), cost_(HUGE_VAL), prefer_rotdir_(RotType::none),
50  robot_model_(new PointRobotFootprint()), initialized_(false), optimized_(false)
51 {
52 }
53 
55 {
56  initialize(cfg, obstacles, robot_model, visual, via_points);
57 }
58 
60 {
61  clearGraph();
62  // free dynamically allocated memory
63  //if (optimizer_)
64  // g2o::Factory::destroy();
65  //g2o::OptimizationAlgorithmFactory::destroy();
66  //g2o::HyperGraphActionLibrary::destroy();
67 }
68 
70 {
71  // init optimizer (set solver and block ordering settings)
73 
74  cfg_ = &cfg;
75  obstacles_ = obstacles;
76  robot_model_ = robot_model;
78  cost_ = HUGE_VAL;
80  setVisualization(visual);
81 
82  vel_start_.first = true;
83  vel_start_.second.linear.x = 0;
84  vel_start_.second.linear.y = 0;
85  vel_start_.second.angular.z = 0;
86 
87  vel_goal_.first = true;
88  vel_goal_.second.linear.x = 0;
89  vel_goal_.second.linear.y = 0;
90  vel_goal_.second.angular.z = 0;
91  initialized_ = true;
92 }
93 
94 
96 {
97  visualization_ = visualization;
98 }
99 
101 {
102  if (!visualization_)
103  return;
104 
105  visualization_->publishLocalPlanAndPoses(teb_);
106 
107  if (teb_.sizePoses() > 0)
108  visualization_->publishRobotFootprintModel(teb_.Pose(0), *robot_model_);
109 
111  visualization_->publishFeedbackMessage(*this, *obstacles_);
112 
113 }
114 
115 
116 /*
117  * registers custom vertices and edges in g2o framework
118  */
120 {
121  g2o::Factory* factory = g2o::Factory::instance();
122  factory->registerType("VERTEX_POSE", new g2o::HyperGraphElementCreator<VertexPose>);
123  factory->registerType("VERTEX_TIMEDIFF", new g2o::HyperGraphElementCreator<VertexTimeDiff>);
124 
125  factory->registerType("EDGE_TIME_OPTIMAL", new g2o::HyperGraphElementCreator<EdgeTimeOptimal>);
126  factory->registerType("EDGE_VELOCITY", new g2o::HyperGraphElementCreator<EdgeVelocity>);
127  factory->registerType("EDGE_VELOCITY_HOLONOMIC", new g2o::HyperGraphElementCreator<EdgeVelocityHolonomic>);
128  factory->registerType("EDGE_ACCELERATION", new g2o::HyperGraphElementCreator<EdgeAcceleration>);
129  factory->registerType("EDGE_ACCELERATION_START", new g2o::HyperGraphElementCreator<EdgeAccelerationStart>);
130  factory->registerType("EDGE_ACCELERATION_GOAL", new g2o::HyperGraphElementCreator<EdgeAccelerationGoal>);
131  factory->registerType("EDGE_ACCELERATION_HOLONOMIC", new g2o::HyperGraphElementCreator<EdgeAccelerationHolonomic>);
132  factory->registerType("EDGE_ACCELERATION_HOLONOMIC_START", new g2o::HyperGraphElementCreator<EdgeAccelerationHolonomicStart>);
133  factory->registerType("EDGE_ACCELERATION_HOLONOMIC_GOAL", new g2o::HyperGraphElementCreator<EdgeAccelerationHolonomicGoal>);
134  factory->registerType("EDGE_KINEMATICS_DIFF_DRIVE", new g2o::HyperGraphElementCreator<EdgeKinematicsDiffDrive>);
135  factory->registerType("EDGE_KINEMATICS_CARLIKE", new g2o::HyperGraphElementCreator<EdgeKinematicsCarlike>);
136  factory->registerType("EDGE_OBSTACLE", new g2o::HyperGraphElementCreator<EdgeObstacle>);
137  factory->registerType("EDGE_INFLATED_OBSTACLE", new g2o::HyperGraphElementCreator<EdgeInflatedObstacle>);
138  factory->registerType("EDGE_DYNAMIC_OBSTACLE", new g2o::HyperGraphElementCreator<EdgeDynamicObstacle>);
139  factory->registerType("EDGE_VIA_POINT", new g2o::HyperGraphElementCreator<EdgeViaPoint>);
140  factory->registerType("EDGE_PREFER_ROTDIR", new g2o::HyperGraphElementCreator<EdgePreferRotDir>);
141  return;
142 }
143 
144 /*
145  * initialize g2o optimizer. Set solver settings here.
146  * Return: pointer to new SparseOptimizer Object.
147  */
149 {
150  // Call register_g2o_types once, even for multiple TebOptimalPlanner instances (thread-safe)
151  static boost::once_flag flag = BOOST_ONCE_INIT;
152  boost::call_once(&registerG2OTypes, flag);
153 
154  // allocating the optimizer
155  boost::shared_ptr<g2o::SparseOptimizer> optimizer = boost::make_shared<g2o::SparseOptimizer>();
156  TEBLinearSolver* linearSolver = new TEBLinearSolver(); // see typedef in optimization.h
157  linearSolver->setBlockOrdering(true);
158  TEBBlockSolver* blockSolver = new TEBBlockSolver(linearSolver);
159  g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg(blockSolver);
160 
161  optimizer->setAlgorithm(solver);
162 
163  optimizer->initMultiThreading(); // required for >Eigen 3.1
164 
165  return optimizer;
166 }
167 
168 
169 bool TebOptimalPlanner::optimizeTEB(int iterations_innerloop, int iterations_outerloop, bool compute_cost_afterwards,
170  double obst_cost_scale, double viapoint_cost_scale, bool alternative_time_cost)
171 {
172  if (cfg_->optim.optimization_activate==false)
173  return false;
174 
175  bool success = false;
176  optimized_ = false;
177 
178  double weight_multiplier = 1.0;
179 
180  // TODO(roesmann): we introduced the non-fast mode with the support of dynamic obstacles
181  // (which leads to better results in terms of x-y-t homotopy planning).
182  // however, we have not tested this mode intensively yet, so we keep
183  // the legacy fast mode as default until we finish our tests.
184  bool fast_mode = !cfg_->obstacles.include_dynamic_obstacles;
185 
186  for(int i=0; i<iterations_outerloop; ++i)
187  {
189  {
190  //teb_.autoResize(cfg_->trajectory.dt_ref, cfg_->trajectory.dt_hysteresis, cfg_->trajectory.min_samples, cfg_->trajectory.max_samples);
192 
193  }
194 
195  success = buildGraph(weight_multiplier);
196  if (!success)
197  {
198  clearGraph();
199  return false;
200  }
201  success = optimizeGraph(iterations_innerloop, false);
202  if (!success)
203  {
204  clearGraph();
205  return false;
206  }
207  optimized_ = true;
208 
209  if (compute_cost_afterwards && i==iterations_outerloop-1) // compute cost vec only in the last iteration
210  computeCurrentCost(obst_cost_scale, viapoint_cost_scale, alternative_time_cost);
211 
212  clearGraph();
213 
214  weight_multiplier *= cfg_->optim.weight_adapt_factor;
215  }
216 
217  return true;
218 }
219 
221 {
222  vel_start_.first = true;
223  vel_start_.second.linear.x = vel_start.linear.x;
224  vel_start_.second.linear.y = vel_start.linear.y;
225  vel_start_.second.angular.z = vel_start.angular.z;
226 }
227 
229 {
230  vel_goal_.first = true;
231  vel_goal_.second = vel_goal;
232 }
233 
234 bool TebOptimalPlanner::plan(const std::vector<geometry_msgs::PoseStamped>& initial_plan, const geometry_msgs::Twist* start_vel, bool free_goal_vel)
235 {
236  ROS_ASSERT_MSG(initialized_, "Call initialize() first.");
237  if (!teb_.isInit())
238  {
239  // init trajectory
241  }
242  else // warm start
243  {
244  PoseSE2 start_(initial_plan.front().pose);
245  PoseSE2 goal_(initial_plan.back().pose);
246  if (teb_.sizePoses()>0 && (goal_.position() - teb_.BackPose().position()).norm() < cfg_->trajectory.force_reinit_new_goal_dist) // actual warm start!
247  teb_.updateAndPruneTEB(start_, goal_, cfg_->trajectory.min_samples); // update TEB
248  else // goal too far away -> reinit
249  {
250  ROS_DEBUG("New goal: distance to existing goal is higher than the specified threshold. Reinitalizing trajectories.");
253  }
254  }
255  if (start_vel)
256  setVelocityStart(*start_vel);
257  if (free_goal_vel)
259  else
260  vel_goal_.first = true; // we just reactivate and use the previously set velocity (should be zero if nothing was modified)
261 
262  // now optimize
264 }
265 
266 
267 bool TebOptimalPlanner::plan(const tf::Pose& start, const tf::Pose& goal, const geometry_msgs::Twist* start_vel, bool free_goal_vel)
268 {
269  PoseSE2 start_(start);
270  PoseSE2 goal_(goal);
271  return plan(start_, goal_, start_vel);
272 }
273 
274 bool TebOptimalPlanner::plan(const PoseSE2& start, const PoseSE2& goal, const geometry_msgs::Twist* start_vel, bool free_goal_vel)
275 {
276  ROS_ASSERT_MSG(initialized_, "Call initialize() first.");
277  if (!teb_.isInit())
278  {
279  // init trajectory
280  teb_.initTrajectoryToGoal(start, goal, 0, cfg_->robot.max_vel_x, cfg_->trajectory.min_samples, cfg_->trajectory.allow_init_with_backwards_motion); // 0 intermediate samples, but dt=1 -> autoResize will add more samples before calling first optimization
281  }
282  else // warm start
283  {
284  if (teb_.sizePoses()>0 && (goal.position() - teb_.BackPose().position()).norm() < cfg_->trajectory.force_reinit_new_goal_dist) // actual warm start!
286  else // goal too far away -> reinit
287  {
288  ROS_DEBUG("New goal: distance to existing goal is higher than the specified threshold. Reinitalizing trajectories.");
291  }
292  }
293  if (start_vel)
294  setVelocityStart(*start_vel);
295  if (free_goal_vel)
297  else
298  vel_goal_.first = true; // we just reactivate and use the previously set velocity (should be zero if nothing was modified)
299 
300  // now optimize
302 }
303 
304 
305 bool TebOptimalPlanner::buildGraph(double weight_multiplier)
306 {
307  if (!optimizer_->edges().empty() || !optimizer_->vertices().empty())
308  {
309  ROS_WARN("Cannot build graph, because it is not empty. Call graphClear()!");
310  return false;
311  }
312 
313  // add TEB vertices
314  AddTEBVertices();
315 
316  // add Edges (local cost functions)
318  AddEdgesObstaclesLegacy(weight_multiplier);
319  else
320  AddEdgesObstacles(weight_multiplier);
321 
324 
326 
328 
330 
332 
334  AddEdgesKinematicsDiffDrive(); // we have a differential drive robot
335  else
336  AddEdgesKinematicsCarlike(); // we have a carlike robot since the turning radius is bounded from below.
337 
338 
340 
341  return true;
342 }
343 
344 bool TebOptimalPlanner::optimizeGraph(int no_iterations,bool clear_after)
345 {
346  if (cfg_->robot.max_vel_x<0.01)
347  {
348  ROS_WARN("optimizeGraph(): Robot Max Velocity is smaller than 0.01m/s. Optimizing aborted...");
349  if (clear_after) clearGraph();
350  return false;
351  }
352 
354  {
355  ROS_WARN("optimizeGraph(): TEB is empty or has too less elements. Skipping optimization.");
356  if (clear_after) clearGraph();
357  return false;
358  }
359 
361  optimizer_->initializeOptimization();
362 
363  int iter = optimizer_->optimize(no_iterations);
364 
365  // Save Hessian for visualization
366  // g2o::OptimizationAlgorithmLevenberg* lm = dynamic_cast<g2o::OptimizationAlgorithmLevenberg*> (optimizer_->solver());
367  // lm->solver()->saveHessian("~/MasterThesis/Matlab/Hessian.txt");
368 
369  if(!iter)
370  {
371  ROS_ERROR("optimizeGraph(): Optimization failed! iter=%i", iter);
372  return false;
373  }
374 
375  if (clear_after) clearGraph();
376 
377  return true;
378 }
379 
381 {
382  // clear optimizer states
383  //optimizer.edges().clear(); // optimizer.clear deletes edges!!! Therefore do not run optimizer.edges().clear()
384  optimizer_->vertices().clear(); // neccessary, because optimizer->clear deletes pointer-targets (therefore it deletes TEB states!)
385  optimizer_->clear();
386 }
387 
388 
389 
391 {
392  // add vertices to graph
393  ROS_DEBUG_COND(cfg_->optim.optimization_verbose, "Adding TEB vertices ...");
394  unsigned int id_counter = 0; // used for vertices ids
395  for (int i=0; i<teb_.sizePoses(); ++i)
396  {
397  teb_.PoseVertex(i)->setId(id_counter++);
398  optimizer_->addVertex(teb_.PoseVertex(i));
399  if (teb_.sizeTimeDiffs()!=0 && i<teb_.sizeTimeDiffs())
400  {
401  teb_.TimeDiffVertex(i)->setId(id_counter++);
402  optimizer_->addVertex(teb_.TimeDiffVertex(i));
403  }
404  }
405 }
406 
407 
408 void TebOptimalPlanner::AddEdgesObstacles(double weight_multiplier)
409 {
410  if (cfg_->optim.weight_obstacle==0 || weight_multiplier==0 || obstacles_==nullptr )
411  return; // if weight equals zero skip adding edges!
412 
413 
415 
416  Eigen::Matrix<double,1,1> information;
417  information.fill(cfg_->optim.weight_obstacle * weight_multiplier);
418 
419  Eigen::Matrix<double,2,2> information_inflated;
420  information_inflated(0,0) = cfg_->optim.weight_obstacle * weight_multiplier;
421  information_inflated(1,1) = cfg_->optim.weight_inflation;
422  information_inflated(0,1) = information_inflated(1,0) = 0;
423 
424  // iterate all teb points (skip first and last)
425  for (int i=1; i < teb_.sizePoses()-1; ++i)
426  {
427  double left_min_dist = std::numeric_limits<double>::max();
428  double right_min_dist = std::numeric_limits<double>::max();
429  Obstacle* left_obstacle = nullptr;
430  Obstacle* right_obstacle = nullptr;
431 
432  std::vector<Obstacle*> relevant_obstacles;
433 
434  const Eigen::Vector2d pose_orient = teb_.Pose(i).orientationUnitVec();
435 
436  // iterate obstacles
437  for (const ObstaclePtr& obst : *obstacles_)
438  {
439  // we handle dynamic obstacles differently below
440  if(cfg_->obstacles.include_dynamic_obstacles && obst->isDynamic())
441  continue;
442 
443  // calculate distance to current pose
444  // TODO we ignore the robot footprint here in the association stage
445  double dist = obst->getMinimumDistance(teb_.Pose(i).position());
446 
447  // force considering obstacle if really close to the current pose
448  if (dist < cfg_->obstacles.min_obstacle_dist*cfg_->obstacles.obstacle_association_force_inclusion_factor)
449  {
450  relevant_obstacles.push_back(obst.get());
451  continue;
452  }
453  // cut-off distance
455  continue;
456 
457  // determine side (left or right) and assign obstacle if closer than the previous one
458  if (cross2d(pose_orient, obst->getCentroid()) > 0) // left
459  {
460  if (dist < left_min_dist)
461  {
462  left_min_dist = dist;
463  left_obstacle = obst.get();
464  }
465  }
466  else
467  {
468  if (dist < right_min_dist)
469  {
470  right_min_dist = dist;
471  right_obstacle = obst.get();
472  }
473  }
474  }
475 
476  // create obstacle edges
477  if (left_obstacle)
478  {
479  if (inflated)
480  {
481  EdgeInflatedObstacle* dist_bandpt_obst = new EdgeInflatedObstacle;
482  dist_bandpt_obst->setVertex(0,teb_.PoseVertex(i));
483  dist_bandpt_obst->setInformation(information_inflated);
484  dist_bandpt_obst->setParameters(*cfg_, robot_model_.get(), left_obstacle);
485  optimizer_->addEdge(dist_bandpt_obst);
486  }
487  else
488  {
489  EdgeObstacle* dist_bandpt_obst = new EdgeObstacle;
490  dist_bandpt_obst->setVertex(0,teb_.PoseVertex(i));
491  dist_bandpt_obst->setInformation(information);
492  dist_bandpt_obst->setParameters(*cfg_, robot_model_.get(), left_obstacle);
493  optimizer_->addEdge(dist_bandpt_obst);
494  }
495  }
496 
497  if (right_obstacle)
498  {
499  if (inflated)
500  {
501  EdgeInflatedObstacle* dist_bandpt_obst = new EdgeInflatedObstacle;
502  dist_bandpt_obst->setVertex(0,teb_.PoseVertex(i));
503  dist_bandpt_obst->setInformation(information_inflated);
504  dist_bandpt_obst->setParameters(*cfg_, robot_model_.get(), right_obstacle);
505  optimizer_->addEdge(dist_bandpt_obst);
506  }
507  else
508  {
509  EdgeObstacle* dist_bandpt_obst = new EdgeObstacle;
510  dist_bandpt_obst->setVertex(0,teb_.PoseVertex(i));
511  dist_bandpt_obst->setInformation(information);
512  dist_bandpt_obst->setParameters(*cfg_, robot_model_.get(), right_obstacle);
513  optimizer_->addEdge(dist_bandpt_obst);
514  }
515  }
516 
517  for (const Obstacle* obst : relevant_obstacles)
518  {
519  if (inflated)
520  {
521  EdgeInflatedObstacle* dist_bandpt_obst = new EdgeInflatedObstacle;
522  dist_bandpt_obst->setVertex(0,teb_.PoseVertex(i));
523  dist_bandpt_obst->setInformation(information_inflated);
524  dist_bandpt_obst->setParameters(*cfg_, robot_model_.get(), obst);
525  optimizer_->addEdge(dist_bandpt_obst);
526  }
527  else
528  {
529  EdgeObstacle* dist_bandpt_obst = new EdgeObstacle;
530  dist_bandpt_obst->setVertex(0,teb_.PoseVertex(i));
531  dist_bandpt_obst->setInformation(information);
532  dist_bandpt_obst->setParameters(*cfg_, robot_model_.get(), obst);
533  optimizer_->addEdge(dist_bandpt_obst);
534  }
535  }
536  }
537 
538 }
539 
540 
541 void TebOptimalPlanner::AddEdgesObstaclesLegacy(double weight_multiplier)
542 {
543  if (cfg_->optim.weight_obstacle==0 || weight_multiplier==0 || obstacles_==nullptr)
544  return; // if weight equals zero skip adding edges!
545 
546  Eigen::Matrix<double,1,1> information;
547  information.fill(cfg_->optim.weight_obstacle * weight_multiplier);
548 
549  Eigen::Matrix<double,2,2> information_inflated;
550  information_inflated(0,0) = cfg_->optim.weight_obstacle * weight_multiplier;
551  information_inflated(1,1) = cfg_->optim.weight_inflation;
552  information_inflated(0,1) = information_inflated(1,0) = 0;
553 
555 
556  for (ObstContainer::const_iterator obst = obstacles_->begin(); obst != obstacles_->end(); ++obst)
557  {
558  if (cfg_->obstacles.include_dynamic_obstacles && (*obst)->isDynamic()) // we handle dynamic obstacles differently below
559  continue;
560 
561  int index;
562 
564  index = teb_.sizePoses() / 2;
565  else
566  index = teb_.findClosestTrajectoryPose(*(obst->get()));
567 
568 
569  // check if obstacle is outside index-range between start and goal
570  if ( (index <= 1) || (index > teb_.sizePoses()-2) ) // start and goal are fixed and findNearestBandpoint finds first or last conf if intersection point is outside the range
571  continue;
572 
573  if (inflated)
574  {
575  EdgeInflatedObstacle* dist_bandpt_obst = new EdgeInflatedObstacle;
576  dist_bandpt_obst->setVertex(0,teb_.PoseVertex(index));
577  dist_bandpt_obst->setInformation(information_inflated);
578  dist_bandpt_obst->setParameters(*cfg_, robot_model_.get(), obst->get());
579  optimizer_->addEdge(dist_bandpt_obst);
580  }
581  else
582  {
583  EdgeObstacle* dist_bandpt_obst = new EdgeObstacle;
584  dist_bandpt_obst->setVertex(0,teb_.PoseVertex(index));
585  dist_bandpt_obst->setInformation(information);
586  dist_bandpt_obst->setParameters(*cfg_, robot_model_.get(), obst->get());
587  optimizer_->addEdge(dist_bandpt_obst);
588  }
589 
590  for (int neighbourIdx=0; neighbourIdx < floor(cfg_->obstacles.obstacle_poses_affected/2); neighbourIdx++)
591  {
592  if (index+neighbourIdx < teb_.sizePoses())
593  {
594  if (inflated)
595  {
596  EdgeInflatedObstacle* dist_bandpt_obst_n_r = new EdgeInflatedObstacle;
597  dist_bandpt_obst_n_r->setVertex(0,teb_.PoseVertex(index+neighbourIdx));
598  dist_bandpt_obst_n_r->setInformation(information_inflated);
599  dist_bandpt_obst_n_r->setParameters(*cfg_, robot_model_.get(), obst->get());
600  optimizer_->addEdge(dist_bandpt_obst_n_r);
601  }
602  else
603  {
604  EdgeObstacle* dist_bandpt_obst_n_r = new EdgeObstacle;
605  dist_bandpt_obst_n_r->setVertex(0,teb_.PoseVertex(index+neighbourIdx));
606  dist_bandpt_obst_n_r->setInformation(information);
607  dist_bandpt_obst_n_r->setParameters(*cfg_, robot_model_.get(), obst->get());
608  optimizer_->addEdge(dist_bandpt_obst_n_r);
609  }
610  }
611  if ( index - neighbourIdx >= 0) // needs to be casted to int to allow negative values
612  {
613  if (inflated)
614  {
615  EdgeInflatedObstacle* dist_bandpt_obst_n_l = new EdgeInflatedObstacle;
616  dist_bandpt_obst_n_l->setVertex(0,teb_.PoseVertex(index-neighbourIdx));
617  dist_bandpt_obst_n_l->setInformation(information_inflated);
618  dist_bandpt_obst_n_l->setParameters(*cfg_, robot_model_.get(), obst->get());
619  optimizer_->addEdge(dist_bandpt_obst_n_l);
620  }
621  else
622  {
623  EdgeObstacle* dist_bandpt_obst_n_l = new EdgeObstacle;
624  dist_bandpt_obst_n_l->setVertex(0,teb_.PoseVertex(index-neighbourIdx));
625  dist_bandpt_obst_n_l->setInformation(information);
626  dist_bandpt_obst_n_l->setParameters(*cfg_, robot_model_.get(), obst->get());
627  optimizer_->addEdge(dist_bandpt_obst_n_l);
628  }
629  }
630  }
631 
632  }
633 }
634 
635 
636 void TebOptimalPlanner::AddEdgesDynamicObstacles(double weight_multiplier)
637 {
638  if (cfg_->optim.weight_obstacle==0 || weight_multiplier==0 || obstacles_==NULL )
639  return; // if weight equals zero skip adding edges!
640 
641  Eigen::Matrix<double,2,2> information;
642  information(0,0) = cfg_->optim.weight_dynamic_obstacle * weight_multiplier;
643  information(1,1) = cfg_->optim.weight_dynamic_obstacle_inflation;
644  information(0,1) = information(1,0) = 0;
645 
646  for (ObstContainer::const_iterator obst = obstacles_->begin(); obst != obstacles_->end(); ++obst)
647  {
648  if (!(*obst)->isDynamic())
649  continue;
650 
651  // Skip first and last pose, as they are fixed
652  double time = teb_.TimeDiff(0);
653  for (int i=1; i < teb_.sizePoses() - 1; ++i)
654  {
655  EdgeDynamicObstacle* dynobst_edge = new EdgeDynamicObstacle(time);
656  dynobst_edge->setVertex(0,teb_.PoseVertex(i));
657  dynobst_edge->setInformation(information);
658  dynobst_edge->setParameters(*cfg_, robot_model_.get(), obst->get());
659  optimizer_->addEdge(dynobst_edge);
660  time += teb_.TimeDiff(i); // we do not need to check the time diff bounds, since we iterate to "< sizePoses()-1".
661  }
662  }
663 }
664 
666 {
667  if (cfg_->optim.weight_viapoint==0 || via_points_==NULL || via_points_->empty() )
668  return; // if weight equals zero skip adding edges!
669 
670  int start_pose_idx = 0;
671 
672  int n = teb_.sizePoses();
673  if (n<3) // we do not have any degrees of freedom for reaching via-points
674  return;
675 
676  for (ViaPointContainer::const_iterator vp_it = via_points_->begin(); vp_it != via_points_->end(); ++vp_it)
677  {
678 
679  int index = teb_.findClosestTrajectoryPose(*vp_it, NULL, start_pose_idx);
681  start_pose_idx = index+2; // skip a point to have a DOF inbetween for further via-points
682 
683  // check if point conicides with goal or is located behind it
684  if ( index > n-2 )
685  index = n-2; // set to a pose before the goal, since we can move it away!
686  // check if point coincides with start or is located before it
687  if ( index < 1)
688  {
690  {
691  index = 1; // try to connect the via point with the second (and non-fixed) pose. It is likely that autoresize adds new poses inbetween later.
692  }
693  else
694  {
695  ROS_DEBUG("TebOptimalPlanner::AddEdgesViaPoints(): skipping a via-point that is close or behind the current robot pose.");
696  continue; // skip via points really close or behind the current robot pose
697  }
698  }
699  Eigen::Matrix<double,1,1> information;
700  information.fill(cfg_->optim.weight_viapoint);
701 
702  EdgeViaPoint* edge_viapoint = new EdgeViaPoint;
703  edge_viapoint->setVertex(0,teb_.PoseVertex(index));
704  edge_viapoint->setInformation(information);
705  edge_viapoint->setParameters(*cfg_, &(*vp_it));
706  optimizer_->addEdge(edge_viapoint);
707  }
708 }
709 
711 {
712  if (cfg_->robot.max_vel_y == 0) // non-holonomic robot
713  {
715  return; // if weight equals zero skip adding edges!
716 
717  int n = teb_.sizePoses();
718  Eigen::Matrix<double,2,2> information;
719  information(0,0) = cfg_->optim.weight_max_vel_x;
720  information(1,1) = cfg_->optim.weight_max_vel_theta;
721  information(0,1) = 0.0;
722  information(1,0) = 0.0;
723 
724  for (int i=0; i < n - 1; ++i)
725  {
726  EdgeVelocity* velocity_edge = new EdgeVelocity;
727  velocity_edge->setVertex(0,teb_.PoseVertex(i));
728  velocity_edge->setVertex(1,teb_.PoseVertex(i+1));
729  velocity_edge->setVertex(2,teb_.TimeDiffVertex(i));
730  velocity_edge->setInformation(information);
731  velocity_edge->setTebConfig(*cfg_);
732  optimizer_->addEdge(velocity_edge);
733  }
734  }
735  else // holonomic-robot
736  {
738  return; // if weight equals zero skip adding edges!
739 
740  int n = teb_.sizePoses();
741  Eigen::Matrix<double,3,3> information;
742  information.fill(0);
743  information(0,0) = cfg_->optim.weight_max_vel_x;
744  information(1,1) = cfg_->optim.weight_max_vel_y;
745  information(2,2) = cfg_->optim.weight_max_vel_theta;
746 
747  for (int i=0; i < n - 1; ++i)
748  {
749  EdgeVelocityHolonomic* velocity_edge = new EdgeVelocityHolonomic;
750  velocity_edge->setVertex(0,teb_.PoseVertex(i));
751  velocity_edge->setVertex(1,teb_.PoseVertex(i+1));
752  velocity_edge->setVertex(2,teb_.TimeDiffVertex(i));
753  velocity_edge->setInformation(information);
754  velocity_edge->setTebConfig(*cfg_);
755  optimizer_->addEdge(velocity_edge);
756  }
757 
758  }
759 }
760 
762 {
764  return; // if weight equals zero skip adding edges!
765 
766  int n = teb_.sizePoses();
767 
768  if (cfg_->robot.max_vel_y == 0 || cfg_->robot.acc_lim_y == 0) // non-holonomic robot
769  {
770  Eigen::Matrix<double,2,2> information;
771  information.fill(0);
772  information(0,0) = cfg_->optim.weight_acc_lim_x;
773  information(1,1) = cfg_->optim.weight_acc_lim_theta;
774 
775  // check if an initial velocity should be taken into accound
776  if (vel_start_.first)
777  {
778  EdgeAccelerationStart* acceleration_edge = new EdgeAccelerationStart;
779  acceleration_edge->setVertex(0,teb_.PoseVertex(0));
780  acceleration_edge->setVertex(1,teb_.PoseVertex(1));
781  acceleration_edge->setVertex(2,teb_.TimeDiffVertex(0));
782  acceleration_edge->setInitialVelocity(vel_start_.second);
783  acceleration_edge->setInformation(information);
784  acceleration_edge->setTebConfig(*cfg_);
785  optimizer_->addEdge(acceleration_edge);
786  }
787 
788  // now add the usual acceleration edge for each tuple of three teb poses
789  for (int i=0; i < n - 2; ++i)
790  {
791  EdgeAcceleration* acceleration_edge = new EdgeAcceleration;
792  acceleration_edge->setVertex(0,teb_.PoseVertex(i));
793  acceleration_edge->setVertex(1,teb_.PoseVertex(i+1));
794  acceleration_edge->setVertex(2,teb_.PoseVertex(i+2));
795  acceleration_edge->setVertex(3,teb_.TimeDiffVertex(i));
796  acceleration_edge->setVertex(4,teb_.TimeDiffVertex(i+1));
797  acceleration_edge->setInformation(information);
798  acceleration_edge->setTebConfig(*cfg_);
799  optimizer_->addEdge(acceleration_edge);
800  }
801 
802  // check if a goal velocity should be taken into accound
803  if (vel_goal_.first)
804  {
805  EdgeAccelerationGoal* acceleration_edge = new EdgeAccelerationGoal;
806  acceleration_edge->setVertex(0,teb_.PoseVertex(n-2));
807  acceleration_edge->setVertex(1,teb_.PoseVertex(n-1));
808  acceleration_edge->setVertex(2,teb_.TimeDiffVertex( teb_.sizeTimeDiffs()-1 ));
809  acceleration_edge->setGoalVelocity(vel_goal_.second);
810  acceleration_edge->setInformation(information);
811  acceleration_edge->setTebConfig(*cfg_);
812  optimizer_->addEdge(acceleration_edge);
813  }
814  }
815  else // holonomic robot
816  {
817  Eigen::Matrix<double,3,3> information;
818  information.fill(0);
819  information(0,0) = cfg_->optim.weight_acc_lim_x;
820  information(1,1) = cfg_->optim.weight_acc_lim_y;
821  information(2,2) = cfg_->optim.weight_acc_lim_theta;
822 
823  // check if an initial velocity should be taken into accound
824  if (vel_start_.first)
825  {
827  acceleration_edge->setVertex(0,teb_.PoseVertex(0));
828  acceleration_edge->setVertex(1,teb_.PoseVertex(1));
829  acceleration_edge->setVertex(2,teb_.TimeDiffVertex(0));
830  acceleration_edge->setInitialVelocity(vel_start_.second);
831  acceleration_edge->setInformation(information);
832  acceleration_edge->setTebConfig(*cfg_);
833  optimizer_->addEdge(acceleration_edge);
834  }
835 
836  // now add the usual acceleration edge for each tuple of three teb poses
837  for (int i=0; i < n - 2; ++i)
838  {
839  EdgeAccelerationHolonomic* acceleration_edge = new EdgeAccelerationHolonomic;
840  acceleration_edge->setVertex(0,teb_.PoseVertex(i));
841  acceleration_edge->setVertex(1,teb_.PoseVertex(i+1));
842  acceleration_edge->setVertex(2,teb_.PoseVertex(i+2));
843  acceleration_edge->setVertex(3,teb_.TimeDiffVertex(i));
844  acceleration_edge->setVertex(4,teb_.TimeDiffVertex(i+1));
845  acceleration_edge->setInformation(information);
846  acceleration_edge->setTebConfig(*cfg_);
847  optimizer_->addEdge(acceleration_edge);
848  }
849 
850  // check if a goal velocity should be taken into accound
851  if (vel_goal_.first)
852  {
854  acceleration_edge->setVertex(0,teb_.PoseVertex(n-2));
855  acceleration_edge->setVertex(1,teb_.PoseVertex(n-1));
856  acceleration_edge->setVertex(2,teb_.TimeDiffVertex( teb_.sizeTimeDiffs()-1 ));
857  acceleration_edge->setGoalVelocity(vel_goal_.second);
858  acceleration_edge->setInformation(information);
859  acceleration_edge->setTebConfig(*cfg_);
860  optimizer_->addEdge(acceleration_edge);
861  }
862  }
863 }
864 
865 
866 
868 {
869  if (cfg_->optim.weight_optimaltime==0)
870  return; // if weight equals zero skip adding edges!
871 
872  Eigen::Matrix<double,1,1> information;
873  information.fill(cfg_->optim.weight_optimaltime);
874 
875  for (int i=0; i < teb_.sizeTimeDiffs(); ++i)
876  {
877  EdgeTimeOptimal* timeoptimal_edge = new EdgeTimeOptimal;
878  timeoptimal_edge->setVertex(0,teb_.TimeDiffVertex(i));
879  timeoptimal_edge->setInformation(information);
880  timeoptimal_edge->setTebConfig(*cfg_);
881  optimizer_->addEdge(timeoptimal_edge);
882  }
883 }
884 
885 
886 
888 {
890  return; // if weight equals zero skip adding edges!
891 
892  // create edge for satisfiying kinematic constraints
893  Eigen::Matrix<double,2,2> information_kinematics;
894  information_kinematics.fill(0.0);
895  information_kinematics(0, 0) = cfg_->optim.weight_kinematics_nh;
896  information_kinematics(1, 1) = cfg_->optim.weight_kinematics_forward_drive;
897 
898  for (int i=0; i < teb_.sizePoses()-1; i++) // ignore twiced start only
899  {
900  EdgeKinematicsDiffDrive* kinematics_edge = new EdgeKinematicsDiffDrive;
901  kinematics_edge->setVertex(0,teb_.PoseVertex(i));
902  kinematics_edge->setVertex(1,teb_.PoseVertex(i+1));
903  kinematics_edge->setInformation(information_kinematics);
904  kinematics_edge->setTebConfig(*cfg_);
905  optimizer_->addEdge(kinematics_edge);
906  }
907 }
908 
910 {
912  return; // if weight equals zero skip adding edges!
913 
914  // create edge for satisfiying kinematic constraints
915  Eigen::Matrix<double,2,2> information_kinematics;
916  information_kinematics.fill(0.0);
917  information_kinematics(0, 0) = cfg_->optim.weight_kinematics_nh;
918  information_kinematics(1, 1) = cfg_->optim.weight_kinematics_turning_radius;
919 
920  for (int i=0; i < teb_.sizePoses()-1; i++) // ignore twiced start only
921  {
922  EdgeKinematicsCarlike* kinematics_edge = new EdgeKinematicsCarlike;
923  kinematics_edge->setVertex(0,teb_.PoseVertex(i));
924  kinematics_edge->setVertex(1,teb_.PoseVertex(i+1));
925  kinematics_edge->setInformation(information_kinematics);
926  kinematics_edge->setTebConfig(*cfg_);
927  optimizer_->addEdge(kinematics_edge);
928  }
929 }
930 
931 
933 {
934  //TODO(roesmann): Note, these edges can result in odd predictions, in particular
935  // we can observe a substantional mismatch between open- and closed-loop planning
936  // leading to a poor control performance.
937  // At the moment, we keep these functionality for oscillation recovery:
938  // Activating the edge for a short time period might not be crucial and
939  // could move the robot to a new oscillation-free state.
940  // This needs to be analyzed in more detail!
942  return; // if weight equals zero skip adding edges!
943 
945  {
946  ROS_WARN("TebOptimalPlanner::AddEdgesPreferRotDir(): unsupported RotType selected. Skipping edge creation.");
947  return;
948  }
949 
950  // create edge for satisfiying kinematic constraints
951  Eigen::Matrix<double,1,1> information_rotdir;
952  information_rotdir.fill(cfg_->optim.weight_prefer_rotdir);
953 
954  for (int i=0; i < teb_.sizePoses()-1 && i < 3; ++i) // currently: apply to first 3 rotations
955  {
956  EdgePreferRotDir* rotdir_edge = new EdgePreferRotDir;
957  rotdir_edge->setVertex(0,teb_.PoseVertex(i));
958  rotdir_edge->setVertex(1,teb_.PoseVertex(i+1));
959  rotdir_edge->setInformation(information_rotdir);
960 
962  rotdir_edge->preferLeft();
963  else if (prefer_rotdir_ == RotType::right)
964  rotdir_edge->preferRight();
965 
966  optimizer_->addEdge(rotdir_edge);
967  }
968 }
969 
970 void TebOptimalPlanner::computeCurrentCost(double obst_cost_scale, double viapoint_cost_scale, bool alternative_time_cost)
971 {
972  // check if graph is empty/exist -> important if function is called between buildGraph and optimizeGraph/clearGraph
973  bool graph_exist_flag(false);
974  if (optimizer_->edges().empty() && optimizer_->vertices().empty())
975  {
976  // here the graph is build again, for time efficiency make sure to call this function
977  // between buildGraph and Optimize (deleted), but it depends on the application
978  buildGraph();
979  optimizer_->initializeOptimization();
980  }
981  else
982  {
983  graph_exist_flag = true;
984  }
985 
986  optimizer_->computeInitialGuess();
987 
988  cost_ = 0;
989 
990  if (alternative_time_cost)
991  {
993  // TEST we use SumOfAllTimeDiffs() here, because edge cost depends on number of samples, which is not always the same for similar TEBs,
994  // since we are using an AutoResize Function with hysteresis.
995  }
996 
997  // now we need pointers to all edges -> calculate error for each edge-type
998  // since we aren't storing edge pointers, we need to check every edge
999  for (std::vector<g2o::OptimizableGraph::Edge*>::const_iterator it = optimizer_->activeEdges().begin(); it!= optimizer_->activeEdges().end(); it++)
1000  {
1001  EdgeTimeOptimal* edge_time_optimal = dynamic_cast<EdgeTimeOptimal*>(*it);
1002  if (edge_time_optimal!=NULL && !alternative_time_cost)
1003  {
1004  cost_ += edge_time_optimal->getError().squaredNorm();
1005  continue;
1006  }
1007 
1008  EdgeKinematicsDiffDrive* edge_kinematics_dd = dynamic_cast<EdgeKinematicsDiffDrive*>(*it);
1009  if (edge_kinematics_dd!=NULL)
1010  {
1011  cost_ += edge_kinematics_dd->getError().squaredNorm();
1012  continue;
1013  }
1014 
1015  EdgeKinematicsCarlike* edge_kinematics_cl = dynamic_cast<EdgeKinematicsCarlike*>(*it);
1016  if (edge_kinematics_cl!=NULL)
1017  {
1018  cost_ += edge_kinematics_cl->getError().squaredNorm();
1019  continue;
1020  }
1021 
1022  EdgeVelocity* edge_velocity = dynamic_cast<EdgeVelocity*>(*it);
1023  if (edge_velocity!=NULL)
1024  {
1025  cost_ += edge_velocity->getError().squaredNorm();
1026  continue;
1027  }
1028 
1029  EdgeAcceleration* edge_acceleration = dynamic_cast<EdgeAcceleration*>(*it);
1030  if (edge_acceleration!=NULL)
1031  {
1032  cost_ += edge_acceleration->getError().squaredNorm();
1033  continue;
1034  }
1035 
1036  EdgeObstacle* edge_obstacle = dynamic_cast<EdgeObstacle*>(*it);
1037  if (edge_obstacle!=NULL)
1038  {
1039  cost_ += edge_obstacle->getError().squaredNorm() * obst_cost_scale;
1040  continue;
1041  }
1042 
1043  EdgeInflatedObstacle* edge_inflated_obstacle = dynamic_cast<EdgeInflatedObstacle*>(*it);
1044  if (edge_inflated_obstacle!=NULL)
1045  {
1046  cost_ += std::sqrt(std::pow(edge_inflated_obstacle->getError()[0],2) * obst_cost_scale
1047  + std::pow(edge_inflated_obstacle->getError()[1],2));
1048  continue;
1049  }
1050 
1051  EdgeDynamicObstacle* edge_dyn_obstacle = dynamic_cast<EdgeDynamicObstacle*>(*it);
1052  if (edge_dyn_obstacle!=NULL)
1053  {
1054  cost_ += edge_dyn_obstacle->getError().squaredNorm() * obst_cost_scale;
1055  continue;
1056  }
1057 
1058  EdgeViaPoint* edge_viapoint = dynamic_cast<EdgeViaPoint*>(*it);
1059  if (edge_viapoint!=NULL)
1060  {
1061  cost_ += edge_viapoint->getError().squaredNorm() * viapoint_cost_scale;
1062  continue;
1063  }
1064  }
1065 
1066  // delete temporary created graph
1067  if (!graph_exist_flag)
1068  clearGraph();
1069 }
1070 
1071 
1072 void TebOptimalPlanner::extractVelocity(const PoseSE2& pose1, const PoseSE2& pose2, double dt, double& vx, double& vy, double& omega) const
1073 {
1074  if (dt == 0)
1075  {
1076  vx = 0;
1077  vy = 0;
1078  omega = 0;
1079  return;
1080  }
1081 
1082  Eigen::Vector2d deltaS = pose2.position() - pose1.position();
1083 
1084  if (cfg_->robot.max_vel_y == 0) // nonholonomic robot
1085  {
1086  Eigen::Vector2d conf1dir( cos(pose1.theta()), sin(pose1.theta()) );
1087  // translational velocity
1088  double dir = deltaS.dot(conf1dir);
1089  vx = (double) g2o::sign(dir) * deltaS.norm()/dt;
1090  vy = 0;
1091  }
1092  else // holonomic robot
1093  {
1094  // transform pose 2 into the current robot frame (pose1)
1095  // for velocities only the rotation of the direction vector is necessary.
1096  // (map->pose1-frame: inverse 2d rotation matrix)
1097  double cos_theta1 = std::cos(pose1.theta());
1098  double sin_theta1 = std::sin(pose1.theta());
1099  double p1_dx = cos_theta1*deltaS.x() + sin_theta1*deltaS.y();
1100  double p1_dy = -sin_theta1*deltaS.x() + cos_theta1*deltaS.y();
1101  vx = p1_dx / dt;
1102  vy = p1_dy / dt;
1103  }
1104 
1105  // rotational velocity
1106  double orientdiff = g2o::normalize_theta(pose2.theta() - pose1.theta());
1107  omega = orientdiff/dt;
1108 }
1109 
1110 bool TebOptimalPlanner::getVelocityCommand(double& vx, double& vy, double& omega) const
1111 {
1112  if (teb_.sizePoses()<2)
1113  {
1114  ROS_ERROR("TebOptimalPlanner::getVelocityCommand(): The trajectory contains less than 2 poses. Make sure to init and optimize/plan the trajectory fist.");
1115  vx = 0;
1116  vy = 0;
1117  omega = 0;
1118  return false;
1119  }
1120 
1121  double dt = teb_.TimeDiff(0);
1122  if (dt<=0)
1123  {
1124  ROS_ERROR("TebOptimalPlanner::getVelocityCommand() - timediff<=0 is invalid!");
1125  vx = 0;
1126  vy = 0;
1127  omega = 0;
1128  return false;
1129  }
1130 
1131  // Get velocity from the first two configurations
1132  extractVelocity(teb_.Pose(0), teb_.Pose(1), dt, vx, vy, omega);
1133  return true;
1134 }
1135 
1136 void TebOptimalPlanner::getVelocityProfile(std::vector<geometry_msgs::Twist>& velocity_profile) const
1137 {
1138  int n = teb_.sizePoses();
1139  velocity_profile.resize( n+1 );
1140 
1141  // start velocity
1142  velocity_profile.front().linear.z = 0;
1143  velocity_profile.front().angular.x = velocity_profile.front().angular.y = 0;
1144  velocity_profile.front().linear.x = vel_start_.second.linear.x;
1145  velocity_profile.front().linear.y = vel_start_.second.linear.y;
1146  velocity_profile.front().angular.z = vel_start_.second.angular.z;
1147 
1148  for (int i=1; i<n; ++i)
1149  {
1150  velocity_profile[i].linear.z = 0;
1151  velocity_profile[i].angular.x = velocity_profile[i].angular.y = 0;
1152  extractVelocity(teb_.Pose(i-1), teb_.Pose(i), teb_.TimeDiff(i-1), velocity_profile[i].linear.x, velocity_profile[i].linear.y, velocity_profile[i].angular.z);
1153  }
1154 
1155  // goal velocity
1156  velocity_profile.back().linear.z = 0;
1157  velocity_profile.back().angular.x = velocity_profile.back().angular.y = 0;
1158  velocity_profile.back().linear.x = vel_goal_.second.linear.x;
1159  velocity_profile.back().linear.y = vel_goal_.second.linear.y;
1160  velocity_profile.back().angular.z = vel_goal_.second.angular.z;
1161 }
1162 
1163 void TebOptimalPlanner::getFullTrajectory(std::vector<TrajectoryPointMsg>& trajectory) const
1164 {
1165  int n = teb_.sizePoses();
1166 
1167  trajectory.resize(n);
1168 
1169  if (n == 0)
1170  return;
1171 
1172  double curr_time = 0;
1173 
1174  // start
1175  TrajectoryPointMsg& start = trajectory.front();
1176  teb_.Pose(0).toPoseMsg(start.pose);
1177  start.velocity.linear.z = 0;
1178  start.velocity.angular.x = start.velocity.angular.y = 0;
1179  start.velocity.linear.x = vel_start_.second.linear.x;
1180  start.velocity.linear.y = vel_start_.second.linear.y;
1181  start.velocity.angular.z = vel_start_.second.angular.z;
1182  start.time_from_start.fromSec(curr_time);
1183 
1184  curr_time += teb_.TimeDiff(0);
1185 
1186  // intermediate points
1187  for (int i=1; i < n-1; ++i)
1188  {
1189  TrajectoryPointMsg& point = trajectory[i];
1190  teb_.Pose(i).toPoseMsg(point.pose);
1191  point.velocity.linear.z = 0;
1192  point.velocity.angular.x = point.velocity.angular.y = 0;
1193  double vel1_x, vel1_y, vel2_x, vel2_y, omega1, omega2;
1194  extractVelocity(teb_.Pose(i-1), teb_.Pose(i), teb_.TimeDiff(i-1), vel1_x, vel1_y, omega1);
1195  extractVelocity(teb_.Pose(i), teb_.Pose(i+1), teb_.TimeDiff(i), vel2_x, vel2_y, omega2);
1196  point.velocity.linear.x = 0.5*(vel1_x+vel2_x);
1197  point.velocity.linear.y = 0.5*(vel1_y+vel2_y);
1198  point.velocity.angular.z = 0.5*(omega1+omega2);
1199  point.time_from_start.fromSec(curr_time);
1200 
1201  curr_time += teb_.TimeDiff(i);
1202  }
1203 
1204  // goal
1205  TrajectoryPointMsg& goal = trajectory.back();
1206  teb_.BackPose().toPoseMsg(goal.pose);
1207  goal.velocity.linear.z = 0;
1208  goal.velocity.angular.x = goal.velocity.angular.y = 0;
1209  goal.velocity.linear.x = vel_goal_.second.linear.x;
1210  goal.velocity.linear.y = vel_goal_.second.linear.y;
1211  goal.velocity.angular.z = vel_goal_.second.angular.z;
1212  goal.time_from_start.fromSec(curr_time);
1213 }
1214 
1215 
1216 bool TebOptimalPlanner::isTrajectoryFeasible(base_local_planner::CostmapModel* costmap_model, const std::vector<geometry_msgs::Point>& footprint_spec,
1217  double inscribed_radius, double circumscribed_radius, int look_ahead_idx)
1218 {
1219  if (look_ahead_idx < 0 || look_ahead_idx >= teb().sizePoses())
1220  look_ahead_idx = teb().sizePoses() - 1;
1221 
1222  for (int i=0; i <= look_ahead_idx; ++i)
1223  {
1224  if ( costmap_model->footprintCost(teb().Pose(i).x(), teb().Pose(i).y(), teb().Pose(i).theta(), footprint_spec, inscribed_radius, circumscribed_radius) < 0 )
1225  return false;
1226 
1227  // check if distance between two poses is higher than the robot radius and interpolate in that case
1228  // (if obstacles are pushing two consecutive poses away, the center between two consecutive poses might coincide with the obstacle ;-)!
1229  if (i<look_ahead_idx)
1230  {
1231  if ( (teb().Pose(i+1).position()-teb().Pose(i).position()).norm() > inscribed_radius)
1232  {
1233  // check one more time
1234  PoseSE2 center = PoseSE2::average(teb().Pose(i), teb().Pose(i+1));
1235  if ( costmap_model->footprintCost(center.x(), center.y(), center.theta(), footprint_spec, inscribed_radius, circumscribed_radius) < 0 )
1236  return false;
1237  }
1238 
1239  }
1240  }
1241  return true;
1242 }
1243 
1244 
1245 bool TebOptimalPlanner::isHorizonReductionAppropriate(const std::vector<geometry_msgs::PoseStamped>& initial_plan) const
1246 {
1247  if (teb_.sizePoses() < int( 1.5*double(cfg_->trajectory.min_samples) ) ) // trajectory is short already
1248  return false;
1249 
1250  // check if distance is at least 2m long // hardcoded for now
1251  double dist = 0;
1252  for (int i=1; i < teb_.sizePoses(); ++i)
1253  {
1254  dist += ( teb_.Pose(i).position() - teb_.Pose(i-1).position() ).norm();
1255  if (dist > 2)
1256  break;
1257  }
1258  if (dist <= 2)
1259  return false;
1260 
1261  // check if goal orientation is differing with more than 90° and the horizon is still long enough to exclude parking maneuvers.
1262  // use case: Sometimes the robot accomplish the following navigation task:
1263  // 1. wall following 2. 180° curve 3. following along the other side of the wall.
1264  // If the trajectory is too long, the trajectory might intersect with the obstace and the optimizer does
1265  // push the trajectory to the correct side.
1266  if ( std::abs( g2o::normalize_theta( teb_.Pose(0).theta() - teb_.BackPose().theta() ) ) > M_PI/2)
1267  {
1268  ROS_DEBUG("TebOptimalPlanner::isHorizonReductionAppropriate(): Goal orientation - start orientation > 90° ");
1269  return true;
1270  }
1271 
1272  // check if goal heading deviates more than 90° w.r.t. start orienation
1273  if (teb_.Pose(0).orientationUnitVec().dot(teb_.BackPose().position() - teb_.Pose(0).position()) < 0)
1274  {
1275  ROS_DEBUG("TebOptimalPlanner::isHorizonReductionAppropriate(): Goal heading - start orientation > 90° ");
1276  return true;
1277  }
1278 
1279  // check ratio: distance along the inital plan and distance of the trajectory (maybe too much is cut off)
1280  int idx=0; // first get point close to the robot (should be fast if the global path is already pruned!)
1281  for (; idx < (int)initial_plan.size(); ++idx)
1282  {
1283  if ( std::sqrt(std::pow(initial_plan[idx].pose.position.x-teb_.Pose(0).x(), 2) + std::pow(initial_plan[idx].pose.position.y-teb_.Pose(0).y(), 2)) )
1284  break;
1285  }
1286  // now calculate length
1287  double ref_path_length = 0;
1288  for (; idx < int(initial_plan.size())-1; ++idx)
1289  {
1290  ref_path_length += std::sqrt(std::pow(initial_plan[idx+1].pose.position.x-initial_plan[idx].pose.position.x, 2)
1291  + std::pow(initial_plan[idx+1].pose.position.y-initial_plan[idx].pose.position.y, 2) );
1292  }
1293 
1294  // check distances along the teb trajectory (by the way, we also check if the distance between two poses is > obst_dist)
1295  double teb_length = 0;
1296  for (int i = 1; i < teb_.sizePoses(); ++i )
1297  {
1298  double dist = (teb_.Pose(i).position() - teb_.Pose(i-1).position()).norm();
1299  if (dist > 0.95*cfg_->obstacles.min_obstacle_dist)
1300  {
1301  ROS_DEBUG("TebOptimalPlanner::isHorizonReductionAppropriate(): Distance between consecutive poses > 0.9*min_obstacle_dist");
1302  return true;
1303  }
1304  ref_path_length += dist;
1305  }
1306  if (ref_path_length>0 && teb_length/ref_path_length < 0.7) // now check ratio
1307  {
1308  ROS_DEBUG("TebOptimalPlanner::isHorizonReductionAppropriate(): Planned trajectory is at least 30° shorter than the initial plan");
1309  return true;
1310  }
1311 
1312 
1313  // otherwise we do not suggest shrinking the horizon:
1314  return false;
1315 }
1316 
1317 } // namespace teb_local_planner
static void registerG2OTypes()
Register the vertices and edges defined for the TEB to the g2o::Factory.
Edge defining the cost function for limiting the translational and rotational acceleration at the end...
void setVelocityStart(const geometry_msgs::Twist &vel_start)
Set the initial velocity at the trajectory&#39;s start pose (e.g. the robot&#39;s velocity) [twist overload]...
bool optimizeGraph(int no_iterations, bool clear_after=true)
Optimize the previously constructed hyper-graph to deform / optimize the TEB.
void setVelocityGoalFree()
Set the desired final velocity at the trajectory&#39;s goal pose to be the maximum velocity limit...
Edge defining the cost function for keeping a distance from dynamic (moving) obstacles.
std::pair< bool, geometry_msgs::Twist > vel_start_
Store the initial velocity at the start pose.
double max_vel_y
Maximum strafing velocity of the robot (should be zero for non-holonomic robots!) ...
Definition: teb_config.h:92
TimedElasticBand & teb()
Access the internal TimedElasticBand trajectory.
int max_samples
Maximum number of samples; Warning: if too small the discretization/resolution might not be sufficien...
Definition: teb_config.h:75
void clearGraph()
Clear an existing internal hyper-graph.
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.
double weight_adapt_factor
Some special weights (currently &#39;weight_obstacle&#39;) are repeatedly scaled by this factor in each outer...
Definition: teb_config.h:159
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
void setVisualization(TebVisualizationPtr visualization)
Register a TebVisualization class to enable visiualization routines (e.g. publish the local plan and ...
std::pair< bool, geometry_msgs::Twist > vel_goal_
Store the final velocity at the goal pose.
double min_obstacle_dist
Minimum desired separation from obstacles.
Definition: teb_config.h:115
virtual void visualize()
Publish the local plan and pose sequence via ros topics (e.g. subscribe with rviz).
double weight_prefer_rotdir
Optimization weight for preferring a specific turning direction (-> currently only activated if an os...
Definition: teb_config.h:157
Edge defining the cost function for limiting the translational and rotational acceleration at the end...
Edge defining the cost function for penalzing a specified turning direction, in particular left resp...
Eigen::Vector2d & position()
Access the 2D position part.
Definition: pose_se2.h:145
double cross2d(const V1 &v1, const V2 &v2)
Calculate the 2d cross product (returns length of the resulting vector along the z-axis in 3d) ...
Definition: misc.h:120
void setTebConfig(const TebConfig &cfg)
Assign the TebConfig class for parameters.
Edge defining the cost function for minimizing transition time of the trajectory. ...
ErrorVector & getError()
Compute and return error / cost value.
double getSumOfAllTimeDiffs() const
Calculate the total transition time (sum over all time intervals of the timediff sequence) ...
void AddEdgesTimeOptimal()
Add all edges (local cost functions) for minimizing the transition time (resp. minimize time differen...
double dt_hysteresis
Hysteresis for automatic resizing depending on the current temporal resolution (dt): usually 10% of d...
Definition: teb_config.h:73
Config class for the teb_local_planner and its components.
Definition: teb_config.h:61
void AddEdgesAcceleration()
Add all edges (local cost functions) for limiting the translational and angular acceleration.
double weight_max_vel_theta
Optimization weight for satisfying the maximum allowed angular velocity.
Definition: teb_config.h:144
static PoseSE2 average(const PoseSE2 &pose1, const PoseSE2 &pose2)
Get the mean / average of two poses and return the result (static) For the position part: 0...
Definition: pose_se2.h:266
boost::shared_ptr< g2o::SparseOptimizer > optimizer()
Access the internal g2o optimizer.
void setInitialVelocity(const geometry_msgs::Twist &vel_start)
Set the initial velocity that is taken into account for calculating the acceleration.
Edge defining the cost function for limiting the translational and rotational acceleration.
Edge defining the cost function for limiting the translational and rotational acceleration at the beg...
bool initTrajectoryToGoal(const PoseSE2 &start, const PoseSE2 &goal, double diststep=0, double max_vel_x=0.5, int min_samples=3, bool guess_backwards_motion=false)
Initialize a trajectory between a given start and goal pose.
void setVelocityGoal(const geometry_msgs::Twist &vel_goal)
Set the desired final velocity at the trajectory&#39;s goal pose.
struct teb_local_planner::TebConfig::Robot robot
Robot related parameters.
void setTebConfig(const TebConfig &cfg)
Assign the TebConfig class for parameters.
double min_turning_radius
Minimum turning radius of a carlike robot (diff-drive robot: zero);.
Definition: teb_config.h:97
double & x()
Access the x-coordinate the pose.
Definition: pose_se2.h:158
Edge defining the cost function for limiting the translational and rotational velocity.
Definition: edge_velocity.h:75
void setParameters(const TebConfig &cfg, const BaseRobotFootprintModel *robot_model, const Obstacle *obstacle)
Set all parameters at once.
struct teb_local_planner::TebConfig::Trajectory trajectory
Trajectory related parameters.
PoseSE2 & BackPose()
Access the last PoseSE2 in the pose sequence.
TebVisualizationPtr visual
double weight_max_vel_y
Optimization weight for satisfying the maximum allowed strafing velocity (in use only for holonomic r...
Definition: teb_config.h:143
TFSIMD_FORCE_INLINE const tfScalar & y() const
void AddEdgesVelocity()
Add all edges (local cost functions) for limiting the translational and angular velocity.
void updateAndPruneTEB(boost::optional< const PoseSE2 & > new_start, boost::optional< const PoseSE2 & > new_goal, int min_samples=3)
Hot-Start from an existing trajectory with updated start and goal poses.
#define ROS_WARN(...)
double weight_kinematics_turning_radius
Optimization weight for enforcing a minimum turning radius (carlike robots)
Definition: teb_config.h:150
bool legacy_obstacle_association
If true, the old association strategy is used (for each obstacle, find the nearest TEB pose)...
Definition: teb_config.h:122
double weight_acc_lim_x
Optimization weight for satisfying the maximum allowed translational acceleration.
Definition: teb_config.h:145
void computeCurrentCost(double obst_cost_scale=1.0, double viapoint_cost_scale=1.0, bool alternative_time_cost=false)
Compute the cost vector of a given optimization problen (hyper-graph must exist). ...
int findClosestTrajectoryPose(const Eigen::Ref< const Eigen::Vector2d > &ref_point, double *distance=NULL, int begin_idx=0) const
Find the closest point on the trajectory w.r.t. to a provided reference point.
Eigen::Vector2d orientationUnitVec() const
Return the unit vector of the current orientation.
Definition: pose_se2.h:215
void setInitialVelocity(const geometry_msgs::Twist &vel_start)
Set the initial velocity that is taken into account for calculating the acceleration.
Edge defining the cost function for limiting the translational and rotational acceleration.
void AddEdgesPreferRotDir()
Add all edges (local cost functions) for prefering a specifiy turning direction (by penalizing the ot...
double weight_acc_lim_y
Optimization weight for satisfying the maximum allowed strafing acceleration (in use only for holonom...
Definition: teb_config.h:146
virtual ~TebOptimalPlanner()
Destruct the optimal planner.
virtual bool getVelocityCommand(double &vx, double &vy, double &omega) const
Get the velocity command from a previously optimized plan to control the robot at the current samplin...
bool optimization_activate
Activate the optimization.
Definition: teb_config.h:137
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.
bool buildGraph(double weight_multiplier=1.0)
Build the hyper-graph representing the TEB optimization problem.
void AddTEBVertices()
Add all relevant vertices to the hyper-graph as optimizable variables.
ObstContainer * obstacles_
Store obstacles that are relevant for planning.
TebVisualizationPtr visualization_
Instance of the visualization class.
g2o::BlockSolver< g2o::BlockSolverTraits<-1,-1 > > TEBBlockSolver
Typedef for the block solver utilized for optimization.
double teb_autosize
Enable automatic resizing of the trajectory w.r.t to the temporal resolution (recommended) ...
Definition: teb_config.h:71
Edge defining the cost function for keeping a minimum distance from obstacles.
Definition: edge_obstacle.h:70
bool via_points_ordered
If true, the planner adheres to the order of via-points in the storage container. ...
Definition: teb_config.h:79
double obstacle_association_force_inclusion_factor
The non-legacy obstacle association technique tries to connect only relevant obstacles with the discr...
Definition: teb_config.h:123
double weight_max_vel_x
Optimization weight for satisfying the maximum allowed translational velocity.
Definition: teb_config.h:142
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:82
double dt_ref
Desired temporal resolution of the trajectory (should be in the magniture of the underlying control r...
Definition: teb_config.h:72
void setTebConfig(const TebConfig &cfg)
Assign the TebConfig class for parameters.
void setGoalVelocity(const geometry_msgs::Twist &vel_goal)
Set the goal / final velocity that is taken into account for calculating the acceleration.
Edge defining the cost function for limiting the translational and rotational velocity according to x...
RotType
Symbols for left/none/right rotations.
Definition: misc.h:53
void AddEdgesViaPoints()
Add all edges (local cost functions) related to minimizing the distance to via-points.
int sizeTimeDiffs() const
Get the length of the internal timediff sequence.
Edge defining the cost function for keeping a minimum distance from inflated obstacles.
ErrorVector & getError()
Compute and return error / cost value.
bool initialized_
Keeps track about the correct initialization of this class.
void AddEdgesDynamicObstacles(double weight_multiplier=1.0)
Add all edges (local cost functions) related to keeping a distance from dynamic (moving) obstacles...
#define ROS_ASSERT_MSG(cond,...)
double weight_inflation
Optimization weight for the inflation penalty (should be small)
Definition: teb_config.h:153
double & TimeDiff(int index)
Access the time difference at pos index of the time sequence.
std::vector< ObstaclePtr > ObstContainer
Abbrev. for containers storing multiple obstacles.
Definition: obstacles.h:297
double weight_obstacle
Optimization weight for satisfying a minimum separation from obstacles.
Definition: teb_config.h:152
bool optimized_
This variable is true as long as the last optimization has been completed successful.
TFSIMD_FORCE_INLINE const tfScalar & x() const
void setParameters(const TebConfig &cfg, const BaseRobotFootprintModel *robot_model, const Obstacle *obstacle)
Set all parameters at once.
TebOptimalPlanner()
Default constructor.
void getFullTrajectory(std::vector< TrajectoryPointMsg > &trajectory) const
Return the complete trajectory including poses, velocity profiles and temporal information.
ErrorVector & getError()
Compute and return error / cost value.
Edge defining the cost function for limiting the translational and rotational acceleration at the beg...
void toPoseMsg(geometry_msgs::Pose &pose) const
Convert PoseSE2 to a geometry_msgs::Pose.
Definition: pose_se2.h:203
void clearTimedElasticBand()
clear all poses and timediffs from the trajectory. The pose and timediff sequences will be empty and ...
PoseSE2 & Pose(int index)
Access the pose at pos index of the pose sequence.
VertexTimeDiff * TimeDiffVertex(int index)
Access the vertex of a time difference at pos index for optimization purposes.
RobotFootprintModelPtr robot_model_
Robot model.
#define ROS_DEBUG_COND(cond,...)
double & y()
Access the y-coordinate the pose.
Definition: pose_se2.h:170
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 obstacle_association_cutoff_factor
See obstacle_association_force_inclusion_factor, but beyond a multiple of [value]*min_obstacle_dist a...
Definition: teb_config.h:124
void initialize(const TebConfig &cfg, ObstContainer *obstacles=NULL, RobotFootprintModelPtr robot_model=boost::make_shared< PointRobotFootprint >(), TebVisualizationPtr visual=TebVisualizationPtr(), const ViaPointContainer *via_points=NULL)
Initializes the optimal planner.
int no_inner_iterations
Number of solver iterations called in each outerloop iteration.
Definition: teb_config.h:134
void getVelocityProfile(std::vector< geometry_msgs::Twist > &velocity_profile) const
Compute the velocity profile of the trajectory.
double weight_acc_lim_theta
Optimization weight for satisfying the maximum allowed angular acceleration.
Definition: teb_config.h:147
double weight_dynamic_obstacle_inflation
Optimization weight for the inflation penalty of dynamic obstacles (should be small) ...
Definition: teb_config.h:155
int obstacle_poses_affected
The obstacle position is attached to the closest pose on the trajectory to reduce computational effor...
Definition: teb_config.h:121
void setParameters(const TebConfig &cfg, const Eigen::Vector2d *via_point)
Set all parameters at once.
double weight_dynamic_obstacle
Optimization weight for satisfying a minimum separation from dynamic obstacles.
Definition: teb_config.h:154
void setParameters(const TebConfig &cfg, const BaseRobotFootprintModel *robot_model, const Obstacle *obstacle)
Set all parameters at once.
double weight_optimaltime
Optimization weight for contracting the trajectory w.r.t transition time.
Definition: teb_config.h:151
VertexPose * PoseVertex(int index)
Access the vertex of a pose at pos index for optimization purposes.
virtual double footprintCost(const geometry_msgs::Point &position, const std::vector< geometry_msgs::Point > &footprint, double inscribed_radius, double circumscribed_radius)
bool global_plan_overwrite_orientation
Overwrite orientation of local subgoals provided by the global planner.
Definition: teb_config.h:76
double weight_kinematics_nh
Optimization weight for satisfying the non-holonomic kinematics.
Definition: teb_config.h:148
void setGoalVelocity(const geometry_msgs::Twist &vel_goal)
Set the goal / final velocity that is taken into account for calculating the acceleration.
Edge defining the cost function for satisfying the non-holonomic kinematics of a carlike mobile robot...
Edge defining the cost function for pushing a configuration towards a via point.
double cost_
Store cost value of the current hyper-graph.
bool include_dynamic_obstacles
Specify whether the movement of dynamic obstacles should be predicted by a constant velocity model (t...
Definition: teb_config.h:118
void AddEdgesKinematicsCarlike()
Add all edges (local cost functions) for satisfying kinematic constraints of a carlike robot...
void extractVelocity(const PoseSE2 &pose1, const PoseSE2 &pose2, double dt, double &vx, double &vy, double &omega) const
Extract the velocity from consecutive poses and a time difference (including strafing velocity for ho...
double weight_viapoint
Optimization weight for minimizing the distance to via-points.
Definition: teb_config.h:156
ViaPointContainer via_points
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
int no_outer_iterations
Each outerloop iteration automatically resizes the trajectory and invokes the internal optimizer with...
Definition: teb_config.h:135
struct teb_local_planner::TebConfig::Optimization optim
Optimization related parameters.
const ViaPointContainer * via_points_
Store via points for planning.
TimedElasticBand teb_
Actual trajectory object.
void autoResize(double dt_ref, double dt_hysteresis, int min_samples=3, int max_samples=1000, bool fast_mode=false)
Resize the trajectory by removing or inserting a (pose,dt) pair depending on a reference temporal res...
int sizePoses() const
Get the length of the internal pose sequence.
bool publish_feedback
Publish planner feedback containing the full trajectory and a list of active obstacles (should be ena...
Definition: teb_config.h:84
boost::shared_ptr< g2o::SparseOptimizer > initOptimizer()
Initialize and configure the g2o sparse optimizer.
double acc_lim_y
Maximum strafing acceleration of the robot.
Definition: teb_config.h:95
g2o::LinearSolverCSparse< TEBBlockSolver::PoseMatrixType > TEBLinearSolver
Typedef for the linear solver utilized for optimization.
#define ROS_ERROR(...)
void AddEdgesObstacles(double weight_multiplier=1.0)
Add all edges (local cost functions) related to keeping a distance from static obstacles.
bool isInit() const
Check whether the trajectory is initialized (nonzero pose and timediff sequences) ...
virtual bool isHorizonReductionAppropriate(const std::vector< geometry_msgs::PoseStamped > &initial_plan) const
Check if the planner suggests a shorter horizon (e.g. to resolve problems)
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
const TebConfig * cfg_
Config class that stores and manages all related parameters.
double inflation_dist
buffer zone around obstacles with non-zero penalty costs (should be larger than min_obstacle_dist in ...
Definition: teb_config.h:116
struct teb_local_planner::TebConfig::Obstacles obstacles
Obstacle related parameters.
double weight_kinematics_forward_drive
Optimization weight for forcing the robot to choose only forward directions (positive transl...
Definition: teb_config.h:149
void AddEdgesKinematicsDiffDrive()
Add all edges (local cost functions) for satisfying kinematic constraints of a differential drive rob...
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).
Abstract class that defines the interface for modelling obstacles.
Definition: obstacles.h:67
double max_vel_x
Maximum translational velocity of the robot.
Definition: teb_config.h:90
bool optimization_verbose
Print verbose information.
Definition: teb_config.h:138
boost::shared_ptr< g2o::SparseOptimizer > optimizer_
g2o optimizer for trajectory optimization
void AddEdgesObstaclesLegacy(double weight_multiplier=1.0)
Add all edges (local cost functions) related to keeping a distance from static obstacles (legacy asso...
RotType prefer_rotdir_
Store whether to prefer a specific initial rotation in optimization (might be activated in case the r...
def sign(number)
Edge defining the cost function for satisfying the non-holonomic kinematics of a differential drive m...
#define ROS_DEBUG(...)


teb_local_planner
Author(s): Christoph Rösmann
autogenerated on Wed Jun 5 2019 19:25:10