teb_local_planner_ros.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 <tf2_eigen/tf2_eigen.h>
43 
44 #include <boost/algorithm/string.hpp>
45 
46 // MBF return codes
47 #include <mbf_msgs/ExePathResult.h>
48 
49 // pluginlib macros
51 
52 #include "g2o/core/sparse_optimizer.h"
53 #include "g2o/core/block_solver.h"
54 #include "g2o/core/factory.h"
55 #include "g2o/core/optimization_algorithm_gauss_newton.h"
56 #include "g2o/core/optimization_algorithm_levenberg.h"
57 #include "g2o/solvers/csparse/linear_solver_csparse.h"
58 #include "g2o/solvers/cholmod/linear_solver_cholmod.h"
59 
60 
61 // register this planner both as a BaseLocalPlanner and as a MBF's CostmapController plugin
64 
65 namespace teb_local_planner
66 {
67 
68 
69 TebLocalPlannerROS::TebLocalPlannerROS() : costmap_ros_(NULL), tf_(NULL), costmap_model_(NULL),
70  costmap_converter_loader_("costmap_converter", "costmap_converter::BaseCostmapToPolygons"),
71  dynamic_recfg_(NULL), custom_via_points_active_(false), goal_reached_(false), no_infeasible_plans_(0),
72  last_preferred_rotdir_(RotType::none), initialized_(false)
73 {
74 }
75 
76 
78 {
79 }
80 
81 void TebLocalPlannerROS::reconfigureCB(TebLocalPlannerReconfigureConfig& config, uint32_t level)
82 {
83  cfg_.reconfigure(config);
84  ros::NodeHandle nh("~/" + name_);
85  // create robot footprint/contour model for optimization
87  planner_->updateRobotModel(robot_model);
88 }
89 
91 {
92  // check if the plugin is already initialized
93  if(!initialized_)
94  {
95  name_ = name;
96  // create Node Handle with name of plugin (as used in move_base for loading)
97  ros::NodeHandle nh("~/" + name);
98 
99  // get parameters of TebConfig via the nodehandle and override the default config
101 
102  // reserve some memory for obstacles
103  obstacles_.reserve(500);
104 
105  // create visualization instance
107 
108  // create robot footprint/contour model for optimization
110 
111  // create the planner instance
113  {
115  ROS_INFO("Parallel planning in distinctive topologies enabled.");
116  }
117  else
118  {
120  ROS_INFO("Parallel planning in distinctive topologies disabled.");
121  }
122 
123  // init other variables
124  tf_ = tf;
125  costmap_ros_ = costmap_ros;
126  costmap_ = costmap_ros_->getCostmap(); // locking should be done in MoveBase.
127 
128  costmap_model_ = boost::make_shared<base_local_planner::CostmapModel>(*costmap_);
129 
131  cfg_.map_frame = global_frame_; // TODO
133 
134  //Initialize a costmap to polygon converter
136  {
137  try
138  {
141  // replace '::' by '/' to convert the c++ namespace to a NodeHandle namespace
142  boost::replace_all(converter_name, "::", "/");
143  costmap_converter_->setOdomTopic(cfg_.odom_topic);
144  costmap_converter_->initialize(ros::NodeHandle(nh, "costmap_converter/" + converter_name));
145  costmap_converter_->setCostmap2D(costmap_);
146 
148  ROS_INFO_STREAM("Costmap conversion plugin " << cfg_.obstacles.costmap_converter_plugin << " loaded.");
149  }
151  {
152  ROS_WARN("The specified costmap converter plugin cannot be loaded. All occupied costmap cells are treaten as point obstacles. Error message: %s", ex.what());
153  costmap_converter_.reset();
154  }
155  }
156  else
157  ROS_INFO("No costmap conversion plugin specified. All occupied costmap cells are treaten as point obstacles.");
158 
159 
160  // Get footprint of the robot and minimum and maximum distance from the center of the robot to its footprint vertices.
163 
164  // init the odom helper to receive the robot's velocity from odom messages
166 
167  // setup dynamic reconfigure
168  dynamic_recfg_ = boost::make_shared< dynamic_reconfigure::Server<TebLocalPlannerReconfigureConfig> >(nh);
169  dynamic_reconfigure::Server<TebLocalPlannerReconfigureConfig>::CallbackType cb = boost::bind(&TebLocalPlannerROS::reconfigureCB, this, _1, _2);
170  dynamic_recfg_->setCallback(cb);
171 
172  // validate optimization footprint and costmap footprint
173  validateFootprints(robot_model->getInscribedRadius(), robot_inscribed_radius_, cfg_.obstacles.min_obstacle_dist);
174 
175  // setup callback for custom obstacles
177 
178  // setup callback for custom via-points
180 
181  // initialize failure detector
182  ros::NodeHandle nh_move_base("~");
183  double controller_frequency = 5;
184  nh_move_base.param("controller_frequency", controller_frequency, controller_frequency);
186 
187  // set initialized flag
188  initialized_ = true;
189 
190  ROS_DEBUG("teb_local_planner plugin initialized.");
191  }
192  else
193  {
194  ROS_WARN("teb_local_planner has already been initialized, doing nothing.");
195  }
196 }
197 
198 
199 
200 bool TebLocalPlannerROS::setPlan(const std::vector<geometry_msgs::PoseStamped>& orig_global_plan)
201 {
202  // check if plugin is initialized
203  if(!initialized_)
204  {
205  ROS_ERROR("teb_local_planner has not been initialized, please call initialize() before using this planner");
206  return false;
207  }
208 
209  // store the global plan
210  global_plan_.clear();
211  global_plan_ = orig_global_plan;
212 
213  // we do not clear the local planner here, since setPlan is called frequently whenever the global planner updates the plan.
214  // the local planner checks whether it is required to reinitialize the trajectory or not within each velocity computation step.
215 
216  // reset goal_reached_ flag
217  goal_reached_ = false;
218 
219  return true;
220 }
221 
222 
224 {
225  std::string dummy_message;
226  geometry_msgs::PoseStamped dummy_pose;
227  geometry_msgs::TwistStamped dummy_velocity, cmd_vel_stamped;
228  uint32_t outcome = computeVelocityCommands(dummy_pose, dummy_velocity, cmd_vel_stamped, dummy_message);
229  cmd_vel = cmd_vel_stamped.twist;
230  return outcome == mbf_msgs::ExePathResult::SUCCESS;
231 }
232 
233 uint32_t TebLocalPlannerROS::computeVelocityCommands(const geometry_msgs::PoseStamped& pose,
234  const geometry_msgs::TwistStamped& velocity,
235  geometry_msgs::TwistStamped &cmd_vel,
236  std::string &message)
237 {
238  // check if plugin initialized
239  if(!initialized_)
240  {
241  ROS_ERROR("teb_local_planner has not been initialized, please call initialize() before using this planner");
242  message = "teb_local_planner has not been initialized";
243  return mbf_msgs::ExePathResult::NOT_INITIALIZED;
244  }
245 
246  static uint32_t seq = 0;
247  cmd_vel.header.seq = seq++;
248  cmd_vel.header.stamp = ros::Time::now();
249  cmd_vel.header.frame_id = robot_base_frame_;
250  cmd_vel.twist.linear.x = cmd_vel.twist.linear.y = cmd_vel.twist.angular.z = 0;
251  goal_reached_ = false;
252 
253  // Get robot pose
254  geometry_msgs::PoseStamped robot_pose;
255  costmap_ros_->getRobotPose(robot_pose);
256  robot_pose_ = PoseSE2(robot_pose.pose);
257 
258  // Get robot velocity
259  geometry_msgs::PoseStamped robot_vel_tf;
260  odom_helper_.getRobotVel(robot_vel_tf);
261  robot_vel_.linear.x = robot_vel_tf.pose.position.x;
262  robot_vel_.linear.y = robot_vel_tf.pose.position.y;
263  robot_vel_.angular.z = tf2::getYaw(robot_vel_tf.pose.orientation);
264 
265  // prune global plan to cut off parts of the past (spatially before the robot)
267 
268  // Transform global plan to the frame of interest (w.r.t. the local costmap)
269  std::vector<geometry_msgs::PoseStamped> transformed_plan;
270  int goal_idx;
271  geometry_msgs::TransformStamped tf_plan_to_global;
273  transformed_plan, &goal_idx, &tf_plan_to_global))
274  {
275  ROS_WARN("Could not transform the global plan to the frame of the controller");
276  message = "Could not transform the global plan to the frame of the controller";
277  return mbf_msgs::ExePathResult::INTERNAL_ERROR;
278  }
279 
280  // update via-points container
283 
284  nav_msgs::Odometry base_odom;
285  odom_helper_.getOdom(base_odom);
286 
287  // check if global goal is reached
288  geometry_msgs::PoseStamped global_goal;
289  tf2::doTransform(global_plan_.back(), global_goal, tf_plan_to_global);
290  double dx = global_goal.pose.position.x - robot_pose_.x();
291  double dy = global_goal.pose.position.y - robot_pose_.y();
292  double delta_orient = g2o::normalize_theta( tf2::getYaw(global_goal.pose.orientation) - robot_pose_.theta() );
293  if(fabs(std::sqrt(dx*dx+dy*dy)) < cfg_.goal_tolerance.xy_goal_tolerance
294  && fabs(delta_orient) < cfg_.goal_tolerance.yaw_goal_tolerance
298  {
299  goal_reached_ = true;
300  return mbf_msgs::ExePathResult::SUCCESS;
301  }
302 
303  // check if we should enter any backup mode and apply settings
304  configureBackupModes(transformed_plan, goal_idx);
305 
306 
307  // Return false if the transformed global plan is empty
308  if (transformed_plan.empty())
309  {
310  ROS_WARN("Transformed plan is empty. Cannot determine a local plan.");
311  message = "Transformed plan is empty";
312  return mbf_msgs::ExePathResult::INVALID_PATH;
313  }
314 
315  // Get current goal point (last point of the transformed plan)
316  robot_goal_.x() = transformed_plan.back().pose.position.x;
317  robot_goal_.y() = transformed_plan.back().pose.position.y;
318  // Overwrite goal orientation if needed
320  {
321  robot_goal_.theta() = estimateLocalGoalOrientation(global_plan_, transformed_plan.back(), goal_idx, tf_plan_to_global);
322  // overwrite/update goal orientation of the transformed plan with the actual goal (enable using the plan as initialization)
323  tf2::Quaternion q;
324  q.setRPY(0, 0, robot_goal_.theta());
325  tf2::convert(q, transformed_plan.back().pose.orientation);
326  }
327  else
328  {
329  robot_goal_.theta() = tf2::getYaw(transformed_plan.back().pose.orientation);
330  }
331 
332  // overwrite/update start of the transformed plan with the actual robot position (allows using the plan as initial trajectory)
333  if (transformed_plan.size()==1) // plan only contains the goal
334  {
335  transformed_plan.insert(transformed_plan.begin(), geometry_msgs::PoseStamped()); // insert start (not yet initialized)
336  }
337  transformed_plan.front() = robot_pose; // update start
338 
339  // clear currently existing obstacles
340  obstacles_.clear();
341 
342  // Update obstacle container with costmap information or polygons provided by a costmap_converter plugin
343  if (costmap_converter_)
345  else
347 
348  // also consider custom obstacles (must be called after other updates, since the container is not cleared)
350 
351 
352  // Do not allow config changes during the following optimization step
353  boost::mutex::scoped_lock cfg_lock(cfg_.configMutex());
354 
355  // Now perform the actual planning
356 // bool success = planner_->plan(robot_pose_, robot_goal_, robot_vel_, cfg_.goal_tolerance.free_goal_vel); // straight line init
357  bool success = planner_->plan(transformed_plan, &robot_vel_, cfg_.goal_tolerance.free_goal_vel);
358  if (!success)
359  {
360  planner_->clearPlanner(); // force reinitialization for next time
361  ROS_WARN("teb_local_planner was not able to obtain a local plan for the current setting.");
362 
363  ++no_infeasible_plans_; // increase number of infeasible solutions in a row
365  last_cmd_ = cmd_vel.twist;
366  message = "teb_local_planner was not able to obtain a local plan";
367  return mbf_msgs::ExePathResult::NO_VALID_CMD;
368  }
369 
370  // Check for divergence
371  if (planner_->hasDiverged())
372  {
373  cmd_vel.twist.linear.x = cmd_vel.twist.linear.y = cmd_vel.twist.angular.z = 0;
374 
375  // Reset everything to start again with the initialization of new trajectories.
376  planner_->clearPlanner();
377  ROS_WARN_THROTTLE(1.0, "TebLocalPlannerROS: the trajectory has diverged. Resetting planner...");
378 
379  ++no_infeasible_plans_; // increase number of infeasible solutions in a row
381  last_cmd_ = cmd_vel.twist;
382  return mbf_msgs::ExePathResult::NO_VALID_CMD;
383  }
384 
385  // Check feasibility (but within the first few states only)
387  {
388  // Update footprint of the robot and minimum and maximum distance from the center of the robot to its footprint vertices.
391  }
392 
394  if (!feasible)
395  {
396  cmd_vel.twist.linear.x = cmd_vel.twist.linear.y = cmd_vel.twist.angular.z = 0;
397 
398  // now we reset everything to start again with the initialization of new trajectories.
399  planner_->clearPlanner();
400  ROS_WARN("TebLocalPlannerROS: trajectory is not feasible. Resetting planner...");
401 
402  ++no_infeasible_plans_; // increase number of infeasible solutions in a row
404  last_cmd_ = cmd_vel.twist;
405  message = "teb_local_planner trajectory is not feasible";
406  return mbf_msgs::ExePathResult::NO_VALID_CMD;
407  }
408 
409  // Get the velocity command for this sampling interval
410  if (!planner_->getVelocityCommand(cmd_vel.twist.linear.x, cmd_vel.twist.linear.y, cmd_vel.twist.angular.z, cfg_.trajectory.control_look_ahead_poses))
411 
412  {
413  planner_->clearPlanner();
414  ROS_WARN("TebLocalPlannerROS: velocity command invalid. Resetting planner...");
415  ++no_infeasible_plans_; // increase number of infeasible solutions in a row
417  last_cmd_ = cmd_vel.twist;
418  message = "teb_local_planner velocity command invalid";
419  return mbf_msgs::ExePathResult::NO_VALID_CMD;
420  }
421 
422  // Saturate velocity, if the optimization results violates the constraints (could be possible due to soft constraints).
423  saturateVelocity(cmd_vel.twist.linear.x, cmd_vel.twist.linear.y, cmd_vel.twist.angular.z,
426 
427  // convert rot-vel to steering angle if desired (carlike robot).
428  // The min_turning_radius is allowed to be slighly smaller since it is a soft-constraint
429  // and opposed to the other constraints not affected by penalty_epsilon. The user might add a safety margin to the parameter itself.
431  {
432  cmd_vel.twist.angular.z = convertTransRotVelToSteeringAngle(cmd_vel.twist.linear.x, cmd_vel.twist.angular.z,
434  if (!std::isfinite(cmd_vel.twist.angular.z))
435  {
436  cmd_vel.twist.linear.x = cmd_vel.twist.linear.y = cmd_vel.twist.angular.z = 0;
437  last_cmd_ = cmd_vel.twist;
438  planner_->clearPlanner();
439  ROS_WARN("TebLocalPlannerROS: Resulting steering angle is not finite. Resetting planner...");
440  ++no_infeasible_plans_; // increase number of infeasible solutions in a row
442  message = "teb_local_planner steering angle is not finite";
443  return mbf_msgs::ExePathResult::NO_VALID_CMD;
444  }
445  }
446 
447  // a feasible solution should be found, reset counter
449 
450  // store last command (for recovery analysis etc.)
451  last_cmd_ = cmd_vel.twist;
452 
453  // Now visualize everything
454  planner_->visualize();
455  visualization_->publishObstacles(obstacles_);
456  visualization_->publishViaPoints(via_points_);
457  visualization_->publishGlobalPlan(global_plan_);
458  return mbf_msgs::ExePathResult::SUCCESS;
459 }
460 
461 
463 {
464  if (goal_reached_)
465  {
466  ROS_INFO("GOAL Reached!");
467  planner_->clearPlanner();
468  return true;
469  }
470  return false;
471 }
472 
473 
474 
476 {
477  // Add costmap obstacles if desired
479  {
480  Eigen::Vector2d robot_orient = robot_pose_.orientationUnitVec();
481 
482  for (unsigned int i=0; i<costmap_->getSizeInCellsX()-1; ++i)
483  {
484  for (unsigned int j=0; j<costmap_->getSizeInCellsY()-1; ++j)
485  {
487  {
488  Eigen::Vector2d obs;
489  costmap_->mapToWorld(i,j,obs.coeffRef(0), obs.coeffRef(1));
490 
491  // check if obstacle is interesting (e.g. not far behind the robot)
492  Eigen::Vector2d obs_dir = obs-robot_pose_.position();
493  if ( obs_dir.dot(robot_orient) < 0 && obs_dir.norm() > cfg_.obstacles.costmap_obstacles_behind_robot_dist )
494  continue;
495 
496  obstacles_.push_back(ObstaclePtr(new PointObstacle(obs)));
497  }
498  }
499  }
500  }
501 }
502 
504 {
505  if (!costmap_converter_)
506  return;
507 
508  //Get obstacles from costmap converter
510  if (!obstacles)
511  return;
512 
513  for (std::size_t i=0; i<obstacles->obstacles.size(); ++i)
514  {
515  const costmap_converter::ObstacleMsg* obstacle = &obstacles->obstacles.at(i);
516  const geometry_msgs::Polygon* polygon = &obstacle->polygon;
517 
518  if (polygon->points.size()==1 && obstacle->radius > 0) // Circle
519  {
520  obstacles_.push_back(ObstaclePtr(new CircularObstacle(polygon->points[0].x, polygon->points[0].y, obstacle->radius)));
521  }
522  else if (polygon->points.size()==1) // Point
523  {
524  obstacles_.push_back(ObstaclePtr(new PointObstacle(polygon->points[0].x, polygon->points[0].y)));
525  }
526  else if (polygon->points.size()==2) // Line
527  {
528  obstacles_.push_back(ObstaclePtr(new LineObstacle(polygon->points[0].x, polygon->points[0].y,
529  polygon->points[1].x, polygon->points[1].y )));
530  }
531  else if (polygon->points.size()>2) // Real polygon
532  {
533  PolygonObstacle* polyobst = new PolygonObstacle;
534  for (std::size_t j=0; j<polygon->points.size(); ++j)
535  {
536  polyobst->pushBackVertex(polygon->points[j].x, polygon->points[j].y);
537  }
538  polyobst->finalizePolygon();
539  obstacles_.push_back(ObstaclePtr(polyobst));
540  }
541 
542  // Set velocity, if obstacle is moving
543  if(!obstacles_.empty())
544  obstacles_.back()->setCentroidVelocity(obstacles->obstacles[i].velocities, obstacles->obstacles[i].orientation);
545  }
546 }
547 
548 
550 {
551  // Add custom obstacles obtained via message
552  boost::mutex::scoped_lock l(custom_obst_mutex_);
553 
554  if (!custom_obstacle_msg_.obstacles.empty())
555  {
556  // We only use the global header to specify the obstacle coordinate system instead of individual ones
557  Eigen::Affine3d obstacle_to_map_eig;
558  try
559  {
560  geometry_msgs::TransformStamped obstacle_to_map = tf_->lookupTransform(global_frame_, ros::Time(0),
561  custom_obstacle_msg_.header.frame_id, ros::Time(0),
563  obstacle_to_map_eig = tf2::transformToEigen(obstacle_to_map);
564  }
565  catch (tf::TransformException ex)
566  {
567  ROS_ERROR("%s",ex.what());
568  obstacle_to_map_eig.setIdentity();
569  }
570 
571  for (size_t i=0; i<custom_obstacle_msg_.obstacles.size(); ++i)
572  {
573  if (custom_obstacle_msg_.obstacles.at(i).polygon.points.size() == 1 && custom_obstacle_msg_.obstacles.at(i).radius > 0 ) // circle
574  {
575  Eigen::Vector3d pos( custom_obstacle_msg_.obstacles.at(i).polygon.points.front().x,
576  custom_obstacle_msg_.obstacles.at(i).polygon.points.front().y,
577  custom_obstacle_msg_.obstacles.at(i).polygon.points.front().z );
578  obstacles_.push_back(ObstaclePtr(new CircularObstacle( (obstacle_to_map_eig * pos).head(2), custom_obstacle_msg_.obstacles.at(i).radius)));
579  }
580  else if (custom_obstacle_msg_.obstacles.at(i).polygon.points.size() == 1 ) // point
581  {
582  Eigen::Vector3d pos( custom_obstacle_msg_.obstacles.at(i).polygon.points.front().x,
583  custom_obstacle_msg_.obstacles.at(i).polygon.points.front().y,
584  custom_obstacle_msg_.obstacles.at(i).polygon.points.front().z );
585  obstacles_.push_back(ObstaclePtr(new PointObstacle( (obstacle_to_map_eig * pos).head(2) )));
586  }
587  else if (custom_obstacle_msg_.obstacles.at(i).polygon.points.size() == 2 ) // line
588  {
589  Eigen::Vector3d line_start( custom_obstacle_msg_.obstacles.at(i).polygon.points.front().x,
590  custom_obstacle_msg_.obstacles.at(i).polygon.points.front().y,
591  custom_obstacle_msg_.obstacles.at(i).polygon.points.front().z );
592  Eigen::Vector3d line_end( custom_obstacle_msg_.obstacles.at(i).polygon.points.back().x,
593  custom_obstacle_msg_.obstacles.at(i).polygon.points.back().y,
594  custom_obstacle_msg_.obstacles.at(i).polygon.points.back().z );
595  obstacles_.push_back(ObstaclePtr(new LineObstacle( (obstacle_to_map_eig * line_start).head(2),
596  (obstacle_to_map_eig * line_end).head(2) )));
597  }
598  else if (custom_obstacle_msg_.obstacles.at(i).polygon.points.empty())
599  {
600  ROS_WARN("Invalid custom obstacle received. List of polygon vertices is empty. Skipping...");
601  continue;
602  }
603  else // polygon
604  {
605  PolygonObstacle* polyobst = new PolygonObstacle;
606  for (size_t j=0; j<custom_obstacle_msg_.obstacles.at(i).polygon.points.size(); ++j)
607  {
608  Eigen::Vector3d pos( custom_obstacle_msg_.obstacles.at(i).polygon.points[j].x,
609  custom_obstacle_msg_.obstacles.at(i).polygon.points[j].y,
610  custom_obstacle_msg_.obstacles.at(i).polygon.points[j].z );
611  polyobst->pushBackVertex( (obstacle_to_map_eig * pos).head(2) );
612  }
613  polyobst->finalizePolygon();
614  obstacles_.push_back(ObstaclePtr(polyobst));
615  }
616 
617  // Set velocity, if obstacle is moving
618  if(!obstacles_.empty())
619  obstacles_.back()->setCentroidVelocity(custom_obstacle_msg_.obstacles[i].velocities, custom_obstacle_msg_.obstacles[i].orientation);
620  }
621  }
622 }
623 
624 void TebLocalPlannerROS::updateViaPointsContainer(const std::vector<geometry_msgs::PoseStamped>& transformed_plan, double min_separation)
625 {
626  via_points_.clear();
627 
628  if (min_separation<=0)
629  return;
630 
631  std::size_t prev_idx = 0;
632  for (std::size_t i=1; i < transformed_plan.size(); ++i) // skip first one, since we do not need any point before the first min_separation [m]
633  {
634  // check separation to the previous via-point inserted
635  if (distance_points2d( transformed_plan[prev_idx].pose.position, transformed_plan[i].pose.position ) < min_separation)
636  continue;
637 
638  // add via-point
639  via_points_.push_back( Eigen::Vector2d( transformed_plan[i].pose.position.x, transformed_plan[i].pose.position.y ) );
640  prev_idx = i;
641  }
642 
643 }
644 
646 {
647  Eigen::Vector2d vel;
648  vel.coeffRef(0) = std::sqrt( tf_vel.getOrigin().getX() * tf_vel.getOrigin().getX() + tf_vel.getOrigin().getY() * tf_vel.getOrigin().getY() );
649  vel.coeffRef(1) = tf::getYaw(tf_vel.getRotation());
650  return vel;
651 }
652 
653 
654 bool TebLocalPlannerROS::pruneGlobalPlan(const tf2_ros::Buffer& tf, const geometry_msgs::PoseStamped& global_pose, std::vector<geometry_msgs::PoseStamped>& global_plan, double dist_behind_robot)
655 {
656  if (global_plan.empty())
657  return true;
658 
659  try
660  {
661  // transform robot pose into the plan frame (we do not wait here, since pruning not crucial, if missed a few times)
662  geometry_msgs::TransformStamped global_to_plan_transform = tf.lookupTransform(global_plan.front().header.frame_id, global_pose.header.frame_id, ros::Time(0));
663  geometry_msgs::PoseStamped robot;
664  tf2::doTransform(global_pose, robot, global_to_plan_transform);
665 
666  double dist_thresh_sq = dist_behind_robot*dist_behind_robot;
667 
668  // iterate plan until a pose close the robot is found
669  std::vector<geometry_msgs::PoseStamped>::iterator it = global_plan.begin();
670  std::vector<geometry_msgs::PoseStamped>::iterator erase_end = it;
671  while (it != global_plan.end())
672  {
673  double dx = robot.pose.position.x - it->pose.position.x;
674  double dy = robot.pose.position.y - it->pose.position.y;
675  double dist_sq = dx * dx + dy * dy;
676  if (dist_sq < dist_thresh_sq)
677  {
678  erase_end = it;
679  break;
680  }
681  ++it;
682  }
683  if (erase_end == global_plan.end())
684  return false;
685 
686  if (erase_end != global_plan.begin())
687  global_plan.erase(global_plan.begin(), erase_end);
688  }
689  catch (const tf::TransformException& ex)
690  {
691  ROS_DEBUG("Cannot prune path since no transform is available: %s\n", ex.what());
692  return false;
693  }
694  return true;
695 }
696 
697 
698 bool TebLocalPlannerROS::transformGlobalPlan(const tf2_ros::Buffer& tf, const std::vector<geometry_msgs::PoseStamped>& global_plan,
699  const geometry_msgs::PoseStamped& global_pose, const costmap_2d::Costmap2D& costmap, const std::string& global_frame, double max_plan_length,
700  std::vector<geometry_msgs::PoseStamped>& transformed_plan, int* current_goal_idx, geometry_msgs::TransformStamped* tf_plan_to_global) const
701 {
702  // this method is a slightly modified version of base_local_planner/goal_functions.h
703 
704  const geometry_msgs::PoseStamped& plan_pose = global_plan[0];
705 
706  transformed_plan.clear();
707 
708  try
709  {
710  if (global_plan.empty())
711  {
712  ROS_ERROR("Received plan with zero length");
713  *current_goal_idx = 0;
714  return false;
715  }
716 
717  // get plan_to_global_transform from plan frame to global_frame
718  geometry_msgs::TransformStamped plan_to_global_transform = tf.lookupTransform(global_frame, ros::Time(), plan_pose.header.frame_id, plan_pose.header.stamp,
719  plan_pose.header.frame_id, ros::Duration(cfg_.robot.transform_tolerance));
720 
721  //let's get the pose of the robot in the frame of the plan
722  geometry_msgs::PoseStamped robot_pose;
723  tf.transform(global_pose, robot_pose, plan_pose.header.frame_id);
724 
725  //we'll discard points on the plan that are outside the local costmap
726  double dist_threshold = std::max(costmap.getSizeInCellsX() * costmap.getResolution() / 2.0,
727  costmap.getSizeInCellsY() * costmap.getResolution() / 2.0);
728  dist_threshold *= 0.85; // just consider 85% of the costmap size to better incorporate point obstacle that are
729  // located on the border of the local costmap
730 
731 
732  int i = 0;
733  double sq_dist_threshold = dist_threshold * dist_threshold;
734  double sq_dist = 1e10;
735 
736  //we need to loop to a point on the plan that is within a certain distance of the robot
737  bool robot_reached = false;
738  for(int j=0; j < (int)global_plan.size(); ++j)
739  {
740  double x_diff = robot_pose.pose.position.x - global_plan[j].pose.position.x;
741  double y_diff = robot_pose.pose.position.y - global_plan[j].pose.position.y;
742  double new_sq_dist = x_diff * x_diff + y_diff * y_diff;
743 
744  if (robot_reached && new_sq_dist > sq_dist)
745  break;
746 
747  if (new_sq_dist < sq_dist) // find closest distance
748  {
749  sq_dist = new_sq_dist;
750  i = j;
751  if (sq_dist < 0.05) // 2.5 cm to the robot; take the immediate local minima; if it's not the global
752  robot_reached = true; // minima, probably means that there's a loop in the path, and so we prefer this
753  }
754  }
755 
756  geometry_msgs::PoseStamped newer_pose;
757 
758  double plan_length = 0; // check cumulative Euclidean distance along the plan
759 
760  //now we'll transform until points are outside of our distance threshold
761  while(i < (int)global_plan.size() && sq_dist <= sq_dist_threshold && (max_plan_length<=0 || plan_length <= max_plan_length))
762  {
763  const geometry_msgs::PoseStamped& pose = global_plan[i];
764  tf2::doTransform(pose, newer_pose, plan_to_global_transform);
765 
766  transformed_plan.push_back(newer_pose);
767 
768  double x_diff = robot_pose.pose.position.x - global_plan[i].pose.position.x;
769  double y_diff = robot_pose.pose.position.y - global_plan[i].pose.position.y;
770  sq_dist = x_diff * x_diff + y_diff * y_diff;
771 
772  // caclulate distance to previous pose
773  if (i>0 && max_plan_length>0)
774  plan_length += distance_points2d(global_plan[i-1].pose.position, global_plan[i].pose.position);
775 
776  ++i;
777  }
778 
779  // if we are really close to the goal (<sq_dist_threshold) and the goal is not yet reached (e.g. orientation error >>0)
780  // the resulting transformed plan can be empty. In that case we explicitly inject the global goal.
781  if (transformed_plan.empty())
782  {
783  tf2::doTransform(global_plan.back(), newer_pose, plan_to_global_transform);
784 
785  transformed_plan.push_back(newer_pose);
786 
787  // Return the index of the current goal point (inside the distance threshold)
788  if (current_goal_idx) *current_goal_idx = int(global_plan.size())-1;
789  }
790  else
791  {
792  // Return the index of the current goal point (inside the distance threshold)
793  if (current_goal_idx) *current_goal_idx = i-1; // subtract 1, since i was increased once before leaving the loop
794  }
795 
796  // Return the transformation from the global plan to the global planning frame if desired
797  if (tf_plan_to_global) *tf_plan_to_global = plan_to_global_transform;
798  }
799  catch(tf::LookupException& ex)
800  {
801  ROS_ERROR("No Transform available Error: %s\n", ex.what());
802  return false;
803  }
804  catch(tf::ConnectivityException& ex)
805  {
806  ROS_ERROR("Connectivity Error: %s\n", ex.what());
807  return false;
808  }
809  catch(tf::ExtrapolationException& ex)
810  {
811  ROS_ERROR("Extrapolation Error: %s\n", ex.what());
812  if (global_plan.size() > 0)
813  ROS_ERROR("Global Frame: %s Plan Frame size %d: %s\n", global_frame.c_str(), (unsigned int)global_plan.size(), global_plan[0].header.frame_id.c_str());
814 
815  return false;
816  }
817 
818  return true;
819 }
820 
821 
822 
823 
824 double TebLocalPlannerROS::estimateLocalGoalOrientation(const std::vector<geometry_msgs::PoseStamped>& global_plan, const geometry_msgs::PoseStamped& local_goal,
825  int current_goal_idx, const geometry_msgs::TransformStamped& tf_plan_to_global, int moving_average_length) const
826 {
827  int n = (int)global_plan.size();
828 
829  // check if we are near the global goal already
830  if (current_goal_idx > n-moving_average_length-2)
831  {
832  if (current_goal_idx >= n-1) // we've exactly reached the goal
833  {
834  return tf2::getYaw(local_goal.pose.orientation);
835  }
836  else
837  {
838  tf2::Quaternion global_orientation;
839  tf2::convert(global_plan.back().pose.orientation, global_orientation);
840  tf2::Quaternion rotation;
841  tf2::convert(tf_plan_to_global.transform.rotation, rotation);
842  // TODO(roesmann): avoid conversion to tf2::Quaternion
843  return tf2::getYaw(rotation * global_orientation);
844  }
845  }
846 
847  // reduce number of poses taken into account if the desired number of poses is not available
848  moving_average_length = std::min(moving_average_length, n-current_goal_idx-1 ); // maybe redundant, since we have checked the vicinity of the goal before
849 
850  std::vector<double> candidates;
851  geometry_msgs::PoseStamped tf_pose_k = local_goal;
852  geometry_msgs::PoseStamped tf_pose_kp1;
853 
854  int range_end = current_goal_idx + moving_average_length;
855  for (int i = current_goal_idx; i < range_end; ++i)
856  {
857  // Transform pose of the global plan to the planning frame
858  tf2::doTransform(global_plan.at(i+1), tf_pose_kp1, tf_plan_to_global);
859 
860  // calculate yaw angle
861  candidates.push_back( std::atan2(tf_pose_kp1.pose.position.y - tf_pose_k.pose.position.y,
862  tf_pose_kp1.pose.position.x - tf_pose_k.pose.position.x ) );
863 
864  if (i<range_end-1)
865  tf_pose_k = tf_pose_kp1;
866  }
867  return average_angles(candidates);
868 }
869 
870 
871 void TebLocalPlannerROS::saturateVelocity(double& vx, double& vy, double& omega, double max_vel_x, double max_vel_y, double max_vel_trans, double max_vel_theta,
872  double max_vel_x_backwards) const
873 {
874  double ratio_x = 1, ratio_omega = 1, ratio_y = 1;
875  // Limit translational velocity for forward driving
876  if (vx > max_vel_x)
877  ratio_x = max_vel_x / vx;
878 
879  // limit strafing velocity
880  if (vy > max_vel_y || vy < -max_vel_y)
881  ratio_y = std::abs(max_vel_y / vy);
882 
883  // Limit angular velocity
884  if (omega > max_vel_theta || omega < -max_vel_theta)
885  ratio_omega = std::abs(max_vel_theta / omega);
886 
887  // Limit backwards velocity
888  if (max_vel_x_backwards<=0)
889  {
890  ROS_WARN_ONCE("TebLocalPlannerROS(): Do not choose max_vel_x_backwards to be <=0. Disable backwards driving by increasing the optimization weight for penalyzing backwards driving.");
891  }
892  else if (vx < -max_vel_x_backwards)
893  ratio_x = - max_vel_x_backwards / vx;
894 
896  {
897  double ratio = std::min(std::min(ratio_x, ratio_y), ratio_omega);
898  vx *= ratio;
899  vy *= ratio;
900  omega *= ratio;
901  }
902  else
903  {
904  vx *= ratio_x;
905  vy *= ratio_y;
906  omega *= ratio_omega;
907  }
908 
909  double vel_linear = std::hypot(vx, vy);
910  if (vel_linear > max_vel_trans)
911  {
912  double max_vel_trans_ratio = max_vel_trans / vel_linear;
913  vx *= max_vel_trans_ratio;
914  vy *= max_vel_trans_ratio;
915  }
916 }
917 
918 
919 double TebLocalPlannerROS::convertTransRotVelToSteeringAngle(double v, double omega, double wheelbase, double min_turning_radius) const
920 {
921  if (omega==0 || v==0)
922  return 0;
923 
924  double radius = v/omega;
925 
926  if (fabs(radius) < min_turning_radius)
927  radius = double(g2o::sign(radius)) * min_turning_radius;
928 
929  return std::atan(wheelbase / radius);
930 }
931 
932 
933 void TebLocalPlannerROS::validateFootprints(double opt_inscribed_radius, double costmap_inscribed_radius, double min_obst_dist)
934 {
935  ROS_WARN_COND(opt_inscribed_radius + min_obst_dist < costmap_inscribed_radius,
936  "The inscribed radius of the footprint specified for TEB optimization (%f) + min_obstacle_dist (%f) are smaller "
937  "than the inscribed radius of the robot's footprint in the costmap parameters (%f, including 'footprint_padding'). "
938  "Infeasible optimziation results might occur frequently!", opt_inscribed_radius, min_obst_dist, costmap_inscribed_radius);
939 }
940 
941 
942 
943 void TebLocalPlannerROS::configureBackupModes(std::vector<geometry_msgs::PoseStamped>& transformed_plan, int& goal_idx)
944 {
945  ros::Time current_time = ros::Time::now();
946 
947  // reduced horizon backup mode
949  goal_idx < (int)transformed_plan.size()-1 && // we do not reduce if the goal is already selected (because the orientation might change -> can introduce oscillations)
950  (no_infeasible_plans_>0 || (current_time - time_last_infeasible_plan_).toSec() < cfg_.recovery.shrink_horizon_min_duration )) // keep short horizon for at least a few seconds
951  {
952  ROS_INFO_COND(no_infeasible_plans_==1, "Activating reduced horizon backup mode for at least %.2f sec (infeasible trajectory detected).", cfg_.recovery.shrink_horizon_min_duration);
953 
954 
955  // Shorten horizon if requested
956  // reduce to 50 percent:
957  int horizon_reduction = goal_idx/2;
958 
959  if (no_infeasible_plans_ > 9)
960  {
961  ROS_INFO_COND(no_infeasible_plans_==10, "Infeasible trajectory detected 10 times in a row: further reducing horizon...");
962  horizon_reduction /= 2;
963  }
964 
965  // we have a small overhead here, since we already transformed 50% more of the trajectory.
966  // But that's ok for now, since we do not need to make transformGlobalPlan more complex
967  // and a reduced horizon should occur just rarely.
968  int new_goal_idx_transformed_plan = int(transformed_plan.size()) - horizon_reduction - 1;
969  goal_idx -= horizon_reduction;
970  if (new_goal_idx_transformed_plan>0 && goal_idx >= 0)
971  transformed_plan.erase(transformed_plan.begin()+new_goal_idx_transformed_plan, transformed_plan.end());
972  else
973  goal_idx += horizon_reduction; // this should not happen, but safety first ;-)
974  }
975 
976 
977  // detect and resolve oscillations
979  {
980  double max_vel_theta;
981  double max_vel_current = last_cmd_.linear.x >= 0 ? cfg_.robot.max_vel_x : cfg_.robot.max_vel_x_backwards;
982  if (cfg_.robot.min_turning_radius!=0 && max_vel_current>0)
983  max_vel_theta = std::max( max_vel_current/std::abs(cfg_.robot.min_turning_radius), cfg_.robot.max_vel_theta );
984  else
985  max_vel_theta = cfg_.robot.max_vel_theta;
986 
989 
990  bool oscillating = failure_detector_.isOscillating();
991  bool recently_oscillated = (ros::Time::now()-time_last_oscillation_).toSec() < cfg_.recovery.oscillation_recovery_min_duration; // check if we have already detected an oscillation recently
992 
993  if (oscillating)
994  {
995  if (!recently_oscillated)
996  {
997  // save current turning direction
998  if (robot_vel_.angular.z > 0)
1000  else
1002  ROS_WARN("TebLocalPlannerROS: possible oscillation (of the robot or its local plan) detected. Activating recovery strategy (prefer current turning direction during optimization).");
1003  }
1005  planner_->setPreferredTurningDir(last_preferred_rotdir_);
1006  }
1007  else if (!recently_oscillated && last_preferred_rotdir_ != RotType::none) // clear recovery behavior
1008  {
1010  planner_->setPreferredTurningDir(last_preferred_rotdir_);
1011  ROS_INFO("TebLocalPlannerROS: oscillation recovery disabled/expired.");
1012  }
1013  }
1014 
1015 }
1016 
1017 void TebLocalPlannerROS::customObstacleCB(const costmap_converter::ObstacleArrayMsg::ConstPtr& obst_msg)
1018 {
1019  boost::mutex::scoped_lock l(custom_obst_mutex_);
1020  custom_obstacle_msg_ = *obst_msg;
1021 }
1022 
1023 void TebLocalPlannerROS::customViaPointsCB(const nav_msgs::Path::ConstPtr& via_points_msg)
1024 {
1025  ROS_INFO_ONCE("Via-points received. This message is printed once.");
1027  {
1028  ROS_WARN("Via-points are already obtained from the global plan (global_plan_viapoint_sep>0)."
1029  "Ignoring custom via-points.");
1030  custom_via_points_active_ = false;
1031  return;
1032  }
1033 
1034  boost::mutex::scoped_lock l(via_point_mutex_);
1035  via_points_.clear();
1036  for (const geometry_msgs::PoseStamped& pose : via_points_msg->poses)
1037  {
1038  via_points_.emplace_back(pose.pose.position.x, pose.pose.position.y);
1039  }
1041 }
1042 
1044 {
1045  std::string model_name;
1046  if (!nh.getParam("footprint_model/type", model_name))
1047  {
1048  ROS_INFO("No robot footprint model specified for trajectory optimization. Using point-shaped model.");
1049  return boost::make_shared<PointRobotFootprint>();
1050  }
1051 
1052  // point
1053  if (model_name.compare("point") == 0)
1054  {
1055  ROS_INFO("Footprint model 'point' loaded for trajectory optimization.");
1056  return boost::make_shared<PointRobotFootprint>(config.obstacles.min_obstacle_dist);
1057  }
1058 
1059  // circular
1060  if (model_name.compare("circular") == 0)
1061  {
1062  // get radius
1063  double radius;
1064  if (!nh.getParam("footprint_model/radius", radius))
1065  {
1066  ROS_ERROR_STREAM("Footprint model 'circular' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
1067  << "/footprint_model/radius' does not exist. Using point-model instead.");
1068  return boost::make_shared<PointRobotFootprint>();
1069  }
1070  ROS_INFO_STREAM("Footprint model 'circular' (radius: " << radius <<"m) loaded for trajectory optimization.");
1071  return boost::make_shared<CircularRobotFootprint>(radius);
1072  }
1073 
1074  // line
1075  if (model_name.compare("line") == 0)
1076  {
1077  // check parameters
1078  if (!nh.hasParam("footprint_model/line_start") || !nh.hasParam("footprint_model/line_end"))
1079  {
1080  ROS_ERROR_STREAM("Footprint model 'line' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
1081  << "/footprint_model/line_start' and/or '.../line_end' do not exist. Using point-model instead.");
1082  return boost::make_shared<PointRobotFootprint>();
1083  }
1084  // get line coordinates
1085  std::vector<double> line_start, line_end;
1086  nh.getParam("footprint_model/line_start", line_start);
1087  nh.getParam("footprint_model/line_end", line_end);
1088  if (line_start.size() != 2 || line_end.size() != 2)
1089  {
1090  ROS_ERROR_STREAM("Footprint model 'line' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
1091  << "/footprint_model/line_start' and/or '.../line_end' do not contain x and y coordinates (2D). Using point-model instead.");
1092  return boost::make_shared<PointRobotFootprint>();
1093  }
1094 
1095  ROS_INFO_STREAM("Footprint model 'line' (line_start: [" << line_start[0] << "," << line_start[1] <<"]m, line_end: ["
1096  << line_end[0] << "," << line_end[1] << "]m) loaded for trajectory optimization.");
1097  return boost::make_shared<LineRobotFootprint>(Eigen::Map<const Eigen::Vector2d>(line_start.data()), Eigen::Map<const Eigen::Vector2d>(line_end.data()), config.obstacles.min_obstacle_dist);
1098  }
1099 
1100  // two circles
1101  if (model_name.compare("two_circles") == 0)
1102  {
1103  // check parameters
1104  if (!nh.hasParam("footprint_model/front_offset") || !nh.hasParam("footprint_model/front_radius")
1105  || !nh.hasParam("footprint_model/rear_offset") || !nh.hasParam("footprint_model/rear_radius"))
1106  {
1107  ROS_ERROR_STREAM("Footprint model 'two_circles' cannot be loaded for trajectory optimization, since params '" << nh.getNamespace()
1108  << "/footprint_model/front_offset', '.../front_radius', '.../rear_offset' and '.../rear_radius' do not exist. Using point-model instead.");
1109  return boost::make_shared<PointRobotFootprint>();
1110  }
1111  double front_offset, front_radius, rear_offset, rear_radius;
1112  nh.getParam("footprint_model/front_offset", front_offset);
1113  nh.getParam("footprint_model/front_radius", front_radius);
1114  nh.getParam("footprint_model/rear_offset", rear_offset);
1115  nh.getParam("footprint_model/rear_radius", rear_radius);
1116  ROS_INFO_STREAM("Footprint model 'two_circles' (front_offset: " << front_offset <<"m, front_radius: " << front_radius
1117  << "m, rear_offset: " << rear_offset << "m, rear_radius: " << rear_radius << "m) loaded for trajectory optimization.");
1118  return boost::make_shared<TwoCirclesRobotFootprint>(front_offset, front_radius, rear_offset, rear_radius);
1119  }
1120 
1121  // polygon
1122  if (model_name.compare("polygon") == 0)
1123  {
1124 
1125  // check parameters
1126  XmlRpc::XmlRpcValue footprint_xmlrpc;
1127  if (!nh.getParam("footprint_model/vertices", footprint_xmlrpc) )
1128  {
1129  ROS_ERROR_STREAM("Footprint model 'polygon' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
1130  << "/footprint_model/vertices' does not exist. Using point-model instead.");
1131  return boost::make_shared<PointRobotFootprint>();
1132  }
1133  // get vertices
1134  if (footprint_xmlrpc.getType() == XmlRpc::XmlRpcValue::TypeArray)
1135  {
1136  try
1137  {
1138  Point2dContainer polygon = makeFootprintFromXMLRPC(footprint_xmlrpc, "/footprint_model/vertices");
1139  ROS_INFO_STREAM("Footprint model 'polygon' loaded for trajectory optimization.");
1140  return boost::make_shared<PolygonRobotFootprint>(polygon);
1141  }
1142  catch(const std::exception& ex)
1143  {
1144  ROS_ERROR_STREAM("Footprint model 'polygon' cannot be loaded for trajectory optimization: " << ex.what() << ". Using point-model instead.");
1145  return boost::make_shared<PointRobotFootprint>();
1146  }
1147  }
1148  else
1149  {
1150  ROS_ERROR_STREAM("Footprint model 'polygon' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
1151  << "/footprint_model/vertices' does not define an array of coordinates. Using point-model instead.");
1152  return boost::make_shared<PointRobotFootprint>();
1153  }
1154 
1155  }
1156 
1157  // otherwise
1158  ROS_WARN_STREAM("Unknown robot footprint model specified with parameter '" << nh.getNamespace() << "/footprint_model/type'. Using point model instead.");
1159  return boost::make_shared<PointRobotFootprint>();
1160 }
1161 
1162 
1163 
1164 
1165 Point2dContainer TebLocalPlannerROS::makeFootprintFromXMLRPC(XmlRpc::XmlRpcValue& footprint_xmlrpc, const std::string& full_param_name)
1166 {
1167  // Make sure we have an array of at least 3 elements.
1168  if (footprint_xmlrpc.getType() != XmlRpc::XmlRpcValue::TypeArray ||
1169  footprint_xmlrpc.size() < 3)
1170  {
1171  ROS_FATAL("The footprint must be specified as list of lists on the parameter server, %s was specified as %s",
1172  full_param_name.c_str(), std::string(footprint_xmlrpc).c_str());
1173  throw std::runtime_error("The footprint must be specified as list of lists on the parameter server with at least "
1174  "3 points eg: [[x1, y1], [x2, y2], ..., [xn, yn]]");
1175  }
1176 
1177  Point2dContainer footprint;
1178  Eigen::Vector2d pt;
1179 
1180  for (int i = 0; i < footprint_xmlrpc.size(); ++i)
1181  {
1182  // Make sure each element of the list is an array of size 2. (x and y coordinates)
1183  XmlRpc::XmlRpcValue point = footprint_xmlrpc[ i ];
1184  if (point.getType() != XmlRpc::XmlRpcValue::TypeArray ||
1185  point.size() != 2)
1186  {
1187  ROS_FATAL("The footprint (parameter %s) must be specified as list of lists on the parameter server eg: "
1188  "[[x1, y1], [x2, y2], ..., [xn, yn]], but this spec is not of that form.",
1189  full_param_name.c_str());
1190  throw std::runtime_error("The footprint must be specified as list of lists on the parameter server eg: "
1191  "[[x1, y1], [x2, y2], ..., [xn, yn]], but this spec is not of that form");
1192  }
1193 
1194  pt.x() = getNumberFromXMLRPC(point[ 0 ], full_param_name);
1195  pt.y() = getNumberFromXMLRPC(point[ 1 ], full_param_name);
1196 
1197  footprint.push_back(pt);
1198  }
1199  return footprint;
1200 }
1201 
1202 double TebLocalPlannerROS::getNumberFromXMLRPC(XmlRpc::XmlRpcValue& value, const std::string& full_param_name)
1203 {
1204  // Make sure that the value we're looking at is either a double or an int.
1205  if (value.getType() != XmlRpc::XmlRpcValue::TypeInt &&
1207  {
1208  std::string& value_string = value;
1209  ROS_FATAL("Values in the footprint specification (param %s) must be numbers. Found value %s.",
1210  full_param_name.c_str(), value_string.c_str());
1211  throw std::runtime_error("Values in the footprint specification must be numbers");
1212  }
1213  return value.getType() == XmlRpc::XmlRpcValue::TypeInt ? (int)(value) : (double)(value);
1214 }
1215 
1216 } // end namespace teb_local_planner
1217 
1218 
bool include_costmap_obstacles
Specify whether the obstacles in the costmap should be taken into account directly.
Definition: teb_config.h:129
bool cmd_angle_instead_rotvel
Substitute the rotational velocity in the commanded velocity message by the corresponding steering an...
Definition: teb_config.h:105
boost::shared_ptr< PlannerInterface > PlannerInterfacePtr
Abbrev. for shared instances of PlannerInterface or it&#39;s subclasses.
Implements a 2D circular obstacle (point obstacle plus radius)
Definition: obstacles.h:447
boost::shared_ptr< dynamic_reconfigure::Server< TebLocalPlannerReconfigureConfig > > dynamic_recfg_
Dynamic reconfigure server to allow config modifications at runtime.
bool isGoalReached()
Check if the goal pose has been achieved.
std::string map_frame
Global planning frame.
Definition: teb_config.h:66
void setRPY(const tf2Scalar &roll, const tf2Scalar &pitch, const tf2Scalar &yaw)
unsigned int getSizeInCellsX() const
TebVisualizationPtr visualization_
Instance of the visualization class (local/global plan, obstacles, ...)
TebLocalPlannerROS()
Default constructor of the teb plugin.
double max_vel_y
Maximum strafing velocity of the robot (should be zero for non-holonomic robots!) ...
Definition: teb_config.h:97
#define ROS_FATAL(...)
T & transform(const T &in, T &out, const std::string &target_frame, ros::Duration timeout=ros::Duration(0.0)) const
void finalizePolygon()
Call finalizePolygon after the polygon is created with the help of pushBackVertex() methods...
Definition: obstacles.h:1076
boost::mutex via_point_mutex_
Mutex that locks the via_points container (multi-threaded)
costmap_converter::ObstacleArrayMsg custom_obstacle_msg_
Copy of the most recent obstacle message.
TebConfig config
PoseSE2 robot_goal_
Store current robot goal.
Eigen::Vector2d orientationUnitVec() const
Return the unit vector of the current orientation.
Definition: pose_se2.h:215
bool initialized_
Keeps track about the correct initialization of this class.
double min_obstacle_dist
Minimum desired separation from obstacles.
Definition: teb_config.h:125
Quaternion getRotation() const
bool shrink_horizon_backup
Allows the planner to shrink the horizon temporary (50%) in case of automatically detected issues...
Definition: teb_config.h:216
unsigned int getSizeInCellsY() const
#define ROS_INFO_ONCE(...)
Eigen::Vector2d & position()
Access the 2D position part.
Definition: pose_se2.h:145
double robot_circumscribed_radius
The radius of the circumscribed circle of the robot.
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void updateViaPointsContainer(const std::vector< geometry_msgs::PoseStamped > &transformed_plan, double min_separation)
Update internal via-point container based on the current reference plan.
Config class for the teb_local_planner and its components.
Definition: teb_config.h:61
boost::shared_ptr< T > createInstance(const std::string &lookup_name)
boost::shared_ptr< base_local_planner::CostmapModel > costmap_model_
static double getYaw(const Quaternion &bt_q)
bool custom_via_points_active_
Keep track whether valid via-points have been received from via_points_sub_.
double theta_stopped_vel
Below what maximum rotation velocity we consider the robot to be stopped in rotation.
Definition: teb_config.h:118
double xy_goal_tolerance
Allowed final euclidean distance to the goal position.
Definition: teb_config.h:115
double global_plan_viapoint_sep
Min. separation between each two consecutive via-points extracted from the global plan (if negative: ...
Definition: teb_config.h:78
void customViaPointsCB(const nav_msgs::Path::ConstPtr &via_points_msg)
Callback for custom via-points.
pluginlib::ClassLoader< costmap_converter::BaseCostmapToPolygons > costmap_converter_loader_
Load costmap converter plugins at runtime.
std::string getGlobalFrameID()
void getOdom(nav_msgs::Odometry &base_odom)
struct teb_local_planner::TebConfig::Robot robot
Robot related parameters.
std::vector< geometry_msgs::Point > footprint_spec_
Store the footprint of the robot.
int no_infeasible_plans_
Store how many times in a row the planner failed to find a feasible plan.
tf2_ros::Buffer * tf_
Local planner that explores alternative homotopy classes, create a plan for each alternative and fina...
double yaw_goal_tolerance
Allowed final orientation error.
Definition: teb_config.h:114
bool isOscillating() const
Check if the robot got stucked.
double min_turning_radius
Minimum turning radius of a carlike robot (diff-drive robot: zero);.
Definition: teb_config.h:103
FailureDetector failure_detector_
Detect if the robot got stucked.
bool goal_reached_
store whether the goal is reached or not
double & x()
Access the x-coordinate the pose.
Definition: pose_se2.h:158
void setBufferLength(int length)
Set buffer length (measurement history)
std::vector< Eigen::Vector2d, Eigen::aligned_allocator< Eigen::Vector2d > > Point2dContainer
Abbrev. for a container storing 2d points.
ros::Time time_last_infeasible_plan_
Store at which time stamp the last infeasible plan was detected.
void doTransform(const T &data_in, T &data_out, const geometry_msgs::TransformStamped &transform)
struct teb_local_planner::TebConfig::Trajectory trajectory
Trajectory related parameters.
void updateObstacleContainerWithCostmapConverter()
Update internal obstacle vector based on polygons provided by a costmap_converter plugin...
ros::Subscriber custom_obst_sub_
Subscriber for custom obstacles received via a ObstacleMsg.
#define ROS_WARN(...)
Implements a polygon obstacle with an arbitrary number of vertices.
Definition: obstacles.h:898
Implements a 2D line obstacle.
Definition: obstacles.h:597
void initialize(std::string name, tf2_ros::Buffer *tf, costmap_2d::Costmap2DROS *costmap_ros)
Initializes the teb plugin.
bool stopped(const nav_msgs::Odometry &base_odom, const double &rot_stopped_velocity, const double &trans_stopped_velocity)
int feasibility_check_no_poses
Specify up to which pose on the predicted plan the feasibility should be checked each sampling interv...
Definition: teb_config.h:85
double costmap_obstacles_behind_robot_dist
Limit the occupied local costmap obstacles taken into account for planning behind the robot (specify ...
Definition: teb_config.h:130
struct teb_local_planner::TebConfig::GoalTolerance goal_tolerance
Goal tolerance related parameters.
bool computeVelocityCommands(geometry_msgs::Twist &cmd_vel)
Given the current position, orientation, and velocity of the robot, compute velocity commands to send...
boost::mutex custom_obst_mutex_
Mutex that locks the obstacle array (multi-threaded)
boost::shared_ptr< Obstacle > ObstaclePtr
Abbrev. for shared obstacle pointers.
Definition: obstacles.h:293
std::vector< geometry_msgs::PoseStamped > global_plan_
Store the current global plan.
void update(const geometry_msgs::Twist &twist, double v_max, double v_backwards_max, double omega_max, double v_eps, double omega_eps)
Add a new twist measurement to the internal buffer and compute a new decision.
void reconfigure(TebLocalPlannerReconfigureConfig &cfg)
Reconfigure parameters from the dynamic_reconfigure config. Change parameters dynamically (e...
Definition: teb_config.cpp:184
bool param(const std::string &param_name, T &param_val, const T &default_val) const
Type const & getType() const
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
void updateObstacleContainerWithCustomObstacles()
Update internal obstacle vector based on custom messages received via subscriber. ...
RotType
Symbols for left/none/right rotations.
Definition: misc.h:53
costmap_2d::Costmap2DROS * costmap_ros_
Pointer to the costmap ros wrapper, received from the navigation stack.
#define ROS_WARN_THROTTLE(period,...)
void loadRosParamFromNodeHandle(const ros::NodeHandle &nh)
Load parmeters from the ros param server.
Definition: teb_config.cpp:44
RotType last_preferred_rotdir_
Store recent preferred turning direction.
#define ROS_WARN_ONCE(...)
struct teb_local_planner::TebConfig::HomotopyClasses hcp
#define ROS_INFO(...)
bool getParam(const std::string &key, std::string &s) const
std::string odom_topic
Topic name of the odometry message, provided by the robot driver or simulator.
Definition: teb_config.h:65
~TebLocalPlannerROS()
Destructor of the plugin.
boost::shared_ptr< TebVisualization > TebVisualizationPtr
Abbrev. for shared instances of the TebVisualization.
Implements a 2D point obstacle.
Definition: obstacles.h:305
bool pruneGlobalPlan(const tf2_ros::Buffer &tf, const geometry_msgs::PoseStamped &global_pose, std::vector< geometry_msgs::PoseStamped > &global_plan, double dist_behind_robot=1)
Prune global plan such that already passed poses are cut off.
int control_look_ahead_poses
Min angular resolution used during the costmap collision check. If not respected, intermediate sample...
Definition: teb_config.h:88
std::string getBaseFrameID()
double trans_stopped_vel
Below what maximum velocity we consider the robot to be stopped in translation.
Definition: teb_config.h:117
TebConfig cfg_
Config class that stores and manages all related parameters.
static Point2dContainer makeFootprintFromXMLRPC(XmlRpc::XmlRpcValue &footprint_xmlrpc, const std::string &full_param_name)
Set the footprint from the given XmlRpcValue.
bool enable_homotopy_class_planning
Activate homotopy class planning (Requires much more resources that simple planning, since multiple trajectories are optimized at once).
Definition: teb_config.h:181
bool getRobotPose(geometry_msgs::PoseStamped &global_pose) const
geometry_msgs::Twist last_cmd_
Store the last control command generated in computeVelocityCommands()
bool free_goal_vel
Allow the robot&#39;s velocity to be nonzero (usally max_vel) for planning purposes.
Definition: teb_config.h:116
void pushBackVertex(const Eigen::Ref< const Eigen::Vector2d > &vertex)
Add a vertex to the polygon (edge-point)
Definition: obstacles.h:1054
double max_vel_theta
Maximum angular velocity of the robot.
Definition: teb_config.h:99
static Eigen::Vector2d tfPoseToEigenVector2dTransRot(const tf::Pose &tf_vel)
Transform a tf::Pose type into a Eigen::Vector2d containing the translational and angular velocities...
double oscillation_v_eps
Threshold for the average normalized linear velocity: if oscillation_v_eps and oscillation_omega_eps ...
Definition: teb_config.h:219
static double getNumberFromXMLRPC(XmlRpc::XmlRpcValue &value, const std::string &full_param_name)
Get a number from the given XmlRpcValue.
#define ROS_WARN_STREAM(args)
virtual std::string getName(const std::string &lookup_name)
TFSIMD_FORCE_INLINE Vector3 & getOrigin()
bool hasParam(const std::string &key) const
double oscillation_filter_duration
Filter length/duration [sec] for the detection of oscillations.
Definition: teb_config.h:222
double getYaw(const A &a)
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
void configureBackupModes(std::vector< geometry_msgs::PoseStamped > &transformed_plan, int &goal_idx)
PoseSE2 robot_pose_
Store current robot pose.
base_local_planner::OdometryHelperRos odom_helper_
Provides an interface to receive the current velocity from the robot.
double shrink_horizon_min_duration
Specify minimum duration for the reduced horizon in case an infeasible trajectory is detected...
Definition: teb_config.h:217
costmap_2d::Costmap2D * costmap_
Pointer to the 2d costmap (obtained from the costmap ros wrapper)
Implements both nav_core::BaseLocalPlanner and mbf_costmap_core::CostmapController abstract interface...
bool costmap_converter_spin_thread
If true, the costmap converter invokes its callback queue in a different thread.
Definition: teb_config.h:136
#define ROS_INFO_STREAM(args)
void customObstacleCB(const costmap_converter::ObstacleArrayMsg::ConstPtr &obst_msg)
Callback for custom obstacles that are not obtained from the costmap.
static const unsigned char LETHAL_OBSTACLE
const std::string & getNamespace() const
std::string robot_base_frame_
Used as the base frame id of the robot.
double average_angles(const std::vector< double > &angles)
Return the average angle of an arbitrary number of given angles [rad].
Definition: misc.h:72
std::string costmap_converter_plugin
Define a plugin name of the costmap_converter package (costmap cells are converted to points/lines/po...
Definition: teb_config.h:135
void saturateVelocity(double &vx, double &vy, double &omega, double max_vel_x, double max_vel_y, double max_vel_trans, double max_vel_theta, double max_vel_x_backwards) const
Saturate the translational and angular velocity to given limits.
tf2_ros::Buffer * tf_
pointer to tf buffer
double estimateLocalGoalOrientation(const std::vector< geometry_msgs::PoseStamped > &global_plan, const geometry_msgs::PoseStamped &local_goal, int current_goal_idx, const geometry_msgs::TransformStamped &tf_plan_to_global, int moving_average_length=3) const
Estimate the orientation of a pose from the global_plan that is treated as a local goal for the local...
void validateFootprints(double opt_inscribed_radius, double costmap_inscribed_radius, double min_obst_dist)
Validate current parameter values of the footprint for optimization, obstacle distance and the costma...
ros::Subscriber via_points_sub_
Subscriber for custom via-points received via a Path msg.
void updateObstacleContainerWithCostmap()
Update internal obstacle vector based on occupied costmap cells.
static RobotFootprintModelPtr getRobotFootprintFromParamServer(const ros::NodeHandle &nh, const TebConfig &config)
Get the current robot footprint/contour model.
#define ROS_WARN_COND(cond,...)
This class optimizes an internal Timed Elastic Band trajectory using the g2o-framework.
#define ROS_INFO_COND(cond,...)
bool global_plan_overwrite_orientation
Overwrite orientation of local subgoals provided by the global planner.
Definition: teb_config.h:76
struct teb_local_planner::TebConfig::Recovery recovery
Parameters related to recovery and backup strategies.
static Time now()
double robot_inscribed_radius_
The radius of the inscribed circle of the robot (collision possible)
void convert(const A &a, B &b)
boost::shared_ptr< costmap_converter::BaseCostmapToPolygons > costmap_converter_
Store the current costmap_converter.
double getResolution() const
int costmap_converter_rate
The rate that defines how often the costmap_converter plugin processes the current costmap (the value...
Definition: teb_config.h:137
void setOdomTopic(std::string odom_topic)
double global_plan_prune_distance
Distance between robot and via_points of global plan which is used for pruning.
Definition: teb_config.h:81
PlannerInterfacePtr planner_
Instance of the underlying optimal planner class.
double max_vel_x_backwards
Maximum translational velocity of the robot for driving backwards.
Definition: teb_config.h:96
double convertTransRotVelToSteeringAngle(double v, double omega, double wheelbase, double min_turning_radius=0) const
Convert translational and rotational velocities to a steering angle of a carlike robot.
std::string global_frame_
The frame in which the controller will run.
double oscillation_recovery_min_duration
Minumum duration [sec] for which the recovery mode is activated after an oscillation is detected...
Definition: teb_config.h:221
ros::Time time_last_oscillation_
Store at which time stamp the last oscillation was detected.
void getRobotVel(geometry_msgs::PoseStamped &robot_vel)
Costmap2D * getCostmap()
ObstContainer obstacles_
Obstacle vector that should be considered during local trajectory optimization.
#define ROS_ERROR_STREAM(args)
double distance_points2d(const P1 &point1, const P2 &point2)
Calculate Euclidean distance between two 2D point datatypes.
Definition: misc.h:107
std::vector< geometry_msgs::Point > getRobotFootprint()
bool setPlan(const std::vector< geometry_msgs::PoseStamped > &orig_global_plan)
Set the plan that the teb local planner is following.
ViaPointContainer via_points_
Container of via-points that should be considered during local trajectory optimization.
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
double oscillation_omega_eps
Threshold for the average normalized angular velocity: if oscillation_v_eps and oscillation_omega_eps...
Definition: teb_config.h:220
#define ROS_ERROR(...)
double wheelbase
The distance between the drive shaft and steering axle (only required for a carlike robot with &#39;cmd_a...
Definition: teb_config.h:104
void mapToWorld(unsigned int mx, unsigned int my, double &wx, double &wy) const
double max_global_plan_lookahead_dist
Specify maximum length (cumulative Euclidean distances) of the subset of the global plan taken into a...
Definition: teb_config.h:80
bool transformGlobalPlan(const tf2_ros::Buffer &tf, const std::vector< geometry_msgs::PoseStamped > &global_plan, const geometry_msgs::PoseStamped &global_pose, const costmap_2d::Costmap2D &costmap, const std::string &global_frame, double max_plan_length, std::vector< geometry_msgs::PoseStamped > &transformed_plan, int *current_goal_idx=NULL, geometry_msgs::TransformStamped *tf_plan_to_global=NULL) const
Transforms the global plan of the robot from the planner frame to the local frame (modified)...
geometry_msgs::Twist robot_vel_
Store current robot translational and angular velocity (vx, vy, omega)
bool oscillation_recovery
Try to detect and resolve oscillations between multiple solutions in the same equivalence class (robo...
Definition: teb_config.h:218
struct teb_local_planner::TebConfig::Obstacles obstacles
Obstacle related parameters.
unsigned char getCost(unsigned int mx, unsigned int my) const
double max_vel_trans
Maximum translational velocity of the robot for omni robots, which is different from max_vel_x...
Definition: teb_config.h:98
Eigen::Isometry3d transformToEigen(const geometry_msgs::Transform &t)
boost::mutex & configMutex()
Return the internal config mutex.
Definition: teb_config.h:419
double max_vel_x
Maximum translational velocity of the robot.
Definition: teb_config.h:95
void reconfigureCB(TebLocalPlannerReconfigureConfig &config, uint32_t level)
Callback for the dynamic_reconfigure node.
void calculateMinAndMaxDistances(const std::vector< geometry_msgs::Point > &footprint, double &min_dist, double &max_dist)
def sign(number)
std::string name_
For use with the ros nodehandle.
#define ROS_DEBUG(...)


teb_local_planner
Author(s): Christoph Rösmann
autogenerated on Wed Jun 1 2022 02:26:31