00001
00008 #include <youbot_overhead_localization/pathPlanner.h>
00009
00010 using namespace std;
00011 using namespace costmap_2d;
00012 using namespace navfn;
00013
00014 pathPlanner::pathPlanner(std::string name)
00015 :as(n, name, boost::bind(&pathPlanner::executePathPlanning, this, _1), false), actionName(name), cmObj(NULL), navFnObj(NULL)
00016 {
00017 ROS_INFO("Starting pathPlanner");
00018 string moveCmdTopicName;
00019
00020 cmInitialized = false;
00021 navFnInitialized = false;
00022
00023 n.getParam("/planner_map_server/map_file_location", mapFileName);
00024 n.getParam("/planner_map_server/map_save_debug_location", mapDebugLocation);
00025 if(!n.getParam("/path_planner/moveCommandTopic", moveCmdTopicName))
00026 baseCommandPublisher = n.advertise<geometry_msgs::Twist>("/cmd_vel", -1);
00027 else
00028 baseCommandPublisher = n.advertise<geometry_msgs::Twist>(moveCmdTopicName, -1);
00029
00030 mapSubscriber = n.subscribe("/map", 0, &pathPlanner::mapserverCallback, this);
00031 localizationSubscriber = n.subscribe("base_localizer/pose_2d", 1, &pathPlanner::localizationCallback, this);
00032 coordConversionClient = n.serviceClient<youbot_overhead_vision::CoordConversion>("coord_conversion");
00033 costPublisher = n.advertise<youbot_overhead_localization::CostmapCost>("base_localizer/cost", 1);\
00034 costServer = n.advertiseService("/path_planner/get_cost", &pathPlanner::getCost, this);
00035
00036
00037 as.start();
00038
00039
00040 currentPose.x = 0.0;
00041 currentPose.y = 0.0;
00042 currentPose.theta = 0.0;
00043 n.getParam("/path_planner/run_from_simulation", useSim);
00044
00045
00046 vector<float> xVec;
00047 vector<float> yVec;
00048 vector<float> dst;
00049 plan.pathLength = 0;
00050 plan.pathx = xVec;
00051 plan.pathy = yVec;
00052 plan.distances = dst;
00053 plan.remainingDistance = 0;
00054 goalx = 2.0;
00055 goaly = 1.4;
00056
00057
00058 inRadius = 0.24;
00059 circumRadius = 0.4;
00060 inflationRadius = 0.4;
00061 costScalingFactor = 1.0;
00062 obstacleRange = 2.5;
00063 raytraceRange = 3.0;
00064 maxObstacleHeight = .8;
00065 lethalCostThreshold = 100;
00066 trackUnknownSpace = false;
00067 unknownCostValue = 0;
00068
00069
00070 allowUnknown = true;
00071
00072
00073 currentPose.x = 0;
00074 currentPose.y = 0;
00075 currentPose.theta = 0;
00076
00077
00078 prevTurnError = 0.0;
00079 prevError = 0.0;
00080 totalTurnError = 0.0;
00081 totalError = 0.0;
00082 }
00083
00084 pathPlanner::~pathPlanner()
00085 {
00086 if (cmObj)
00087 delete cmObj;
00088
00089 if (navFnObj)
00090 delete navFnObj;
00091 }
00092
00093 void pathPlanner::executePathPlanning(const youbot_overhead_localization::PlanPathGoalConstPtr& goal)
00094 {
00095 ros::Rate r(30);
00096 bool success = true;
00097 youbot_overhead_vision::CoordConversion srv;
00098
00099 if (goal->topic_name.compare("/logitech_9000_camera1/image_raw") == 0)
00100 {
00101 if (useSim)
00102 {
00103 srv.request.x = goal->x + SIM_TOP_CAMERA_OFFSET;
00104 }
00105 else
00106 {
00107 srv.request.x = goal->x + REAL_TOP_CAMERA_OFFSET;
00108 }
00109 }
00110 else
00111 {
00112 srv.request.x = goal->x;
00113 }
00114 srv.request.y = goal->y;
00115 srv.request.type = 1;
00116 coordConversionClient.call(srv);
00117 goalx = srv.response.xConverted;
00118 goaly = srv.response.yConverted;
00119
00120 int returnValue = makePlanAstar();
00121
00122
00123 if (returnValue == 1)
00124 {
00125 asFeedback.pathLength = plan.remainingDistance;
00126 as.publishFeedback(asFeedback);
00127
00128
00129 clock_gettime(CLOCK_REALTIME, &prevPointTime);
00130
00131 while (plan.pathLength > 0)
00132 {
00133
00134 timespec currentTime;
00135 clock_gettime(CLOCK_REALTIME, ¤tTime);
00136 float elapsedTime = (currentTime.tv_sec + currentTime.tv_nsec / 1000000000.0) - (prevPointTime.tv_sec + prevPointTime.tv_nsec / 1000000000.0);
00137
00138
00139 if (as.isPreemptRequested() || !ros::ok() || elapsedTime > PATH_TIMEOUT)
00140 {
00141 ROS_INFO("%s: Preempted", actionName.c_str());
00142 baseCommand.linear.x = 0.0;
00143 baseCommand.linear.y = 0.0;
00144 baseCommand.angular.z = 0.0;
00145 baseCommandPublisher.publish(baseCommand);
00146 as.setPreempted();
00147 success = false;
00148 break;
00149 }
00150
00151 followPath();
00152
00153 r.sleep();
00154 }
00155
00156 if (success)
00157 {
00158 asResult.posx = currentPose.x;
00159 asResult.posy = currentPose.y;
00160 ROS_INFO("%s: Succeeded", actionName.c_str());
00161 as.setSucceeded(asResult);
00162 }
00163 }
00164 else
00165 {
00166 ROS_INFO("%s: Preempted, no path found...", actionName.c_str());
00167 as.setPreempted();
00168 }
00169 }
00170
00171 void pathPlanner::followPath()
00172 {
00173 float currentx = currentPose.x;
00174 float currenty = currentPose.y;
00175 float currentHeading = currentPose.theta;
00176
00177 float targetx = plan.pathx.front();
00178 float targety = plan.pathy.front();
00179 float targetHeading = atan2(targety - currenty, targetx - currentx);
00180
00181 float forwardVel = 0.0;
00182 float leftVel = 0.0;
00183 float turnVel = 0.0;
00184
00185 float turnError = targetHeading - currentHeading;
00186 while (turnError < -PI)
00187 {
00188 turnError += 2*PI;
00189 }
00190 while (turnError > PI)
00191 {
00192 turnError -= 2*PI;
00193 }
00194
00195 float forwardError = sqrt(pow(targetx - currentx, 2) + pow(targety - currenty, 2));
00196
00197
00198 if (forwardError < .1)
00199 {
00200 totalTurnError = 0;
00201 totalError = 0;
00202 prevTurnError = 0;
00203 turnError = 0;
00204
00205
00206 plan.pathx.erase(plan.pathx.begin());
00207 plan.pathy.erase(plan.pathy.begin());
00208 if (plan.distances.size() > 0)
00209 plan.distances.erase(plan.distances.begin());
00210 plan.pathLength --;
00211 if (plan.pathLength > 0)
00212 plan.remainingDistance = sqrt(pow(plan.pathx.front() - currentx, 2) + pow(plan.pathy.front() - currenty, 2));
00213
00214
00215 clock_gettime(CLOCK_REALTIME, &prevPointTime);
00216 }
00217
00218 else if (forwardError < .5)
00219 {
00220 float angleToGoal = atan2(targety - currenty, targetx - currentx)
00221 - (currentHeading - PI/2.0);
00222
00223
00224 forwardVel = KP_LIN*forwardError + KI_LIN*totalError
00225 + KD_LIN*(forwardError - prevError);
00226 leftVel = -1 * forwardVel * cos(angleToGoal);
00227 forwardVel = forwardVel * sin(angleToGoal);
00228 totalError += forwardError;
00229 prevError = forwardError;
00230
00231 plan.remainingDistance = forwardError;
00232 }
00233
00234 else
00235 {
00236
00237 turnVel = KP_ANG*turnError + KI_ANG*totalTurnError
00238 + KD_ANG*(turnError - prevTurnError);
00239 totalTurnError += turnError;
00240 prevTurnError = turnError;
00241
00242 if (abs(turnError) < .08727)
00243 {
00244 totalTurnError = 0;
00245
00246 forwardVel = KP_LIN*forwardError + KI_LIN*totalError
00247 + KD_LIN*(forwardError - prevError);
00248 totalError += forwardError;
00249 prevError = forwardError;
00250 }
00251
00252 plan.remainingDistance = forwardError;
00253 }
00254
00255
00256 for (unsigned int i = 0; i < plan.distances.size(); i++)
00257 {
00258 plan.remainingDistance += plan.distances[i];
00259 }
00260
00261
00262 if (forwardVel > 1)
00263 forwardVel = 1;
00264 else if (forwardVel < -1)
00265 forwardVel = -1;
00266 if (leftVel > 1)
00267 leftVel = 1;
00268 else if (leftVel < -1)
00269 leftVel = -1;
00270 if (turnVel > 1)
00271 turnVel = 1;
00272 else if (turnVel < -1)
00273 turnVel = -1;
00274
00275
00276
00277 forwardVel = forwardVel * MAX_SPEED;
00278 leftVel = leftVel * MAX_SPEED;
00279 turnVel = turnVel * MAX_SPEED;
00280
00281 ROS_INFO("Forward vel %f, Left %f, turn %f", forwardVel, leftVel, turnVel);
00282
00283
00284
00285 baseCommand.linear.x = forwardVel;
00286 baseCommand.linear.y = leftVel;
00287 baseCommand.angular.z = turnVel;
00288 baseCommandPublisher.publish(baseCommand);
00289
00290 asFeedback.pathLength = plan.remainingDistance;
00291 as.publishFeedback(asFeedback);
00292 }
00293
00294 void pathPlanner::localizationCallback(const youbot_overhead_localization::Pose2d& newPose)
00295 {
00296 currentPose.x = newPose.x;
00297 currentPose.y = newPose.y;
00298 currentPose.theta = newPose.theta;
00299
00300 if (cmInitialized)
00301 {
00302 unsigned int costmapx, costmapy;
00303 youbot_overhead_localization::CostmapCost currentCost;
00304 if (!cmObj->worldToMap(currentPose.y, currentPose.x, costmapx, costmapy))
00305 currentCost.cost = 255;
00306 else
00307 currentCost.cost = cmObj->getCost(costmapx, costmapy);
00308 costPublisher.publish(currentCost);
00309 }
00310 }
00311
00312 void pathPlanner::mapserverCallback(const nav_msgs::OccupancyGridConstPtr& newMap)
00313 {
00314 mapWidth = newMap->info.width;
00315 mapHeight = newMap->info.height;
00316 mapResolution = newMap->info.resolution;
00317 mapOriginx = newMap->info.origin.position.x;
00318 mapOriginy = newMap->info.origin.position.y;
00319 unsigned int mapCellCount = mapWidth * mapHeight;
00320 mapData.clear();
00321 mapData.reserve(mapCellCount);
00322 for(unsigned int i = 0; i < mapCellCount; i++)
00323 mapData.push_back((unsigned char)newMap->data[i]);
00324 if (!cmInitialized)
00325 {
00326 cmObj = new Costmap2D(mapWidth, mapHeight,
00327 mapResolution, mapOriginx, mapOriginy, inRadius, circumRadius,
00328 inflationRadius, obstacleRange, maxObstacleHeight, raytraceRange,
00329 costScalingFactor, mapData, lethalCostThreshold, trackUnknownSpace,
00330 unknownCostValue);
00331 cmInitialized = true;
00332 }
00333 else
00334 {
00335 cmObj->replaceFullMap(mapOriginx, mapOriginy, mapWidth, mapHeight, mapData);
00336 }
00337 unsigned int costmapx, costmapy;
00338 youbot_overhead_localization::CostmapCost currentCost;
00339
00340
00341 if (!cmObj->worldToMap(currentPose.y, currentPose.x, costmapx, costmapy))
00342 currentCost.cost = 255;
00343 else
00344 currentCost.cost = cmObj->getCost(costmapx, costmapy);
00345 costPublisher.publish(currentCost);
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355 if (!navFnInitialized)
00356 {
00357 navFnObj = new NavFn(cmObj->getSizeInCellsX(), cmObj->getSizeInCellsY());
00358 navFnInitialized = true;
00359 }
00360 navFnObj->setCostmap(cmObj->getCharMap(), true, allowUnknown);
00361 }
00362
00363 int pathPlanner::makePlanAstar()
00364 {
00365 if (!(cmInitialized && navFnInitialized))
00366 {
00367 ROS_INFO("Costmap or planner not initialized. Unable to plan.");
00368 return -1;
00369 }
00370
00371
00372
00373 unsigned int mapStartx, mapStarty;
00374 if (!cmObj->worldToMap(currentPose.y, currentPose.x, mapStartx, mapStarty))
00375 {
00376 ROS_INFO("Robot start position is outside map area.");
00377 return -1;
00378 }
00379
00380 unsigned int mapGoalx, mapGoaly;
00381 if (!cmObj->worldToMap(goaly, goalx, mapGoalx, mapGoaly))
00382 {
00383 ROS_INFO("Robot goal position is outside map area.");
00384 return -1;
00385 }
00386
00387 plan.pathx.clear();
00388 plan.pathy.clear();
00389 plan.distances.clear();
00390
00391 int mapStart[2];
00392 mapStart[0] = mapStartx;
00393 mapStart[1] = mapStarty;
00394
00395 int mapGoal[2];
00396 mapGoal[0] = mapGoalx;
00397 mapGoal[1] = mapGoaly;
00398
00399 navFnObj->setStart(mapStart);
00400 navFnObj->setGoal(mapGoal);
00401
00402
00403 navFnObj->calcNavFnAstar();
00404 navFnObj->calcPath(cmObj->getSizeInCellsX() * 4);
00405
00406
00407 int len = navFnObj->getPathLen();
00408 float *tempPathy = navFnObj->getPathX();
00409 float *tempPathx = navFnObj->getPathY();
00410
00411 if (len <= 1)
00412 {
00413 ROS_INFO("Goal is unreachable");
00414 return -1;
00415 }
00416
00417
00418 float xfilt[len];
00419 float yfilt[len];
00420
00421 for(int i=0; i < len; i++)
00422 {
00423 xfilt[i] = tempPathx[i];
00424 yfilt[i] = tempPathy[i];
00425 }
00426
00427
00428 if (len > 1)
00429 {
00430 for (int i=1; i < len; i++)
00431 {
00432 xfilt[i] = (B1*tempPathx[i] + B2*tempPathx[(i-1)] - A*xfilt[(i-1)]);
00433 yfilt[i] = (B1*tempPathy[i] + B2*tempPathy[(i-1)] - A*yfilt[(i-1)]);
00434 }
00435 }
00436
00437
00438 int i;
00439 int k = 5;
00440 float c[len];
00441 for (i=0; i < len; i++)
00442 {
00443 float ax, ay, bx, by;
00444 if (i+k < len)
00445 {
00446 ax = xfilt[i] - xfilt[i+k];
00447 ay = yfilt[i] - yfilt[i+k];
00448 }
00449 else
00450 {
00451 ax = xfilt[i] - xfilt[len-1];
00452 ay = yfilt[i] - yfilt[len-1];
00453 }
00454 if (i-k > 0)
00455 {
00456 bx = xfilt[i] - xfilt[i-k];
00457 by = yfilt[i] - yfilt[i-k];
00458 }
00459 else
00460 {
00461 bx = xfilt[i] - xfilt[0];
00462 by = yfilt[i] - yfilt[0];
00463 }
00464 c[i] = (ax*bx + ay*by) / (sqrt(ax*ax+ay*ay)*sqrt(bx*bx+by*by));
00465 }
00466
00467 float sigx[len];
00468 float sigy[len];
00469 int numSig = 0;
00470 for (i=1; i<len-1; i++)
00471 {
00472 if (c[i] > c[i-1] && c[i] > c[i+1] && c[i] > SIG_THRESH)
00473 {
00474 sigx[numSig] = xfilt[i];
00475 sigy[numSig] = yfilt[i];
00476 numSig ++;
00477 }
00478 }
00479
00480
00481 sigx[numSig] = xfilt[len-1];
00482 sigy[numSig] = yfilt[len-1];
00483 numSig ++;
00484
00485
00486 for (i=0; i < numSig; i++)
00487 {
00488 youbot_overhead_vision::CoordConversion srv;
00489 srv.request.x = sigx[i];
00490 srv.request.y = sigy[i];
00491 srv.request.type = 3;
00492 coordConversionClient.call(srv);
00493
00494 plan.pathx.push_back(srv.response.xConverted);
00495 plan.pathy.push_back(srv.response.yConverted);
00496 }
00497
00498 plan.pathLength = numSig;
00499
00500
00501 for (i=0; i < plan.pathLength - 1; i++)
00502 {
00503 plan.distances.push_back(sqrt(pow(plan.pathx[i+1] - plan.pathx[i], 2) + pow(plan.pathy[i+1] - plan.pathy[i], 2)));
00504 }
00505 if (plan.pathLength > 0)
00506 {
00507 plan.remainingDistance = sqrt(pow(plan.pathx[0] - currentPose.x, 2) + pow(plan.pathy[0] - currentPose.y, 2));
00508 for (unsigned int j=0; j < plan.distances.size(); j++)
00509 {
00510 plan.remainingDistance += plan.distances[j];
00511 }
00512 }
00513 else
00514 plan.remainingDistance = 0;
00515
00516 return 1;
00517 }
00518
00519 bool pathPlanner::getCost(youbot_overhead_localization::GetCost::Request &req, youbot_overhead_localization::GetCost::Response &res)
00520 {
00521 unsigned int mapPosx, mapPosy;
00522
00523
00524 if (!cmObj->worldToMap(req.y, req.x, mapPosx, mapPosy))
00525 {
00526 ROS_INFO("Requested cost position is outside map area.");
00527 return false;
00528 }
00529
00530 res.cost = cmObj->getCost(mapPosx, mapPosy);
00531
00532 return true;
00533 }
00534
00535 void pathPlanner::printOccupancyGrid()
00536 {
00537 FILE *fp = fopen(mapDebugLocation.c_str(), "w");
00538
00539 if(!fp)
00540 {
00541 ROS_INFO("Can't open file %s.", mapDebugLocation.c_str());
00542 }
00543 else
00544 {
00545 fprintf(fp, "P2\n%d\n%d\n%d\n", cmObj->getSizeInCellsX(), cmObj->getSizeInCellsY(), 0xff);
00546 for(unsigned int iy = 0; iy < cmObj->getSizeInCellsY(); iy++) {
00547 for(unsigned int ix = 0; ix < cmObj->getSizeInCellsX(); ix++) {
00548 unsigned char cost = cmObj->getCost(ix,iy);
00549 if (cost == LETHAL_OBSTACLE) {
00550 fprintf(fp, "255 ");
00551 }
00552 else if (cost == NO_INFORMATION){
00553 fprintf(fp, "180 ");
00554 }
00555 else if (cost == INSCRIBED_INFLATED_OBSTACLE ) {
00556 fprintf(fp, "128 ");
00557 }
00558 else if (cost > 0){
00559 fprintf(fp, "50 ");
00560 }
00561 else {
00562 fprintf(fp, "0 ");
00563 }
00564 }
00565 fprintf(fp, "\n");
00566 }
00567 fclose(fp);
00568 }
00569 }
00570
00571 void pathPlanner::printCostmap()
00572 {
00573 FILE *fp = fopen(mapDebugLocation.c_str(), "w");
00574
00575 if(!fp)
00576 {
00577 ROS_INFO("Can't open file %s.", mapDebugLocation.c_str());
00578 }
00579 else
00580 {
00581 fprintf(fp, "P2\n%d\n%d\n%d\n", cmObj->getSizeInCellsX(), cmObj->getSizeInCellsY(), 0xff);
00582 for(unsigned int iy = 0; iy < cmObj->getSizeInCellsY(); iy++) {
00583 for(unsigned int ix = 0; ix < cmObj->getSizeInCellsX(); ix++) {
00584 unsigned char cost = cmObj->getCost(ix,iy);
00585 fprintf(fp, "%d ", cost);
00586 }
00587 fprintf(fp, "\n");
00588 }
00589 fclose(fp);
00590 }
00591 }
00592
00596 int main(int argc, char **argv)
00597 {
00598 ros::init(argc, argv, "path_planner");
00599 pathPlanner planner2d(ros::this_node::getName());
00600 ros::spin();
00601
00602 return 0;
00603 }