navstack_module.cpp
Go to the documentation of this file.
00001 #include "planner_modules_pr2/navstack_module.h"
00002 #include <ros/ros.h>
00003 #include <nav_msgs/GetPlan.h>
00004 #include <geometry_msgs/PoseStamped.h>
00005 #include <angles/angles.h>
00006 #include <map>
00007 using std::map;
00008 #include <utility>
00009 using std::pair; using std::make_pair;
00010 #include <boost/foreach.hpp>
00011 #ifdef __CDT_PARSER__
00012 #define forEach(a, b) for(a : b)
00013 #else
00014 #define forEach BOOST_FOREACH
00015 #endif
00016 #include <sys/times.h>
00017 #include <tf/tf.h>
00018 #include <tf/transform_listener.h>
00019 
00020 using namespace modules;
00021 
00022 
00023 VERIFY_CONDITIONCHECKER_DEF(pathCost);
00024 
00025 ros::NodeHandle* g_NodeHandle = NULL;
00026 ros::ServiceClient g_GetPlan;
00027 
00029 std::string g_WorldFrame;
00030 
00031 double g_GoalTolerance = 0.5;
00032 
00033 double g_TransSpeed = 0.3;
00034 double g_RotSpeed = angles::from_degrees(30);
00035 
00036 // Using a cache of queried path costs to prevent calling the path planning service multiple times
00037 // Better: Can we assume symmetric path costs?
00038 //map< pair<string,string>, double> g_PathCostCache;
00039 ModuleParamCacheDouble g_PathCostCache;
00040 string computePathCacheKey(const string& startLocation, const string& goalLocation,
00041         const geometry_msgs::Pose & startPose, const geometry_msgs::Pose & goalPose)
00042 {
00043     std::string startPoseStr = createPoseParamString(startPose, 0.01, 0.02);
00044     std::string goalPoseStr = createPoseParamString(goalPose, 0.01, 0.02);
00045 
00046     if (startLocation < goalLocation)
00047     {
00048         return startLocation + "_" + startPoseStr + "__" + goalLocation + "_" + goalPoseStr;
00049     }
00050     else
00051     {
00052         return goalLocation + "_" + goalPoseStr + "__" + startLocation + "_" + startPoseStr;
00053     }
00054 }
00055 
00056 void navstack_init(int argc, char** argv)
00057 {
00058     ROS_ASSERT(argc >= 4);
00059 
00060     // get world frame
00061     ros::NodeHandle nhPriv("~");
00062     std::string tfPrefix = tf::getPrefixParam(nhPriv);
00063     g_WorldFrame = tf::resolve(tfPrefix, argv[1]);
00064     ROS_INFO("World frame is: %s", g_WorldFrame.c_str());
00065 
00066     nhPriv.param("trans_speed", g_TransSpeed, g_TransSpeed);
00067     nhPriv.param("rot_speed", g_RotSpeed, g_RotSpeed);
00068 
00069     // get goal tolerance
00070     char* checkPtr;
00071     g_GoalTolerance = strtod(argv[2], &checkPtr);
00072     if(checkPtr == argv[2]) {    // conversion error!
00073         ROS_ERROR("%s: Could not convert argument for goal tolerance: %s", __func__, argv[2]);
00074         g_GoalTolerance = 0.5;
00075     }
00076 
00077     ros::NodeHandle nh;
00078     std::string base_local_planner_ns;
00079     if(strcmp(argv[3], "0") == 0) {
00080         ROS_INFO("Using absolute goal tolerance.");
00081     } else if(strcmp(argv[3], "1") == 0) {
00082         ROS_INFO("Trying to estimate base_local_planner namespace");
00083 
00084         std::string local_planner;
00085         if(!nh.getParam("move_base_node/base_local_planner", local_planner)
00086                 && !nh.getParam("move_base/base_local_planner", local_planner)) {
00087             ROS_ERROR("move_base(_node)/base_local_planner not set - falling back to absolute mode.");
00088         } else {
00089             // dwa_local_planner/DWAPlannerROS -> DWAPlannerROS
00090             std::string::size_type x = local_planner.find_last_of("/");
00091             if(x == std::string::npos)
00092                 base_local_planner_ns = local_planner;
00093             else
00094                 base_local_planner_ns = local_planner.substr(x + 1);
00095             // add move_base(_node) prefix
00096             string dummy;
00097             if(nh.getParam("move_base_node/base_local_planner", dummy)) {
00098                 base_local_planner_ns = "move_base_node/" + base_local_planner_ns;
00099             } else if(nh.getParam("move_base/base_local_planner", dummy)) {
00100                 base_local_planner_ns = "move_base/" + base_local_planner_ns;
00101             } else {
00102                 ROS_ASSERT(false);
00103             }
00104 
00105             ROS_INFO("Estimated base_local_planner_ns to %s.", base_local_planner_ns.c_str());
00106         }
00107     } else {
00108         base_local_planner_ns = argv[3];
00109     }
00110 
00111     if(!base_local_planner_ns.empty()) {
00112         // relative goal tolerance, argv[3] contains the base_local_planner namespace
00113         ROS_INFO("Using relative goal tolerance.");
00114         // get base_local_planner's xy_goal_tolerance
00115         double move_base_tol;
00116         if(!nh.getParam(base_local_planner_ns + "/xy_goal_tolerance", move_base_tol)) {
00117             ROS_ERROR_STREAM("requested relative goal tolerance, but "
00118                     << (base_local_planner_ns + "/xy_goal_tolerance") << " was not set"
00119                     << " - falling back to absolute mode");
00120         } else { // 2. add move_base's tolerance to our relative tolerance
00121             g_GoalTolerance += move_base_tol;
00122         }
00123     }
00124 
00125     ROS_INFO("Goal Tolerance is: %f.", g_GoalTolerance);
00126 
00127     // init service query for make plan
00128     string service_name = "move_base/make_plan";
00129     g_NodeHandle = new ros::NodeHandle();
00130     while(!ros::service::waitForService(service_name, ros::Duration(3.0))) {
00131         ROS_ERROR("Service %s not available - waiting.", service_name.c_str());
00132     }
00133 
00134     g_GetPlan = g_NodeHandle->serviceClient<nav_msgs::GetPlan>(service_name, true);
00135     if(!g_GetPlan) {
00136         ROS_FATAL("Could not initialize get plan service from %s (client name: %s)", service_name.c_str(), g_GetPlan.getService().c_str());
00137     }
00138     ROS_INFO("Service connection to %s established.", g_GetPlan.getService().c_str());
00139 
00140     g_PathCostCache.initialize("move_base", g_NodeHandle);
00141 
00142     ROS_INFO("Initialized Navstack Module.");
00143 }
00144 
00145 bool fillPathRequest(const ParameterList & parameterList, numericalFluentCallbackType numericalFluentCallback,
00146         nav_msgs::GetPlan::Request & request)
00147 {
00148     // get robot and target location from planner interface
00149     ROS_ASSERT(parameterList.size() == 2);
00150 
00151     ParameterList startParams;
00152     startParams.push_back(parameterList[0]);
00153     ParameterList goalParams;
00154     goalParams.push_back(parameterList[1]);
00155     NumericalFluentList nfRequest;
00156     nfRequest.reserve(14);
00157     nfRequest.push_back(NumericalFluent("x", startParams));
00158     nfRequest.push_back(NumericalFluent("y", startParams));
00159     nfRequest.push_back(NumericalFluent("z", startParams));
00160     nfRequest.push_back(NumericalFluent("qx", startParams));
00161     nfRequest.push_back(NumericalFluent("qy", startParams));
00162     nfRequest.push_back(NumericalFluent("qz", startParams));
00163     nfRequest.push_back(NumericalFluent("qw", startParams));
00164     nfRequest.push_back(NumericalFluent("x", goalParams));
00165     nfRequest.push_back(NumericalFluent("y", goalParams));
00166     nfRequest.push_back(NumericalFluent("z", goalParams));
00167     nfRequest.push_back(NumericalFluent("qx", goalParams));
00168     nfRequest.push_back(NumericalFluent("qy", goalParams));
00169     nfRequest.push_back(NumericalFluent("qz", goalParams));
00170     nfRequest.push_back(NumericalFluent("qw", goalParams));
00171 
00172     NumericalFluentList* nfRequestP = &nfRequest;
00173     if(!numericalFluentCallback(nfRequestP)) {
00174         ROS_ERROR("numericalFluentCallback failed.");
00175         return false;
00176     }
00177 
00178     // create the path planning query for service
00179     request.start.header.frame_id = g_WorldFrame;
00180     request.goal.header.frame_id = g_WorldFrame;
00181     request.start.pose.position.x = nfRequest[0].value;
00182     request.start.pose.position.y = nfRequest[1].value;
00183     request.start.pose.position.z = nfRequest[2].value;
00184     request.start.pose.orientation.x = nfRequest[3].value;
00185     request.start.pose.orientation.y = nfRequest[4].value;
00186     request.start.pose.orientation.z = nfRequest[5].value;
00187     request.start.pose.orientation.w = nfRequest[6].value;
00188     request.goal.pose.position.x = nfRequest[7].value;
00189     request.goal.pose.position.y = nfRequest[8].value;
00190     request.goal.pose.position.z = nfRequest[9].value;
00191     request.goal.pose.orientation.x = nfRequest[10].value;
00192     request.goal.pose.orientation.y = nfRequest[11].value;
00193     request.goal.pose.orientation.z = nfRequest[12].value;
00194     request.goal.pose.orientation.w = nfRequest[13].value;
00195     request.tolerance = g_GoalTolerance;
00196     return true;
00197 }
00198 
00199 double getPlanCost(const std::vector<geometry_msgs::PoseStamped> & plan)
00200 {
00201     if(plan.empty())
00202         return 0;
00203 
00204     double pathLength = 0;
00205     double rotLength = 0;
00206     geometry_msgs::PoseStamped lastPose = plan[0];
00207     forEach(const geometry_msgs::PoseStamped & p, plan) {
00208         double d = hypot(lastPose.pose.position.x - p.pose.position.x,
00209                 lastPose.pose.position.y - p.pose.position.y);
00210         pathLength += d;
00211 
00212         double yawCur = tf::getYaw(p.pose.orientation);
00213         double yawLast = tf::getYaw(lastPose.pose.orientation);
00214         double da = fabs(angles::normalize_angle(yawCur - yawLast));
00215         rotLength += da;
00216 
00217         lastPose = p;
00218     }
00219     return pathLength/g_TransSpeed + rotLength/g_RotSpeed;
00220 }
00221 
00222 double callPlanningService(nav_msgs::GetPlan& srv, const string& startLocationName, const string& goalLocationName,
00223         bool & callSuccessful)
00224 {
00225     callSuccessful = false;
00226     double cost = INFINITE_COST;
00227     if (!g_GetPlan)
00228     {
00229         ROS_ERROR("GetPlan Persistent service connection to %s failed.", g_GetPlan.getService().c_str());
00230         // FIXME reconnect - this shouldn't happen.
00231         return INFINITE_COST;
00232     }
00233 
00234     // statistics about using the ros path planner service
00235     static double plannerCalls = 0;
00236     static ros::Duration totalCallsTime = ros::Duration(0.0);
00237     plannerCalls += 1.0;
00238 
00239     ros::Time callStartTime = ros::Time::now();
00240     // This construct is here, because when the robot is moving move_base will not produce other paths
00241     // we retry for a certain amount of time to not fail directly.
00242     ROS_INFO_STREAM("planner call: start " << startLocationName << " (" << srv.request.start.pose.position
00243             << "). goal " << goalLocationName << " (" << srv.request.goal.pose.position << ")");
00244     static unsigned int failCounter = 0;
00245     ros::Rate retryRate = 1;
00246     do
00247     {
00248         // perform the actual path planner call
00249         if(g_GetPlan.call(srv))
00250         {
00251             failCounter = 0;    // will also exit loop
00252             callSuccessful = true;
00253 
00254             if (!srv.response.plan.poses.empty())
00255             {
00256                 // get plan cost
00257                 cost = getPlanCost(srv.response.plan.poses);
00258             }
00259             else    // no plan found.
00260             {
00261                 ROS_WARN("Got empty plan: %s -> %s", startLocationName.c_str(), goalLocationName.c_str());
00262                 cost = INFINITE_COST;
00263             }
00264             ROS_DEBUG("Got plan: %s -> %s cost: %f.", startLocationName.c_str(), goalLocationName.c_str(), cost);
00265 
00266             if(g_Debug) {
00267                 ros::Time callEndTime = ros::Time::now();
00268                 ros::Duration dt = callEndTime - callStartTime;
00269                 totalCallsTime += dt;
00270                 ROS_DEBUG("ServiceCall took: %f, avg: %f (num %f).",
00271                         dt.toSec(), totalCallsTime.toSec()/plannerCalls, plannerCalls);
00272             }
00273         }
00274         else
00275         {
00276             ROS_ERROR("Failed to call service %s - is the robot moving?", g_GetPlan.getService().c_str());
00277             failCounter++;
00278             retryRate.sleep();
00279         }
00280     } while (failCounter < 300 && failCounter > 0);
00281 
00282     return cost;
00283 }
00284 
00285 double pathCost(const ParameterList & parameterList,
00286         predicateCallbackType predicateCallback, numericalFluentCallbackType numericalFluentCallback, int relaxed)
00287 {
00288     if (g_Debug)
00289     { // prevent spamming ROS_DEBUG calls unless we really want debug
00290         // debugging raw planner calls
00291         static unsigned int calls = 0;
00292         calls++;
00293         if (calls % 10000 == 0)
00294         {
00295             ROS_DEBUG("Got %d module calls.\n", calls);
00296         }
00297     }
00298     ROS_ASSERT(parameterList.size() == 2);
00299 
00300     nav_msgs::GetPlan srv;
00301     if (!fillPathRequest(parameterList, numericalFluentCallback, srv.request))
00302     {
00303         return INFINITE_COST;
00304     }
00305 
00306     // first lookup in the cache if we answered the query already
00307     double cost = INFINITE_COST;
00308     string cacheKey = computePathCacheKey(parameterList[0].value, parameterList[1].value, srv.request.start.pose, srv.request.goal.pose);
00309     if (g_PathCostCache.get(cacheKey, cost))
00310     {
00311         return cost;
00312     }
00313 
00314     bool callSuccessful;
00315     cost = callPlanningService(srv, parameterList[0].value, parameterList[1].value, callSuccessful);
00316     if(callSuccessful) {      // only cache real computed paths (including INFINITE_COST)
00317         //bool isRobotLocation =
00318         //    (parameterList[0].value == "robot_location" || parameterList[1].value == "robot_location");
00319         //g_PathCostCache.set(cacheKey, cost, !isRobotLocation);  // do no param cache robot_location calls
00320         g_PathCostCache.set(cacheKey, cost, true);  // do param cache robot_location calls - they contain the location pose now (safe)
00321     }
00322     return cost;
00323 }
00324 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Friends Defines


planner_modules_pr2
Author(s): Christian Dornhege, Andreas Hertle
autogenerated on Wed Dec 26 2012 15:49:38