00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
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
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
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
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
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
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
00171 if(gmu::contains(current_nodes, plan[i]))
00172 {
00173
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
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
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
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
00219 planner_.computePotential(robot_point);
00220
00221
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
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
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
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
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
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
00333 while(ros::ok() && !finished_plan){
00334 move_base_msgs::MoveBaseGoal mb_goal;
00335 {
00336
00337 Lock l(mutex_);
00338
00339
00340
00341
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
00353 continue;
00354 }
00355
00356
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
00380 string global_frame = costmap_.getGlobalFrameID();
00381 current_waypoint = lastWaypointOnGridIndex(*plan, global_frame);
00382 grid_switch_ = false;
00383
00384
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 }
00395
00396
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
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
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 }
00417
00418 r.sleep();
00419 }
00420
00421
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
00431
00432
00433
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
00441
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
00448 nm::GetPlan mb_plan;
00449
00450
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
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
00473 removeBlockedEdges(blocked_edges_, roadmap_);
00474 edge_cut = true;
00475 }
00476 ++check_node;
00477 }
00478
00479
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
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
00505 if(current_waypoint == plan->first.size() - 1)
00506 finished_plan = true;
00507 }
00508
00509
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
00534
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
00551 removeBlockedEdges(blocked_edges_, roadmap_);
00552 ROS_DEBUG_NAMED("roadmap_cb", "Received roadmap with %zu nodes",
00553 roadmap->nodes.size());
00554 }
00555
00556
00557
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
00594 return Grid();
00595 }
00596
00597
00598
00599
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 }