00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #include <teb_local_planner/teb_local_planner_ros.h>
00040
00041 #include <tf_conversions/tf_eigen.h>
00042
00043 #include <boost/algorithm/string.hpp>
00044
00045
00046 #include <pluginlib/class_list_macros.h>
00047
00048 #include "g2o/core/sparse_optimizer.h"
00049 #include "g2o/core/block_solver.h"
00050 #include "g2o/core/factory.h"
00051 #include "g2o/core/optimization_algorithm_gauss_newton.h"
00052 #include "g2o/core/optimization_algorithm_levenberg.h"
00053 #include "g2o/solvers/csparse/linear_solver_csparse.h"
00054 #include "g2o/solvers/cholmod/linear_solver_cholmod.h"
00055
00056
00057
00058 PLUGINLIB_DECLARE_CLASS(teb_local_planner, TebLocalPlannerROS, teb_local_planner::TebLocalPlannerROS, nav_core::BaseLocalPlanner)
00059
00060 namespace teb_local_planner
00061 {
00062
00063
00064 TebLocalPlannerROS::TebLocalPlannerROS() : costmap_ros_(NULL), tf_(NULL), costmap_model_(NULL),
00065 costmap_converter_loader_("costmap_converter", "costmap_converter::BaseCostmapToPolygons"),
00066 dynamic_recfg_(NULL), goal_reached_(false), horizon_reduced_(false), initialized_(false)
00067 {
00068 }
00069
00070
00071 TebLocalPlannerROS::~TebLocalPlannerROS()
00072 {
00073 }
00074
00075 void TebLocalPlannerROS::reconfigureCB(TebLocalPlannerReconfigureConfig& config, uint32_t level)
00076 {
00077 cfg_.reconfigure(config);
00078 }
00079
00080 void TebLocalPlannerROS::initialize(std::string name, tf::TransformListener* tf, costmap_2d::Costmap2DROS* costmap_ros)
00081 {
00082
00083 if(!initialized_)
00084 {
00085
00086 ros::NodeHandle nh("~/" + name);
00087
00088
00089 cfg_.loadRosParamFromNodeHandle(nh);
00090
00091
00092 obstacles_.reserve(500);
00093
00094
00095 visualization_ = TebVisualizationPtr(new TebVisualization(nh, cfg_));
00096
00097
00098 RobotFootprintModelPtr robot_model = getRobotFootprintFromParamServer(nh);
00099
00100
00101 if (cfg_.hcp.enable_homotopy_class_planning)
00102 {
00103 planner_ = PlannerInterfacePtr(new HomotopyClassPlanner(cfg_, &obstacles_, robot_model, visualization_, &via_points_));
00104 ROS_INFO("Parallel planning in distinctive topologies enabled.");
00105 }
00106 else
00107 {
00108 planner_ = PlannerInterfacePtr(new TebOptimalPlanner(cfg_, &obstacles_, robot_model, visualization_, &via_points_));
00109 ROS_INFO("Parallel planning in distinctive topologies disabled.");
00110 }
00111
00112
00113 tf_ = tf;
00114 costmap_ros_ = costmap_ros;
00115 costmap_ = costmap_ros_->getCostmap();
00116
00117 costmap_model_ = boost::make_shared<base_local_planner::CostmapModel>(*costmap_);
00118
00119 global_frame_ = costmap_ros_->getGlobalFrameID();
00120 cfg_.map_frame = global_frame_;
00121 robot_base_frame_ = costmap_ros_->getBaseFrameID();
00122
00123
00124 if (!cfg_.obstacles.costmap_converter_plugin.empty())
00125 {
00126 try
00127 {
00128 costmap_converter_ = costmap_converter_loader_.createInstance(cfg_.obstacles.costmap_converter_plugin);
00129 std::string converter_name = costmap_converter_loader_.getName(cfg_.obstacles.costmap_converter_plugin);
00130
00131 boost::replace_all(converter_name, "::", "/");
00132 costmap_converter_->initialize(ros::NodeHandle(nh, "costmap_converter/" + converter_name));
00133 costmap_converter_->setCostmap2D(costmap_);
00134
00135 costmap_converter_->startWorker(ros::Rate(cfg_.obstacles.costmap_converter_rate), costmap_, cfg_.obstacles.costmap_converter_spin_thread);
00136 ROS_INFO_STREAM("Costmap conversion plugin " << cfg_.obstacles.costmap_converter_plugin << " loaded.");
00137 }
00138 catch(pluginlib::PluginlibException& ex)
00139 {
00140 ROS_WARN("The specified costmap converter plugin cannot be loaded. All occupied costmap cells are treaten as point obstacles. Error message: %s", ex.what());
00141 costmap_converter_.reset();
00142 }
00143 }
00144 else
00145 ROS_INFO("No costmap conversion plugin specified. All occupied costmap cells are treaten as point obstacles.");
00146
00147
00148
00149 footprint_spec_ = costmap_ros_->getRobotFootprint();
00150 costmap_2d::calculateMinAndMaxDistances(footprint_spec_, robot_inscribed_radius_, robot_circumscribed_radius);
00151
00152
00153 odom_helper_.setOdomTopic(cfg_.odom_topic);
00154
00155
00156 dynamic_recfg_ = boost::make_shared< dynamic_reconfigure::Server<TebLocalPlannerReconfigureConfig> >(nh);
00157 dynamic_reconfigure::Server<TebLocalPlannerReconfigureConfig>::CallbackType cb = boost::bind(&TebLocalPlannerROS::reconfigureCB, this, _1, _2);
00158 dynamic_recfg_->setCallback(cb);
00159
00160
00161 custom_obst_sub_ = nh.subscribe("obstacles", 1, &TebLocalPlannerROS::customObstacleCB, this);
00162
00163
00164 initialized_ = true;
00165
00166 ROS_DEBUG("teb_local_planner plugin initialized.");
00167 }
00168 else
00169 {
00170 ROS_WARN("teb_local_planner has already been initialized, doing nothing.");
00171 }
00172 }
00173
00174
00175
00176 bool TebLocalPlannerROS::setPlan(const std::vector<geometry_msgs::PoseStamped>& orig_global_plan)
00177 {
00178
00179 if(!initialized_)
00180 {
00181 ROS_ERROR("teb_local_planner has not been initialized, please call initialize() before using this planner");
00182 return false;
00183 }
00184
00185
00186 global_plan_.clear();
00187 global_plan_ = orig_global_plan;
00188
00189
00190
00191
00192
00193 goal_reached_ = false;
00194
00195 return true;
00196 }
00197
00198
00199 bool TebLocalPlannerROS::computeVelocityCommands(geometry_msgs::Twist& cmd_vel)
00200 {
00201
00202 if(!initialized_)
00203 {
00204 ROS_ERROR("teb_local_planner has not been initialized, please call initialize() before using this planner");
00205 return false;
00206 }
00207
00208 cmd_vel.linear.x = 0;
00209 cmd_vel.angular.z = 0;
00210 goal_reached_ = false;
00211
00212
00213 tf::Stamped<tf::Pose> robot_pose;
00214 costmap_ros_->getRobotPose(robot_pose);
00215 robot_pose_ = PoseSE2(robot_pose);
00216
00217
00218 tf::Stamped<tf::Pose> robot_vel_tf;
00219 odom_helper_.getRobotVel(robot_vel_tf);
00220 robot_vel_ = tfPoseToEigenVector2dTransRot(robot_vel_tf);
00221 geometry_msgs::Twist robot_vel_twist;
00222 robot_vel_twist.linear.x = robot_vel_[0];
00223 robot_vel_twist.angular.z = robot_vel_[1];
00224
00225
00226 pruneGlobalPlan(*tf_, robot_pose, global_plan_);
00227
00228
00229 std::vector<geometry_msgs::PoseStamped> transformed_plan;
00230 int goal_idx;
00231 tf::StampedTransform tf_plan_to_global;
00232 if (!transformGlobalPlan(*tf_, global_plan_, robot_pose, *costmap_, global_frame_, cfg_.trajectory.max_global_plan_lookahead_dist,
00233 transformed_plan, &goal_idx, &tf_plan_to_global))
00234 {
00235 ROS_WARN("Could not transform the global plan to the frame of the controller");
00236 return false;
00237 }
00238
00239
00240 tf::Stamped<tf::Pose> global_goal;
00241 tf::poseStampedMsgToTF(global_plan_.back(), global_goal);
00242 global_goal.setData( tf_plan_to_global * global_goal );
00243 double dx = global_goal.getOrigin().getX() - robot_pose_.x();
00244 double dy = global_goal.getOrigin().getY() - robot_pose_.y();
00245 double delta_orient = g2o::normalize_theta( tf::getYaw(global_goal.getRotation()) - robot_pose_.theta() );
00246 if(fabs(std::sqrt(dx*dx+dy*dy)) < cfg_.goal_tolerance.xy_goal_tolerance
00247 && fabs(delta_orient) < cfg_.goal_tolerance.yaw_goal_tolerance)
00248 {
00249 goal_reached_ = true;
00250 return true;
00251 }
00252
00253
00254 if (horizon_reduced_)
00255 {
00256
00257 int horizon_reduction = goal_idx/2;
00258
00259
00260
00261 int new_goal_idx_transformed_plan = int(transformed_plan.size()) - horizon_reduction - 1;
00262 goal_idx -= horizon_reduction;
00263 if (new_goal_idx_transformed_plan>0 && goal_idx >= 0)
00264 transformed_plan.erase(transformed_plan.begin()+new_goal_idx_transformed_plan, transformed_plan.end());
00265 else goal_idx += horizon_reduction;
00266 }
00267
00268
00269 if (transformed_plan.empty())
00270 {
00271 ROS_WARN("Transformed plan is empty. Cannot determine a local plan.");
00272 return false;
00273 }
00274
00275
00276 tf::Stamped<tf::Pose> goal_point;
00277 tf::poseStampedMsgToTF(transformed_plan.back(), goal_point);
00278 robot_goal_.x() = goal_point.getOrigin().getX();
00279 robot_goal_.y() = goal_point.getOrigin().getY();
00280 if (cfg_.trajectory.global_plan_overwrite_orientation)
00281 {
00282 robot_goal_.theta() = estimateLocalGoalOrientation(global_plan_, goal_point, goal_idx, tf_plan_to_global);
00283
00284 transformed_plan.back().pose.orientation = tf::createQuaternionMsgFromYaw(robot_goal_.theta());
00285 }
00286 else
00287 {
00288 robot_goal_.theta() = tf::getYaw(goal_point.getRotation());
00289 }
00290
00291
00292 if (transformed_plan.size()==1)
00293 {
00294 transformed_plan.insert(transformed_plan.begin(), geometry_msgs::PoseStamped());
00295 }
00296 tf::poseTFToMsg(robot_pose, transformed_plan.front().pose);
00297
00298
00299 obstacles_.clear();
00300
00301
00302 if (costmap_converter_)
00303 updateObstacleContainerWithCostmapConverter();
00304 else
00305 updateObstacleContainerWithCostmap();
00306
00307
00308 updateObstacleContainerWithCustomObstacles();
00309
00310
00311 updateViaPointsContainer(transformed_plan, cfg_.trajectory.global_plan_viapoint_sep);
00312
00313
00314 boost::mutex::scoped_lock cfg_lock(cfg_.configMutex());
00315
00316
00317
00318 bool success = planner_->plan(transformed_plan, &robot_vel_twist, cfg_.goal_tolerance.free_goal_vel);
00319 if (!success)
00320 {
00321 planner_->clearPlanner();
00322 ROS_WARN("teb_local_planner was not able to obtain a local plan for the current setting.");
00323 return false;
00324 }
00325
00326
00327 if (horizon_reduced_ && (ros::Time::now()-horizon_reduced_stamp_).toSec() >= 10 && !planner_->isHorizonReductionAppropriate(transformed_plan))
00328 {
00329 horizon_reduced_ = false;
00330 ROS_INFO("Switching back to full horizon length.");
00331 }
00332
00333
00334 bool feasible = planner_->isTrajectoryFeasible(costmap_model_.get(), footprint_spec_, robot_inscribed_radius_, robot_circumscribed_radius, cfg_.trajectory.feasibility_check_no_poses);
00335 if (!feasible)
00336 {
00337 cmd_vel.linear.x = 0;
00338 cmd_vel.angular.z = 0;
00339
00340 if (!horizon_reduced_ && cfg_.trajectory.shrink_horizon_backup && planner_->isHorizonReductionAppropriate(transformed_plan))
00341 {
00342 horizon_reduced_ = true;
00343 ROS_WARN("TebLocalPlannerROS: trajectory is not feasible, but the planner suggests a shorter horizon temporary. Complying with its wish for at least 10s...");
00344 horizon_reduced_stamp_ = ros::Time::now();
00345 return true;
00346 }
00347
00348 planner_->clearPlanner();
00349 ROS_WARN("TebLocalPlannerROS: trajectory is not feasible. Resetting planner...");
00350
00351 return false;
00352 }
00353
00354
00355 if (!planner_->getVelocityCommand(cmd_vel.linear.x, cmd_vel.angular.z))
00356 {
00357 planner_->clearPlanner();
00358 ROS_WARN("TebLocalPlannerROS: velocity command invalid. Resetting planner...");
00359 return false;
00360 }
00361
00362
00363 saturateVelocity(cmd_vel.linear.x, cmd_vel.angular.z, cfg_.robot.max_vel_x, cfg_.robot.max_vel_theta, cfg_.robot.max_vel_x_backwards);
00364
00365
00366
00367
00368 if (cfg_.robot.cmd_angle_instead_rotvel)
00369 {
00370 cmd_vel.angular.z = convertTransRotVelToSteeringAngle(cmd_vel.linear.x, cmd_vel.angular.z, cfg_.robot.wheelbase, 0.95*cfg_.robot.min_turning_radius);
00371 if (!std::isfinite(cmd_vel.angular.z))
00372 {
00373 cmd_vel.linear.x = cmd_vel.angular.z = 0;
00374 planner_->clearPlanner();
00375 ROS_WARN("TebLocalPlannerROS: Resulting steering angle is not finite. Resetting planner...");
00376 return false;
00377 }
00378 }
00379
00380
00381 planner_->visualize();
00382 visualization_->publishObstacles(obstacles_);
00383 visualization_->publishViaPoints(via_points_);
00384 visualization_->publishGlobalPlan(global_plan_);
00385 return true;
00386 }
00387
00388
00389 bool TebLocalPlannerROS::isGoalReached()
00390 {
00391 if (goal_reached_)
00392 {
00393 ROS_INFO("GOAL Reached!");
00394 planner_->clearPlanner();
00395 return true;
00396 }
00397 return false;
00398 }
00399
00400
00401
00402 void TebLocalPlannerROS::updateObstacleContainerWithCostmap()
00403 {
00404
00405 if (cfg_.obstacles.include_costmap_obstacles)
00406 {
00407 Eigen::Vector2d robot_orient = robot_pose_.orientationUnitVec();
00408
00409 for (unsigned int i=0; i<costmap_->getSizeInCellsX()-1; ++i)
00410 {
00411 for (unsigned int j=0; j<costmap_->getSizeInCellsY()-1; ++j)
00412 {
00413 if (costmap_->getCost(i,j) == costmap_2d::LETHAL_OBSTACLE)
00414 {
00415 Eigen::Vector2d obs;
00416 costmap_->mapToWorld(i,j,obs.coeffRef(0), obs.coeffRef(1));
00417
00418
00419 Eigen::Vector2d obs_dir = obs-robot_pose_.position();
00420 if ( obs_dir.dot(robot_orient) < 0 && obs_dir.norm() > cfg_.obstacles.costmap_obstacles_behind_robot_dist )
00421 continue;
00422
00423 obstacles_.push_back(ObstaclePtr(new PointObstacle(obs)));
00424 }
00425 }
00426 }
00427 }
00428 }
00429
00430 void TebLocalPlannerROS::updateObstacleContainerWithCostmapConverter()
00431 {
00432 if (!costmap_converter_)
00433 return;
00434
00435
00436 costmap_converter::PolygonContainerConstPtr polygons = costmap_converter_->getPolygons();
00437 if (!polygons)
00438 return;
00439
00440 for (std::size_t i=0; i<polygons->size(); ++i)
00441 {
00442 if (polygons->at(i).points.size()==1)
00443 {
00444 obstacles_.push_back(ObstaclePtr(new PointObstacle(polygons->at(i).points[0].x, polygons->at(i).points[0].y)));
00445 }
00446 else if (polygons->at(i).points.size()==2)
00447 {
00448 obstacles_.push_back(ObstaclePtr(new LineObstacle(polygons->at(i).points[0].x, polygons->at(i).points[0].y,
00449 polygons->at(i).points[1].x, polygons->at(i).points[1].y )));
00450 }
00451 else if (polygons->at(i).points.size()>2)
00452 {
00453 PolygonObstacle* polyobst = new PolygonObstacle;
00454 for (std::size_t j=0; j<polygons->at(i).points.size(); ++j)
00455 {
00456 polyobst->pushBackVertex(polygons->at(i).points[j].x, polygons->at(i).points[j].y);
00457 }
00458 polyobst->finalizePolygon();
00459 obstacles_.push_back(ObstaclePtr(polyobst));
00460 }
00461 }
00462 }
00463
00464
00465 void TebLocalPlannerROS::updateObstacleContainerWithCustomObstacles()
00466 {
00467
00468 boost::mutex::scoped_lock l(custom_obst_mutex_);
00469
00470 if (!custom_obstacle_msg_.obstacles.empty())
00471 {
00472
00473 Eigen::Affine3d obstacle_to_map_eig;
00474 try
00475 {
00476 tf::StampedTransform obstacle_to_map;
00477 tf_->waitForTransform(global_frame_, ros::Time(0),
00478 custom_obstacle_msg_.header.frame_id, ros::Time(0),
00479 custom_obstacle_msg_.header.frame_id, ros::Duration(0.5));
00480 tf_->lookupTransform(global_frame_, ros::Time(0),
00481 custom_obstacle_msg_.header.frame_id, ros::Time(0),
00482 custom_obstacle_msg_.header.frame_id, obstacle_to_map);
00483 tf::transformTFToEigen(obstacle_to_map, obstacle_to_map_eig);
00484 }
00485 catch (tf::TransformException ex)
00486 {
00487 ROS_ERROR("%s",ex.what());
00488 obstacle_to_map_eig.setIdentity();
00489 }
00490
00491 for (std::vector<geometry_msgs::PolygonStamped>::const_iterator obst_it = custom_obstacle_msg_.obstacles.begin(); obst_it != custom_obstacle_msg_.obstacles.end(); ++obst_it)
00492 {
00493 if (obst_it->polygon.points.size() == 1 )
00494 {
00495 Eigen::Vector3d pos( obst_it->polygon.points.front().x, obst_it->polygon.points.front().y, obst_it->polygon.points.front().z );
00496 obstacles_.push_back(ObstaclePtr(new PointObstacle( (obstacle_to_map_eig * pos).head(2) )));
00497 }
00498 else if (obst_it->polygon.points.size() == 2 )
00499 {
00500 Eigen::Vector3d line_start( obst_it->polygon.points.front().x, obst_it->polygon.points.front().y, obst_it->polygon.points.front().z );
00501 Eigen::Vector3d line_end( obst_it->polygon.points.back().x, obst_it->polygon.points.back().y, obst_it->polygon.points.back().z );
00502 obstacles_.push_back(ObstaclePtr(new LineObstacle( (obstacle_to_map_eig * line_start).head(2), (obstacle_to_map_eig * line_end).head(2) )));
00503 }
00504 else
00505 {
00506 PolygonObstacle* polyobst = new PolygonObstacle;
00507 for (int i=0; i<(int)obst_it->polygon.points.size(); ++i)
00508 {
00509 Eigen::Vector3d pos( obst_it->polygon.points[i].x, obst_it->polygon.points[i].y, obst_it->polygon.points[i].z );
00510 polyobst->pushBackVertex( (obstacle_to_map_eig * pos).head(2) );
00511 }
00512 polyobst->finalizePolygon();
00513 obstacles_.push_back(ObstaclePtr(polyobst));
00514 }
00515 }
00516 }
00517 }
00518
00519 void TebLocalPlannerROS::updateViaPointsContainer(const std::vector<geometry_msgs::PoseStamped>& transformed_plan, double min_separation)
00520 {
00521 via_points_.clear();
00522
00523 if (min_separation<0)
00524 return;
00525
00526 std::size_t prev_idx = 0;
00527 for (std::size_t i=1; i < transformed_plan.size(); ++i)
00528 {
00529
00530 if (distance_points2d( transformed_plan[prev_idx].pose.position, transformed_plan[i].pose.position ) < min_separation)
00531 continue;
00532
00533
00534 via_points_.push_back( Eigen::Vector2d( transformed_plan[i].pose.position.x, transformed_plan[i].pose.position.y ) );
00535 prev_idx = i;
00536 }
00537
00538 }
00539
00540 Eigen::Vector2d TebLocalPlannerROS::tfPoseToEigenVector2dTransRot(const tf::Pose& tf_vel)
00541 {
00542 Eigen::Vector2d vel;
00543 vel.coeffRef(0) = std::sqrt( tf_vel.getOrigin().getX() * tf_vel.getOrigin().getX() + tf_vel.getOrigin().getY() * tf_vel.getOrigin().getY() );
00544 vel.coeffRef(1) = tf::getYaw(tf_vel.getRotation());
00545 return vel;
00546 }
00547
00548
00549 bool TebLocalPlannerROS::pruneGlobalPlan(const tf::TransformListener& tf, const tf::Stamped<tf::Pose>& global_pose, std::vector<geometry_msgs::PoseStamped>& global_plan, double dist_behind_robot)
00550 {
00551 if (global_plan.empty())
00552 return true;
00553
00554 try
00555 {
00556
00557 tf::StampedTransform global_to_plan_transform;
00558 tf.lookupTransform(global_plan.front().header.frame_id, global_pose.frame_id_, ros::Time(0), global_to_plan_transform);
00559 tf::Stamped<tf::Pose> robot;
00560 robot.setData( global_to_plan_transform * global_pose );
00561
00562 double dist_thresh_sq = dist_behind_robot*dist_behind_robot;
00563
00564
00565 std::vector<geometry_msgs::PoseStamped>::iterator it = global_plan.begin();
00566 std::vector<geometry_msgs::PoseStamped>::iterator erase_end = it;
00567 while (it != global_plan.end())
00568 {
00569 double dx = robot.getOrigin().x() - it->pose.position.x;
00570 double dy = robot.getOrigin().y() - it->pose.position.y;
00571 double dist_sq = dx * dx + dy * dy;
00572 if (dist_sq < dist_thresh_sq)
00573 {
00574 erase_end = it;
00575 break;
00576 }
00577 ++it;
00578 }
00579 if (erase_end == global_plan.end())
00580 return false;
00581
00582 if (erase_end != global_plan.begin())
00583 global_plan.erase(global_plan.begin(), erase_end);
00584 }
00585 catch (const tf::TransformException& ex)
00586 {
00587 ROS_DEBUG("Cannot prune path since no transform is available: %s\n", ex.what());
00588 return false;
00589 }
00590 return true;
00591 }
00592
00593
00594 bool TebLocalPlannerROS::transformGlobalPlan(const tf::TransformListener& tf, const std::vector<geometry_msgs::PoseStamped>& global_plan,
00595 const tf::Stamped<tf::Pose>& global_pose, const costmap_2d::Costmap2D& costmap, const std::string& global_frame, double max_plan_length,
00596 std::vector<geometry_msgs::PoseStamped>& transformed_plan, int* current_goal_idx, tf::StampedTransform* tf_plan_to_global) const
00597 {
00598
00599
00600 const geometry_msgs::PoseStamped& plan_pose = global_plan[0];
00601
00602 transformed_plan.clear();
00603
00604 try
00605 {
00606 if (global_plan.empty())
00607 {
00608 ROS_ERROR("Received plan with zero length");
00609 *current_goal_idx = 0;
00610 return false;
00611 }
00612
00613
00614 tf::StampedTransform plan_to_global_transform;
00615 tf.waitForTransform(global_frame, ros::Time::now(),
00616 plan_pose.header.frame_id, plan_pose.header.stamp,
00617 plan_pose.header.frame_id, ros::Duration(0.5));
00618 tf.lookupTransform(global_frame, ros::Time(),
00619 plan_pose.header.frame_id, plan_pose.header.stamp,
00620 plan_pose.header.frame_id, plan_to_global_transform);
00621
00622
00623 tf::Stamped<tf::Pose> robot_pose;
00624 tf.transformPose(plan_pose.header.frame_id, global_pose, robot_pose);
00625
00626
00627 double dist_threshold = std::max(costmap.getSizeInCellsX() * costmap.getResolution() / 2.0,
00628 costmap.getSizeInCellsY() * costmap.getResolution() / 2.0);
00629 dist_threshold *= 0.85;
00630
00631
00632
00633 int i = 0;
00634 double sq_dist_threshold = dist_threshold * dist_threshold;
00635 double sq_dist = 1e10;
00636
00637
00638 while(i < (int)global_plan.size())
00639 {
00640 double x_diff = robot_pose.getOrigin().x() - global_plan[i].pose.position.x;
00641 double y_diff = robot_pose.getOrigin().y() - global_plan[i].pose.position.y;
00642 double new_sq_dist = x_diff * x_diff + y_diff * y_diff;
00643 if (new_sq_dist > sq_dist && sq_dist < sq_dist_threshold)
00644 {
00645 sq_dist = new_sq_dist;
00646 break;
00647 }
00648 sq_dist = new_sq_dist;
00649 ++i;
00650 }
00651
00652 tf::Stamped<tf::Pose> tf_pose;
00653 geometry_msgs::PoseStamped newer_pose;
00654
00655 double plan_length = 0;
00656
00657
00658 while(i < (int)global_plan.size() && sq_dist <= sq_dist_threshold && (max_plan_length<=0 || plan_length <= max_plan_length))
00659 {
00660 const geometry_msgs::PoseStamped& pose = global_plan[i];
00661 tf::poseStampedMsgToTF(pose, tf_pose);
00662 tf_pose.setData(plan_to_global_transform * tf_pose);
00663 tf_pose.stamp_ = plan_to_global_transform.stamp_;
00664 tf_pose.frame_id_ = global_frame;
00665 tf::poseStampedTFToMsg(tf_pose, newer_pose);
00666
00667 transformed_plan.push_back(newer_pose);
00668
00669 double x_diff = robot_pose.getOrigin().x() - global_plan[i].pose.position.x;
00670 double y_diff = robot_pose.getOrigin().y() - global_plan[i].pose.position.y;
00671 sq_dist = x_diff * x_diff + y_diff * y_diff;
00672
00673
00674 if (i>0 && max_plan_length>0)
00675 plan_length += distance_points2d(global_plan[i-1].pose.position, global_plan[i].pose.position);
00676
00677 ++i;
00678 }
00679
00680
00681
00682 if (transformed_plan.empty())
00683 {
00684 tf::poseStampedMsgToTF(global_plan.back(), tf_pose);
00685 tf_pose.setData(plan_to_global_transform * tf_pose);
00686 tf_pose.stamp_ = plan_to_global_transform.stamp_;
00687 tf_pose.frame_id_ = global_frame;
00688 tf::poseStampedTFToMsg(tf_pose, newer_pose);
00689
00690 transformed_plan.push_back(newer_pose);
00691
00692
00693 if (current_goal_idx) *current_goal_idx = int(global_plan.size())-1;
00694 }
00695 else
00696 {
00697
00698 if (current_goal_idx) *current_goal_idx = i-1;
00699 }
00700
00701
00702 if (tf_plan_to_global) *tf_plan_to_global = plan_to_global_transform;
00703 }
00704 catch(tf::LookupException& ex)
00705 {
00706 ROS_ERROR("No Transform available Error: %s\n", ex.what());
00707 return false;
00708 }
00709 catch(tf::ConnectivityException& ex)
00710 {
00711 ROS_ERROR("Connectivity Error: %s\n", ex.what());
00712 return false;
00713 }
00714 catch(tf::ExtrapolationException& ex)
00715 {
00716 ROS_ERROR("Extrapolation Error: %s\n", ex.what());
00717 if (global_plan.size() > 0)
00718 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());
00719
00720 return false;
00721 }
00722
00723 return true;
00724 }
00725
00726
00727
00728
00729 double TebLocalPlannerROS::estimateLocalGoalOrientation(const std::vector<geometry_msgs::PoseStamped>& global_plan, const tf::Stamped<tf::Pose>& local_goal,
00730 int current_goal_idx, const tf::StampedTransform& tf_plan_to_global, int moving_average_length) const
00731 {
00732 int n = (int)global_plan.size();
00733
00734
00735 if (current_goal_idx > n-moving_average_length-2)
00736 {
00737 if (current_goal_idx >= n-1)
00738 {
00739 return tf::getYaw(local_goal.getRotation());
00740 }
00741 else
00742 {
00743 tf::Quaternion global_orientation;
00744 tf::quaternionMsgToTF(global_plan.back().pose.orientation, global_orientation);
00745 return tf::getYaw(tf_plan_to_global.getRotation() * global_orientation );
00746 }
00747 }
00748
00749
00750 moving_average_length = std::min(moving_average_length, n-current_goal_idx-1 );
00751
00752 std::vector<double> candidates;
00753 tf::Stamped<tf::Pose> tf_pose_k = local_goal;
00754 tf::Stamped<tf::Pose> tf_pose_kp1;
00755
00756 int range_end = current_goal_idx + moving_average_length;
00757 for (int i = current_goal_idx; i < range_end; ++i)
00758 {
00759
00760 const geometry_msgs::PoseStamped& pose = global_plan.at(i+1);
00761 tf::poseStampedMsgToTF(pose, tf_pose_kp1);
00762 tf_pose_kp1.setData(tf_plan_to_global * tf_pose_kp1);
00763
00764
00765 candidates.push_back( std::atan2(tf_pose_kp1.getOrigin().getY() - tf_pose_k.getOrigin().getY(),
00766 tf_pose_kp1.getOrigin().getX() - tf_pose_k.getOrigin().getX() ) );
00767
00768 if (i<range_end-1)
00769 tf_pose_k = tf_pose_kp1;
00770 }
00771 return average_angles(candidates);
00772 }
00773
00774
00775 void TebLocalPlannerROS::saturateVelocity(double& v, double& omega, double max_vel_x, double max_vel_theta, double max_vel_x_backwards) const
00776 {
00777
00778 if (v > max_vel_x)
00779 v = max_vel_x;
00780
00781
00782 if (omega > max_vel_theta)
00783 omega = max_vel_theta;
00784 else if (omega < -max_vel_theta)
00785 omega = -max_vel_theta;
00786
00787
00788 if (max_vel_x_backwards<=0)
00789 {
00790 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.");
00791 }
00792 else if (v < -max_vel_x_backwards)
00793 v = -max_vel_x_backwards;
00794 }
00795
00796
00797 double TebLocalPlannerROS::convertTransRotVelToSteeringAngle(double v, double omega, double wheelbase, double min_turning_radius) const
00798 {
00799 if (omega==0 || v==0)
00800 return 0;
00801
00802 double radius = v/omega;
00803
00804 if (fabs(radius) < min_turning_radius)
00805 radius = double(g2o::sign(radius)) * min_turning_radius;
00806
00807 return std::atan(wheelbase / radius);
00808 }
00809
00810
00811 void TebLocalPlannerROS::customObstacleCB(const teb_local_planner::ObstacleMsg::ConstPtr& obst_msg)
00812 {
00813 boost::mutex::scoped_lock l(custom_obst_mutex_);
00814 custom_obstacle_msg_ = *obst_msg;
00815 }
00816
00817 RobotFootprintModelPtr TebLocalPlannerROS::getRobotFootprintFromParamServer(const ros::NodeHandle& nh)
00818 {
00819 std::string model_name;
00820 if (!nh.getParam("footprint_model/type", model_name))
00821 {
00822 ROS_INFO("No robot footprint model specified for trajectory optimization. Using point-shaped model.");
00823 return boost::make_shared<PointRobotFootprint>();
00824 }
00825
00826
00827 if (model_name.compare("point") == 0)
00828 {
00829 ROS_INFO("Footprint model 'point' loaded for trajectory optimization.");
00830 return boost::make_shared<PointRobotFootprint>();
00831 }
00832
00833
00834 if (model_name.compare("circular") == 0)
00835 {
00836
00837 double radius;
00838 if (!nh.getParam("footprint_model/radius", radius))
00839 {
00840 ROS_ERROR_STREAM("Footprint model 'circular' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
00841 << "/footprint_model/radius' does not exist. Using point-model instead.");
00842 return boost::make_shared<PointRobotFootprint>();
00843 }
00844 ROS_INFO_STREAM("Footprint model 'circular' (radius: " << radius <<"m) loaded for trajectory optimization.");
00845 return boost::make_shared<CircularRobotFootprint>(radius);
00846 }
00847
00848
00849 if (model_name.compare("line") == 0)
00850 {
00851
00852 if (!nh.hasParam("footprint_model/line_start") || !nh.hasParam("footprint_model/line_end"))
00853 {
00854 ROS_ERROR_STREAM("Footprint model 'line' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
00855 << "/footprint_model/line_start' and/or '.../line_end' do not exist. Using point-model instead.");
00856 return boost::make_shared<PointRobotFootprint>();
00857 }
00858
00859 std::vector<double> line_start, line_end;
00860 nh.getParam("footprint_model/line_start", line_start);
00861 nh.getParam("footprint_model/line_end", line_end);
00862 if (line_start.size() != 2 || line_end.size() != 2)
00863 {
00864 ROS_ERROR_STREAM("Footprint model 'line' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
00865 << "/footprint_model/line_start' and/or '.../line_end' do not contain x and y coordinates (2D). Using point-model instead.");
00866 return boost::make_shared<PointRobotFootprint>();
00867 }
00868
00869 ROS_INFO_STREAM("Footprint model 'line' (line_start: [" << line_start[0] << "," << line_start[1] <<"]m, line_end: ["
00870 << line_end[0] << "," << line_end[1] << "]m) loaded for trajectory optimization.");
00871 return boost::make_shared<LineRobotFootprint>(Eigen::Map<const Eigen::Vector2d>(line_start.data()), Eigen::Map<const Eigen::Vector2d>(line_end.data()));
00872 }
00873
00874
00875 if (model_name.compare("two_circles") == 0)
00876 {
00877
00878 if (!nh.hasParam("footprint_model/front_offset") || !nh.hasParam("footprint_model/front_radius")
00879 || !nh.hasParam("footprint_model/rear_offset") || !nh.hasParam("footprint_model/rear_radius"))
00880 {
00881 ROS_ERROR_STREAM("Footprint model 'two_circles' cannot be loaded for trajectory optimization, since params '" << nh.getNamespace()
00882 << "/footprint_model/front_offset', '.../front_radius', '.../rear_offset' and '.../rear_radius' do not exist. Using point-model instead.");
00883 return boost::make_shared<PointRobotFootprint>();
00884 }
00885 double front_offset, front_radius, rear_offset, rear_radius;
00886 nh.getParam("footprint_model/front_offset", front_offset);
00887 nh.getParam("footprint_model/front_radius", front_radius);
00888 nh.getParam("footprint_model/rear_offset", rear_offset);
00889 nh.getParam("footprint_model/rear_radius", rear_radius);
00890 ROS_INFO_STREAM("Footprint model 'two_circles' (front_offset: " << front_offset <<"m, front_radius: " << front_radius
00891 << "m, rear_offset: " << rear_offset << "m, rear_radius: " << rear_radius << "m) loaded for trajectory optimization.");
00892 return boost::make_shared<TwoCirclesRobotFootprint>(front_offset, front_radius, rear_offset, rear_radius);
00893 }
00894
00895
00896 if (model_name.compare("polygon") == 0)
00897 {
00898
00899
00900 XmlRpc::XmlRpcValue footprint_xmlrpc;
00901 if (!nh.getParam("footprint_model/vertices", footprint_xmlrpc) )
00902 {
00903 ROS_ERROR_STREAM("Footprint model 'polygon' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
00904 << "/footprint_model/vertices' does not exist. Using point-model instead.");
00905 return boost::make_shared<PointRobotFootprint>();
00906 }
00907
00908 if (footprint_xmlrpc.getType() == XmlRpc::XmlRpcValue::TypeArray)
00909 {
00910 try
00911 {
00912 Point2dContainer polygon = makeFootprintFromXMLRPC(footprint_xmlrpc, "/footprint_model/vertices");
00913 ROS_INFO_STREAM("Footprint model 'polygon' loaded for trajectory optimization.");
00914 return boost::make_shared<PolygonRobotFootprint>(polygon);
00915 }
00916 catch(const std::exception& ex)
00917 {
00918 ROS_ERROR_STREAM("Footprint model 'polygon' cannot be loaded for trajectory optimization: " << ex.what() << ". Using point-model instead.");
00919 return boost::make_shared<PointRobotFootprint>();
00920 }
00921 }
00922 else
00923 {
00924 ROS_ERROR_STREAM("Footprint model 'polygon' cannot be loaded for trajectory optimization, since param '" << nh.getNamespace()
00925 << "/footprint_model/vertices' does not define an array of coordinates. Using point-model instead.");
00926 return boost::make_shared<PointRobotFootprint>();
00927 }
00928
00929 }
00930
00931
00932 ROS_WARN_STREAM("Unknown robot footprint model specified with parameter '" << nh.getNamespace() << "/footprint_model/type'. Using point model instead.");
00933 return boost::make_shared<PointRobotFootprint>();
00934 }
00935
00936
00937
00938
00939 Point2dContainer TebLocalPlannerROS::makeFootprintFromXMLRPC(XmlRpc::XmlRpcValue& footprint_xmlrpc, const std::string& full_param_name)
00940 {
00941
00942 if (footprint_xmlrpc.getType() != XmlRpc::XmlRpcValue::TypeArray ||
00943 footprint_xmlrpc.size() < 3)
00944 {
00945 ROS_FATAL("The footprint must be specified as list of lists on the parameter server, %s was specified as %s",
00946 full_param_name.c_str(), std::string(footprint_xmlrpc).c_str());
00947 throw std::runtime_error("The footprint must be specified as list of lists on the parameter server with at least "
00948 "3 points eg: [[x1, y1], [x2, y2], ..., [xn, yn]]");
00949 }
00950
00951 Point2dContainer footprint;
00952 Eigen::Vector2d pt;
00953
00954 for (int i = 0; i < footprint_xmlrpc.size(); ++i)
00955 {
00956
00957 XmlRpc::XmlRpcValue point = footprint_xmlrpc[ i ];
00958 if (point.getType() != XmlRpc::XmlRpcValue::TypeArray ||
00959 point.size() != 2)
00960 {
00961 ROS_FATAL("The footprint (parameter %s) must be specified as list of lists on the parameter server eg: "
00962 "[[x1, y1], [x2, y2], ..., [xn, yn]], but this spec is not of that form.",
00963 full_param_name.c_str());
00964 throw std::runtime_error("The footprint must be specified as list of lists on the parameter server eg: "
00965 "[[x1, y1], [x2, y2], ..., [xn, yn]], but this spec is not of that form");
00966 }
00967
00968 pt.x() = getNumberFromXMLRPC(point[ 0 ], full_param_name);
00969 pt.y() = getNumberFromXMLRPC(point[ 1 ], full_param_name);
00970
00971 footprint.push_back(pt);
00972 }
00973 return footprint;
00974 }
00975
00976 double TebLocalPlannerROS::getNumberFromXMLRPC(XmlRpc::XmlRpcValue& value, const std::string& full_param_name)
00977 {
00978
00979 if (value.getType() != XmlRpc::XmlRpcValue::TypeInt &&
00980 value.getType() != XmlRpc::XmlRpcValue::TypeDouble)
00981 {
00982 std::string& value_string = value;
00983 ROS_FATAL("Values in the footprint specification (param %s) must be numbers. Found value %s.",
00984 full_param_name.c_str(), value_string.c_str());
00985 throw std::runtime_error("Values in the footprint specification must be numbers");
00986 }
00987 return value.getType() == XmlRpc::XmlRpcValue::TypeInt ? (int)(value) : (double)(value);
00988 }
00989
00990 }
00991
00992