00001
00002
00003
00004
00005
00006 #include <voronoi_planner/planner_core.h>
00007 #include <pluginlib/class_list_macros.h>
00008 #include <tf/transform_listener.h>
00009 #include <costmap_2d/cost_values.h>
00010 #include <costmap_2d/costmap_2d.h>
00011
00012
00013
00014 PLUGINLIB_EXPORT_CLASS(voronoi_planner::VoronoiPlanner, nav_core::BaseGlobalPlanner)
00015
00016 namespace voronoi_planner {
00017
00018
00019 void visualize(const char *filename, DynamicVoronoi* voronoi, bool** map,
00020 std::vector<std::pair<float, float> > *path) {
00021
00022
00023 FILE* F = fopen(filename, "w");
00024 if (!F) {
00025 std::cerr << "visualize: could not open file for writing!\n";
00026 return;
00027 }
00028 fprintf(F, "P6\n");
00029 fprintf(F, "%d %d 255\n", voronoi->getSizeX(), voronoi->getSizeY());
00030
00031
00032
00033 for(int y = voronoi->getSizeY()-1; y >=0; y--){
00034 for(int x = 0; x<voronoi->getSizeX(); x++){
00035 unsigned char c = 0;
00036 if (voronoi->isVoronoi(x,y) && map[x][y] != 1) {
00037 fputc( 255, F );
00038 fputc( 0, F );
00039 fputc( 0, F );
00040 } else if (map[x][y] == 1 ) {
00041 fputc( 255, F );
00042 fputc( 255, F );
00043 fputc( 255, F );
00044 }
00045 else
00046 {
00047 fputc( 0, F );
00048 fputc( 0, F );
00049 fputc( 0, F );
00050 }
00051 }
00052 }
00053
00054
00055 fclose(F);
00056 }
00057
00058
00059
00060 void VoronoiPlanner::outlineMap(unsigned char* costarr, int nx, int ny, unsigned char value) {
00061 unsigned char* pc = costarr;
00062 for (int i = 0; i < nx; i++)
00063 *pc++ = value;
00064 pc = costarr + (ny - 1) * nx;
00065 for (int i = 0; i < nx; i++)
00066 *pc++ = value;
00067 pc = costarr;
00068 for (int i = 0; i < ny; i++, pc += nx)
00069 *pc = value;
00070 pc = costarr + nx - 1;
00071 for (int i = 0; i < ny; i++, pc += nx)
00072 *pc = value;
00073 }
00074
00075 VoronoiPlanner::VoronoiPlanner() :
00076 costmap_(NULL), initialized_(false), publish_voronoi_grid_(true),
00077 smooth_path_ (true), weight_data_ (0.5), weight_smooth_ (0.3) {
00078 }
00079
00080 VoronoiPlanner::VoronoiPlanner(std::string name, costmap_2d::Costmap2D* costmap, std::string frame_id) :
00081 costmap_(NULL), initialized_(false), publish_voronoi_grid_(true),
00082 smooth_path_ (true), weight_data_ (0.5), weight_smooth_ (0.3)
00083 {
00084
00085 initialize(name, costmap, frame_id);
00086 }
00087
00088 VoronoiPlanner::~VoronoiPlanner() {
00089
00090 }
00091
00092 void VoronoiPlanner::initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros) {
00093 initialize(name, costmap_ros->getCostmap(), costmap_ros->getGlobalFrameID());
00094 }
00095
00096 void VoronoiPlanner::initialize(std::string name, costmap_2d::Costmap2D* costmap, std::string frame_id) {
00097 if (!initialized_) {
00098 ros::NodeHandle private_nh("~/" + name);
00099 costmap_ = costmap;
00100 frame_id_ = frame_id;
00101
00102 unsigned int cx = costmap->getSizeInCellsX(), cy = costmap->getSizeInCellsY();
00103
00104
00105 plan_pub_ = private_nh.advertise<nav_msgs::Path>("plan", 1);
00106 voronoi_grid_pub_ = private_nh.advertise<nav_msgs::OccupancyGrid>("voronoi_grid", 1);
00107
00108
00109
00110 ros::NodeHandle prefix_nh;
00111 tf_prefix_ = tf::getPrefixParam(prefix_nh);
00112
00113 make_plan_srv_ = private_nh.advertiseService("make_plan", &VoronoiPlanner::makePlanService, this);
00114
00115 dsrv_ = new dynamic_reconfigure::Server<voronoi_planner::VoronoiPlannerConfig>(ros::NodeHandle("~/" + name));
00116 dynamic_reconfigure::Server<voronoi_planner::VoronoiPlannerConfig>::CallbackType cb = boost::bind(
00117 &VoronoiPlanner::reconfigureCB, this, _1, _2);
00118 dsrv_->setCallback(cb);
00119
00120
00121 ros::Subscriber costmapUpdateSubscriber = private_nh.subscribe("/move_base/global_costmap/costmap_updates", 10, &VoronoiPlanner::costmapUpdateCallback, this);
00122
00123
00124
00125 initialized_ = true;
00126 } else
00127 ROS_WARN("This planner has already been initialized, you can't call it twice, doing nothing");
00128
00129 }
00130
00131 void VoronoiPlanner::costmapUpdateCallback(const map_msgs::OccupancyGridUpdate::ConstPtr& msg)
00132 {
00133 ROS_INFO("Map update. x %d; y %d; w %d; h %d", msg->x, msg->y, msg->width, msg->height);
00134 }
00135
00136
00137 void VoronoiPlanner::reconfigureCB(voronoi_planner::VoronoiPlannerConfig& config, uint32_t level) {
00138 weight_data_ = config.weight_data;
00139 weight_smooth_ = config.weight_smooth;
00140
00141 publish_voronoi_grid_ = config.publish_voronoi_grid;
00142 smooth_path_ = config.smooth_path;
00143 }
00144
00145 void VoronoiPlanner::clearRobotCell(const tf::Stamped<tf::Pose>& global_pose, unsigned int mx, unsigned int my) {
00146 if (!initialized_) {
00147 ROS_ERROR(
00148 "This planner has not been initialized yet, but it is being used, please call initialize() before use");
00149 return;
00150 }
00151
00152
00153 costmap_->setCost(mx, my, costmap_2d::FREE_SPACE);
00154 }
00155
00156 bool VoronoiPlanner::makePlanService(nav_msgs::GetPlan::Request& req, nav_msgs::GetPlan::Response& resp) {
00157 makePlan(req.start, req.goal, resp.plan.poses);
00158
00159 resp.plan.header.stamp = ros::Time::now();
00160 resp.plan.header.frame_id = frame_id_;
00161
00162 return true;
00163 }
00164
00165 void VoronoiPlanner::mapToWorld(double mx, double my, double& wx, double& wy) {
00166 float convert_offset_ = 0;
00167 wx = costmap_->getOriginX() + (mx+convert_offset_) * costmap_->getResolution();
00168 wy = costmap_->getOriginY() + (my+convert_offset_) * costmap_->getResolution();
00169 }
00170
00171 bool VoronoiPlanner::worldToMap(double wx, double wy, double& mx, double& my) {
00172 double origin_x = costmap_->getOriginX(), origin_y = costmap_->getOriginY();
00173 double resolution = costmap_->getResolution();
00174
00175 if (wx < origin_x || wy < origin_y)
00176 return false;
00177
00178 float convert_offset_ = 0;
00179 mx = (wx - origin_x) / resolution - convert_offset_;
00180 my = (wy - origin_y) / resolution - convert_offset_;
00181
00182 if (mx < costmap_->getSizeInCellsX() && my < costmap_->getSizeInCellsY())
00183 return true;
00184
00185 return false;
00186 }
00187
00188 bool VoronoiPlanner::makePlan(const geometry_msgs::PoseStamped& start, const geometry_msgs::PoseStamped& goal,
00189 std::vector<geometry_msgs::PoseStamped>& plan) {
00190 return makePlan(start, goal, default_tolerance_, plan);
00191 }
00192
00193 bool VoronoiPlanner::makePlan(const geometry_msgs::PoseStamped& start, const geometry_msgs::PoseStamped& goal,
00194 double tolerance, std::vector<geometry_msgs::PoseStamped>& plan) {
00195 boost::mutex::scoped_lock lock(mutex_);
00196 if (!initialized_) {
00197 ROS_ERROR(
00198 "This planner has not been initialized yet, but it is being used, please call initialize() before use");
00199 return false;
00200 }
00201
00202
00203 plan.clear();
00204
00205 ros::NodeHandle n;
00206 std::string global_frame = frame_id_;
00207
00208
00209 if (tf::resolve(tf_prefix_, goal.header.frame_id) != tf::resolve(tf_prefix_, global_frame)) {
00210 ROS_ERROR(
00211 "The goal pose passed to this planner must be in the %s frame. It is instead in the %s frame.", tf::resolve(tf_prefix_, global_frame).c_str(), tf::resolve(tf_prefix_, goal.header.frame_id).c_str());
00212 return false;
00213 }
00214
00215 if (tf::resolve(tf_prefix_, start.header.frame_id) != tf::resolve(tf_prefix_, global_frame)) {
00216 ROS_ERROR(
00217 "The start pose passed to this planner must be in the %s frame. It is instead in the %s frame.", tf::resolve(tf_prefix_, global_frame).c_str(), tf::resolve(tf_prefix_, start.header.frame_id).c_str());
00218 return false;
00219 }
00220
00221 double wx = start.pose.position.x;
00222 double wy = start.pose.position.y;
00223
00224 unsigned int start_x_i, start_y_i, goal_x_i, goal_y_i;
00225 double start_x, start_y, goal_x, goal_y;
00226
00227 if (!costmap_->worldToMap(wx, wy, start_x_i, start_y_i)) {
00228 ROS_WARN(
00229 "The robot's start position is off the global costmap. Planning will always fail, are you sure the robot has been properly localized?");
00230 return false;
00231 }
00232 worldToMap(wx, wy, start_x, start_y);
00233
00234
00235 wx = goal.pose.position.x;
00236 wy = goal.pose.position.y;
00237
00238 if (!costmap_->worldToMap(wx, wy, goal_x_i, goal_y_i)) {
00239 ROS_WARN_THROTTLE(1.0,
00240 "The goal sent to the global planner is off the global costmap. Planning will always fail to this goal.");
00241 return false;
00242 }
00243 worldToMap(wx, wy, goal_x, goal_y);
00244
00245
00246
00247 tf::Stamped<tf::Pose> start_pose;
00248 tf::poseStampedMsgToTF(start, start_pose);
00249 clearRobotCell(start_pose, start_x_i, start_y_i);
00250
00251 int nx = costmap_->getSizeInCellsX(), ny = costmap_->getSizeInCellsY();
00252
00253 outlineMap(costmap_->getCharMap(), nx, ny, costmap_2d::LETHAL_OBSTACLE);
00254
00255
00256
00257
00258
00259 bool **map=NULL;
00260 int sizeX, sizeY;
00261
00262 sizeX = costmap_->getSizeInCellsX();
00263 sizeY = costmap_->getSizeInCellsY();
00264
00265
00266 map = new bool*[sizeX];
00267
00268
00269
00270 ros::Time t = ros::Time::now();
00271 ros::Time t_b = ros::Time::now();
00272
00273
00274 for (int x=0; x<sizeX; x++) {
00275 (map)[x] = new bool[sizeY];
00276 }
00277
00278 for (int y=sizeY-1; y>=0; y--) {
00279 for (int x=0; x<sizeX; x++) {
00280 unsigned char c = costmap_->getCost(x,y);
00281
00282 if ( c == costmap_2d::FREE_SPACE || c == costmap_2d::NO_INFORMATION )
00283 (map)[x][y] = false;
00284 else (map)[x][y] = true;
00285 }
00286 }
00287
00288 ROS_INFO("Time (for map convert): %f sec", (ros::Time::now() - t).toSec());
00289 t = ros::Time::now();
00290
00291
00292 bool doPrune = true;
00293
00294
00295
00296
00297 ROS_INFO("voronoi.initializeMap");
00298 voronoi_.initializeMap(sizeX, sizeY, map);
00299 ROS_INFO("Time (for initializeMap): %f sec", (ros::Time::now() - t).toSec());
00300 t = ros::Time::now();
00301
00302
00303
00304 ROS_INFO("voronoi.update");
00305 voronoi_.update();
00306 ROS_INFO("Time (for update): %f sec", (ros::Time::now() - t).toSec());
00307 t = ros::Time::now();
00308
00309
00310
00311 ROS_INFO("voronoi.prune");
00312 if (doPrune) voronoi_.prune();
00313 ROS_INFO("Time (for prune): %f sec", (ros::Time::now() - t).toSec());
00314 t = ros::Time::now();
00315
00316
00317
00318
00319
00320
00321
00322
00323 std::cerr << "Generated initial frame.\n";
00324
00325
00326
00327 std::vector<std::pair<float, float> > path1;
00328 std::vector<std::pair<float, float> > path2;
00329 std::vector<std::pair<float, float> > path3;
00330
00331
00332
00333
00334
00335
00336
00337 std::cout << "start_x,start_y " << start_x <<
00338 " " << start_y << std::endl;
00339 std::cout << "goal_x,goal_y " << goal_x <<
00340 " " << goal_y << std::endl;
00341
00342
00343 bool res1 = false, res2 = false, res3 = false;
00344
00345 if( !voronoi_.isVoronoi(goal_x,goal_y) )
00346 {
00347
00348 res3 = findPath( &path3, goal_x, goal_y, start_x, start_y, &voronoi_, 0, 1 );
00349 std::cout << "findPath 3 res " << res3 << std::endl;
00350
00351 goal_x = std::get<0>( path3[path3.size()-1] );
00352 goal_y = std::get<1>( path3[path3.size()-1] );
00353
00354 std::cout << "voronoi.isVoronoi(goal_x,goal_y) " << voronoi_.isVoronoi(goal_x,goal_y) << std::endl;
00355
00356
00357
00358 std::reverse(path3.begin(), path3.end());
00359 }
00360
00361 if( !voronoi_.isVoronoi(start_x,start_y) )
00362 {
00363 res1 = findPath( &path1, start_x, start_y, goal_x, goal_y, &voronoi_, 0, 1 );
00364 std::cout << "findPath 1 res " << res1 << std::endl;
00365 start_x = std::get<0>( path1[path1.size()-1] );
00366 start_y = std::get<1>( path1[path1.size()-1] );
00367
00368 std::cout << "voronoi.isVoronoi(start_x,start_y) " << voronoi_.isVoronoi(start_x,start_y) << std::endl;
00369 }
00370
00371 res2 = findPath( &path2, start_x, start_y, goal_x, goal_y, &voronoi_, 1, 0 );
00372 std::cout << "findPath 2 res " << res2 << std::endl;
00373
00374
00375 if(!(res1 && res2 && res3))
00376 {
00377 ROS_INFO("Failed to find full path");
00378 }
00379
00380
00381
00382
00383 path1.insert( path1.end(), path2.begin(), path2.end() );
00384 path1.insert( path1.end(), path3.begin(), path3.end() );
00385
00386
00387 for(int i = 0; i < path1.size(); i++)
00388 {
00389 int x = std::get<0>(path1[i]);
00390 int y = std::get<1>(path1[i]);
00391
00392
00393
00394
00395
00396 if(x > 0 && y > 0)
00397 map[x][y] = 1;
00398 }
00399
00400
00401
00402
00403
00404
00405
00406
00407 for(int i = 0; i < path1.size(); i++)
00408 {
00409
00410 geometry_msgs::PoseStamped new_goal = goal;
00411 tf::Quaternion goal_quat = tf::createQuaternionFromYaw(1.54);
00412
00413 new_goal.pose.position.x = std::get<0>(path1[i]);
00414 new_goal.pose.position.y = std::get<1>(path1[i]);
00415
00416 mapToWorld(new_goal.pose.position.x, new_goal.pose.position.y,
00417 new_goal.pose.position.x, new_goal.pose.position.y);
00418
00419
00420
00421
00422
00423
00424 new_goal.pose.orientation.x = goal_quat.x();
00425 new_goal.pose.orientation.y = goal_quat.y();
00426 new_goal.pose.orientation.z = goal_quat.z();
00427 new_goal.pose.orientation.w = goal_quat.w();
00428 plan.push_back( new_goal );
00429 }
00430
00431
00432
00433
00434
00435
00436 ROS_ERROR("\nTime to get plan: %f sec\n", (ros::Time::now() - t_b).toSec());
00437
00438
00439
00440 publishPlan(plan);
00441
00442 if(publish_voronoi_grid_){
00443 publishVoronoiGrid(&voronoi_);
00444 }
00445
00446
00447 return !plan.empty();
00448 }
00449
00450 bool VoronoiPlanner::findPath(std::vector<std::pair<float, float> > *path,
00451 int init_x, int init_y,
00452 int goal_x, int goal_y,
00453 DynamicVoronoi *voronoi,
00454 bool check_is_voronoi_cell,
00455 bool stop_at_voronoi )
00456 {
00457
00458
00459
00460 std::vector<std::pair<int, int> > delta;
00461 delta.push_back( {-1, 0} );
00462 delta.push_back( {0, -1} );
00463 delta.push_back( {1, 0} );
00464 delta.push_back( {0, 1} );
00465 delta.push_back( {-1, -1} );
00466 delta.push_back( {-1, 1} );
00467 delta.push_back( {1, -1} );
00468 delta.push_back( {1, 1} );
00469
00470
00471 float cost = 1;
00472
00473
00474 unsigned int sizeX = voronoi->getSizeX();
00475 unsigned int sizeY = voronoi->getSizeY();
00476
00477
00478 bool **closed=NULL;
00479 closed = new bool*[sizeX];
00480 for (int x=0; x<sizeX; x++) {
00481 (closed)[x] = new bool[sizeY];
00482 }
00483
00484 for (int y=sizeY-1; y>=0; y--) {
00485 for (int x=0; x<sizeX; x++) {
00486 (closed)[x][y] = false;
00487 }
00488 }
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498 int **action=NULL;
00499 action = new int*[sizeX];
00500 for (int x=0; x<sizeX; x++) {
00501 (action)[x] = new int[sizeY];
00502 }
00503 for (int y=sizeY-1; y>=0; y--) {
00504 for (int x=0; x<sizeX; x++) {
00505 (action)[x][y] = -1;
00506 }
00507 }
00508
00509
00510 int x = init_x;
00511 int y = init_y;
00512
00513
00514 float g = 0;
00515
00516
00517
00518
00519 std::vector<std::tuple<float, int, int> > open;
00520 open.push_back( std::make_tuple( g, x, y ) );
00521
00522
00523 bool found = false;
00524
00525 bool resign = false;
00526
00527 while( !found && !resign )
00528 {
00529 if (open.size() == 0)
00530 {
00531 resign = true;
00532 path->empty();
00533 return false;
00534 }
00535 else
00536 {
00537
00538 sort(open.begin(), open.end());
00539 reverse(open.begin(), open.end());
00540
00541 std::tuple<float, int, int> next = open[open.size()-1];
00542 open.pop_back();
00543 g = std::get<0>(next);
00544 x = std::get<1>(next);
00545 y = std::get<2>(next);
00546
00547
00548 if(stop_at_voronoi)
00549 {
00550
00551 if(voronoi->isVoronoi(x,y))
00552 {
00553 found = true;
00554 goal_x = x;
00555 goal_y = y;
00556 continue;
00557 }
00558 }
00559 else
00560 {
00561 if ( x == goal_x && y == goal_y )
00562 {
00563 found = true;
00564 continue;
00565 }
00566 }
00567 for( int i=0; i < delta.size(); i++ )
00568 {
00569
00570 int x2 = x + std::get<0>(delta[i]);
00571 int y2 = y + std::get<1>(delta[i]);
00572
00573
00574 if ( x2 >= 0 && x2 < sizeX && y2 >= 0 && y2 < sizeY )
00575 {
00576
00577 if(voronoi->isOccupied(x2,y2))
00578 {
00579 continue;
00580 }
00581
00582 if ( closed[x2][y2] ){
00583 continue;
00584 }
00585
00586
00587 if (!voronoi->isVoronoi(x2,y2) && check_is_voronoi_cell){
00588 continue;
00589 }
00590
00591 float g2 = g + cost;
00592
00593 open.push_back( std::make_tuple( g2, x2, y2 ) );
00594 closed[x2][y2] = true;
00595 action[x2][y2] = i;
00596 }
00597 }
00598 }
00599 }
00600
00601
00602 x = goal_x;
00603 y = goal_y;
00604
00605 int i = 0;
00606 path->clear();
00607
00608 while( x != init_x || y != init_y )
00609 {
00610 path->push_back({x,y});
00611 i++;
00612
00613 int x2 = x - std::get<0>( delta[ action[x][y] ] );
00614 int y2 = y - std::get<1>( delta[ action[x][y] ] );
00615
00616
00617 x = x2;
00618 y = y2;
00619 }
00620
00621 reverse(path->begin(), path->end());
00622 return true;
00623 }
00624
00625
00626
00627
00628 void VoronoiPlanner::smoothPath(std::vector<std::pair<float, float> > *path)
00629 {
00630
00631 std::vector<std::pair<float, float> > newpath = *path;
00632
00633 float tolerance = 0.00001;
00634 float change = tolerance;
00635
00636 if(path->size() < 2)
00637 return;
00638
00639 while (change >= tolerance)
00640 {
00641
00642 change = 0.0;
00643 for(int i = 1; i < path->size() - 1; i++)
00644 {
00645 float aux_x = newpath[i].first;
00646 float aux_y = newpath[i].second;
00647
00648
00649 float newpath_x = newpath[i].first + weight_data_ * ( path->at(i).first - newpath[i].first);
00650 float newpath_y = newpath[i].second + weight_data_ * ( path->at(i).second - newpath[i].second);
00651
00652
00653 newpath_x = newpath_x + weight_smooth_ *
00654 (newpath[i-1].first + newpath[i+1].first - (2.0 * newpath_x));
00655 newpath_y = newpath_y + weight_smooth_ *
00656 (newpath[i-1].second + newpath[i+1].second - (2.0 * newpath_y));
00657
00658 change = change + fabs(aux_x - newpath_x);
00659 change = change + fabs(aux_y - newpath_y);
00660
00661 newpath[i] = std::make_pair(newpath_x,newpath_y);
00662 }
00663 }
00664 *path = newpath;
00665 }
00666
00667
00668 void VoronoiPlanner::publishPlan(const std::vector<geometry_msgs::PoseStamped>& path) {
00669 if (!initialized_) {
00670 ROS_ERROR(
00671 "This planner has not been initialized yet, but it is being used, please call initialize() before use");
00672 return;
00673 }
00674
00675
00676 nav_msgs::Path gui_path;
00677 gui_path.poses.resize(path.size());
00678
00679 if (!path.empty()) {
00680 gui_path.header.frame_id = path[0].header.frame_id;
00681 gui_path.header.stamp = ros::Time::now();
00682 }
00683
00684
00685 for (unsigned int i = 0; i < path.size(); i++) {
00686 gui_path.poses[i] = path[i];
00687 }
00688
00689 plan_pub_.publish(gui_path);
00690 }
00691
00692 void VoronoiPlanner::publishVoronoiGrid(DynamicVoronoi *voronoi)
00693 {
00694 int nx = costmap_->getSizeInCellsX(), ny = costmap_->getSizeInCellsY();
00695
00696 ROS_WARN("costmap sx = %d,sy = %d, voronoi sx = %d, sy = %d", nx, ny,
00697 voronoi->getSizeX(), voronoi->getSizeY());
00698
00699 double resolution = costmap_->getResolution();
00700 nav_msgs::OccupancyGrid grid;
00701
00702 grid.header.frame_id = frame_id_;
00703 grid.header.stamp = ros::Time::now();
00704 grid.info.resolution = resolution;
00705
00706 grid.info.width = nx;
00707 grid.info.height = ny;
00708
00709 double wx, wy;
00710 costmap_->mapToWorld(0, 0, wx, wy);
00711 grid.info.origin.position.x = wx - resolution / 2;
00712 grid.info.origin.position.y = wy - resolution / 2;
00713 grid.info.origin.position.z = 0.0;
00714 grid.info.origin.orientation.w = 1.0;
00715
00716 grid.data.resize(nx * ny);
00717
00718 for (unsigned int x = 0; x < nx; x++)
00719 {
00720 for (unsigned int y = 0; y < ny; y++)
00721 {
00722 if(voronoi->isVoronoi(x,y))
00723 grid.data[x + y*nx] = 128;
00724 else
00725 grid.data[x + y*nx] = 0;
00726 }
00727 }
00728 voronoi_grid_pub_.publish(grid);
00729 }
00730
00731
00732
00733 }
00734