00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #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
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
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
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
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
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
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
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
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
00259
00260
00261
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
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
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
00343 if(getPlanFromPotential(best_pose, plan))
00344 {
00345
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
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
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
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
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
00446 plan.clear();
00447
00448
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
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
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
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
00501 publishPlan(plan, 0.0, 1.0, 0.0, 0.0);
00502 return !plan.empty();
00503 }
00504 };