homotopy_class_planner.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2016,
00006  *  TU Dortmund - Institute of Control Theory and Systems Engineering.
00007  *  All rights reserved.
00008  *
00009  *  Redistribution and use in source and binary forms, with or without
00010  *  modification, are permitted provided that the following conditions
00011  *  are met:
00012  *
00013  *   * Redistributions of source code must retain the above copyright
00014  *     notice, this list of conditions and the following disclaimer.
00015  *   * Redistributions in binary form must reproduce the above
00016  *     copyright notice, this list of conditions and the following
00017  *     disclaimer in the documentation and/or other materials provided
00018  *     with the distribution.
00019  *   * Neither the name of the institute nor the names of its
00020  *     contributors may be used to endorse or promote products derived
00021  *     from this software without specific prior written permission.
00022  *
00023  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  *  POSSIBILITY OF SUCH DAMAGE.
00035  *
00036  * Author: Christoph Rösmann
00037  *********************************************************************/
00038 
00039 #include <teb_local_planner/homotopy_class_planner.h>
00040 
00041 namespace teb_local_planner
00042 {
00043   
00045 inline std::complex<long double> getCplxFromVertexPosePtr(const VertexPose* pose)
00046 {
00047   return std::complex<long double>(pose->x(), pose->y());
00048 };
00049 
00051 inline std::complex<long double> getCplxFromHcGraph(HcGraphVertexType vert_descriptor, const HcGraph& graph)
00052 {
00053   return std::complex<long double>(graph[vert_descriptor].pos.x(), graph[vert_descriptor].pos.y());
00054 };
00055   
00057 inline const Eigen::Vector2d& getVector2dFromHcGraph(HcGraphVertexType vert_descriptor, const HcGraph& graph)
00058 {
00059   return graph[vert_descriptor].pos;
00060 };
00061   
00063 inline std::complex<long double> getCplxFromMsgPoseStamped(const geometry_msgs::PoseStamped& pose)
00064 {
00065   return std::complex<long double>(pose.pose.position.x, pose.pose.position.y);
00066 };
00067 
00068 
00069 HomotopyClassPlanner::HomotopyClassPlanner() : cfg_(NULL), obstacles_(NULL), via_points_(NULL), robot_model_(new PointRobotFootprint()), initial_plan_(NULL), initialized_(false)
00070 {
00071 }
00072   
00073 HomotopyClassPlanner::HomotopyClassPlanner(const TebConfig& cfg, ObstContainer* obstacles, RobotFootprintModelPtr robot_model,
00074                                            TebVisualizationPtr visual, const ViaPointContainer* via_points) : initial_plan_(NULL)
00075 {
00076   initialize(cfg, obstacles, robot_model, visual, via_points);
00077 }
00078 
00079 HomotopyClassPlanner::~HomotopyClassPlanner()
00080 {
00081 }
00082 
00083 void HomotopyClassPlanner::initialize(const TebConfig& cfg, ObstContainer* obstacles, RobotFootprintModelPtr robot_model,
00084                                       TebVisualizationPtr visual, const ViaPointContainer* via_points)
00085 {
00086   cfg_ = &cfg;
00087   obstacles_ = obstacles;
00088   via_points_ = via_points;
00089   robot_model_ = robot_model;
00090   initialized_ = true;
00091   
00092   setVisualization(visual);
00093 }
00094 
00095 
00096 void HomotopyClassPlanner::setVisualization(TebVisualizationPtr visualization)
00097 {
00098   visualization_ = visualization;
00099 }
00100 
00101 
00102  
00103 bool HomotopyClassPlanner::plan(const std::vector<geometry_msgs::PoseStamped>& initial_plan, const geometry_msgs::Twist* start_vel, bool free_goal_vel)
00104 {    
00105   ROS_ASSERT_MSG(initialized_, "Call initialize() first.");
00106   
00107   // store initial plan for further initializations (must be valid for the lifetime of this object or clearPlanner() is called!)
00108   initial_plan_ = &initial_plan;
00109   // store the h signature of the initial plan to enable searching a matching teb later.
00110   initial_plan_eq_class_ = calculateEquivalenceClass(initial_plan.begin(), initial_plan.end(), getCplxFromMsgPoseStamped, obstacles_);
00111     
00112   PoseSE2 start(initial_plan.front().pose);
00113   PoseSE2 goal(initial_plan.back().pose);
00114 
00115   return plan(start, goal, start_vel, free_goal_vel);
00116 }
00117 
00118 
00119 bool HomotopyClassPlanner::plan(const tf::Pose& start, const tf::Pose& goal, const geometry_msgs::Twist* start_vel, bool free_goal_vel)
00120 {
00121   ROS_ASSERT_MSG(initialized_, "Call initialize() first.");
00122   PoseSE2 start_pose(start);
00123   PoseSE2 goal_pose(goal);
00124   return plan(start_pose, goal_pose, start_vel, free_goal_vel);
00125 }
00126 
00127 bool HomotopyClassPlanner::plan(const PoseSE2& start, const PoseSE2& goal, const geometry_msgs::Twist* start_vel, bool free_goal_vel)
00128 {       
00129   ROS_ASSERT_MSG(initialized_, "Call initialize() first.");
00130   
00131   // Update old TEBs with new start, goal and velocity
00132   updateAllTEBs(&start, &goal, start_vel);
00133     
00134   // Init new TEBs based on newly explored homotopy classes
00135   exploreEquivalenceClassesAndInitTebs(start, goal, cfg_->obstacles.min_obstacle_dist, start_vel);
00136   // update via-points if activated
00137   updateReferenceTrajectoryViaPoints(cfg_->hcp.viapoints_all_candidates);
00138   // Optimize all trajectories in alternative homotopy classes
00139   optimizeAllTEBs(cfg_->optim.no_inner_iterations, cfg_->optim.no_outer_iterations);
00140   // Delete any detours
00141   deleteTebDetours(-0.1); 
00142   // Select which candidate (based on alternative homotopy classes) should be used
00143   selectBestTeb();
00144 
00145   initial_plan_ = nullptr; // clear pointer to any previous initial plan (any previous plan is useless regarding the h-signature);
00146   return true;
00147 } 
00148  
00149 bool HomotopyClassPlanner::getVelocityCommand(double& vx, double& vy, double& omega) const
00150 {
00151   TebOptimalPlannerConstPtr best_teb = bestTeb();
00152   if (!best_teb)
00153   {
00154     vx = 0;
00155     vy = 0;
00156     omega = 0;
00157     return false;
00158   }
00159  
00160   return best_teb->getVelocityCommand(vx, vy, omega); 
00161 }
00162 
00163 
00164 
00165 
00166 void HomotopyClassPlanner::visualize()
00167 {
00168   if (visualization_)
00169   {
00170     // Visualize graph
00171     if (cfg_->hcp.visualize_hc_graph)
00172       visualization_->publishGraph(graph_);
00173         
00174     // Visualize active tebs as marker
00175     visualization_->publishTebContainer(tebs_);
00176     
00177     // Visualize best teb and feedback message if desired
00178     TebOptimalPlannerConstPtr best_teb = bestTeb();
00179     if (best_teb)
00180     {
00181       visualization_->publishLocalPlanAndPoses(best_teb->teb());
00182       
00183       if (best_teb->teb().sizePoses() > 0) //TODO maybe store current pose (start) within plan method as class field.
00184         visualization_->publishRobotFootprintModel(best_teb->teb().Pose(0), *robot_model_);
00185     
00186       // feedback message
00187       if (cfg_->trajectory.publish_feedback)
00188       {
00189         int best_idx = bestTebIdx();
00190         if (best_idx>=0)
00191           visualization_->publishFeedbackMessage(tebs_, (unsigned int) best_idx, *obstacles_);
00192       }
00193     }
00194   }
00195   else ROS_DEBUG("Ignoring HomotopyClassPlanner::visualize() call, since no visualization class was instantiated before.");
00196 }
00197 
00198 
00199 
00200 
00201 void HomotopyClassPlanner::createGraph(const PoseSE2& start, const PoseSE2& goal, double dist_to_obst, double obstacle_heading_threshold, const geometry_msgs::Twist* start_velocity)
00202 {
00203   // Clear existing graph and paths
00204   clearGraph();
00205   
00206   // Direction-vector between start and goal and normal-vector:
00207   Eigen::Vector2d diff = goal.position()-start.position();
00208   
00209   if (diff.norm()<cfg_->goal_tolerance.xy_goal_tolerance) 
00210   {
00211     ROS_DEBUG("HomotopyClassPlanner::createProbRoadmapGraph(): xy-goal-tolerance already reached.");
00212     if (tebs_.empty())
00213     {
00214       ROS_INFO("HomotopyClassPlanner::createProbRoadmapGraph(): Initializing a small straight line to just correct orientation errors.");
00215       addAndInitNewTeb(start, goal, start_velocity);
00216     }
00217     return;
00218   }
00219   
00220   Eigen::Vector2d normal(-diff[1],diff[0]); // normal-vector
00221   normal.normalize();
00222   normal = normal*dist_to_obst; // scale with obstacle_distance;
00223   
00224   // Insert Vertices
00225   HcGraphVertexType start_vtx = boost::add_vertex(graph_); // start vertex
00226   graph_[start_vtx].pos = start.position();
00227   diff.normalize();
00228   
00229   // store nearest obstacle keypoints -> only used if limit_obstacle_heading is enabled
00230   std::pair<HcGraphVertexType,HcGraphVertexType> nearest_obstacle; // both vertices are stored
00231   double min_dist = DBL_MAX;
00232   
00233   if (obstacles_!=NULL)
00234   {
00235     for (ObstContainer::const_iterator it_obst = obstacles_->begin(); it_obst != obstacles_->end(); ++it_obst)
00236     {
00237       // check if obstacle is placed in front of start point
00238       Eigen::Vector2d start2obst = (*it_obst)->getCentroid() - start.position();
00239       double dist = start2obst.norm();
00240       if (start2obst.dot(diff)/dist<0.1)
00241         continue;
00242       
00243       // Add Keypoints  
00244       HcGraphVertexType u = boost::add_vertex(graph_);
00245       graph_[u].pos = (*it_obst)->getCentroid() + normal;
00246       HcGraphVertexType v = boost::add_vertex(graph_);
00247       graph_[v].pos = (*it_obst)->getCentroid() - normal;
00248       
00249       // store nearest obstacle
00250       if (obstacle_heading_threshold && dist<min_dist)
00251       {
00252         min_dist = dist;
00253         nearest_obstacle.first = u;
00254         nearest_obstacle.second = v;
00255       }
00256     }   
00257   }
00258   
00259   HcGraphVertexType goal_vtx = boost::add_vertex(graph_); // goal vertex
00260   graph_[goal_vtx].pos = goal.position();
00261   
00262   // Insert Edges
00263   HcGraphVertexIterator it_i, end_i, it_j, end_j;
00264   for (boost::tie(it_i,end_i) = boost::vertices(graph_); it_i!=end_i-1; ++it_i) // ignore goal in this loop
00265   {
00266     for (boost::tie(it_j,end_j) = boost::vertices(graph_); it_j!=end_j; ++it_j) // check all forward connections
00267     {
00268       if (it_i==it_j) 
00269         continue;
00270       // TODO: make use of knowing in which order obstacles are inserted and that for each obstacle 2 vertices are added,
00271       // therefore we must only check one of them.
00272       Eigen::Vector2d distij = graph_[*it_j].pos-graph_[*it_i].pos;
00273       distij.normalize();
00274       // Check if the direction is backwards:
00275       if (distij.dot(diff)<=obstacle_heading_threshold)
00276         continue;
00277 
00278     
00279       // Check start angle to nearest obstacle 
00280       if (obstacle_heading_threshold && *it_i==start_vtx && min_dist!=DBL_MAX)
00281       {
00282         if (*it_j == nearest_obstacle.first || *it_j == nearest_obstacle.second)
00283         {
00284           Eigen::Vector2d keypoint_dist = graph_[*it_j].pos-start.position();
00285           keypoint_dist.normalize();
00286           Eigen::Vector2d start_orient_vec( cos(start.theta()), sin(start.theta()) ); // already normalized
00287           // check angle
00288           if (start_orient_vec.dot(keypoint_dist) <= obstacle_heading_threshold) 
00289           {
00290             ROS_DEBUG("createGraph() - deleted edge: limit_obstacle_heading");
00291             continue;
00292           }
00293         }
00294       }
00295 
00296       // Collision Check
00297       
00298       if (obstacles_!=NULL)
00299       {
00300         bool collision = false;
00301         for (ObstContainer::const_iterator it_obst = obstacles_->begin(); it_obst != obstacles_->end(); ++it_obst)
00302         {
00303           if ( (*it_obst)->checkLineIntersection(graph_[*it_i].pos,graph_[*it_j].pos, 0.5*dist_to_obst) ) 
00304           {
00305             collision = true;
00306             break;
00307           }
00308         }
00309         if (collision) 
00310           continue;
00311       }
00312       
00313       // Create Edge
00314       boost::add_edge(*it_i,*it_j,graph_);                      
00315     }
00316   }
00317   
00318    
00319   // Find all paths between start and goal!
00320   std::vector<HcGraphVertexType> visited;
00321   visited.push_back(start_vtx);
00322   DepthFirst(graph_,visited,goal_vtx, start.theta(), goal.theta(), start_velocity);
00323 }
00324 
00325 
00326 void HomotopyClassPlanner::createProbRoadmapGraph(const PoseSE2& start, const PoseSE2& goal, double dist_to_obst, int no_samples, double obstacle_heading_threshold, const geometry_msgs::Twist* start_velocity)
00327 {
00328   // Clear existing graph and paths
00329   clearGraph();
00330   
00331   // Direction-vector between start and goal and normal-vector:
00332   Eigen::Vector2d diff = goal.position()-start.position();
00333   double start_goal_dist = diff.norm();
00334   
00335   if (start_goal_dist<cfg_->goal_tolerance.xy_goal_tolerance) 
00336   {
00337     ROS_DEBUG("HomotopyClassPlanner::createProbRoadmapGraph(): xy-goal-tolerance already reached.");
00338     if (tebs_.empty())
00339     {
00340       ROS_INFO("HomotopyClassPlanner::createProbRoadmapGraph(): Initializing a small straight line to just correct orientation errors.");
00341       addAndInitNewTeb(start, goal, start_velocity);
00342     }
00343     return;
00344   }
00345   Eigen::Vector2d normal(-diff.coeffRef(1),diff.coeffRef(0)); // normal-vector
00346   normal.normalize();
00347 
00348   // Now sample vertices between start, goal and a specified width between both sides
00349   // Let's start with a square area between start and goal (maybe change it later to something like a circle or whatever)
00350   
00351   double area_width = cfg_->hcp.roadmap_graph_area_width;
00352     
00353   boost::random::uniform_real_distribution<double> distribution_x(0, start_goal_dist * cfg_->hcp.roadmap_graph_area_length_scale);  
00354   boost::random::uniform_real_distribution<double> distribution_y(0, area_width); 
00355   
00356   double phi = atan2(diff.coeffRef(1),diff.coeffRef(0)); // rotate area by this angle
00357   Eigen::Rotation2D<double> rot_phi(phi);
00358   
00359   Eigen::Vector2d area_origin;
00360   if (cfg_->hcp.roadmap_graph_area_length_scale != 1.0)
00361     area_origin = start.position() + 0.5*(1.0-cfg_->hcp.roadmap_graph_area_length_scale)*start_goal_dist*diff.normalized() - 0.5*area_width*normal; // bottom left corner of the origin
00362   else
00363     area_origin = start.position() - 0.5*area_width*normal; // bottom left corner of the origin
00364   
00365   // Insert Vertices
00366   HcGraphVertexType start_vtx = boost::add_vertex(graph_); // start vertex
00367   graph_[start_vtx].pos = start.position();
00368   diff.normalize(); // normalize in place
00369   
00370   
00371   // Start sampling
00372   for (int i=0; i < no_samples; ++i)
00373   {
00374     Eigen::Vector2d sample;
00375 //     bool coll_free;
00376 //     do // sample as long as a collision free sample is found
00377 //     {
00378       // Sample coordinates
00379       sample = area_origin + rot_phi*Eigen::Vector2d(distribution_x(rnd_generator_), distribution_y(rnd_generator_));
00380       
00381       // Test for collision
00382       // we do not care for collision checking here to improve efficiency, since we perform resampling repeatedly.
00383       // occupied vertices are ignored in the edge insertion state since they always violate the edge-obstacle collision check.
00384 //       coll_free = true;
00385 //       for (ObstContainer::const_iterator it_obst = obstacles_->begin(); it_obst != obstacles_->end(); ++it_obst)
00386 //       {
00387 //         if ( (*it_obst)->checkCollision(sample, dist_to_obst)) // TODO really keep dist_to_obst here?
00388 //         {
00389 //           coll_free = false;
00390 //           break;
00391 //         }
00392 //       }
00393 // 
00394 //     } while (!coll_free && ros::ok());
00395     
00396     // Add new vertex
00397     HcGraphVertexType v = boost::add_vertex(graph_);
00398     graph_[v].pos = sample;
00399   }
00400   
00401   // Now add goal vertex
00402   HcGraphVertexType goal_vtx = boost::add_vertex(graph_); // goal vertex
00403   graph_[goal_vtx].pos = goal.position();
00404   
00405   
00406   // Insert Edges
00407   HcGraphVertexIterator it_i, end_i, it_j, end_j;
00408   for (boost::tie(it_i,end_i) = boost::vertices(graph_); it_i!=boost::prior(end_i); ++it_i) // ignore goal in this loop
00409   {
00410     for (boost::tie(it_j,end_j) = boost::vertices(graph_); it_j!=end_j; ++it_j) // check all forward connections
00411     {
00412       if (it_i==it_j) // same vertex found
00413         continue;
00414 
00415       Eigen::Vector2d distij = graph_[*it_j].pos-graph_[*it_i].pos;
00416       distij.normalize(); // normalize in place
00417 
00418       // Check if the direction is backwards:
00419       if (distij.dot(diff)<=obstacle_heading_threshold) 
00420           continue; // diff is already normalized
00421       
00422 
00423       // Collision Check        
00424       bool collision = false;
00425       for (ObstContainer::const_iterator it_obst = obstacles_->begin(); it_obst != obstacles_->end(); ++it_obst)
00426       {
00427         if ( (*it_obst)->checkLineIntersection(graph_[*it_i].pos,graph_[*it_j].pos, dist_to_obst) )
00428         {
00429           collision = true;
00430           break;
00431         }
00432       }
00433       if (collision)
00434         continue;
00435       
00436       // Create Edge
00437       boost::add_edge(*it_i,*it_j,graph_);                      
00438     }
00439   }
00440   
00442   std::vector<HcGraphVertexType> visited;
00443   visited.push_back(start_vtx);
00444   DepthFirst(graph_,visited,goal_vtx, start.theta(), goal.theta(), start_velocity);
00445 }
00446 
00447 
00448 void HomotopyClassPlanner::DepthFirst(HcGraph& g, std::vector<HcGraphVertexType>& visited, const HcGraphVertexType& goal, double start_orientation, double goal_orientation, const geometry_msgs::Twist* start_velocity)
00449 {
00450   // see http://www.technical-recipes.com/2011/a-recursive-algorithm-to-find-all-paths-between-two-given-nodes/ for details on finding all simple paths
00451   
00452   if ((int)tebs_.size() >= cfg_->hcp.max_number_classes)
00453     return; // We do not need to search for further possible alternative homotopy classes.
00454   
00455   HcGraphVertexType back = visited.back();
00456 
00458   HcGraphAdjecencyIterator it, end;
00459   for ( boost::tie(it,end) = boost::adjacent_vertices(back,g); it!=end; ++it)
00460   {
00461     if ( std::find(visited.begin(), visited.end(), *it)!=visited.end() )
00462       continue; // already visited
00463 
00464     if ( *it == goal ) // goal reached
00465     {
00466       visited.push_back(*it);
00467       
00468       
00469       // check H-Signature
00470       EquivalenceClassPtr H = calculateEquivalenceClass(visited.begin(), visited.end(), boost::bind(getCplxFromHcGraph, _1, boost::cref(graph_)), obstacles_);
00471       
00472       // check if H-Signature is already known
00473       // and init new TEB if no duplicate was found
00474       if ( addEquivalenceClassIfNew(H) )
00475       {
00476         addAndInitNewTeb(visited.begin(), visited.end(), boost::bind(getVector2dFromHcGraph, _1, boost::cref(graph_)), start_orientation, goal_orientation, start_velocity);
00477       }
00478       
00479       visited.pop_back();
00480       break;
00481     }
00482 }
00483 
00485 for ( boost::tie(it,end) = boost::adjacent_vertices(back,g); it!=end; ++it)
00486 {
00487   if ( std::find(visited.begin(), visited.end(), *it)!=visited.end() || *it == goal)
00488     continue; // already visited || goal reached
00489   
00490   
00491   visited.push_back(*it);
00492   
00493   // recursion step
00494   DepthFirst(g, visited, goal, start_orientation, goal_orientation, start_velocity);
00495   
00496   visited.pop_back();
00497 }
00498 }
00499  
00500  
00501 bool HomotopyClassPlanner::hasEquivalenceClass(const EquivalenceClassPtr& eq_class) const
00502 {
00503   // iterate existing h-signatures and check if there is an existing H-Signature similar the candidate
00504   for (const std::pair<EquivalenceClassPtr, bool>& eqrel : equivalence_classes_)
00505   {
00506      if (eq_class->isEqual(*eqrel.first))
00507         return true; // Found! Homotopy class already exists, therefore nothing added  
00508   }
00509   return false;
00510 }
00511 
00512 bool HomotopyClassPlanner::addEquivalenceClassIfNew(const EquivalenceClassPtr& eq_class, bool lock)
00513 {         
00514   if (!eq_class->isValid())
00515   {
00516     ROS_WARN("HomotopyClassPlanner: Ignoring invalid H-signature");
00517     return false;
00518   }
00519   
00520   if (hasEquivalenceClass(eq_class))
00521     return false;
00522 
00523   // Homotopy class not found -> Add to class-list, return that the h-signature is new
00524   equivalence_classes_.push_back(std::make_pair(eq_class,lock));         
00525   return true;
00526 }
00527  
00528  
00529 void HomotopyClassPlanner::renewAndAnalyzeOldTebs(bool delete_detours)
00530 {
00531   // clear old h-signatures (since they could be changed due to new obstacle positions.
00532   equivalence_classes_.clear();
00533 
00534   // Collect h-signatures for all existing TEBs and store them together with the corresponding iterator / pointer:
00535 //   typedef std::list< std::pair<TebOptPlannerContainer::iterator, std::complex<long double> > > TebCandidateType;
00536 //   TebCandidateType teb_candidates;
00537 
00538   // get new homotopy classes and delete multiple TEBs per homotopy class
00539   TebOptPlannerContainer::iterator it_teb = tebs_.begin();
00540   while(it_teb != tebs_.end())
00541   {
00542     // delete Detours if there is at least one other TEB candidate left in the container
00543     if (delete_detours && tebs_.size()>1 && it_teb->get()->teb().detectDetoursBackwards(-0.1)) 
00544     {
00545       it_teb = tebs_.erase(it_teb); // delete candidate and set iterator to the next valid candidate
00546       continue;
00547     }
00548 
00549     // calculate equivalence class for the current candidate
00550     EquivalenceClassPtr equivalence_class = calculateEquivalenceClass(it_teb->get()->teb().poses().begin(), it_teb->get()->teb().poses().end(), getCplxFromVertexPosePtr ,obstacles_);
00551     
00552 //     teb_candidates.push_back(std::make_pair(it_teb,H));
00553     
00554     // WORKAROUND until the commented code below works
00555     // Here we do not compare cost values. Just first come first serve...
00556     bool new_flag = addEquivalenceClassIfNew(equivalence_class);
00557     if (!new_flag)
00558     {
00559       it_teb = tebs_.erase(it_teb);
00560       continue;
00561     }
00562     
00563     ++it_teb;
00564   }
00565 
00566   // Find multiple candidates and delete the one with higher cost 
00567   // TODO: this code needs to be adpated. Erasing tebs from the teb container_ could make iteratores stored in the candidate list invalid!
00568 //   TebCandidateType::reverse_iterator cand_i = teb_candidates.rbegin();
00569 //   int test_idx = 0;
00570 //   while (cand_i != teb_candidates.rend())
00571 //   {
00572 //          
00573 //     TebCandidateType::reverse_iterator cand_j = std::find_if(boost::next(cand_i),teb_candidates.rend(), boost::bind(compareH,_1,cand_i->second));
00574 //     if (cand_j != teb_candidates.rend() && cand_j != cand_i)
00575 //     {
00576 //         TebOptimalPlannerPtr pt1 = *(cand_j->first);
00577 //         TebOptimalPlannerPtr pt2 = *(cand_i->first);
00578 //         assert(pt1);
00579 //         assert(pt2);
00580 //       if ( cand_j->first->get()->getCurrentCost().sum() > cand_i->first->get()->getCurrentCost().sum() )
00581 //       {
00582 //      // found one that has higher cost, therefore erase cand_j
00583 //      tebs_.erase(cand_j->first);
00584 //      teb_candidates.erase(cand_j);         
00585 //       }
00586 //       else   // otherwise erase cand_i
00587 //       {
00588 //      tebs_.erase(cand_i->first);
00589 //      cand_i = teb_candidates.erase(cand_i);
00590 //       }
00591 //     }
00592 //     else 
00593 //     {
00594 //         ROS_WARN_STREAM("increase cand_i");
00595 //         ++cand_i;    
00596 //     }
00597 //   }
00598   
00599   // now add the h-signatures to the internal lookup-table (but only if there is no existing duplicate)
00600 //   for (TebCandidateType::iterator cand=teb_candidates.begin(); cand!=teb_candidates.end(); ++cand)
00601 //   {
00602 //     bool new_flag = addNewHSignatureIfNew(cand->second, cfg_->hcp.h_signature_threshold);
00603 //     if (!new_flag)
00604 //     {
00605 // //       ROS_ERROR_STREAM("getAndFilterHomotopyClassesTEB() - This schould not be happen.");
00606 //       tebs_.erase(cand->first);
00607 //     }
00608 //   }
00609         
00610 }
00611  
00612 void HomotopyClassPlanner::updateReferenceTrajectoryViaPoints(bool all_trajectories)
00613 {
00614   if ( (!all_trajectories && !initial_plan_) || !via_points_ || via_points_->empty() || cfg_->optim.weight_viapoint <= 0)
00615     return;
00616   
00617   if(equivalence_classes_.size() < tebs_.size())
00618   {
00619     ROS_ERROR("HomotopyClassPlanner::updateReferenceTrajectoryWithViaPoints(): Number of h-signatures does not match number of trajectories.");
00620     return;
00621   }
00622   
00623   if (all_trajectories)
00624   {
00625     // enable via-points for all tebs
00626     for (std::size_t i=0; i < equivalence_classes_.size(); ++i)
00627     {
00628         tebs_[i]->setViaPoints(via_points_);
00629     }
00630   }
00631   else
00632   {
00633     // enable via-points for teb in the same hommotopy class as the initial_plan and deactivate it for all other ones
00634     for (std::size_t i=0; i < equivalence_classes_.size(); ++i)
00635     {
00636       if(initial_plan_eq_class_->isEqual(*equivalence_classes_[i].first))
00637         tebs_[i]->setViaPoints(via_points_);
00638       else
00639         tebs_[i]->setViaPoints(NULL);
00640     }
00641   }
00642 }
00643  
00644  
00645 void HomotopyClassPlanner::exploreEquivalenceClassesAndInitTebs(const PoseSE2& start, const PoseSE2& goal, double dist_to_obst, const geometry_msgs::Twist* start_vel)
00646 {
00647   // first process old trajectories
00648   renewAndAnalyzeOldTebs(false);
00649 
00650   // inject initial plan if available and not yet captured
00651   if (initial_plan_ && addEquivalenceClassIfNew(initial_plan_eq_class_, true)) // also prevent candidate from deletion
00652     initial_plan_teb_ = addAndInitNewTeb(*initial_plan_, start_vel);
00653   else
00654   {
00655     initial_plan_teb_.reset();
00656     initial_plan_teb_ = getInitialPlanTEB(); // this method searches for initial_plan_eq_class_ in the teb container (-> if !initial_plan_teb_)
00657   }
00658     
00659   // now explore new homotopy classes and initialize tebs if new ones are found.
00660   if (cfg_->hcp.simple_exploration)
00661     createGraph(start,goal,dist_to_obst,cfg_->hcp.obstacle_heading_threshold, start_vel);
00662   else
00663     createProbRoadmapGraph(start,goal,dist_to_obst,cfg_->hcp.roadmap_graph_no_samples,cfg_->hcp.obstacle_heading_threshold, start_vel);
00664 } 
00665 
00666 
00667 TebOptimalPlannerPtr HomotopyClassPlanner::addAndInitNewTeb(const PoseSE2& start, const PoseSE2& goal, const geometry_msgs::Twist* start_velocity)
00668 {
00669   tebs_.push_back( TebOptimalPlannerPtr( new TebOptimalPlanner(*cfg_, obstacles_, robot_model_) ) );
00670   tebs_.back()->teb().initTEBtoGoal(start, goal, 0, cfg_->trajectory.dt_ref, cfg_->trajectory.min_samples, cfg_->trajectory.allow_init_with_backwards_motion);
00671   
00672   if (start_velocity)
00673     tebs_.back()->setVelocityStart(*start_velocity);
00674   
00675   return tebs_.back();
00676 }
00677 
00678 TebOptimalPlannerPtr HomotopyClassPlanner::addAndInitNewTeb(const std::vector<geometry_msgs::PoseStamped>& initial_plan, const geometry_msgs::Twist* start_velocity)
00679 {
00680   tebs_.push_back( TebOptimalPlannerPtr( new TebOptimalPlanner(*cfg_, obstacles_, robot_model_) ) );
00681   tebs_.back()->teb().initTEBtoGoal(*initial_plan_, cfg_->trajectory.dt_ref, true, cfg_->trajectory.min_samples, cfg_->trajectory.allow_init_with_backwards_motion); 
00682   
00683   if (start_velocity)
00684     tebs_.back()->setVelocityStart(*start_velocity);
00685   
00686   return tebs_.back();
00687 }
00688 
00689 void HomotopyClassPlanner::updateAllTEBs(const PoseSE2* start, const PoseSE2* goal, const geometry_msgs::Twist* start_velocity)
00690 {
00691   // If new goal is too far away, clear all existing trajectories to let them reinitialize later.
00692   // Since all Tebs are sharing the same fixed goal pose, just take the first candidate:
00693   if (!tebs_.empty() && (goal->position() - tebs_.front()->teb().BackPose().position()).norm() >= cfg_->trajectory.force_reinit_new_goal_dist)
00694   {
00695       ROS_DEBUG("New goal: distance to existing goal is higher than the specified threshold. Reinitalizing trajectories.");
00696       tebs_.clear();
00697       equivalence_classes_.clear();
00698   }  
00699   
00700   // hot-start from previous solutions
00701   for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
00702   {
00703     it_teb->get()->teb().updateAndPruneTEB(*start, *goal);
00704     if (start_velocity)
00705       it_teb->get()->setVelocityStart(*start_velocity);
00706   }
00707 }
00708 
00709  
00710 void HomotopyClassPlanner::optimizeAllTEBs(int iter_innerloop, int iter_outerloop)
00711 {
00712   // optimize TEBs in parallel since they are independend of each other
00713   if (cfg_->hcp.enable_multithreading)
00714   {
00715     boost::thread_group teb_threads;
00716     for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
00717     {
00718       teb_threads.create_thread( boost::bind(&TebOptimalPlanner::optimizeTEB, it_teb->get(), iter_innerloop, iter_outerloop,
00719                                              true, cfg_->hcp.selection_obst_cost_scale, cfg_->hcp.selection_viapoint_cost_scale,
00720                                              cfg_->hcp.selection_alternative_time_cost) );
00721     }
00722     teb_threads.join_all();
00723   }
00724   else
00725   {
00726     for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
00727     {
00728       it_teb->get()->optimizeTEB(iter_innerloop,iter_outerloop, true, cfg_->hcp.selection_obst_cost_scale, 
00729                                  cfg_->hcp.selection_viapoint_cost_scale, cfg_->hcp.selection_alternative_time_cost); // compute cost as well inside optimizeTEB (last argument = true)
00730     }
00731   }
00732 } 
00733  
00734 void HomotopyClassPlanner::deleteTebDetours(double threshold)
00735 {
00736   TebOptPlannerContainer::iterator it_teb = tebs_.begin();
00737   EquivalenceClassContainer::iterator it_eqclasses = equivalence_classes_.begin();
00738   
00739   if (tebs_.size() != equivalence_classes_.size())
00740   {
00741     ROS_ERROR("HomotopyClassPlanner::deleteTebDetours(): number of equivalence classes (%lu) and trajectories (%lu) does not match.", equivalence_classes_.size(), tebs_.size());
00742     return;   
00743   }
00744   
00745   bool modified;
00746   
00747   while(it_teb != tebs_.end())
00748   { 
00749     modified = false;
00750     
00751     if (!it_eqclasses->second) // check if equivalence class is locked
00752     {
00753       // delete Detours if other TEBs will remain!
00754       if (tebs_.size()>1 && it_teb->get()->teb().detectDetoursBackwards(threshold))
00755       {
00756         it_teb = tebs_.erase(it_teb); // 0.05
00757         it_eqclasses = equivalence_classes_.erase(it_eqclasses);
00758         modified = true;
00759       }
00760     }
00761     
00762     // Also delete tebs that cannot be optimized (last optim call failed)
00763     // here, we ignore the lock-state, since we cannot keep trajectories that are not optimizable
00764     if (!it_teb->get()->isOptimized())
00765     {
00766         it_teb = tebs_.erase(it_teb);
00767         it_eqclasses = equivalence_classes_.erase(it_eqclasses);
00768         modified = true;   
00769         ROS_DEBUG("HomotopyClassPlanner::deleteTebDetours(): removing candidate that was not optimized successfully");
00770     }  
00771     
00772     if (!modified)
00773     {
00774        ++it_teb;
00775        ++it_eqclasses;
00776     }
00777   }
00778 } 
00779  
00780 TebOptimalPlannerPtr HomotopyClassPlanner::getInitialPlanTEB()
00781 {
00782     // first check stored teb object
00783     if (initial_plan_teb_)
00784     {
00785         // check if the teb is still part of the teb container
00786         if ( std::find(tebs_.begin(), tebs_.end(), initial_plan_teb_ ) != tebs_.end() )
00787             return initial_plan_teb_;
00788         else
00789         {
00790             initial_plan_teb_.reset(); // reset pointer for next call
00791             ROS_DEBUG("initial teb not found, trying to find a match according to the cached equivalence class");
00792         }
00793     }
00794     
00795     // reset the locked state for equivalence classes // TODO: this might be adapted if not only the plan containing the initial plan is locked!
00796     for (int i=0; i<equivalence_classes_.size(); ++i)
00797     {
00798         equivalence_classes_[i].second = false;
00799     }
00800     
00801     // otherwise check if the stored reference equivalence class exist in the list of known classes
00802     if (initial_plan_eq_class_ && initial_plan_eq_class_->isValid())
00803     {
00804          if (equivalence_classes_.size() == tebs_.size())
00805          {
00806             for (int i=0; i<equivalence_classes_.size(); ++i)
00807             {
00808                 if (equivalence_classes_[i].first->isEqual(*initial_plan_eq_class_))
00809                 {
00810                     equivalence_classes_[i].second = true;
00811                     return tebs_[i];
00812                 }
00813             } 
00814          }
00815          else
00816              ROS_ERROR("HomotopyClassPlanner::getInitialPlanTEB(): number of equivalence classes (%lu) and number of trajectories (%lu) does not match.", equivalence_classes_.size(), tebs_.size());
00817     }
00818     else
00819         ROS_DEBUG("HomotopyClassPlanner::getInitialPlanTEB(): initial TEB not found in the set of available trajectories.");
00820     
00821     return TebOptimalPlannerPtr();
00822 }
00823  
00824 TebOptimalPlannerPtr HomotopyClassPlanner::selectBestTeb()
00825 {
00826     double min_cost = std::numeric_limits<double>::max(); // maximum cost
00827     double min_cost_last_best = std::numeric_limits<double>::max();
00828     double min_cost_initial_plan_teb = std::numeric_limits<double>::max();
00829     TebOptimalPlannerPtr last_best_teb;
00830     TebOptimalPlannerPtr initial_plan_teb = getInitialPlanTEB();
00831         
00832     // check if last best_teb is still a valid candidate
00833     if (std::find(tebs_.begin(), tebs_.end(), best_teb_) != tebs_.end())
00834     {
00835         // get cost of this candidate
00836         min_cost_last_best = best_teb_->getCurrentCost() * cfg_->hcp.selection_cost_hysteresis; // small hysteresis
00837         last_best_teb = best_teb_;
00838     }
00839     
00840     // check if last best_teb is still a valid candidate
00841     if (initial_plan_teb) // the validity was already checked in getInitialPlanTEB()
00842     {
00843         // get cost of this candidate
00844         min_cost_initial_plan_teb = initial_plan_teb->getCurrentCost() * cfg_->hcp.selection_prefer_initial_plan; // small hysteresis
00845     }
00846     
00847 
00848     best_teb_.reset(); // reset pointer
00849 
00850     for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
00851     {
00852         // check if the related TEB leaves the local costmap region
00853 //      if (tebs_.size()>1 && !(*it_teb)->teb().isTrajectoryInsideRegion(20, -1, 1))
00854 //      {
00855 //          ROS_INFO("HomotopyClassPlanner::selectBestTeb(): skipping trajectories that are not inside the local costmap");
00856 //          continue;
00857 //      }
00858 
00859         double teb_cost;
00860         
00861         if (*it_teb == last_best_teb)
00862             teb_cost = min_cost_last_best; // skip already known cost value of the last best_teb
00863         else if (*it_teb == initial_plan_teb)  
00864             teb_cost = min_cost_initial_plan_teb;
00865         else        
00866             teb_cost = it_teb->get()->getCurrentCost();
00867 
00868         if (teb_cost < min_cost)
00869         {
00870         // check if this candidate is currently not selected
00871         best_teb_ = *it_teb;
00872         min_cost = teb_cost;
00873         }
00874      }          
00875     
00876   
00877   // in case we haven't found any teb due to some previous checks, investigate list again
00878 //   if (!best_teb_ && !tebs_.empty())
00879 //   {
00880 //       ROS_DEBUG("all " << tebs_.size() << " tebs rejected previously");
00881 //       if (tebs_.size()==1)
00882 //         best_teb_ = tebs_.front();
00883 //       else // if multiple TEBs are available:
00884 //       {
00885 //           // try to use the one that relates to the initial plan
00886 //           TebOptimalPlannerPtr initial_plan_teb = getInitialPlanTEB();
00887 //           if (initial_plan_teb)
00888 //               best_teb_ = initial_plan_teb;
00889 //           else 
00890 //           {
00891 //              // now compute the cost for the rest (we haven't computed it before)
00892 //              for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
00893 //              {
00894 //                 double teb_cost = it_teb->get()->getCurrentCost();
00895 //                 if (teb_cost < min_cost)
00896 //                 {
00897 //                     // check if this candidate is currently not selected
00898 //                     best_teb_ = *it_teb;
00899 //                     min_cost = teb_cost;
00900 //                 }
00901 //              }
00902 //           }
00903 //       }
00904 //   }
00905   
00906     return best_teb_;
00907 } 
00908 
00909 int HomotopyClassPlanner::bestTebIdx() const
00910 {
00911   if (tebs_.size() == 1)
00912     return 0;
00913     
00914   if (!best_teb_)
00915     return -1;
00916   
00917   int idx = 0;
00918   for (TebOptPlannerContainer::const_iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb, ++idx)
00919   {
00920     if (*it_teb == best_teb_)
00921       return idx;
00922   }
00923   return -1;  
00924 }
00925 
00926 bool HomotopyClassPlanner::isTrajectoryFeasible(base_local_planner::CostmapModel* costmap_model, const std::vector<geometry_msgs::Point>& footprint_spec,
00927                                                 double inscribed_radius, double circumscribed_radius, int look_ahead_idx)
00928 {
00929   TebOptimalPlannerPtr best = bestTeb();
00930   if (!best)
00931     return false;
00932   
00933   return best->isTrajectoryFeasible(costmap_model,footprint_spec, inscribed_radius, circumscribed_radius, look_ahead_idx);
00934 }
00935 
00936 bool HomotopyClassPlanner::isHorizonReductionAppropriate(const std::vector<geometry_msgs::PoseStamped>& initial_plan) const
00937 {
00938   TebOptimalPlannerPtr best = bestTeb();
00939   if (!best)
00940     return false;
00941   
00942   return best->isHorizonReductionAppropriate(initial_plan);
00943 }
00944 
00945 void HomotopyClassPlanner::computeCurrentCost(std::vector<double>& cost, double obst_cost_scale, double viapoint_cost_scale, bool alternative_time_cost)
00946 {
00947   for (TebOptPlannerContainer::iterator it_teb = tebs_.begin(); it_teb != tebs_.end(); ++it_teb)
00948   {
00949     it_teb->get()->computeCurrentCost(cost, obst_cost_scale, viapoint_cost_scale, alternative_time_cost);
00950   } 
00951 }
00952  
00953  
00954 } // end namespace


teb_local_planner
Author(s): Christoph Rösmann
autogenerated on Sat Jun 8 2019 20:21:34