move_base_topo.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2009, Willow Garage, Inc.
00006 *  All rights reserved.
00007 *
00008 *  Redistribution and use in source and binary forms, with or without
00009 *  modification, are permitted provided that the following conditions
00010 *  are met:
00011 *
00012 *   * Redistributions of source code must retain the above copyright
00013 *     notice, this list of conditions and the following disclaimer.
00014 *   * Redistributions in binary form must reproduce the above
00015 *     copyright notice, this list of conditions and the following
00016 *     disclaimer in the documentation and/or other materials provided
00017 *     with the distribution.
00018 *   * Neither the name of Willow Garage, Inc. nor the names of its
00019 *     contributors may be used to endorse or promote products derived
00020 *     from this software without specific prior written permission.
00021 *
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 *  POSSIBILITY OF SUCH DAMAGE.
00034 *
00035 * Author: Eitan Marder-Eppstein
00036 *********************************************************************/
00037 #include <topological_roadmap/move_base_topo.h>
00038 #include <graph_mapping_utils/geometry.h>
00039 #include <topological_nav_msgs/RoadmapPath.h>
00040 #include <graph_mapping_utils/to_string.h>
00041 #include <boost/lexical_cast.hpp>
00042 #include <topological_map_2d/ros_conversion.h>
00043 #include <topological_roadmap/ros_conversion.h>
00044 #include <topological_nav_msgs/SwitchGrid.h>
00045 #include <set>
00046 
00047 namespace topological_roadmap
00048 
00049 {
00050 
00051 namespace gm=geometry_msgs;
00052 namespace mr=mongo_roscpp;
00053 namespace nm=nav_msgs;
00054 namespace gmu=graph_mapping_utils;
00055 namespace tmap=topological_map_2d;
00056 namespace msg=topological_nav_msgs;
00057 
00058 using std::vector;
00059 using std::set;
00060 using std::string;
00061 using std::list;
00062 
00063 typedef boost::mutex::scoped_lock Lock;
00064 
00065 MoveBaseTopo::MoveBaseTopo():
00066   grids_("topological_navigation", "grids"), 
00067   as_(ros::NodeHandle(), "move_base_topo",
00068       boost::bind(&MoveBaseTopo::executeCB, this, _1), false),
00069   costmap_("costmap", tf_), move_base_client_("move_base", true),
00070   grid_switch_(false)
00071 {
00072   ros::NodeHandle n;
00073   ros::NodeHandle private_nh("~");
00074 
00075   private_nh.param("progress_check_frequency", progress_check_frequency_, 5.0);
00076   private_nh.param("next_waypoint_distance", next_waypoint_distance_, 1.0);
00077   private_nh.param("planner_patience", planner_patience_, 10.0);
00078 
00079   double block_check_period;
00080   private_nh.param("block_check_period", block_check_period, 1.0);
00081   private_nh.param("blocked_edge_timeout", blocked_edge_timeout_, 0.0);
00082 
00083   last_valid_plan_ = ros::Time::now();
00084 
00085   //get the tf prefix
00086   ros::NodeHandle prefix_nh;
00087   tf_prefix_ = tf::getPrefixParam(prefix_nh);
00088 
00089   planner_.initialize("planner", &costmap_);
00090   costmap_.stop();
00091   roadmap_sub_ = n.subscribe("topological_roadmap", 1,
00092                              &MoveBaseTopo::roadmapCB, this);
00093   localization_sub_ = n.subscribe("topological_localization", 1,
00094                                   &MoveBaseTopo::localizationCB, this);
00095   grid_update_sub_ = n.subscribe("warehouse/topological_navigation/grids/inserts",
00096                                  100, &MoveBaseTopo::gridUpdateCB, this);
00097   tmap_sub_ = n.subscribe("topological_graph", 1, &MoveBaseTopo::graphCB, this);
00098   path_pub_ = n.advertise<msg::RoadmapPath>("roadmap_path", 5);
00099   edge_watchdog_ = n.createTimer(ros::Duration(block_check_period),
00100                                  &MoveBaseTopo::checkEdgeTimeouts, this);
00101   make_plan_client_ =
00102     n.serviceClient<nm::GetPlan>("move_base_node/make_plan");
00103   switch_grid_client_ =
00104     n.serviceClient<msg::SwitchGrid>("switch_local_grid");
00105   as_.start();
00106   ROS_INFO ("move_base_topo initialization complete");
00107 }
00108 
00109 void MoveBaseTopo::graphCB (const msg::TopologicalGraph::ConstPtr m)
00110 {
00111   Lock l(mutex_);
00112   tmap_ = tmap::fromMessage(*m);
00113 }
00114 
00115 void MoveBaseTopo::cleanup()
00116 {
00117   //we'll clear our list of blocked edges when we finish a goal
00118   ROS_DEBUG_NAMED("exec", "Clearing blocked edges");
00119   blocked_edges_.clear();
00120   costmap_.stop();
00121 }
00122 
00123 gm::PoseStamped MoveBaseTopo::nodePoseOnGrid (const unsigned n, const unsigned g) const
00124 {
00125   gm::PoseStamped pose;
00126   pose.header.frame_id = tmap::gridFrame(g);
00127   pose.pose.position = positionOnGrid(n, g, roadmap_, tmap_);
00128   pose.pose.orientation.w  = 1.0;
00129   return pose;
00130 }
00131 
00132 bool MoveBaseTopo::robotWithinTolerance(double tolerance_dist, unsigned node_id)
00133 {
00134   //get the robot's position
00135   tf::Stamped<tf::Pose> robot_pose;
00136   if(!costmap_.getRobotPose(robot_pose)){
00137     ROS_ERROR("Cannot get the current pose of the robot, checking whether or not the robot is close enough to its next waypoint will fail. This is likely a tf problem");
00138     return false;
00139   }
00140   gm::PoseStamped robot_pose_msg;
00141   tf::poseStampedTFToMsg(robot_pose, robot_pose_msg);
00142 
00143   //ok now we have the pose of the robot so we need to pose of the node that we're going to
00144   const unsigned g = tmap::frameGrid(costmap_.getGlobalFrameID());
00145   const gm::PoseStamped node_pose = nodePoseOnGrid(node_id, g);
00146   double sq_dist = squareDist(robot_pose_msg, node_pose);
00147   if(sq_dist < (tolerance_dist * tolerance_dist))
00148     return true;
00149 
00150   return false;
00151 }
00152 
00153 unsigned MoveBaseTopo::lastWaypointOnGridIndex (const vector<unsigned>& plan,
00154                                                 const unsigned g) const
00155 {
00156   //get the set of nodes contained in the current grid
00157   vector<unsigned> current_nodes_vec =
00158     nodesOnGrid(g, roadmap_, tmap_).first;
00159   set<unsigned> current_nodes(current_nodes_vec.begin(),
00160                               current_nodes_vec.end());
00161 
00162   ROS_DEBUG_STREAM_NAMED ("last_waypoint_on_grid",
00163                           "Finding last waypoint for grid " << g <<
00164                           " with nodes " << gmu::toString(current_nodes_vec));
00165   unsigned int furthest_valid = 0;
00166   for(unsigned int i = 0; i < plan.size(); ++i)
00167   {
00168     ROS_DEBUG_NAMED ("last_waypoint_on_grid", "Furthest valid is %u",
00169                      furthest_valid);
00170     //check if the node in the plan is contained within the current grid
00171     if(gmu::contains(current_nodes, plan[i]))
00172     {
00173       // if so, get pose of the node in the grid and its associated potential
00174       gm::PoseStamped node_pose = nodePoseOnGrid(plan[i], g);
00175       if(planner_.validPointPotential(node_pose.pose.position))  
00176         furthest_valid = i;
00177     }
00178     else
00179       //break once we get to a node that isn't contained within the current grid
00180       break;
00181   }
00182   ROS_DEBUG_NAMED ("last_waypoint_on_grid", "Final furthest valid is %u",
00183                    furthest_valid);
00184   return furthest_valid;
00185 }
00186 
00187 // Note: Assumes that the correct potential function has been computed by planner
00188 unsigned MoveBaseTopo::lastWaypointOnGridIndex(const Path& plan,
00189                                                const string& global_frame)
00190 {
00191   return lastWaypointOnGridIndex(plan.first, tmap::frameGrid(global_frame));
00192 }
00193 
00194 bool MoveBaseTopo::makeRoadmapPlan(const msg::MoveBaseTopoGoal::ConstPtr& goal,
00195                                    boost::optional<Path>& plan, string& error_string)
00196 {
00197   plan.reset();
00198   unsigned best_id(0);
00199   double best_dist = DBL_MAX;
00200 
00201   //first, we need to get the pose of the robot in the frame of the grid
00202   tf::Stamped<tf::Pose> robot_pose;
00203   if(!costmap_.getRobotPose(robot_pose)){
00204     error_string = "Cannot navigate anywhere because the costmap could not get the current pose of the robot. Likely a tf problem.";
00205     return false;
00206   }
00207   else if (!current_grid_)
00208   {
00209     error_string = "Don't have a current grid yet, so can't make a plan.";
00210     return false;
00211   }
00212   
00213 
00214   gm::Point robot_point;
00215   robot_point.x = robot_pose.getOrigin().x();
00216   robot_point.y = robot_pose.getOrigin().y();
00217 
00218   //next, we'll compute the potential function for the current pose of the robot in the grid
00219   planner_.computePotential(robot_point);
00220 
00221   //we need to get all of the nodes that are contained within our current grids
00222   std::vector<unsigned> contained_nodes = nodesOnGrid(*current_grid_, roadmap_, tmap_).first;
00223 
00224   BOOST_FOREACH (const unsigned n, contained_nodes)
00225   {
00226     const unsigned g = tmap::frameGrid(costmap_.getGlobalFrameID());
00227     const gm::PoseStamped node_pose = nodePoseOnGrid(n, g);
00228 
00229     //pick the node with the shortest path distance to project to
00230     double path_dist = shortestPathDistance(node_pose.pose);
00231     if(path_dist < best_dist){
00232       best_dist = path_dist;
00233       best_id = n;
00234     }
00235   }
00236 
00237   if(best_dist == DBL_MAX)
00238   {
00239     error_string = "Cannot navigate anywhere because the robot cannot be projected onto a valid node.";
00240     return false;
00241   }
00242 
00243   ROS_DEBUG_STREAM("The closest node is node " << best_id <<
00244                    ", planning from this to node " << goal->goal_node);
00245 
00246   //now, we need to create a plan using the roadmap from the node to the goal node
00247   ResultPtr res = shortestPaths(roadmap_, best_id);
00248   plan = extractPath(res, goal->goal_node);
00249   if(!plan)
00250   {
00251     std::stringstream error_msg;
00252     error_msg << "Failed to find a plan between node: " << best_id << " and node: " << goal->goal_node;
00253     error_string = error_msg.str();
00254     return false;
00255   }
00256 
00257   return true;
00258 }
00259 
00260 void MoveBaseTopo::checkEdgeTimeouts(const ros::TimerEvent& e)
00261 {
00262   Lock l(mutex_);
00263   //check to see what edges in our blocked list have timed out and remove them from the list
00264   for(list<BlockedEdge>::iterator blocked_it = blocked_edges_.begin(); blocked_it != blocked_edges_.end();){
00265     if(blocked_edge_timeout_ > 0.0 && blocked_it->blocked_time + ros::Duration(blocked_edge_timeout_) < ros::Time::now()){
00266       ROS_ERROR("Unblocking edge");
00267       blocked_it = blocked_edges_.erase(blocked_it);
00268     }
00269     else
00270       ++blocked_it;
00271   }
00272 }
00273 
00274 void MoveBaseTopo::removeBlockedEdges(const list<BlockedEdge>& blocked_edges, Roadmap& roadmap)
00275 {
00276   BOOST_FOREACH (const BlockedEdge& blocked, blocked_edges)
00277   {
00278     const GraphVertex v = roadmap.node(blocked.from);
00279     const GraphVertex w = roadmap.node(blocked.to);
00280     vector<unsigned> edges_to_delete;
00281 
00282     // Two loops to avoid invalidating descriptors 
00283     BOOST_FOREACH (GraphEdge e, edge_range(v, w, roadmap))
00284       edges_to_delete.push_back(roadmap[e].id);
00285     BOOST_FOREACH (const unsigned id, edges_to_delete) 
00286       roadmap.deleteEdge(id);
00287   }
00288 }
00289 
00290 unsigned MoveBaseTopo::bestGrid (const vector<unsigned>& plan) const
00291 {
00292   ROS_ASSERT(!plan.empty());
00293   const unsigned start = plan[0];
00294   set<unsigned> candidates;
00295   const unsigned g0 = roadmap_.nodeInfo(start).grid;
00296   candidates.insert(g0);
00297   BOOST_FOREACH (const tmap::GraphVertex& v,
00298                  adjacent_vertices(tmap_.node(g0), tmap_))
00299     candidates.insert(tmap_[v].id);
00300 
00301   unsigned best_grid=-1;
00302   int best_val = -1;
00303   BOOST_FOREACH (const unsigned g, candidates) 
00304   {
00305     const int val = lastWaypointOnGridIndex(plan, g);
00306     ROS_INFO ("Grid %u has %d waypoints", g, val);
00307     if (val > best_val)
00308     {
00309       best_val = val;
00310       best_grid = g;
00311     }
00312   }
00313   ROS_ASSERT(best_grid>0);
00314   return best_grid;
00315 }
00316 
00317 void MoveBaseTopo::executeCB(const msg::MoveBaseTopoGoal::ConstPtr& goal)
00318 {
00319   ROS_DEBUG_NAMED("exec", "Received a goal, starting costmap");
00320   move_base_client_.waitForServer();
00321   costmap_.start();
00322   ROS_DEBUG_NAMED("exec", "costmap started");
00323 
00324   //we want to get a plan from the roadmap
00325   boost::optional<Path> plan;
00326   string error_string;
00327 
00328   unsigned int current_waypoint = 0;
00329   bool finished_plan = false;
00330   last_valid_plan_ = ros::Time::now();
00331 
00332   //ok... now we have a plan in the roadmap, we need to feed it to move_base
00333   while(ros::ok() && !finished_plan){
00334     move_base_msgs::MoveBaseGoal mb_goal;
00335     {
00336       //make sure that the roadmap doesn't change while we generate a goal for move_base
00337       Lock l(mutex_);
00338 
00339       //if we've just switched grids, then we need to replan
00340       //we'll do this everytime since we should only get close enough to our
00341       //exit points when  we switch grids
00342       ROS_DEBUG_NAMED("exec", "Planning");
00343       if(!makeRoadmapPlan(goal, plan, error_string)){
00344         if(ros::Time::now() > last_valid_plan_ + ros::Duration(planner_patience_)){
00345           ROS_WARN("In loop planning: %s, now: %.3f, last: %.3f, finish: %.2f", error_string.c_str(), ros::Time::now().toSec(),
00346                     last_valid_plan_.toSec(), (last_valid_plan_ + ros::Duration(planner_patience_)).toSec());
00347           as_.setAborted(msg::MoveBaseTopoResult(), error_string);
00348           cleanup();
00349           return;
00350         }
00351         ROS_WARN_THROTTLE(1.0, "Failed to make a plan in roadmap: %s, trying again.", error_string.c_str());
00352         //make sure to just keep looping here to try to replan
00353         continue;
00354       }
00355 
00356       // Beyond here we're guaranteed that makeRoadmapPlan succeeded
00357       
00358       last_valid_plan_ = ros::Time::now();
00359       ROS_DEBUG_STREAM_NAMED("exec", "Found roadmap plan: "
00360                              << gmu::toString(plan->first));
00361       msg::RoadmapPath msg;
00362       msg.path = plan->first;
00363       path_pub_.publish(msg);
00364       const unsigned best_grid = bestGrid(plan->first);
00365       
00366       
00367       msg::SwitchGrid srv;
00368       srv.request.grid = best_grid;
00369       if (!switch_grid_client_.call(srv))
00370         ROS_WARN ("Service call to switch_grid failed");
00371       ROS_DEBUG_NAMED("exec", "Requesting switch to grid %u", best_grid);
00372       unsigned i=0;
00373       while (ros::ok() && i++<20 && tmap::frameGrid(costmap_.getGlobalFrameID())!=best_grid)
00374       {
00375         ROS_DEBUG_NAMED("exec", "Waiting for costmap to catch up on grid switch");
00376         ros::Duration(0.1).sleep();
00377       }
00378 
00379       //make sure to update the frame id since it may have changed
00380       string global_frame = costmap_.getGlobalFrameID();
00381       current_waypoint = lastWaypointOnGridIndex(*plan, global_frame);
00382       grid_switch_ = false;
00383 
00384       //we need to get the pose of our target node in the grid that we're in
00385       gm::PoseStamped node_pose = nodePoseOnGrid(plan->first[current_waypoint],
00386                                                  tmap::frameGrid(global_frame));
00387       ROS_DEBUG_STREAM_NAMED("exec", "Aiming for waypoint " <<
00388                              plan->first[current_waypoint] <<
00389                              " at " << gmu::toString(node_pose.pose) << " on "
00390                              << tmap::frameGrid(global_frame));
00391 
00392       mb_goal.target_pose = node_pose;
00393       move_base_client_.sendGoal(mb_goal);
00394     } // End block guarded by mutex
00395 
00396     //now we want to monitor how close the base is to the desired goal
00397     ros::Rate r(progress_check_frequency_);
00398     bool close_enough = false;
00399     while(ros::ok() && !move_base_client_.getState().isDone() &&!close_enough)
00400     {
00401       {
00402         Lock l(mutex_);
00403         // if within tolerance continue to next waypoint
00404         if(current_waypoint < plan->first.size() - 1 &&
00405            (grid_switch_ || robotWithinTolerance(next_waypoint_distance_,
00406                                                  plan->first[current_waypoint])))
00407         {
00408           ROS_DEBUG_COND_NAMED (grid_switch_, "exec", "Switching to grid %u",
00409                                 *current_grid_);
00410           close_enough = true;
00411           //make sure to update the last valid plan time
00412           last_valid_plan_ = ros::Time::now();
00413         }
00414         ROS_DEBUG_THROTTLE_NAMED (2.0, "exec", "Waiting for waypoint %u;",
00415                                   plan->first[current_waypoint]);
00416       } // End block guarded by mutex
00417       
00418       r.sleep();
00419     }
00420 
00421     //check to see if we're just stuck at the first waypoint in our plan
00422     bool not_going_anywhere = false;
00423     if ((plan->first[current_waypoint] == plan->first[0] &&
00424          plan->first[current_waypoint] != plan->first[plan->first.size() -1])
00425         && move_base_client_.getState().isDone())
00426     {
00427       not_going_anywhere = true;
00428     }
00429 
00430     // if we're not close enough to the next waypoint (by distance or
00431     // the grid switching)
00432     // or we're just not moving along the plan, sitting still at one node
00433     // ... then we need to check the state of move_base to see if it failed
00434     ROS_DEBUG_NAMED("exec", "Not going anywhere: %d, client state: %s; "
00435                     "Close_enough is %d", not_going_anywhere,
00436                     move_base_client_.getState().toString().c_str(), close_enough);
00437     if(!close_enough || not_going_anywhere){
00438       if(not_going_anywhere || move_base_client_.getState() != actionlib::SimpleClientGoalState::SUCCEEDED){
00439 
00440         //if the base aborts on its attempt to make it to a node in the plan... we need to add to our blocked edges list
00441         //but first we need to find what edge is actually blocked by looping backwards from our current waypoint
00442         bool edge_cut = false;
00443 
00444         unsigned int check_node = 0;
00445         while(check_node <= current_waypoint && check_node < plan->first.size() - 1 && !edge_cut){
00446           ROS_ERROR("Trying to prune");
00447           //we'll plan from the first waypoint in the plan to the check point
00448           nm::GetPlan mb_plan;
00449 
00450           //we need to get the pose of this node on the grid we're on
00451           const unsigned g = tmap::frameGrid(costmap_.getGlobalFrameID());
00452 
00453           const gm::PoseStamped check_node_pose =
00454             nodePoseOnGrid(plan->first[check_node], g);
00455           const gm::PoseStamped next_node_pose =
00456             nodePoseOnGrid(plan->first[check_node+1], g);
00457 
00458           mb_plan.request.start = check_node_pose;
00459           mb_plan.request.goal = next_node_pose;
00460 
00461           //if we get a empty plan back we know that we need to cut the edge
00462           if(!make_plan_client_.call(mb_plan) ||
00463              mb_plan.response.plan.poses.empty())
00464           {
00465             Lock l(mutex_);
00466             blocked_edges_.push_back(BlockedEdge(plan->first[check_node],
00467                                                  plan->first[check_node + 1],
00468                                                  ros::Time::now()));
00469             ROS_DEBUG_NAMED("exec", "Adding a blocked edge for %u->%u",
00470                             plan->first[check_node],
00471                             plan->first[check_node + 1]);
00472             //also, make sure to remove the edges from the roadmap
00473             removeBlockedEdges(blocked_edges_, roadmap_);
00474             edge_cut = true;
00475           }
00476           ++check_node;
00477         }
00478  
00479         //we'll also continue in our loop to retry planning
00480         if(edge_cut)
00481           continue;
00482         else if(not_going_anywhere){
00483            Lock l(mutex_);
00484           blocked_edges_.push_back(BlockedEdge(plan->first[0], plan->first[1],
00485                                                ros::Time::now()));
00486           ROS_ERROR("Adding a blocked edge for %u->%u",
00487                     plan->first[0], plan->first[1]);
00488           //also, make sure to remove the edges from the roadmap
00489           removeBlockedEdges(blocked_edges_, roadmap_);
00490            continue;
00491         }
00492 
00493         std::stringstream error_msg;
00494         error_msg << "MoveBase failed to make it to node" << plan->first[current_waypoint] << " state is " << move_base_client_.getState().toString();
00495         ROS_ERROR("%s", error_msg.str().c_str());
00496         as_.setAborted(msg::MoveBaseTopoResult(), error_msg.str());
00497         cleanup();
00498         return;
00499       }
00500       else
00501         ROS_DEBUG("Made it to node: %u", plan->first[current_waypoint]);
00502     }
00503 
00504     //if the waypoint that we've achieved is the last one... then we'll set a flag to exit the loop
00505     if(current_waypoint == plan->first.size() - 1)
00506       finished_plan = true;
00507   }
00508 
00509   //check to make sure things succeeded... just in case
00510   if(move_base_client_.getState() != actionlib::SimpleClientGoalState::SUCCEEDED){
00511     std::stringstream error_msg;
00512     error_msg << "MoveBase failed to make it to node" << plan->first[plan->first.size() - 1] << " state is " << move_base_client_.getState().toString();
00513     ROS_ERROR("%s", error_msg.str().c_str());
00514     as_.setAborted(msg::MoveBaseTopoResult(), error_msg.str());
00515     cleanup();
00516     return;
00517   }
00518 
00519   ROS_INFO("Made it to the goal location");
00520   as_.setSucceeded();
00521   cleanup();
00522 }
00523 
00524 double MoveBaseTopo::shortestPathDistance(const gm::Pose& p1)
00525 {
00526   gm::PoseStamped goal;
00527   goal.header.frame_id = costmap_.getGlobalFrameID();
00528   goal.header.stamp = ros::Time::now();
00529   goal.pose = p1;
00530 
00531   vector<gm::PoseStamped> plan;
00532 
00533   //apparently navfn can return plans of  size 1 when it doesn't actually
00534   //find a valid plan... at least when that's not awesome
00535   if(!planner_.getPlanFromPotential(goal, plan) || plan.size() <= 1){
00536     return DBL_MAX;
00537   }
00538 
00539   double length = 0.0;
00540   for(unsigned int i = 0; i < plan.size() - 1; ++i){
00541     length += gmu::euclideanDistance(plan[i].pose.position, plan[i+1].pose.position);
00542   }
00543   return length;
00544 }
00545 
00546 void MoveBaseTopo::roadmapCB(msg::TopologicalRoadmap::ConstPtr roadmap)
00547 {
00548   Lock l(mutex_);
00549   roadmap_ = fromRosMessage(*roadmap);
00550   //make sure to remove any blocked edges we have from the roadmap
00551   removeBlockedEdges(blocked_edges_, roadmap_);
00552   ROS_DEBUG_NAMED("roadmap_cb", "Received roadmap with %zu nodes",
00553                   roadmap->nodes.size());
00554 }
00555 
00556 // When we receive a localization, if it results in switching to
00557 // a new grid, we load the new grid from the warehouse
00558 void MoveBaseTopo::localizationCB (const gm::PoseStamped& loc)
00559 {
00560   unsigned g;
00561   bool need_to_update;
00562   {
00563     Lock l(mutex_);
00564     g = tmap::frameGrid(loc.header.frame_id);
00565     need_to_update = (!current_grid_ || (g != *current_grid_));
00566   }
00567   if (need_to_update)
00568   {
00569     current_grid_ = g;
00570     nm::OccupancyGrid::ConstPtr grid = getGrid(g);
00571     Lock l(mutex_);
00572     grid_switch_ = true;
00573     costmap_.updateStaticMap(*grid);
00574   }
00575 }
00576 
00577 
00578 nm::OccupancyGrid::ConstPtr MoveBaseTopo::getGrid (const unsigned g) const
00579 {
00580   typedef mr::MessageWithMetadata<nm::OccupancyGrid>::ConstPtr Grid;
00581   mr::Query query("id", g);
00582   ros::Rate r(10);
00583   while (ros::ok())
00584   {
00585     const vector<Grid> results = grids_.pullAllResults(query);
00586     ROS_ASSERT_MSG (results.size()<2, "Unexpected case: there were "
00587                     " multiple grids stored for id %u", g);
00588     if (results.size()==1)
00589       return results[0];
00590     ROS_WARN_THROTTLE (1.0, "Couldn't find grid %u in warehouse", g);
00591     r.sleep();
00592   }
00593   // Only happens on shutdown
00594   return Grid();
00595 }
00596 
00597 // When we get notified of a change in the grid collection in the
00598 // warehouse, we check if the updated grid is the current one, and
00599 // if so, reload it
00600 void MoveBaseTopo::gridUpdateCB (const std_msgs::String& m)
00601 {
00602   mr::Metadata metadata(m.data);
00603   const unsigned g = metadata.getIntField("id");
00604   bool need_to_update;
00605   {
00606     Lock l(mutex_);
00607     need_to_update = (current_grid_ && (g==*current_grid_));
00608   }
00609   if (need_to_update)
00610   {
00611     nm::OccupancyGrid::ConstPtr grid = getGrid(g);
00612     Lock l(mutex_);
00613     costmap_.updateStaticMap(*grid);
00614   }
00615 }
00616 
00617 
00618 
00619 };
00620 
00621 int main(int argc, char** argv)
00622 {
00623   ros::init(argc, argv, "move_base_topo_node");
00624   topological_roadmap::MoveBaseTopo mb;
00625   ros::spin();
00626   return(0);
00627 }


topological_roadmap
Author(s): Bhaskara Marthi
autogenerated on Sun Jan 5 2014 11:39:33