navfn_ros.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2008, Willow Garage, Inc.
00006 *  All rights reserved.
00007 *
00008 *  Redistribution and use in source and binary forms, with or without
00009 *  modification, are permitted provided that the following conditions
00010 *  are met:
00011 *
00012 *   * Redistributions of source code must retain the above copyright
00013 *     notice, this list of conditions and the following disclaimer.
00014 *   * Redistributions in binary form must reproduce the above
00015 *     copyright notice, this list of conditions and the following
00016 *     disclaimer in the documentation and/or other materials provided
00017 *     with the distribution.
00018 *   * Neither the name of the Willow Garage nor the names of its
00019 *     contributors may be used to endorse or promote products derived
00020 *     from this software without specific prior written permission.
00021 *
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 *  POSSIBILITY OF SUCH DAMAGE.
00034 *
00035 * Author: Eitan Marder-Eppstein
00036 *********************************************************************/
00037 #include <navfn/navfn_ros.h>
00038 #include <pluginlib/class_list_macros.h>
00039 #include <tf/transform_listener.h>
00040 #include <costmap_2d/cost_values.h>
00041 #include <costmap_2d/costmap_2d.h>
00042 
00043 #include <pcl_conversions/pcl_conversions.h>
00044 
00045 //register this planner as a BaseGlobalPlanner plugin
00046 PLUGINLIB_DECLARE_CLASS(navfn, NavfnROS, navfn::NavfnROS, nav_core::BaseGlobalPlanner)
00047 
00048 namespace navfn
00049 {
00050 
00051     NavfnROS::NavfnROS()
00052         : costmap_ros_(NULL),  planner_(), initialized_(false), allow_unknown_(true) {}
00053 
00054     NavfnROS::NavfnROS(std::string name, costmap_2d::Costmap2DROS* costmap_ros)
00055         : costmap_ros_(NULL),  planner_(), initialized_(false), allow_unknown_(true)
00056     {
00057         //initialize the planner
00058         initialize(name, costmap_ros);
00059     }
00060 
00061     void NavfnROS::initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros)
00062     {
00063         if(!initialized_)
00064         {
00065             costmap_ros_ = costmap_ros;
00066             costmap_2d::Costmap2D* costmap = costmap_ros_->getCostmap();
00067             planner_ = boost::shared_ptr<NavFn>(new NavFn(costmap->getSizeInCellsX(), costmap->getSizeInCellsY()));
00068 
00069             ros::NodeHandle private_nh("~/Navfn");
00070 
00071             plan_pub_ = private_nh.advertise<nav_msgs::Path>("plan", 1);
00072 
00073             private_nh.param("visualize_potential", visualize_potential_, false);
00074 
00075             //if we're going to visualize the potential array we need to advertise
00076             if(visualize_potential_)
00077                 potarr_pub_.advertise(private_nh, "potential", 1);
00078 
00079             private_nh.param("allow_unknown", allow_unknown_, true);
00080             private_nh.param("planner_window_x", planner_window_x_, 0.0);
00081             private_nh.param("planner_window_y", planner_window_y_, 0.0);
00082             private_nh.param("default_tolerance", default_tolerance_, 0.0);
00083 
00084             //get the tf prefix
00085             ros::NodeHandle prefix_nh;
00086             tf_prefix_ = tf::getPrefixParam(prefix_nh);
00087 
00088             make_plan_srv_ =  private_nh.advertiseService("make_plan", &NavfnROS::makePlanService, this);
00089 
00090             initialized_ = true;
00091         }
00092         else
00093             ROS_WARN("This planner has already been initialized, you can't call it twice, doing nothing");
00094     }
00095 
00096     bool NavfnROS::validPointPotential(const geometry_msgs::Point& world_point)
00097     {
00098         return validPointPotential(world_point, default_tolerance_);
00099     }
00100 
00101     bool NavfnROS::validPointPotential(const geometry_msgs::Point& world_point, double tolerance)
00102     {
00103         if(!initialized_)
00104         {
00105             ROS_ERROR("This planner has not been initialized yet, but it is being used, please call initialize() before use");
00106             return false;
00107         }
00108 
00109         double resolution = costmap_ros_->getCostmap()->getResolution();
00110         geometry_msgs::Point p;
00111         p = world_point;
00112 
00113         p.y = world_point.y - tolerance;
00114 
00115         while(p.y <= world_point.y + tolerance)
00116         {
00117             p.x = world_point.x - tolerance;
00118             while(p.x <= world_point.x + tolerance)
00119             {
00120                 double potential = getPointPotential(p);
00121                 if(potential < POT_HIGH)
00122                 {
00123                     return true;
00124                 }
00125                 p.x += resolution;
00126             }
00127             p.y += resolution;
00128         }
00129 
00130         return false;
00131     }
00132 
00133     double NavfnROS::getPointPotential(const geometry_msgs::Point& world_point)
00134     {
00135         if(!initialized_)
00136         {
00137             ROS_ERROR("This planner has not been initialized yet, but it is being used, please call initialize() before use");
00138             return -1.0;
00139         }
00140 
00141         unsigned int mx, my;
00142         if(!costmap_ros_->getCostmap()->worldToMap(world_point.x, world_point.y, mx, my))
00143             return DBL_MAX;
00144 
00145         unsigned int index = my * planner_->nx + mx;
00146         return planner_->potarr[index];
00147     }
00148 
00149     bool NavfnROS::computePotential(const geometry_msgs::Point& world_point)
00150     {
00151         if(!initialized_)
00152         {
00153             ROS_ERROR("This planner has not been initialized yet, but it is being used, please call initialize() before use");
00154             return false;
00155         }
00156 
00157         costmap_2d::Costmap2D* costmap = costmap_ros_->getCostmap();
00158 
00159         //make sure to resize the underlying array that Navfn uses
00160         planner_->setNavArr(costmap->getSizeInCellsX(), costmap->getSizeInCellsY());
00161         planner_->setCostmap(costmap->getCharMap(), true, allow_unknown_);
00162 
00163         unsigned int mx, my;
00164         if(!costmap->worldToMap(world_point.x, world_point.y, mx, my))
00165             return false;
00166 
00167         int map_start[2];
00168         map_start[0] = 0;
00169         map_start[1] = 0;
00170 
00171         int map_goal[2];
00172         map_goal[0] = mx;
00173         map_goal[1] = my;
00174 
00175         planner_->setStart(map_start);
00176         planner_->setGoal(map_goal);
00177 
00178         return planner_->calcNavFnDijkstra();
00179     }
00180 
00181     void NavfnROS::clearRobotCell(const tf::Stamped<tf::Pose>& global_pose, unsigned int mx, unsigned int my)
00182     {
00183         if(!initialized_)
00184         {
00185             ROS_ERROR("This planner has not been initialized yet, but it is being used, please call initialize() before use");
00186             return;
00187         }
00188 
00189         //set the associated costs in the cost map to be free
00190         costmap_ros_->getCostmap()->setCost(mx, my, costmap_2d::FREE_SPACE);
00191     }
00192 
00193     bool NavfnROS::makePlanService(nav_msgs::GetPlan::Request& req, nav_msgs::GetPlan::Response& resp)
00194     {
00195         makePlan(req.start, req.goal, resp.plan.poses);
00196 
00197         resp.plan.header.stamp = ros::Time::now();
00198         resp.plan.header.frame_id = costmap_ros_->getGlobalFrameID();
00199 
00200         return true;
00201     }
00202 
00203     void NavfnROS::mapToWorld(double mx, double my, double& wx, double& wy)
00204     {
00205         costmap_2d::Costmap2D* costmap = costmap_ros_->getCostmap();
00206         wx = costmap->getOriginX() + mx * costmap->getResolution();
00207         wy = costmap->getOriginY() + my * costmap->getResolution();
00208     }
00209 
00210     bool NavfnROS::makePlan(const geometry_msgs::PoseStamped& start,
00211                             const geometry_msgs::PoseStamped& goal, std::vector<geometry_msgs::PoseStamped>& plan)
00212     {
00213         return makePlan(start, goal, default_tolerance_, plan);
00214     }
00215 
00216     bool NavfnROS::makePlan(const geometry_msgs::PoseStamped& start,
00217                             const geometry_msgs::PoseStamped& goal, double tolerance, std::vector<geometry_msgs::PoseStamped>& plan)
00218     {
00219         boost::mutex::scoped_lock lock(mutex_);
00220         if(!initialized_)
00221         {
00222             ROS_ERROR("This planner has not been initialized yet, but it is being used, please call initialize() before use");
00223             return false;
00224         }
00225 
00226         //clear the plan, just in case
00227         plan.clear();
00228 
00229         ros::NodeHandle n;
00230         costmap_2d::Costmap2D* costmap = costmap_ros_->getCostmap();
00231         std::string global_frame = costmap_ros_->getGlobalFrameID();
00232 
00233         //until tf can handle transforming things that are way in the past... we'll require the goal to be in our global frame
00234         if(tf::resolve(tf_prefix_, goal.header.frame_id) != tf::resolve(tf_prefix_, global_frame))
00235         {
00236             ROS_ERROR("The goal pose passed to this planner must be in the %s frame.  It is instead in the %s frame.",
00237                       tf::resolve(tf_prefix_, global_frame).c_str(), tf::resolve(tf_prefix_, goal.header.frame_id).c_str());
00238             return false;
00239         }
00240 
00241         if(tf::resolve(tf_prefix_, start.header.frame_id) != tf::resolve(tf_prefix_, global_frame))
00242         {
00243             ROS_ERROR("The start pose passed to this planner must be in the %s frame.  It is instead in the %s frame.",
00244                       tf::resolve(tf_prefix_, global_frame).c_str(), tf::resolve(tf_prefix_, start.header.frame_id).c_str());
00245             return false;
00246         }
00247 
00248         double wx = start.pose.position.x;
00249         double wy = start.pose.position.y;
00250 
00251         unsigned int mx, my;
00252         if(!costmap->worldToMap(wx, wy, mx, my))
00253         {
00254             ROS_WARN("The robot's start position is off the global costmap. Planning will always fail, are you sure the robot has been properly localized?");
00255             return false;
00256         }
00257 
00258         //clear the starting cell within the costmap because we know it can't be an obstacle
00259        // tf::Stamped<tf::Pose> start_pose;
00260        // tf::poseStampedMsgToTF(start, start_pose);
00261        // clearRobotCell(start_pose, mx, my);
00262 
00263 #if 0
00264         {
00265             static int n = 0;
00266             static char filename[1000];
00267             snprintf( filename, 1000, "navfnros-makeplan-costmapB-%04d.pgm", n++ );
00268             costmap->saveRawMap( std::string( filename ));
00269         }
00270 #endif
00271 
00272         //make sure to resize the underlying array that Navfn uses
00273         planner_->setNavArr(costmap->getSizeInCellsX(), costmap->getSizeInCellsY());
00274         planner_->setCostmap(costmap->getCharMap(), true, allow_unknown_);
00275 
00276 #if 0
00277         {
00278             static int n = 0;
00279             static char filename[1000];
00280             snprintf( filename, 1000, "navfnros-makeplan-costmapC-%04d", n++ );
00281             planner_->savemap( filename );
00282         }
00283 #endif
00284 
00285         int map_start[2];
00286         map_start[0] = mx;
00287         map_start[1] = my;
00288 
00289         wx = goal.pose.position.x;
00290         wy = goal.pose.position.y;
00291 
00292         if(!costmap->worldToMap(wx, wy, mx, my))
00293         {
00294             if(tolerance <= 0.0)
00295             {
00296                 ROS_WARN_THROTTLE(1.0, "The goal sent to the navfn planner is off the global costmap. Planning will always fail to this goal.");
00297                 return false;
00298             }
00299             mx = 0;
00300             my = 0;
00301         }
00302 
00303         int map_goal[2];
00304         map_goal[0] = mx;
00305         map_goal[1] = my;
00306 
00307         planner_->setStart(map_goal);
00308         planner_->setGoal(map_start);
00309 
00310         //bool success = planner_->calcNavFnAstar();
00311         planner_->calcNavFnDijkstra(true);
00312 
00313         double resolution = costmap->getResolution();
00314         geometry_msgs::PoseStamped p, best_pose;
00315         p = goal;
00316 
00317         bool found_legal = false;
00318         double best_sdist = DBL_MAX;
00319 
00320         p.pose.position.y = goal.pose.position.y - tolerance;
00321 
00322         while(p.pose.position.y <= goal.pose.position.y + tolerance)
00323         {
00324             p.pose.position.x = goal.pose.position.x - tolerance;
00325             while(p.pose.position.x <= goal.pose.position.x + tolerance)
00326             {
00327                 double potential = getPointPotential(p.pose.position);
00328                 double sdist = sq_distance(p, goal);
00329                 if(potential < POT_HIGH && sdist < best_sdist)
00330                 {
00331                     best_sdist = sdist;
00332                     best_pose = p;
00333                     found_legal = true;
00334                 }
00335                 p.pose.position.x += resolution;
00336             }
00337             p.pose.position.y += resolution;
00338         }
00339 
00340         if(found_legal)
00341         {
00342             //extract the plan
00343             if(getPlanFromPotential(best_pose, plan))
00344             {
00345                 //make sure the goal we push on has the same timestamp as the rest of the plan
00346                 geometry_msgs::PoseStamped goal_copy = best_pose;
00347                 goal_copy.header.stamp = ros::Time::now();
00348                 plan.push_back(goal_copy);
00349             }
00350             else
00351             {
00352                 ROS_ERROR("Failed to get a plan from potential when a legal potential was found. This shouldn't happen.");
00353             }
00354         }
00355 
00356         if (visualize_potential_)
00357         {
00358             //publish potential array
00359             pcl::PointCloud<PotarrPoint> pot_area;
00360             pot_area.header.frame_id = global_frame;
00361             pot_area.points.clear();
00362             std_msgs::Header header;
00363             pcl_conversions::fromPCL(pot_area.header, header);
00364             header.stamp = ros::Time::now();
00365             pot_area.header = pcl_conversions::toPCL(header);
00366 
00367             PotarrPoint pt;
00368             float *pp = planner_->potarr;
00369             double pot_x, pot_y;
00370             for (unsigned int i = 0; i < (unsigned int)planner_->ny*planner_->nx ; i++)
00371             {
00372                 if (pp[i] < 10e7)
00373                 {
00374                     mapToWorld(i%planner_->nx, i/planner_->nx, pot_x, pot_y);
00375                     pt.x = pot_x;
00376                     pt.y = pot_y;
00377                     pt.z = pp[i]/pp[planner_->start[1]*planner_->nx + planner_->start[0]]*20;
00378                     pt.pot_value = pp[i];
00379                     pot_area.push_back(pt);
00380                 }
00381             }
00382             potarr_pub_.publish(pot_area);
00383         }
00384 
00385 
00386 
00387         unsigned int mx2;
00388         unsigned int my2;
00389         ROS_DEBUG("Navfn: Gloal position: x %f, y %f",plan.back().pose.position.x,plan.back().pose.position.y );
00390         costmap->worldToMap(plan.back().pose.position.x, plan.back().pose.position.y, mx2, my2);
00391 
00392         if(costmap->getCost(mx2,my2) > 180)
00393         {
00394             ROS_DEBUG("Navfn: Goal in obstabel.");
00395             while(costmap->getCost(mx2,my2) > 180){
00396                 plan.pop_back();
00397                 costmap->worldToMap(plan.back().pose.position.x, plan.back().pose.position.y, mx2, my2);
00398             }
00399             plan.back().pose.orientation = goal.pose.orientation;
00400         }
00401         //publish the plan for visualization purposes
00402         publishPlan(plan, 0.0, 1.0, 0.0, 0.0);
00403 
00404         return !plan.empty();
00405     }
00406 
00407     void NavfnROS::publishPlan(const std::vector<geometry_msgs::PoseStamped>& path, double r, double g, double b, double a)
00408     {
00409         if(!initialized_)
00410         {
00411             ROS_ERROR("This planner has not been initialized yet, but it is being used, please call initialize() before use");
00412             return;
00413         }
00414 
00415         //create a message for the plan
00416         nav_msgs::Path gui_path;
00417         gui_path.poses.resize(path.size());
00418 
00419         if(!path.empty())
00420         {
00421             gui_path.header.frame_id = path[0].header.frame_id;
00422             gui_path.header.stamp = path[0].header.stamp;
00423         }
00424 
00425         // Extract the plan in world co-ordinates, we assume the path is all in the same frame
00426         for(unsigned int i=0; i < path.size(); i++)
00427         {
00428             gui_path.poses[i] = path[i];
00429         }
00430 
00431         plan_pub_.publish(gui_path);
00432     }
00433 
00434     bool NavfnROS::getPlanFromPotential(const geometry_msgs::PoseStamped& goal, std::vector<geometry_msgs::PoseStamped>& plan)
00435     {
00436         if(!initialized_)
00437         {
00438             ROS_ERROR("This planner has not been initialized yet, but it is being used, please call initialize() before use");
00439             return false;
00440         }
00441 
00442         costmap_2d::Costmap2D* costmap = costmap_ros_->getCostmap();
00443         std::string global_frame = costmap_ros_->getGlobalFrameID();
00444 
00445         //clear the plan, just in case
00446         plan.clear();
00447 
00448         //until tf can handle transforming things that are way in the past... we'll require the goal to be in our global frame
00449         if(tf::resolve(tf_prefix_, goal.header.frame_id) != tf::resolve(tf_prefix_, global_frame))
00450         {
00451             ROS_ERROR("The goal pose passed to this planner must be in the %s frame.  It is instead in the %s frame.",
00452                       tf::resolve(tf_prefix_, global_frame).c_str(), tf::resolve(tf_prefix_, goal.header.frame_id).c_str());
00453             return false;
00454         }
00455 
00456         double wx = goal.pose.position.x;
00457         double wy = goal.pose.position.y;
00458 
00459         //the potential has already been computed, so we won't update our copy of the costmap
00460         unsigned int mx, my;
00461         if(!costmap->worldToMap(wx, wy, mx, my))
00462         {
00463             ROS_WARN_THROTTLE(1.0, "The goal sent to the navfn planner is off the global costmap. Planning will always fail to this goal.");
00464             return false;
00465         }
00466 
00467         int map_goal[2];
00468         map_goal[0] = mx;
00469         map_goal[1] = my;
00470 
00471         planner_->setStart(map_goal);
00472 
00473         planner_->calcPath(costmap->getSizeInCellsX() * 4);
00474 
00475         //extract the plan
00476         float *x = planner_->getPathX();
00477         float *y = planner_->getPathY();
00478         int len = planner_->getPathLen();
00479         ros::Time plan_time = ros::Time::now();
00480 
00481         for(int i = len - 1; i >= 0; --i)
00482         {
00483             //convert the plan to world coordinates
00484             double world_x, world_y;
00485             mapToWorld(x[i], y[i], world_x, world_y);
00486 
00487             geometry_msgs::PoseStamped pose;
00488             pose.header.stamp = plan_time;
00489             pose.header.frame_id = global_frame;
00490             pose.pose.position.x = world_x;
00491             pose.pose.position.y = world_y;
00492             pose.pose.position.z = 0.0;
00493             pose.pose.orientation.x = 0.0;
00494             pose.pose.orientation.y = 0.0;
00495             pose.pose.orientation.z = 0.0;
00496             pose.pose.orientation.w = 1.0;
00497             plan.push_back(pose);
00498         }
00499 
00500         //publish the plan for visualization purposes
00501         publishPlan(plan, 0.0, 1.0, 0.0, 0.0);
00502         return !plan.empty();
00503     }
00504 };


asr_navfn
Author(s): Kurt Konolige, Eitan Marder-Eppstein, contradict@gmail.com, Felix Marek
autogenerated on Thu Jun 6 2019 21:20:09