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
00037
00038
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
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
00070 char* checkPtr;
00071 g_GoalTolerance = strtod(argv[2], &checkPtr);
00072 if(checkPtr == argv[2]) {
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
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
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
00113 ROS_INFO("Using relative goal tolerance.");
00114
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 {
00121 g_GoalTolerance += move_base_tol;
00122 }
00123 }
00124
00125 ROS_INFO("Goal Tolerance is: %f.", g_GoalTolerance);
00126
00127
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
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
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
00231 return INFINITE_COST;
00232 }
00233
00234
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
00241
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
00249 if(g_GetPlan.call(srv))
00250 {
00251 failCounter = 0;
00252 callSuccessful = true;
00253
00254 if (!srv.response.plan.poses.empty())
00255 {
00256
00257 cost = getPlanCost(srv.response.plan.poses);
00258 }
00259 else
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 {
00290
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
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) {
00317
00318
00319
00320 g_PathCostCache.set(cacheKey, cost, true);
00321 }
00322 return cost;
00323 }
00324