hector_exploration_planner.cpp
Go to the documentation of this file.
00001 //=================================================================================================
00002 // Copyright (c) 2012, Mark Sollweck, Stefan Kohlbrecher, Florian Berz TU Darmstadt
00003 // All rights reserved.
00004 
00005 // Redistribution and use in source and binary forms, with or without
00006 // modification, are permitted provided that the following conditions are met:
00007 //     * Redistributions of source code must retain the above copyright
00008 //       notice, this list of conditions and the following disclaimer.
00009 //     * Redistributions in binary form must reproduce the above copyright
00010 //       notice, this list of conditions and the following disclaimer in the
00011 //       documentation and/or other materials provided with the distribution.
00012 //     * Neither the name of the Simulation, Systems Optimization and Robotics
00013 //       group, TU Darmstadt nor the names of its contributors may be used to
00014 //       endorse or promote products derived from this software without
00015 //       specific prior written permission.
00016 
00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00018 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00019 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00020 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
00021 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00022 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00024 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00026 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027 //=================================================================================================
00028 
00029 #include "hector_exploration_planner/hector_exploration_planner.h"
00030 
00031 #include <visualization_msgs/Marker.h>
00032 #include <visualization_msgs/MarkerArray.h>
00033 #include <hector_nav_msgs/GetRobotTrajectory.h>
00034 #include <Eigen/Geometry>
00035 
00036 #include <hector_exploration_planner/ExplorationPlannerConfig.h>
00037 
00038 #define STRAIGHT_COST 100
00039 #define DIAGONAL_COST 141
00040 
00041 //#define STRAIGHT_COST 3
00042 //#define DIAGONAL_COST 4
00043 
00044 using namespace hector_exploration_planner;
00045 
00046 HectorExplorationPlanner::HectorExplorationPlanner()
00047 : costmap_ros_(0)
00048 , occupancy_grid_array_(0)
00049 , exploration_trans_array_(0)
00050 , obstacle_trans_array_(0)
00051 , frontier_map_array_(0)
00052 , is_goal_array_(0)
00053 , initialized_(false)
00054 , map_width_(0)
00055 , map_height_(0)
00056 , num_map_cells_(0)
00057 {}
00058 
00059 HectorExplorationPlanner::~HectorExplorationPlanner(){
00060   this->deleteMapData();
00061 }
00062 
00063 HectorExplorationPlanner::HectorExplorationPlanner(std::string name, costmap_2d::Costmap2DROS *costmap_ros_in) :
00064 costmap_ros_(NULL), initialized_(false) {
00065   HectorExplorationPlanner::initialize(name, costmap_ros_in);
00066 }
00067 
00068 void HectorExplorationPlanner::initialize(std::string name, costmap_2d::Costmap2DROS *costmap_ros_in){
00069 
00070   last_mode_ = FRONTIER_EXPLORE;
00071   // unknown: 255, obstacle 254, inflated: 253, free: 0
00072 
00073   if(initialized_){
00074     ROS_ERROR("[hector_exploration_planner] HectorExplorationPlanner is already initialized_! Please check why initialize() got called twice.");
00075     return;
00076   }
00077 
00078   ROS_INFO("[hector_exploration_planner] Initializing HectorExplorationPlanner");
00079 
00080   // initialize costmaps
00081   this->costmap_ros_ = costmap_ros_in;
00082   this->setupMapData();
00083 
00084   // initialize parameters
00085   ros::NodeHandle private_nh_("~/" + name);
00086   ros::NodeHandle nh;
00087   visualization_pub_ = private_nh_.advertise<visualization_msgs::Marker>("visualization_marker", 0);
00088 
00089   dyn_rec_server_.reset(new dynamic_reconfigure::Server<hector_exploration_planner::ExplorationPlannerConfig>());
00090 
00091   dyn_rec_server_->setCallback(boost::bind(&HectorExplorationPlanner::dynRecParamCallback, this, _1, _2));
00092 
00093   path_service_client_ = nh.serviceClient<hector_nav_msgs::GetRobotTrajectory>("trajectory");
00094 
00095   ROS_INFO("[hector_exploration_planner] Parameter set. security_const: %f, min_obstacle_dist: %d, plan_in_unknown: %d, use_inflated_obstacle: %d, p_goal_angle_penalty_:%d , min_frontier_size: %d, p_dist_for_goal_reached_: %f, same_frontier: %f", p_alpha_, p_min_obstacle_dist_, p_plan_in_unknown_, p_use_inflated_obs_, p_goal_angle_penalty_, p_min_frontier_size_,p_dist_for_goal_reached_,p_same_frontier_dist_);
00096   //p_min_obstacle_dist_ = p_min_obstacle_dist_ * STRAIGHT_COST;
00097 
00098   this->name = name;
00099   this->initialized_ = true;
00100   this->previous_goal_ = -1;
00101 
00102   vis_.reset(new ExplorationTransformVis("exploration_transform"));
00103   inner_vis_.reset(new ExplorationTransformVis("inner_exploration_transform"));
00104 
00105 }
00106 
00107 void HectorExplorationPlanner::dynRecParamCallback(hector_exploration_planner::ExplorationPlannerConfig &config, uint32_t level)
00108 {
00109   if (p_plan_in_unknown_ != config.plan_in_unknown){
00110     p_plan_in_unknown_ = config.plan_in_unknown;
00111   }
00112 
00113   if (p_use_inflated_obs_ != config.use_inflated_obstacles){
00114     p_use_inflated_obs_ = config.use_inflated_obstacles;
00115   }
00116 
00117   if (p_goal_angle_penalty_ != config.goal_angle_penalty){
00118     p_goal_angle_penalty_ = config.goal_angle_penalty;
00119   }
00120 
00121   if (p_alpha_ != config.security_constant){
00122     p_alpha_ = config.security_constant;
00123   }
00124 
00125   if (p_dist_for_goal_reached_ != config.dist_for_goal_reached){
00126     p_dist_for_goal_reached_ = config.dist_for_goal_reached;
00127   }
00128 
00129   if (p_same_frontier_dist_ != config.same_frontier_distance){
00130     p_same_frontier_dist_ = config.same_frontier_distance;
00131   }
00132 
00133   if (p_min_frontier_size_ != config.min_frontier_size){
00134     p_min_frontier_size_ = config.min_frontier_size;
00135   }
00136 
00137   p_min_obstacle_dist_ = config.min_obstacle_dist * STRAIGHT_COST;
00138 
00139 }
00140 
00141 
00142 
00143 bool HectorExplorationPlanner::makePlan(const geometry_msgs::PoseStamped &start, const geometry_msgs::PoseStamped &original_goal, std::vector<geometry_msgs::PoseStamped> &plan){
00144 
00145   this->setupMapData();
00146 
00147   // do exploration? (not used anymore? -> call doExploration())
00148 
00149   if ((original_goal.pose.orientation.w == 0.0) && (original_goal.pose.orientation.x == 0.0) &&
00150   (original_goal.pose.orientation.y == 0.0) && (original_goal.pose.orientation.z == 0.0)){
00151       ROS_ERROR("Trying to plan with invalid quaternion, this shouldn't be done anymore, but we'll start exploration for now.");
00152       return doExploration(start,plan);
00153   }
00154 
00155 
00156   // planning
00157   ROS_INFO("[hector_exploration_planner] planning: starting to make a plan to given goal point");
00158 
00159   // setup maps and goals
00160   resetMaps();
00161   plan.clear();
00162 
00163   std::vector<geometry_msgs::PoseStamped> goals;
00164 
00165   // create obstacle tranform
00166   buildobstacle_trans_array_(p_use_inflated_obs_);
00167 
00168   geometry_msgs::PoseStamped adjusted_goal;
00169   if (!this->getObservationPose(original_goal, 0.5, adjusted_goal)){
00170       ROS_ERROR("getObservationPose returned false, no area around target point available to drive to!");
00171       return false;
00172   }
00173 
00174   // plan to given goal
00175   goals.push_back(adjusted_goal);
00176 
00177   // make plan
00178   if(!buildexploration_trans_array_(start,goals,true)){
00179     return false;
00180   }
00181   if(!getTrajectory(start,goals,plan)){
00182     return false;
00183   }
00184 
00185   // save and add last point
00186   plan.push_back(adjusted_goal);
00187   unsigned int mx,my;
00188   costmap_.worldToMap(adjusted_goal.pose.position.x,adjusted_goal.pose.position.y,mx,my);
00189   previous_goal_ = costmap_.getIndex(mx,my);
00190 
00191   ROS_INFO("[hector_exploration_planner] planning: plan has been found! plansize: %u ", (unsigned int)plan.size());
00192   return true;
00193 }
00194 
00195 bool HectorExplorationPlanner::doExploration(const geometry_msgs::PoseStamped &start, std::vector<geometry_msgs::PoseStamped> &plan){
00196 
00197   ROS_INFO("[hector_exploration_planner] exploration: starting exploration");
00198 
00199   this->setupMapData();
00200 
00201   // setup maps and goals
00202 
00203   resetMaps();
00204   clearFrontiers();
00205   plan.clear();
00206 
00207   std::vector<geometry_msgs::PoseStamped> goals;
00208 
00209   // create obstacle tranform
00210   buildobstacle_trans_array_(p_use_inflated_obs_);
00211 
00212   // search for frontiers
00213   if(findFrontiers(goals)){
00214 
00215     last_mode_ = FRONTIER_EXPLORE;
00216     ROS_INFO("[hector_exploration_planner] exploration: found %u frontiers!", (unsigned int)goals.size());
00217   } else {
00218     ROS_INFO("[hector_exploration_planner] exploration: no frontiers have been found! starting inner-exploration");
00219     return doInnerExploration(start,plan);
00220   }
00221 
00222   // make plan
00223   if(!buildexploration_trans_array_(start,goals,true)){
00224     return false;
00225   }
00226 
00227   vis_->publishVisOnDemand(costmap_, exploration_trans_array_);
00228 
00229   if(!getTrajectory(start,goals,plan)){
00230     ROS_INFO("[hector_exploration_planner] exploration: could not plan to frontier, starting inner-exploration");
00231     return doInnerExploration(start,plan);
00232   }
00233 
00234   // update previous goal
00235   if(!plan.empty()){
00236     geometry_msgs::PoseStamped thisgoal = plan.back();
00237     unsigned int mx,my;
00238     costmap_.worldToMap(thisgoal.pose.position.x,thisgoal.pose.position.y,mx,my);
00239     previous_goal_ = costmap_.getIndex(mx,my);
00240   }
00241 
00242 
00243   ROS_INFO("[hector_exploration_planner] exploration: plan to a frontier has been found! plansize: %u", (unsigned int)plan.size());
00244   return true;
00245 }
00246 
00247 bool HectorExplorationPlanner::doInnerExploration(const geometry_msgs::PoseStamped &start, std::vector<geometry_msgs::PoseStamped> &plan){
00248   ROS_INFO("[hector_exploration_planner] inner-exploration: starting exploration");
00249 
00250   // setup maps and goals
00251 
00252   resetMaps();
00253   clearFrontiers();
00254   plan.clear();
00255 
00256   std::vector<geometry_msgs::PoseStamped> goals;
00257 
00258   // create obstacle tranform
00259   buildobstacle_trans_array_(p_use_inflated_obs_);
00260 
00261   // If we have been in inner explore before, check if we have reached the previous inner explore goal
00262   if (last_mode_ == INNER_EXPLORE){
00263 
00264     tf::Stamped<tf::Pose> robotPose;
00265     if(!costmap_ros_->getRobotPose(robotPose)){
00266       ROS_WARN("[hector_exploration_planner]: Failed to get RobotPose");
00267     }
00268 
00269     unsigned int xm, ym;
00270     costmap_.indexToCells(previous_goal_, xm, ym);
00271 
00272     double xw, yw;
00273     costmap_.mapToWorld(xm, ym, xw, yw);
00274 
00275     double dx = xw - robotPose.getOrigin().getX();
00276     double dy = yw - robotPose.getOrigin().getY();
00277 
00278     //Have we reached the previous goal?
00279     if ( (dx*dx + dy*dy) > 0.5*0.5){
00280 
00281       geometry_msgs::PoseStamped robotPoseMsg;
00282       tf::poseStampedTFToMsg(robotPose, robotPoseMsg);
00283 
00284       geometry_msgs::PoseStamped goalMsg;
00285       goalMsg.pose.position.x = xw;
00286       goalMsg.pose.position.y = yw;
00287       goalMsg.pose.orientation.w = 1.0;
00288 
00289       if(makePlan(robotPoseMsg, goalMsg, plan)){
00290         //Successfully generated plan to (previous) inner explore goal
00291         ROS_INFO("[hector_exploration_planner] inner-exploration: Planning to previous inner frontier");
00292         last_mode_ = INNER_EXPLORE;
00293         return true;
00294       }
00295     }
00296   }
00297 
00298   // search for frontiers
00299   if(findInnerFrontier(goals)){
00300     ROS_INFO("[hector_exploration_planner] inner-exploration: found %u inner-frontiers!", (unsigned int)goals.size());
00301   } else {
00302     ROS_WARN("[hector_exploration_planner] inner-exploration: no inner-frontiers have been found! exploration failed!");
00303     return false;
00304   }
00305 
00306   // make plan
00307   if(!buildexploration_trans_array_(start,goals,false)){
00308     ROS_WARN("[hector_exploration_planner] inner-exploration: Creating exploration transform failed!");
00309     return false;
00310   }
00311   if(!getTrajectory(start,goals,plan)){
00312     ROS_WARN("[hector_exploration_planner] inner-exploration: could not plan to inner-frontier. exploration failed!");
00313     return false;
00314   }
00315 
00316   // cutoff last points of plan due to sbpl error when planning close to walls
00317 
00318   int plansize = plan.size() - 5;
00319   if(plansize > 0 ){
00320     plan.resize(plansize);
00321   }
00322 
00323   // update previous goal
00324   if(!plan.empty()){
00325     const geometry_msgs::PoseStamped& thisgoal = plan.back();
00326     unsigned int mx,my;
00327     costmap_.worldToMap(thisgoal.pose.position.x,thisgoal.pose.position.y,mx,my);
00328     previous_goal_ = costmap_.getIndex(mx,my);
00329     last_mode_ = INNER_EXPLORE;
00330   }
00331 
00332   ROS_INFO("[hector_exploration_planner] inner-exploration: plan to an inner-frontier has been found! plansize: %u", (unsigned int)plan.size());
00333   return true;
00334 }
00335 
00336 bool HectorExplorationPlanner::getObservationPose(const geometry_msgs::PoseStamped& observation_pose, const double desired_distance, geometry_msgs::PoseStamped& new_observation_pose)
00337 {
00338   // We call this from inside the planner, so map data setup and reset already happened
00339   //this->setupMapData();
00340   //resetMaps();
00341 
00342   unsigned int mxs,mys;
00343   costmap_.worldToMap(observation_pose.pose.position.x, observation_pose.pose.position.y, mxs, mys);
00344 
00345   double pose_yaw = tf::getYaw(observation_pose.pose.orientation);
00346 
00347   Eigen::Vector2f obs_pose_dir_vec (cos(pose_yaw), sin(pose_yaw));
00348 
00349   this->buildobstacle_trans_array_(true);
00350 
00351   int searchSize = 2.0 / costmap_.getResolution();
00352 
00353   int min_x = mxs - searchSize/2;
00354   int min_y = mys - searchSize/2;
00355 
00356   if (min_x < 0){
00357     min_x = 0;
00358   }
00359 
00360   if (min_y < 0){
00361     min_y = 0;
00362   }
00363 
00364   int max_x = mxs + searchSize/2;
00365   int max_y = mys + searchSize/2;
00366 
00367   if (max_x > static_cast<int>(costmap_.getSizeInCellsX())){
00368     max_x = static_cast<int>(costmap_.getSizeInCellsX()-1);
00369   }
00370 
00371   if (max_y > static_cast<int>(costmap_.getSizeInCellsY())){
00372     max_y = static_cast<int>(costmap_.getSizeInCellsY()-1);
00373   }
00374 
00375   int closest_x = -1;
00376   int closest_y = -1;
00377 
00378   int closest_sqr_dist = INT_MAX;
00379 
00380   for (int x = min_x; x < max_x; ++x){
00381     for (int y = min_y; y < max_y; ++y){
00382 
00383       unsigned int obstacle_trans_val = obstacle_trans_array_[costmap_.getIndex(x,y)];
00384       if ( (obstacle_trans_val != INT_MAX) && (obstacle_trans_val != 0) ){
00385         int diff_x = (int)mxs - x;
00386         int diff_y = (int)mys - y;
00387 
00388         int sqr_dist = diff_x*diff_x + diff_y*diff_y;
00389 
00390         //std::cout << "diff: " << diff_x << " , " << diff_y << " sqr_dist: " << sqr_dist << " pos: " << x << " , " << y << " closest sqr dist: " << closest_sqr_dist << " obstrans " << obstacle_trans_array_[costmap_.getIndex(x,y)] << "\n";
00391 
00392         if (sqr_dist < closest_sqr_dist){
00393 
00394           Eigen::Vector2f curr_dir_vec(static_cast<float>(diff_x), static_cast<float>(diff_y));
00395           curr_dir_vec.normalize();
00396 
00397           if (curr_dir_vec.dot(obs_pose_dir_vec) > 0){
00398 
00399             closest_x = (unsigned int)x;
00400             closest_y = (unsigned int)y;
00401             closest_sqr_dist = sqr_dist;
00402           }
00403         }
00404       }
00405     }
00406   }
00407 
00408   //std::cout << "start: " << mxs << " , " << mys << " min: " << min_x << " , " << min_y << " max: " <<  max_x << " , " << max_y << "\n";
00409   //std::cout << "pos: " << closest_x << " , " << closest_y << "\n";
00410 
00411   // Found valid pose if both coords are larger than -1
00412   if ((closest_x > -1) && (closest_y > -1)){
00413 
00414     Eigen::Vector2d closest_point_world;
00415     costmap_.mapToWorld(closest_x, closest_y, closest_point_world.x(),  closest_point_world.y());
00416 
00417     Eigen::Vector2d original_goal_pose(observation_pose.pose.position.x, observation_pose.pose.position.y);
00418 
00419     //geometry_msgs::PoseStamped pose;
00420     new_observation_pose.header.frame_id = "map";
00421     new_observation_pose.header.stamp = observation_pose.header.stamp;
00422 
00423     Eigen::Vector2d dir_vec(original_goal_pose - closest_point_world);
00424 
00425     double distance = dir_vec.norm();
00426 
00427     //If we get back the original observation pose (or another one very close to it), return that
00428     if (distance < (costmap_.getResolution() * 1.5)){
00429       new_observation_pose.pose = observation_pose.pose;
00430       new_observation_pose.pose.position.z = 0.0;
00431       ROS_INFO("Observation pose unchanged");
00432     }else{
00433 
00434       if (desired_distance < distance){
00435         new_observation_pose.pose.position.x = closest_point_world.x();
00436         new_observation_pose.pose.position.y = closest_point_world.y();
00437         new_observation_pose.pose.position.z = 0.0;
00438       }else{
00439 
00440         double test_distance = distance;
00441 
00442         Eigen::Vector2d last_valid_pos(closest_point_world);
00443 
00444         do{
00445           test_distance += 0.1;
00446 
00447           double distance_factor = test_distance / distance;
00448 
00449           Eigen::Vector2d new_pos(original_goal_pose - dir_vec*distance_factor);
00450 
00451           unsigned int x, y;
00452           costmap_.worldToMap(new_pos[0], new_pos[1], x, y);
00453           unsigned int idx = costmap_.getIndex(x,y);
00454 
00455           if(!this->isFree(idx)){
00456             break;
00457           }
00458 
00459           last_valid_pos = new_pos;
00460 
00461         }while (test_distance < desired_distance);
00462 
00463         new_observation_pose.pose.position.x = last_valid_pos.x();
00464         new_observation_pose.pose.position.y = last_valid_pos.y();
00465         new_observation_pose.pose.position.z = 0.0;
00466       }
00467 
00468       double angle = std::atan2(dir_vec.y(), dir_vec.x());
00469 
00470       new_observation_pose.pose.orientation.w = cos(angle*0.5);
00471       new_observation_pose.pose.orientation.x = 0.0;
00472       new_observation_pose.pose.orientation.y = 0.0;
00473       new_observation_pose.pose.orientation.z = sin(angle*0.5);
00474       ROS_INFO("Observation pose moved away from wall");
00475     }
00476 
00477     return true;
00478   }else{
00479     // If closest vals are still -1, we didn't find a position
00480     ROS_ERROR("Couldn't find observation pose for given point.");
00481     return false;
00482   }
00483 }
00484 
00485 bool HectorExplorationPlanner::doAlternativeExploration(const geometry_msgs::PoseStamped &start, std::vector<geometry_msgs::PoseStamped> &plan, std::vector<geometry_msgs::PoseStamped> &oldplan){
00486   ROS_INFO("[hector_exploration_planner] alternative exploration: starting alternative exploration");
00487 
00488   // setup maps and goals
00489   resetMaps();
00490   plan.clear();
00491 
00492   std::vector<geometry_msgs::PoseStamped> goals;
00493 
00494   std::vector<geometry_msgs::PoseStamped> old_frontier;
00495   old_frontier.push_back(oldplan.back());
00496 
00497   // create obstacle tranform
00498   buildobstacle_trans_array_(p_use_inflated_obs_);
00499 
00500   // search for frontiers
00501   if(findFrontiers(goals,old_frontier)){
00502     ROS_INFO("[hector_exploration_planner] alternative exploration: found %u frontiers!", (unsigned int) goals.size());
00503   } else {
00504     ROS_WARN("[hector_exploration_planner] alternative exploration: no frontiers have been found!");
00505     return false;
00506   }
00507 
00508   // make plan
00509   if(!buildexploration_trans_array_(start,goals,true)){
00510     return false;
00511   }
00512   if(!getTrajectory(start,goals,plan)){
00513     return false;
00514   }
00515 
00516   const geometry_msgs::PoseStamped& this_goal = plan.back();
00517   unsigned int mx,my;
00518   costmap_.worldToMap(this_goal.pose.position.x,this_goal.pose.position.y,mx,my);
00519   previous_goal_ = costmap_.getIndex(mx,my);
00520 
00521   ROS_INFO("[hector_exploration_planner] alternative exploration: plan to a frontier has been found! plansize: %u ", (unsigned int)plan.size());
00522   return true;
00523 }
00524 
00525 float HectorExplorationPlanner::angleDifferenceWall(const geometry_msgs::PoseStamped &start, const geometry_msgs::PoseStamped &goal){
00526   // setup start positions
00527   unsigned int mxs,mys;
00528   costmap_.worldToMap(start.pose.position.x,start.pose.position.y,mxs,mys);
00529 
00530   unsigned int gx,gy;
00531   costmap_.worldToMap(goal.pose.position.x,goal.pose.position.y,gx,gy);
00532 
00533   int goal_proj_x = gx-mxs;
00534   int goal_proj_y = gy-mys;
00535 
00536   float start_angle = tf::getYaw(start.pose.orientation);
00537   float goal_angle = std::atan2(goal_proj_y,goal_proj_x);
00538 
00539   float both_angle = 0;
00540   if(start_angle > goal_angle){
00541     both_angle = start_angle - goal_angle;
00542   } else {
00543     both_angle = goal_angle - start_angle;
00544   }
00545 
00546   return both_angle;
00547 }
00548 
00549 bool HectorExplorationPlanner::exploreWalls(const geometry_msgs::PoseStamped &start, std::vector<geometry_msgs::PoseStamped> &plan){
00550 
00551   //@TODO: Properly set this parameters
00552   int startExploreWall = 1;
00553 
00554   ROS_DEBUG("[hector_exploration_planner] wall-follow: exploreWalls");
00555   unsigned int mx,my;
00556   if(!costmap_.worldToMap(start.pose.position.x, start.pose.position.y, mx, my)){
00557     ROS_WARN("[hector_exploration_planner] wall-follow: The start coordinates are outside the costmap!");
00558     return false;
00559   }
00560   int currentPoint=costmap_.getIndex(mx, my);
00561   int nextPoint;
00562   int oldDirection = -1;
00563   int k=0;
00564   int loop=0;
00565 
00566   while(k<50){
00567     int adjacentPoints [8];
00568     getAdjacentPoints(currentPoint, adjacentPoints);
00569     int dirPoints [3];
00570 
00571     int minDelta=INT_MAX;
00572     int maxDelta=0;
00573     int thisDelta;
00574     float minAngle=3.1415; //Rad -> 180°
00575 
00576     geometry_msgs::PoseStamped trajPoint;
00577     unsigned int gx,gy;
00578 
00579     if(oldDirection==-1){
00580       // find robot orientation
00581       for ( int i=0; i<8; i++){
00582         costmap_.indexToCells((unsigned int)adjacentPoints[i],gx,gy);
00583         double wx,wy;
00584         costmap_.mapToWorld(gx,gy,wx,wy);
00585         std::string global_frame = costmap_ros_->getGlobalFrameID();
00586         trajPoint.header.frame_id = global_frame;
00587         trajPoint.pose.position.x = wx;
00588         trajPoint.pose.position.y = wy;
00589         trajPoint.pose.position.z = 0.0;
00590         float yaw = angleDifferenceWall(start, trajPoint);
00591         if(yaw < minAngle){
00592           minAngle=yaw;
00593           oldDirection=i;
00594         }
00595       }
00596     }
00597 
00598     //search possible orientation
00599 
00600     if (oldDirection == 0){
00601       dirPoints[0]=oldDirection+4; //right-forward
00602       dirPoints[1]=oldDirection;   //forward
00603       dirPoints[2]=oldDirection+7; //left-forward
00604     }
00605     else if (oldDirection < 3){
00606       dirPoints[0]=oldDirection+4;
00607       dirPoints[1]=oldDirection;
00608       dirPoints[2]=oldDirection+3;
00609     }
00610     else if (oldDirection == 3){
00611       dirPoints[0]=oldDirection+4;
00612       dirPoints[1]=oldDirection;
00613       dirPoints[2]=oldDirection+3;
00614     }
00615     else if (oldDirection == 4){
00616       dirPoints[0]=oldDirection-3;
00617       dirPoints[1]=oldDirection;
00618       dirPoints[2]=oldDirection-4;
00619     }
00620     else if (oldDirection < 7){
00621       dirPoints[0]=oldDirection-3;
00622       dirPoints[1]=oldDirection;
00623       dirPoints[2]=oldDirection-4;
00624     }
00625     else if (oldDirection == 7){
00626       dirPoints[0]=oldDirection-7;
00627       dirPoints[1]=oldDirection;
00628       dirPoints[2]=oldDirection-4;
00629     }
00630 
00631     // decide LHR or RHR
00632     if(startExploreWall == -1){
00633       if(obstacle_trans_array_[adjacentPoints[dirPoints[0]]] <= obstacle_trans_array_[adjacentPoints[dirPoints[2]]]){
00634         startExploreWall = 0;
00635         ROS_INFO("[hector_exploration_planner] wall-follow: RHR");//mirror inverted??
00636       }
00637       else {
00638         startExploreWall = 1;
00639         ROS_INFO("[hector_exploration_planner] wall-follow: LHR");//mirror inverted??
00640       }
00641     }
00642 
00643 
00644 
00645     //switch left and right, LHR
00646     if(startExploreWall == 1){
00647       int temp=dirPoints[0];
00648       dirPoints[0]=dirPoints[2];
00649       dirPoints[2]=temp;
00650     }
00651 
00652 
00653     // find next point
00654     int t=0;
00655     for(int i=0; i<3; i++){
00656       thisDelta= obstacle_trans_array_[adjacentPoints[dirPoints[i]]];
00657 
00658       if (thisDelta > 3000 || loop > 7) // point is unknown or robot drive loop
00659       {
00660         int plansize = plan.size() - 4;
00661         if(plansize > 0 ){
00662           plan.resize(plansize);
00663         }
00664         ROS_DEBUG("[hector_exploration_planner] wall-follow: END: exploreWalls. Plansize %d", (int)plan.size());
00665         return !plan.empty();
00666       }
00667 
00668       if(thisDelta >= p_min_obstacle_dist_){
00669         if(obstacle_trans_array_[currentPoint] >= p_min_obstacle_dist_){
00670           if(abs(thisDelta - p_min_obstacle_dist_) < minDelta){
00671             minDelta = abs(thisDelta - p_min_obstacle_dist_);
00672             nextPoint = adjacentPoints[dirPoints[i]];
00673             oldDirection = dirPoints[i];
00674           }
00675         }
00676         if(obstacle_trans_array_[currentPoint] < p_min_obstacle_dist_){
00677           if(thisDelta > maxDelta){
00678             maxDelta = thisDelta;
00679             nextPoint = adjacentPoints[dirPoints[i]];
00680             oldDirection = dirPoints[i];
00681           }
00682         }
00683       }
00684       else {
00685         if(thisDelta < obstacle_trans_array_[currentPoint]){
00686           t++;
00687         }
00688         if(thisDelta > maxDelta){
00689           maxDelta = thisDelta;
00690           nextPoint = adjacentPoints[dirPoints[i]];
00691           oldDirection = dirPoints[i];
00692 
00693         }
00694       }
00695     }
00696 
00697     if(t==3 && abs(obstacle_trans_array_[adjacentPoints[dirPoints[0]]] - obstacle_trans_array_[adjacentPoints[dirPoints[1]]]) < STRAIGHT_COST
00698     && abs(obstacle_trans_array_[adjacentPoints[dirPoints[0]]] - obstacle_trans_array_[adjacentPoints[dirPoints[2]]]) < STRAIGHT_COST
00699     && abs(obstacle_trans_array_[adjacentPoints[dirPoints[1]]] - obstacle_trans_array_[adjacentPoints[dirPoints[2]]]) < STRAIGHT_COST){
00700       nextPoint=adjacentPoints[dirPoints[2]];
00701       oldDirection=dirPoints[2];
00702     }
00703 
00704     if(oldDirection==dirPoints[2])
00705       loop++;
00706     else
00707       loop=0;
00708 
00709     // add point
00710     unsigned int sx,sy;
00711     costmap_.indexToCells((unsigned int)currentPoint,sx,sy);
00712     costmap_.indexToCells((unsigned int)nextPoint,gx,gy);
00713     double wx,wy;
00714     costmap_.mapToWorld(sx,sy,wx,wy);
00715     std::string global_frame = costmap_ros_->getGlobalFrameID();
00716     trajPoint.header.frame_id = global_frame;
00717     trajPoint.pose.position.x = wx;
00718     trajPoint.pose.position.y = wy;
00719     trajPoint.pose.position.z = 0.0;
00720     // assign orientation
00721     int dx = gx-sx;
00722     int dy = gy-sy;
00723     double yaw_path = std::atan2(dy,dx);
00724     trajPoint.pose.orientation.x = 0.0;
00725     trajPoint.pose.orientation.y = 0.0;
00726     trajPoint.pose.orientation.z = sin(yaw_path*0.5f);
00727     trajPoint.pose.orientation.w = cos(yaw_path*0.5f);
00728     plan.push_back(trajPoint);
00729 
00730     currentPoint=nextPoint;
00731     k++;
00732   }
00733   ROS_DEBUG("[hector_exploration_planner] wall-follow: END: exploreWalls. Plansize %d", (int)plan.size());
00734   return !plan.empty();
00735 }
00736 
00737 void HectorExplorationPlanner::setupMapData()
00738 {
00739   costmap_ros_->getCostmapCopy(costmap_);
00740 
00741   if ((this->map_width_ != costmap_ros_->getSizeInCellsX()) || (this->map_height_ != costmap_ros_->getSizeInCellsY())){
00742     this->deleteMapData();
00743 
00744     map_width_ = costmap_.getSizeInCellsX();
00745     map_height_ = costmap_.getSizeInCellsY();
00746     num_map_cells_ = map_width_ * map_height_;
00747 
00748     // initialize exploration_trans_array_, obstacle_trans_array_, goalMap and frontier_map_array_
00749     exploration_trans_array_ = new unsigned int[num_map_cells_];
00750     obstacle_trans_array_ = new unsigned int[num_map_cells_];
00751     is_goal_array_ = new bool[num_map_cells_];
00752     frontier_map_array_ = new int[num_map_cells_];
00753     clearFrontiers();
00754     resetMaps();
00755   }
00756 
00757 
00758   occupancy_grid_array_ = costmap_.getCharMap();
00759 }
00760 
00761 void HectorExplorationPlanner::deleteMapData()
00762 {
00763   if(exploration_trans_array_){
00764     delete[] exploration_trans_array_;
00765     exploration_trans_array_ = 0;
00766   }
00767   if(obstacle_trans_array_){
00768     delete[] obstacle_trans_array_;
00769     obstacle_trans_array_ = 0;
00770   }
00771   if(is_goal_array_){
00772     delete[] is_goal_array_;
00773     is_goal_array_ = 0;
00774   }
00775   if(frontier_map_array_){
00776     delete[] frontier_map_array_;
00777     frontier_map_array_=0;
00778   }
00779 }
00780 
00781 
00782 bool HectorExplorationPlanner::buildexploration_trans_array_(const geometry_msgs::PoseStamped &start, std::vector<geometry_msgs::PoseStamped> goals, bool useAnglePenalty){
00783 
00784   ROS_DEBUG("[hector_exploration_planner] buildexploration_trans_array_");
00785 
00786   // reset exploration transform
00787   std::fill_n(exploration_trans_array_, num_map_cells_, INT_MAX);
00788   std::fill_n(is_goal_array_, num_map_cells_, false);
00789 
00790   std::queue<int> myqueue;
00791 
00792   size_t num_free_goals = 0;
00793 
00794   // initialize goals
00795   for(unsigned int i = 0; i < goals.size(); ++i){
00796     // setup goal positions
00797     unsigned int mx,my;
00798 
00799     if(!costmap_.worldToMap(goals[i].pose.position.x,goals[i].pose.position.y,mx,my)){
00800       //ROS_WARN("[hector_exploration_planner] The goal coordinates are outside the costmap!");
00801       continue;
00802     }
00803 
00804     int goal_point = costmap_.getIndex(mx,my);
00805 
00806     // Ignore free goal for the moment, check after iterating over all goals if there is not valid one at all
00807     if(!isFree(goal_point)){
00808       continue;
00809     }else{
00810       ++num_free_goals;
00811     }
00812 
00813     unsigned int init_cost = 0;
00814     if(false){
00815       init_cost = angleDanger(angleDifference(start,goals[i])) * getDistanceWeight(start,goals[i]);
00816     }
00817 
00818     exploration_trans_array_[goal_point] = init_cost;
00819 
00820     // do not punish previous frontiers (oscillation)
00821     if(false && isValid(previous_goal_)){
00822       if(isSameFrontier(goal_point, previous_goal_)){
00823         ROS_DEBUG("[hector_exploration_planner] same frontier: init with 0");
00824         exploration_trans_array_[goal_point] = 0;
00825       }
00826     }
00827 
00828     ROS_DEBUG("[hector_exploration_planner] Goal init cost: %d, point: %d", exploration_trans_array_[goal_point], goal_point);
00829     is_goal_array_[goal_point] = true;
00830     myqueue.push(goal_point);
00831   }
00832 
00833   if (num_free_goals == 0){
00834     ROS_WARN("[hector_exploration_planner] All goal coordinates for exploration transform invalid (occupied or out of bounds), aborting.");
00835     return false;
00836   }
00837 
00838   // exploration transform algorithm
00839   while(myqueue.size()){
00840     int point = myqueue.front();
00841     myqueue.pop();
00842 
00843     unsigned int minimum = exploration_trans_array_[point];
00844 
00845     int straightPoints[4];
00846     getStraightPoints(point,straightPoints);
00847     int diagonalPoints[4];
00848     getDiagonalPoints(point,diagonalPoints);
00849 
00850 
00851     // calculate the minimum exploration value of all adjacent cells
00852     if(!is_goal_array_[point]){
00853 
00854       const unsigned int point_cell_danger = cellDanger(point);
00855 
00856       for(int i = 0; i < 4; ++i){
00857         if(isFree(straightPoints[i]) && (exploration_trans_array_[straightPoints[i]] + STRAIGHT_COST + point_cell_danger) < minimum){
00858           minimum = exploration_trans_array_[straightPoints[i]] + STRAIGHT_COST + point_cell_danger;
00859         }
00860         if(isFree(diagonalPoints[i]) && (exploration_trans_array_[diagonalPoints[i]] + DIAGONAL_COST + point_cell_danger) < minimum){
00861           minimum = exploration_trans_array_[diagonalPoints[i]] + DIAGONAL_COST + point_cell_danger;
00862         }
00863       }
00864     }
00865 
00866     // if exploration_trans_array_ of the point changes, add all adjacent cells (theirs might change too)
00867     if(minimum < exploration_trans_array_[point] || is_goal_array_[point]){
00868       exploration_trans_array_[point] = minimum;
00869 
00870       for(int i = 0; i < 4; ++i){
00871         if(isFree(straightPoints[i]) && !is_goal_array_[straightPoints[i]]){
00872           myqueue.push(straightPoints[i]);
00873         }
00874         if(isFree(diagonalPoints[i]) && !is_goal_array_[diagonalPoints[i]]){
00875           myqueue.push(diagonalPoints[i]);
00876         }
00877       }
00878     }
00879 
00880   }
00881 
00882   ROS_DEBUG("[hector_exploration_planner] END: buildexploration_trans_array_");
00883   return true;
00884 
00885 }
00886 
00887 bool HectorExplorationPlanner::buildobstacle_trans_array_(bool use_inflated_obstacles){
00888   ROS_DEBUG("[hector_exploration_planner] buildobstacle_trans_array_");
00889   std::queue<int> myqueue;
00890 
00891   // init obstacles
00892   for(unsigned int i=0; i < num_map_cells_; ++i){
00893     if(occupancy_grid_array_[i] == costmap_2d::LETHAL_OBSTACLE){
00894       myqueue.push(i);
00895       obstacle_trans_array_[i] = 0;
00896     } else if(use_inflated_obstacles){
00897       if(occupancy_grid_array_[i] == costmap_2d::INSCRIBED_INFLATED_OBSTACLE){
00898         myqueue.push(i);
00899         obstacle_trans_array_[i] = 0;
00900       }
00901     }
00902   }
00903 
00904   // obstacle transform algorithm
00905   while(myqueue.size()){
00906     int point = myqueue.front();
00907     myqueue.pop();
00908 
00909     unsigned int minimum = obstacle_trans_array_[point];
00910 
00911     int straightPoints[4];
00912     getStraightPoints(point,straightPoints);
00913     int diagonalPoints[4];
00914     getDiagonalPoints(point,diagonalPoints);
00915 
00916     if(obstacle_trans_array_[point] != 0){
00917 
00918       // check all 8 directions
00919       for(int i = 0; i < 4; ++i){
00920         if(isValid(straightPoints[i]) && ((obstacle_trans_array_[straightPoints[i]] + STRAIGHT_COST) < minimum)){
00921           minimum = obstacle_trans_array_[straightPoints[i]] + STRAIGHT_COST;
00922         }
00923         if(isValid(diagonalPoints[i]) && ((obstacle_trans_array_[diagonalPoints[i]] + DIAGONAL_COST) < minimum)){
00924           minimum = obstacle_trans_array_[diagonalPoints[i]] + DIAGONAL_COST;
00925         }
00926       }
00927     }
00928 
00929     // if obstacle_trans_array_ of the point changes, add all adjacent cells (theirs might change too)
00930     if((minimum < obstacle_trans_array_[point]) || (obstacle_trans_array_[point] == 0)){
00931       obstacle_trans_array_[point] = minimum;
00932 
00933       for(int i = 0; i < 4; ++i){
00934         if(isFree(straightPoints[i])){
00935           myqueue.push(straightPoints[i]);
00936         }
00937         if(isFree(diagonalPoints[i])){
00938           myqueue.push(diagonalPoints[i]);
00939         }
00940       }
00941     }
00942   }
00943   ROS_DEBUG("[hector_exploration_planner] END: buildobstacle_trans_array_");
00944   return true;
00945 }
00946 
00947 bool HectorExplorationPlanner::getTrajectory(const geometry_msgs::PoseStamped &start, std::vector<geometry_msgs::PoseStamped> goals, std::vector<geometry_msgs::PoseStamped> &plan){
00948 
00949   ROS_DEBUG("[hector_exploration_planner] getTrajectory");
00950 
00951   // setup start positions
00952   unsigned int mx,my;
00953 
00954   if(!costmap_.worldToMap(start.pose.position.x,start.pose.position.y,mx,my)){
00955     ROS_WARN("[hector_exploration_planner] The start coordinates are outside the costmap!");
00956     return false;
00957   }
00958 
00959   int currentPoint = costmap_.getIndex(mx,my);
00960   int nextPoint = currentPoint;
00961   int maxDelta = 0;
00962 
00963 
00964   geometry_msgs::PoseStamped trajPoint;
00965   std::string global_frame = costmap_ros_->getGlobalFrameID();
00966   trajPoint.header.frame_id = global_frame;
00967 
00968   while(!is_goal_array_[currentPoint]){
00969     int thisDelta;
00970     int adjacentPoints[8];
00971     getAdjacentPoints(currentPoint,adjacentPoints);
00972 
00973     for(int i = 0; i < 8; ++i){
00974       if(isFree(adjacentPoints[i])){
00975         thisDelta = exploration_trans_array_[currentPoint] - exploration_trans_array_[adjacentPoints[i]];
00976         if(thisDelta > maxDelta){
00977           maxDelta = thisDelta;
00978           nextPoint = adjacentPoints[i];
00979         }
00980       }
00981     }
00982 
00983     // This happens when there is no valid exploration transform data at the start point for example
00984     if(maxDelta == 0){
00985       ROS_WARN("[hector_exploration_planner] No path to the goal could be found by following gradient!");
00986       return false;
00987     }
00988 
00989 
00990     // make trajectory point
00991     unsigned int sx,sy,gx,gy;
00992     costmap_.indexToCells((unsigned int)currentPoint,sx,sy);
00993     costmap_.indexToCells((unsigned int)nextPoint,gx,gy);
00994     double wx,wy;
00995     costmap_.mapToWorld(sx,sy,wx,wy);
00996 
00997     trajPoint.pose.position.x = wx;
00998     trajPoint.pose.position.y = wy;
00999     trajPoint.pose.position.z = 0.0;
01000 
01001     // assign orientation
01002     int dx = gx-sx;
01003     int dy = gy-sy;
01004     double yaw_path = std::atan2(dy,dx);
01005     trajPoint.pose.orientation.x = 0.0;
01006     trajPoint.pose.orientation.y = 0.0;
01007     trajPoint.pose.orientation.z = sin(yaw_path*0.5f);
01008     trajPoint.pose.orientation.w = cos(yaw_path*0.5f);
01009 
01010     plan.push_back(trajPoint);
01011 
01012     currentPoint = nextPoint;
01013     maxDelta = 0;
01014   }
01015 
01016 
01017 
01018   ROS_DEBUG("[hector_exploration_planner] END: getTrajectory. Plansize %u", (unsigned int)plan.size());
01019   return !plan.empty();
01020 }
01021 
01022 bool HectorExplorationPlanner::findFrontiers(std::vector<geometry_msgs::PoseStamped> &frontiers){
01023   std::vector<geometry_msgs::PoseStamped> empty_vec;
01024   return findFrontiers(frontiers,empty_vec);
01025 }
01026 
01027 /*
01028  * searches the occupancy grid for frontier cells and merges them into one target point per frontier.
01029  * The returned frontiers are in world coordinates.
01030  */
01031 bool HectorExplorationPlanner::findFrontiers(std::vector<geometry_msgs::PoseStamped> &frontiers, std::vector<geometry_msgs::PoseStamped> &noFrontiers){
01032 
01033   // get latest costmap
01034   clearFrontiers();
01035 
01036   // list of all frontiers in the occupancy grid
01037   std::vector<int> allFrontiers;
01038 
01039   // check for all cells in the occupancy grid whether or not they are frontier cells
01040   for(unsigned int i = 0; i < num_map_cells_; ++i){
01041     if(isFrontier(i)){
01042       allFrontiers.push_back(i);
01043     }
01044   }
01045 
01046   tf::Stamped<tf::Pose> robotPose;
01047   if(!costmap_ros_->getRobotPose(robotPose)){
01048     ROS_WARN("[hector_exploration_planner]: Failed to get RobotPose");
01049   }
01050 
01051 
01052   for(unsigned int i = 0; i < allFrontiers.size(); ++i){
01053     if(!isFrontierReached(allFrontiers[i])){
01054       geometry_msgs::PoseStamped finalFrontier;
01055       double wx,wy;
01056       unsigned int mx,my;
01057       costmap_.indexToCells(allFrontiers[i], mx, my);
01058       costmap_.mapToWorld(mx,my,wx,wy);
01059       std::string global_frame = costmap_ros_->getGlobalFrameID();
01060       finalFrontier.header.frame_id = global_frame;
01061       finalFrontier.pose.position.x = wx;
01062       finalFrontier.pose.position.y = wy;
01063       finalFrontier.pose.position.z = 0.0;
01064 
01065       double yaw = getYawToUnknown(costmap_.getIndex(mx,my));
01066 
01067       //if(frontier_is_valid){
01068 
01069       finalFrontier.pose.orientation = tf::createQuaternionMsgFromYaw(yaw);
01070 
01071       frontiers.push_back(finalFrontier);
01072     }
01073     //}
01074   }
01075 
01076   return true;
01077 
01078   // value of the next blob
01079   int nextBlobValue = 1;
01080   std::list<int> usedBlobs;
01081 
01082   for(unsigned int i = 0; i < allFrontiers.size(); ++i){
01083 
01084     // get all adjacent blobs to the current frontier point
01085     int currentPoint = allFrontiers[i];
01086     int adjacentPoints[8];
01087     getAdjacentPoints(currentPoint,adjacentPoints);
01088 
01089     std::list<int> blobs;
01090 
01091     for(int j = 0; j < 8; j++){
01092       if(isValid(adjacentPoints[j]) && (frontier_map_array_[adjacentPoints[j]] > 0)){
01093         blobs.push_back(frontier_map_array_[adjacentPoints[j]]);
01094       }
01095     }
01096     blobs.unique();
01097 
01098     if(blobs.empty()){
01099       // create new blob
01100       frontier_map_array_[currentPoint] = nextBlobValue;
01101       usedBlobs.push_back(nextBlobValue);
01102       nextBlobValue++;
01103     } else {
01104       // merge all found blobs
01105       int blobMergeVal = 0;
01106 
01107       for(std::list<int>::iterator adjBlob = blobs.begin(); adjBlob != blobs.end(); ++adjBlob){
01108         if(adjBlob == blobs.begin()){
01109           blobMergeVal = *adjBlob;
01110           frontier_map_array_[currentPoint] = blobMergeVal;
01111         } else {
01112 
01113           for(unsigned int k = 0; k < allFrontiers.size(); k++){
01114             if(frontier_map_array_[allFrontiers[k]] == *adjBlob){
01115               usedBlobs.remove(*adjBlob);
01116               frontier_map_array_[allFrontiers[k]] = blobMergeVal;
01117             }
01118           }
01119         }
01120       }
01121     }
01122   }
01123 
01124   int id = 1;
01125 
01126   bool visualization_requested = (visualization_pub_.getNumSubscribers() > 0);
01127 
01128   // summarize every blob into a single point (maximum obstacle_trans_array_ value)
01129   for(std::list<int>::iterator currentBlob = usedBlobs.begin(); currentBlob != usedBlobs.end(); ++currentBlob){
01130     int current_frontier_size = 0;
01131     int max_obs_idx = 0;
01132 
01133     for(unsigned int i = 0; i < allFrontiers.size(); ++i){
01134       int point = allFrontiers[i];
01135 
01136       if(frontier_map_array_[point] == *currentBlob){
01137         current_frontier_size++;
01138         if(obstacle_trans_array_[point] > obstacle_trans_array_[allFrontiers[max_obs_idx]]){
01139           max_obs_idx = i;
01140         }
01141       }
01142     }
01143 
01144     if(current_frontier_size < p_min_frontier_size_){
01145       continue;
01146     }
01147 
01148     int frontier_point = allFrontiers[max_obs_idx];
01149     unsigned int x,y;
01150     costmap_.indexToCells(frontier_point,x,y);
01151 
01152     // check if frontier is valid (not to close to robot and not in noFrontiers vector
01153     bool frontier_is_valid = true;
01154 
01155     if(isFrontierReached(frontier_point)){
01156       frontier_is_valid = false;
01157     }
01158 
01159     for(size_t i = 0; i < noFrontiers.size(); ++i){
01160       const geometry_msgs::PoseStamped& noFrontier = noFrontiers[i];
01161       unsigned int mx,my;
01162       costmap_.worldToMap(noFrontier.pose.position.x,noFrontier.pose.position.y,mx,my);
01163       int no_frontier_point = costmap_.getIndex(x,y);
01164       if(isSameFrontier(frontier_point,no_frontier_point)){
01165         frontier_is_valid = false;
01166       }
01167     }
01168 
01169     geometry_msgs::PoseStamped finalFrontier;
01170     double wx,wy;
01171     costmap_.mapToWorld(x,y,wx,wy);
01172     std::string global_frame = costmap_ros_->getGlobalFrameID();
01173     finalFrontier.header.frame_id = global_frame;
01174     finalFrontier.pose.position.x = wx;
01175     finalFrontier.pose.position.y = wy;
01176     finalFrontier.pose.position.z = 0.0;
01177 
01178     double yaw = getYawToUnknown(costmap_.getIndex(x,y));
01179 
01180     if(frontier_is_valid){
01181 
01182       finalFrontier.pose.orientation = tf::createQuaternionMsgFromYaw(yaw);
01183       frontiers.push_back(finalFrontier);
01184     }
01185 
01186     // visualization (export to method?)
01187     if(visualization_requested){
01188       visualization_msgs::Marker marker;
01189       marker.header.frame_id = "map";
01190       marker.header.stamp = ros::Time();
01191       marker.ns = "hector_exploration_planner";
01192       marker.id = id++;
01193       marker.type = visualization_msgs::Marker::ARROW;
01194       marker.action = visualization_msgs::Marker::ADD;
01195       marker.pose.position.x = wx;
01196       marker.pose.position.y = wy;
01197       marker.pose.position.z = 0.0;
01198       marker.pose.orientation = tf::createQuaternionMsgFromYaw(yaw);
01199       marker.scale.x = 0.2;
01200       marker.scale.y = 0.2;
01201       marker.scale.z = 0.2;
01202       marker.color.a = 1.0;
01203 
01204       if(frontier_is_valid){
01205         marker.color.r = 0.0;
01206         marker.color.g = 1.0;
01207       }else{
01208         marker.color.r = 1.0;
01209         marker.color.g = 0.0;
01210       }
01211 
01212       marker.color.b = 0.0;
01213       marker.lifetime = ros::Duration(5,0);
01214       visualization_pub_.publish(marker);
01215     }
01216 
01217   }
01218   return !frontiers.empty();
01219 }
01220 
01221 bool HectorExplorationPlanner::findInnerFrontier(std::vector<geometry_msgs::PoseStamped> &innerFrontier){
01222   clearFrontiers();
01223 
01224   // get the trajectory as seeds for the exploration transform
01225   hector_nav_msgs::GetRobotTrajectory srv_path;
01226   if (path_service_client_.call(srv_path)){
01227 
01228     std::vector<geometry_msgs::PoseStamped>& traj_vector (srv_path.response.trajectory.poses);
01229 
01230     // We push poses of the travelled trajectory to the goals vector for building the exploration transform
01231     std::vector<geometry_msgs::PoseStamped> goals;
01232 
01233     size_t size = traj_vector.size();
01234     ROS_DEBUG("[hector_exploration_planner] size of trajectory vector %u", (unsigned int)size);
01235 
01236     if(size > 0){
01237       geometry_msgs::PoseStamped lastPose = traj_vector[size-1];
01238       goals.push_back(lastPose);
01239 
01240       if (size > 1){
01241 
01242         // Allow collision at start in case vehicle is (very) close to wall
01243         bool collision_allowed = true;
01244 
01245         for(int i = static_cast<int>(size-2); i >= 0; --i){
01246           const geometry_msgs::PoseStamped& pose = traj_vector[i];
01247           unsigned int x,y;
01248           costmap_.worldToMap(pose.pose.position.x,pose.pose.position.y,x,y);
01249           unsigned int m_point = costmap_.getIndex(x,y);
01250 
01251           double dx = lastPose.pose.position.x - pose.pose.position.x;
01252           double dy = lastPose.pose.position.y - pose.pose.position.y;
01253 
01254           bool point_in_free_space = isFree(m_point);
01255 
01256           // extract points with 0.5m distance (if free)
01257           if(point_in_free_space){
01258             if((dx*dx) + (dy*dy) > (0.25*0.25)){
01259               goals.push_back(pose);
01260               lastPose = pose;
01261               collision_allowed = false;
01262             }
01263           }
01264 
01265           if (!point_in_free_space && !collision_allowed){
01266             break;
01267           }
01268         }
01269       }
01270 
01271 
01272       ROS_DEBUG("[hector_exploration_planner] pushed %u goals (trajectory) for inner frontier-search", (unsigned int)goals.size());
01273 
01274       // make exploration transform
01275       tf::Stamped<tf::Pose> robotPose;
01276       if(!costmap_ros_->getRobotPose(robotPose)){
01277         ROS_WARN("[hector_exploration_planner]: Failed to get RobotPose");
01278       }
01279       geometry_msgs::PoseStamped robotPoseMsg;
01280       tf::poseStampedTFToMsg(robotPose, robotPoseMsg);
01281 
01282       if (!buildexploration_trans_array_(robotPoseMsg, goals, false)){
01283         ROS_WARN("[hector_exploration_planner]: Creating exploration transform array in find inner frontier failed, aborting.");
01284         return false;
01285       }
01286 
01287       inner_vis_->publishVisOnDemand(costmap_, exploration_trans_array_);
01288 
01289       unsigned int x,y;
01290       costmap_.worldToMap(robotPoseMsg.pose.position.x,robotPoseMsg.pose.position.y,x,y);
01291 
01292 
01293 
01294 
01295       // get point with maximal distance to trajectory
01296       int max_exploration_trans_point = -1;
01297       unsigned int max_exploration_trans_val = 0;
01298 
01299       for(unsigned int i = 0; i < num_map_cells_; ++i){
01300 
01301         if(exploration_trans_array_[i] < INT_MAX){
01302           if(exploration_trans_array_[i] > max_exploration_trans_val){
01303             if(!isFrontierReached(i)){
01304               max_exploration_trans_point = i;
01305               max_exploration_trans_val = exploration_trans_array_[i];
01306             }
01307           }
01308         }
01309       }
01310 
01311       if (max_exploration_trans_point == 0){
01312         ROS_WARN("[hector_exploration_planner]: Couldn't find max exploration transform point for inner exploration, aborting.");
01313         return false;
01314       }
01315 
01316       geometry_msgs::PoseStamped finalFrontier;
01317       unsigned int fx,fy;
01318       double wfx,wfy;
01319       costmap_.indexToCells(max_exploration_trans_point,fx,fy);
01320       costmap_.mapToWorld(fx,fy,wfx,wfy);
01321       std::string global_frame = costmap_ros_->getGlobalFrameID();
01322       finalFrontier.header.frame_id = global_frame;
01323       finalFrontier.pose.position.x = wfx;
01324       finalFrontier.pose.position.y = wfy;
01325       finalFrontier.pose.position.z = 0.0;
01326 
01327       // assign orientation
01328       int dx = fx-x;
01329       int dy = fy-y;
01330       double yaw_path = std::atan2(dy,dx);
01331       finalFrontier.pose.orientation.x = 0.0;
01332       finalFrontier.pose.orientation.y = 0.0;
01333       finalFrontier.pose.orientation.z = sin(yaw_path*0.5f);
01334       finalFrontier.pose.orientation.w = cos(yaw_path*0.5f);
01335 
01336       innerFrontier.push_back(finalFrontier);
01337 
01338       if(visualization_pub_.getNumSubscribers() > 0){
01339         visualization_msgs::Marker marker;
01340         marker.header.frame_id = "map";
01341         marker.header.stamp = ros::Time();
01342         marker.ns = "hector_exploration_planner";
01343         marker.id = 100;
01344         marker.type = visualization_msgs::Marker::ARROW;
01345         marker.action = visualization_msgs::Marker::ADD;
01346         marker.pose.position.x = wfx;
01347         marker.pose.position.y = wfy;
01348         marker.pose.position.z = 0.0;
01349         marker.pose.orientation = tf::createQuaternionMsgFromYaw(yaw_path);
01350         marker.scale.x = 0.2;
01351         marker.scale.y = 0.2;
01352         marker.scale.z = 0.2;
01353         marker.color.a = 1.0;
01354         marker.color.r = 0.0;
01355         marker.color.g = 0.0;
01356         marker.color.b = 1.0;
01357         marker.lifetime = ros::Duration(5,0);
01358         visualization_pub_.publish(marker);
01359       }
01360       return true;
01361     }
01362   }
01363   return false;
01364 }
01365 
01366 /*
01367  * checks if a given point is a frontier cell. a frontier cell is a cell in the occupancy grid
01368  * that seperates known from unknown space. Therefore the cell has to be free but at least three
01369  * of its neighbours need to be unknown
01370  */
01371 bool HectorExplorationPlanner::isFrontier(int point){
01372   if(isFree(point)){
01373 
01374     int adjacentPoints[8];
01375     getAdjacentPoints(point,adjacentPoints);
01376 
01377     for(int i = 0; i < 8; ++i){
01378       if(isValid(adjacentPoints[i])){
01379         if(occupancy_grid_array_[adjacentPoints[i]] == costmap_2d::NO_INFORMATION){
01380 
01381           int no_inf_count = 0;
01382           int noInfPoints[8];
01383           getAdjacentPoints(adjacentPoints[i],noInfPoints);
01384           for(int j = 0; j < 8; j++){
01385             if(occupancy_grid_array_[noInfPoints[j]] == costmap_2d::NO_INFORMATION){
01386               no_inf_count++;
01387             }
01388           }
01389           if(no_inf_count > 2){
01390             return true;
01391           }
01392 
01393         }
01394       }
01395     }
01396   }
01397 
01398   return false;
01399 }
01400 
01401 
01402 void HectorExplorationPlanner::resetMaps(){
01403   std::fill_n(exploration_trans_array_, num_map_cells_, INT_MAX);
01404   std::fill_n(obstacle_trans_array_, num_map_cells_, INT_MAX);
01405   std::fill_n(is_goal_array_, num_map_cells_, false);
01406 }
01407 
01408 void HectorExplorationPlanner::clearFrontiers(){
01409   std::fill_n(frontier_map_array_, num_map_cells_, 0);
01410 }
01411 
01412 inline bool HectorExplorationPlanner::isValid(int point){
01413   return (point>=0);
01414 }
01415 
01416 bool HectorExplorationPlanner::isFree(int point){
01417 
01418   if(isValid(point)){
01419     // if a point is not inscribed_inflated_obstacle, leathal_obstacle or no_information, its free
01420     if(p_use_inflated_obs_){
01421       if(occupancy_grid_array_[point] < costmap_2d::INSCRIBED_INFLATED_OBSTACLE){
01422         return true;
01423       }
01424     } else {
01425       if(occupancy_grid_array_[point] <= costmap_2d::INSCRIBED_INFLATED_OBSTACLE){
01426         return true;
01427       }
01428     }
01429 
01430     if(p_plan_in_unknown_){
01431       if(occupancy_grid_array_[point] == costmap_2d::NO_INFORMATION){
01432         return true;
01433       }
01434     }
01435   }
01436   return false;
01437 }
01438 
01439 bool HectorExplorationPlanner::isFrontierReached(int point){
01440 
01441   tf::Stamped<tf::Pose> robotPose;
01442   if(!costmap_ros_->getRobotPose(robotPose)){
01443     ROS_WARN("[hector_exploration_planner]: Failed to get RobotPose");
01444   }
01445   geometry_msgs::PoseStamped robotPoseMsg;
01446   tf::poseStampedTFToMsg(robotPose, robotPoseMsg);
01447 
01448   unsigned int fx,fy;
01449   double wfx,wfy;
01450   costmap_.indexToCells(point,fx,fy);
01451   costmap_.mapToWorld(fx,fy,wfx,wfy);
01452 
01453 
01454   double dx = robotPoseMsg.pose.position.x - wfx;
01455   double dy = robotPoseMsg.pose.position.y - wfy;
01456 
01457   if ( (dx*dx) + (dy*dy) < (p_dist_for_goal_reached_*p_dist_for_goal_reached_)) {
01458     ROS_DEBUG("[hector_exploration_planner]: frontier is within the squared range of: %f", p_dist_for_goal_reached_);
01459     return true;
01460   }
01461   return false;
01462 
01463 }
01464 
01465 bool HectorExplorationPlanner::isSameFrontier(int frontier_point1, int frontier_point2){
01466   unsigned int fx1,fy1;
01467   unsigned int fx2,fy2;
01468   double wfx1,wfy1;
01469   double wfx2,wfy2;
01470   costmap_.indexToCells(frontier_point1,fx1,fy1);
01471   costmap_.indexToCells(frontier_point2,fx2,fy2);
01472   costmap_.mapToWorld(fx1,fy1,wfx1,wfy1);
01473   costmap_.mapToWorld(fx2,fy2,wfx2,wfy2);
01474 
01475   double dx = wfx1 - wfx2;
01476   double dy = wfy1 - wfy2;
01477 
01478   if((dx*dx) + (dy*dy) < (p_same_frontier_dist_*p_same_frontier_dist_)){
01479     return true;
01480   }
01481   return false;
01482 }
01483 
01484 inline unsigned int HectorExplorationPlanner::cellDanger(int point){
01485 
01486   if((int)obstacle_trans_array_[point] <= p_min_obstacle_dist_){
01487     return p_alpha_ * std::pow(p_min_obstacle_dist_ - obstacle_trans_array_[point],2);
01488   }
01489   return 0;
01490 }
01491 
01492 float HectorExplorationPlanner::angleDifference(const geometry_msgs::PoseStamped &start, const geometry_msgs::PoseStamped &goal){
01493   // setup start positions
01494   unsigned int mxs,mys;
01495   costmap_.worldToMap(start.pose.position.x,start.pose.position.y,mxs,mys);
01496 
01497   unsigned int gx,gy;
01498   costmap_.worldToMap(goal.pose.position.x,goal.pose.position.y,gx,gy);
01499 
01500   int goal_proj_x = gx-mxs;
01501   int goal_proj_y = gy-mys;
01502 
01503   float start_angle = tf::getYaw(start.pose.orientation);
01504   float goal_angle = std::atan2(goal_proj_y,goal_proj_x);
01505 
01506   float both_angle = 0;
01507   if(start_angle > goal_angle){
01508     both_angle = start_angle - goal_angle;
01509   } else {
01510     both_angle = goal_angle - start_angle;
01511   }
01512 
01513   if(both_angle > M_PI){
01514     both_angle = (M_PI - std::abs(start_angle)) + (M_PI - std::abs(goal_angle));
01515   }
01516 
01517   return both_angle;
01518 }
01519 
01520 // Used to generate direction for frontiers
01521 double HectorExplorationPlanner::getYawToUnknown(int point){
01522   int adjacentPoints[8];
01523   getAdjacentPoints(point,adjacentPoints);
01524 
01525   int max_obs_idx = 0;
01526 
01527   for(int i = 0; i < 8; ++i){
01528     if(isValid(adjacentPoints[i])){
01529       if(occupancy_grid_array_[adjacentPoints[i]] == costmap_2d::NO_INFORMATION){
01530         if(obstacle_trans_array_[adjacentPoints[i]] > obstacle_trans_array_[adjacentPoints[max_obs_idx]]){
01531           max_obs_idx = i;
01532         }
01533       }
01534     }
01535   }
01536 
01537   int orientationPoint = adjacentPoints[max_obs_idx];
01538   unsigned int sx,sy,gx,gy;
01539   costmap_.indexToCells((unsigned int)point,sx,sy);
01540   costmap_.indexToCells((unsigned int)orientationPoint,gx,gy);
01541   int x = gx-sx;
01542   int y = gy-sy;
01543   double yaw = std::atan2(y,x);
01544 
01545   return yaw;
01546 
01547 }
01548 
01549 unsigned int HectorExplorationPlanner::angleDanger(float angle){
01550   float angle_fraction = std::pow(angle,3);
01551   unsigned int result = static_cast<unsigned int>(p_goal_angle_penalty_ * angle_fraction);
01552   return result;
01553 }
01554 
01555 float HectorExplorationPlanner::getDistanceWeight(const geometry_msgs::PoseStamped &point1, const geometry_msgs::PoseStamped &point2){
01556   float distance = std::sqrt(std::pow(point1.pose.position.x - point2.pose.position.x,2) + std::pow(point1.pose.position.y - point2.pose.position.y,2));
01557   if(distance < 0.5){
01558     return 5.0;
01559   } else {
01560     return 1;
01561   }
01562 }
01563 
01564 /*
01565  These functions calculate the index of an adjacent point (left,upleft,up,upright,right,downright,down,downleft) to the
01566  given point. If there is no such point (meaning the point would cause the index to be out of bounds), -1 is returned.
01567  */
01568 inline void HectorExplorationPlanner::getStraightPoints(int point, int points[]){
01569 
01570   points[0] = left(point);
01571   points[1] = up(point);
01572   points[2] = right(point);
01573   points[3] = down(point);
01574 
01575 }
01576 
01577 inline void HectorExplorationPlanner::getDiagonalPoints(int point, int points[]){
01578 
01579   points[0] = upleft(point);
01580   points[1] = upright(point);
01581   points[2] = downright(point);
01582   points[3] = downleft(point);
01583 
01584 }
01585 
01586 /*
01587 inline void HectorExplorationPlanner::getStraightAndDiagonalPoints(int point, int straight_points[], int diag_points[]){
01588   /
01589   // Can go up if index is larger than width
01590   bool up = (point >= (int)map_width_);
01591 
01592   // Can go down if
01593   bool down = ((point/map_width_) < (map_width_-1));
01594 
01595 
01596   bool right = ((point + 1) % map_width_ != 0);
01597   bool left = ((point % map_width_ != 0));
01598 
01599 }
01600 */
01601 
01602 inline void HectorExplorationPlanner::getAdjacentPoints(int point, int points[]){
01603 
01604   points[0] = left(point);
01605   points[1] = up(point);
01606   points[2] = right(point);
01607   points[3] = down(point);
01608   points[4] = upleft(point);
01609   points[5] = upright(point);
01610   points[6] = downright(point);
01611   points[7] = downleft(point);
01612 
01613 }
01614 
01615 inline int HectorExplorationPlanner::left(int point){
01616   // only go left if no index error and if current point is not already on the left boundary
01617   if((point % map_width_ != 0)){
01618     return point-1;
01619   }
01620   return -1;
01621 }
01622 inline int HectorExplorationPlanner::upleft(int point){
01623   if((point % map_width_ != 0) && (point >= (int)map_width_)){
01624     return point-map_width_-1;
01625   }
01626   return -1;
01627 
01628 }
01629 inline int HectorExplorationPlanner::up(int point){
01630   if(point >= (int)map_width_){
01631     return point-map_width_;
01632   }
01633   return -1;
01634 }
01635 inline int HectorExplorationPlanner::upright(int point){
01636   if((point >= (int)map_width_) && ((point + 1) % (int)map_width_ != 0)){
01637     return point-map_width_+1;
01638   }
01639   return -1;
01640 }
01641 inline int HectorExplorationPlanner::right(int point){
01642   if((point + 1) % map_width_ != 0){
01643     return point+1;
01644   }
01645   return -1;
01646 
01647 }
01648 inline int HectorExplorationPlanner::downright(int point){
01649   if(((point + 1) % map_width_ != 0) && ((point/map_width_) < (map_width_-1))){
01650     return point+map_width_+1;
01651   }
01652   return -1;
01653 
01654 }
01655 inline int HectorExplorationPlanner::down(int point){
01656   if((point/map_width_) < (map_width_-1)){
01657     return point+map_width_;
01658   }
01659   return -1;
01660 
01661 }
01662 inline int HectorExplorationPlanner::downleft(int point){
01663   if(((point/map_width_) < (map_width_-1)) && (point % map_width_ != 0)){
01664     return point+map_width_-1;
01665   }
01666   return -1;
01667 
01668 }
01669 
01670 //        // visualization (export to another method?)
01671 //        visualization_msgs::Marker marker;
01672 //        marker.header.frame_id = "map";
01673 //        marker.header.stamp = ros::Time();
01674 //        marker.ns = "hector_exploration_planner";
01675 //        marker.id = i + 500;
01676 //        marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
01677 //        marker.action = visualization_msgs::Marker::ADD;
01678 //        marker.pose.position = goals[i].pose.position;
01679 //        marker.scale.x = 0.2;
01680 //        marker.scale.y = 0.2;
01681 //        marker.scale.z = 0.2;
01682 //        marker.color.a = 1.0;
01683 //        marker.color.r = 0.0;
01684 //        marker.color.g = 0.0;
01685 //        marker.color.b = 1.0;
01686 //        marker.lifetime = ros::Duration(5,0);
01687 //        marker.text = boost::lexical_cast<std::string>((int)init_cost) + " - " + boost::lexical_cast<std::string>(getDistanceWeight(start,goals[i]));
01688 //        visualization_pub_.publish(marker);
01689 
01690 //void HectorExplorationPlanner::saveMaps(std::string path){
01691 
01692 //    char costmapPath[1000];
01693 //    sprintf(costmapPath,"%s.map",path.data());
01694 //    char explorationPath[1000];
01695 //    sprintf(explorationPath,"%s.expl",path.data());
01696 //    char obstaclePath[1000];
01697 //    sprintf(obstaclePath,"%s.obs",path.data());
01698 //    char frontierPath[1000];
01699 //    sprintf(frontierPath,"%s.front",path.data());
01700 
01701 
01702 //    costmap.saveMap(costmapPath);
01703 //    FILE *fp_expl = fopen(explorationPath,"w");
01704 //    FILE *fp_obs = fopen(obstaclePath,"w");
01705 //    FILE *fp_front = fopen(frontierPath,"w");
01706 
01707 //    if (!fp_expl || !fp_obs || !fp_front)
01708 //    {
01709 //        ROS_WARN("[hector_exploration_planner] Cannot save maps");
01710 //        return;
01711 //    }
01712 
01713 //    for(unsigned int y = 0; y < map_height_; ++y){
01714 //        for(unsigned int x = 0;x < map_width_; ++x){
01715 //            unsigned int expl = exploration_trans_array_[costmap.getIndex(x,y)];
01716 //            unsigned int obs = obstacle_trans_array_[costmap.getIndex(x,y)];
01717 //            int blobVal = frontier_map_array_[costmap.getIndex(x,y)];
01718 //            fprintf(fp_front,"%d\t", blobVal);
01719 //            fprintf(fp_expl,"%d\t", expl);
01720 //            fprintf(fp_obs, "%d\t", obs);
01721 //        }
01722 //        fprintf(fp_expl,"\n");
01723 //        fprintf(fp_obs,"\n");
01724 //        fprintf(fp_front,"\n");
01725 //    }
01726 
01727 //    fclose(fp_expl);
01728 //    fclose(fp_obs);
01729 //    fclose(fp_front);
01730 //    ROS_INFO("[hector_exploration_planner] Maps have been saved!");
01731 //    return;
01732 
01733 //}
01734 
01735 //    // add last point to path (goal point)
01736 //    for(unsigned int i = 0; i < goals.size(); ++i){
01737 //        unsigned int mx,my;
01738 //        costmap.worldToMap(goals[i].pose.position.x,goals[i].pose.position.y,mx,my);
01739 
01740 //        if(currentPoint == (int)costmap.getIndex(mx,my)){
01741 //            plan.push_back(goals[i]);
01742 //            previous_goal_ = currentPoint;
01743 //        }
01744 
01745 //    }
01746 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines


hector_exploration_planner
Author(s): Mark Sollweck, Stefan Kohlbrecher, Florian Berz
autogenerated on Mon Jul 15 2013 16:50:20