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
00039 #include <graph_slam/graph_mapper.h>
00040 #include "odometer.h"
00041 #include <graph_slam/exception.h>
00042 #include <graph_mapping_utils/utils.h>
00043 #include <pose_graph/spa_2d_conversion.h>
00044 #include <pose_graph/message_conversion.h>
00045 #include <pose_graph/graph_search.h>
00046 #include <visualization_msgs/Marker.h>
00047 #include <boost/foreach.hpp>
00048 #include <boost/filesystem.hpp>
00049
00050 namespace graph_slam
00051 {
00052
00053 namespace pg=pose_graph;
00054 namespace msg=graph_mapping_msgs;
00055 namespace util=graph_mapping_utils;
00056 namespace gm=geometry_msgs;
00057 namespace vm=visualization_msgs;
00058 namespace wh=warehouse;
00059 using std::string;
00060 using std::vector;
00061 typedef msg::LocalizationDistribution::ConstPtr LocPtr;
00062 typedef boost::optional<ros::Time> MaybeTime;
00063
00064
00065 struct Lock
00066 {
00067
00068 Lock(boost::mutex& m)
00069 {
00070 ROS_DEBUG_STREAM_NAMED ("lock", "Waiting for lock");
00071 l = boost::mutex::scoped_lock(m);
00072 ROS_DEBUG_STREAM_NAMED ("lock", "Acquired lock");
00073 }
00074
00075 ~Lock()
00076 {
00077 ROS_DEBUG_STREAM_NAMED ("lock", "Releasing lock");
00078 }
00079
00080 boost::mutex::scoped_lock l;
00081 };
00082
00083
00084 GraphMapper::GraphMapper () :
00085 param_nh_("~"), db_("graph_mapping"),
00086 angle_threshold_(util::getParam<double>(param_nh_, "angle_displacement_threshold")),
00087 pos_threshold_(util::getParam<double>(param_nh_, "position_displacement_threshold")),
00088 base_frame_(util::getParam<string>(param_nh_, "base_frame")),
00089 fixed_frame_(util::getParam<string>(param_nh_, "fixed_frame")),
00090 optimization_frame_(util::getParam<string>(param_nh_, "optimization_frame", "graph_optimization")),
00091 update_duration_(util::duration(util::getParam<double>(param_nh_, "update_rate", 2.0))),
00092 constraint_wait_(ros::Duration(util::getParam<double>(param_nh_, "constraint_wait_time", 0.5))),
00093 optimization_algorithm_(util::getParam<string>(param_nh_, "optimization_algorithm", "spa")),
00094 loc_transform_wait_(0.1), ref_transform_timeout_(8.0),
00095 add_new_nodes_(util::getParam<bool>(param_nh_, "add_new_nodes", true)),
00096 tf_(new tf::TransformListener()),
00097 localizations_(util::getParam<int>(param_nh_, "localization_buffer_size", 20), fixed_frame_, base_frame_, tf_, 5.0),
00098 initialized_(false), optimize_flag_(true), ff_poses_(&db_, "fixed_frame_poses"),
00099 saved_graph_(db_.setupCollection<msg::ConstraintGraphMessage> ("constraint_graph")),
00100 visualizer_(util::getParam<bool>(param_nh_, "visualize_node_ids", false)),
00101 update_odometer_(new Odometer(tf_.get(), base_frame_, fixed_frame_, ros::Duration(15.0))),
00102 constraint_sub_(nh_.subscribe("graph_constraints", 100, &GraphMapper::addConstraint, this)),
00103 loc_sub_(nh_.subscribe("graph_localization", 10, &GraphMapper::updateLocalization, this)),
00104 marker_pub_(nh_.advertise<vm::Marker>("visualization_marker", 5)),
00105 pose_pub_(nh_.advertise<gm::PoseStamped>("optimized_pose", 5)),
00106 get_poses_srv_(nh_.advertiseService("get_node_poses", &GraphMapper::getPoses,
00107 this)),
00108 update_timer_(nh_.createTimer(update_duration_, &GraphMapper::updateGraph, this)),
00109 vis_timer_(nh_.createTimer(util::duration(util::getParam<double>(param_nh_, "visualization_rate", 1.0)),
00110 &GraphMapper::visualize, this)),
00111 save_timer_(nh_.createTimer(util::duration(util::getParam<double>(param_nh_, "save_rate", 0.1)),
00112 &GraphMapper::saveGraphTimed, this)),
00113 ref_pub_timer_(nh_.createTimer(util::duration(util::getParam<double>(param_nh_, "ref_transform_pub_rate_", 5.0)),
00114 &GraphMapper::publishRefTransform, this))
00115
00116 {
00117 Lock l(mutex_);
00118
00120 wh::OrderingCriterion order;
00121 order.field = "_creation_time";
00122 order.reverse = true;
00123 vector<wh::Condition> no_conditions;
00124 wh::QueryResults<msg::ConstraintGraphMessage>::range_t graphs =
00125 saved_graph_.queryResults(no_conditions, false, order);
00126 if (graphs.first != graphs.second)
00127 graph_ = pg::constraintGraphFromMessage(**graphs.first);
00128
00129 diff_pub_.setGraph(graph_);
00130 ref_transform_ = tf::StampedTransform(btTransform::getIdentity(), ros::Time::now(), fixed_frame_, optimization_frame_);
00131
00132
00133 ref_transform_->getOrigin().setX(0.42);
00134 ref_transform_->getOrigin().setY(-0.42);
00135
00136 ROS_ASSERT_MSG (add_new_nodes_ || !graph_.allNodes().empty(),
00137 "Can't run in localization-only mode without a saved graph.");
00138
00139 initialized_ = true;
00140 }
00141
00142 GraphMapper::~GraphMapper ()
00143 {
00144 }
00145
00146
00147
00148
00149
00150 void GraphMapper::visualize (const ros::TimerEvent& e) const
00151 {
00152 if (!initialized_)
00153 return;
00154 visualizer_.visualize(graphSnapshot());
00155
00156
00157 boost::optional<gm::PoseStamped> l = localizations_.lastLocalization();
00158 if (l) {
00159 const unsigned ref = util::refNode(*l);
00160 if (graph_.hasOptimizedPose(ref)) {
00161 const tf::Pose p = graph_.getOptimizedPose(ref);
00162 vm::Marker m;
00163 m.ns = "graph_localization";
00164 m.header.frame_id = optimization_frame_;
00165 m.header.stamp = ros::Time::now();
00166 m.id = 1;
00167 m.type = vm::Marker::ARROW;
00168 m.action = vm::Marker::ADD;
00169 m.scale.x = 0.1;
00170 m.scale.y = 0.1;
00171 m.points.resize(2);
00172 m.points[0] = util::toPose(p).position;
00173 gm::PoseStamped opt_pose;
00174 opt_pose.pose = util::transformPose(p, l->pose);
00175 opt_pose.header.frame_id = optimization_frame_;
00176 opt_pose.header.stamp = ros::Time::now();
00177 pose_pub_.publish(opt_pose);
00178 m.points[1] = opt_pose.pose.position;
00179 m.color.r = 0.6;
00180 m.color.g = 0.2;
00181 m.color.b = 0.8;
00182 m.color.a = 1.0;
00183 marker_pub_.publish(m);
00184 }
00185 else
00186 ROS_DEBUG_STREAM_NAMED ("visualization", "Not visualizing localization: "
00187 << ref << " has no optimized pose");
00188 }
00189 else
00190 ROS_INFO_THROTTLE (3, "Not visualizing localization because "
00191 "no localizations received yet");
00192 }
00193
00194
00195
00196
00197
00200 void GraphMapper::updateLocalization (LocPtr m)
00201 {
00202 ROS_DEBUG_STREAM_NAMED ("localization", "Updating localization");
00203 ROS_ASSERT (m->samples.size() == 1);
00204 const gm::PoseStamped& p = m->samples[0];
00205 try {
00206 const tf::Pose fixed_frame_pose =
00207 fixedFramePoseAt(m->stamp, loc_transform_wait_);
00208 const unsigned ref = util::refNode(p);
00209 Lock lock(mutex_);
00210 if (graph_.hasOptimizedPose(ref)) {
00211 const tf::Pose ref_pose = graph_.getOptimizedPose(ref);
00212 const tf::Pose opt_pose = util::absolutePose(ref_pose, p.pose);
00213 ref_transform_ =
00214 tf::StampedTransform(util::transformBetween(opt_pose, fixed_frame_pose),
00215 m->stamp, fixed_frame_, optimization_frame_);
00216 }
00217 else {
00218 ROS_DEBUG_STREAM_NAMED ("localization", "Not updating ref transform due "
00219 "to lack of optimized pose for " << ref);
00220 optimize_flag_ = true;
00221 }
00222 }
00223 catch (tf::TransformException& e) {
00224 ROS_WARN_STREAM ("Transform exception during localization update: "
00225 << e.what() << "; skipping localization update");
00226 }
00227
00228 }
00229
00230 void GraphMapper::publishRefTransform (const ros::TimerEvent& e)
00231 {
00232 Lock l(mutex_);
00233
00234
00235 if (ref_transform_) {
00236 const ros::Time t = ros::Time::now();
00237 const ros::Duration age = t - ref_transform_->stamp_;
00238 if (age > ref_transform_timeout_)
00239 ROS_WARN_STREAM_THROTTLE_NAMED (3.0, "localization",
00240 "Ref transform is " << age <<
00241 " seconds old.");
00242 tf::StampedTransform tr = *ref_transform_;
00243 tr.stamp_ = t;
00244 tfb_.sendTransform(tr);
00245 ROS_DEBUG_STREAM_NAMED ("localization", "Published ref transform");
00246 }
00247 }
00248
00249
00250
00251
00252
00253
00254 void GraphMapper::addConstraint (msg::GraphConstraint::ConstPtr constraint)
00255 {
00256 Lock l(mutex_);
00257
00258 const unsigned e = graph_.addEdge (constraint->src,
00259 constraint->dest,
00260 constraint->constraint);
00261 optimize_flag_ = true;
00262 ROS_DEBUG_STREAM_NAMED ("update_graph", "Added edge from " <<
00263 constraint->src << " to " << constraint->dest);
00264
00265 msg::ConstraintGraphDiff diff;
00266 diff.new_edges.resize(1);
00267 diff.new_edges[0].id = e;
00268 diff.new_edges[0].constraint = *constraint;
00269 diff_pub_.addDiff(&diff);
00270
00271 }
00272
00273
00274
00275
00276
00277 tf::Pose GraphMapper::fixedFramePoseAt (const ros::Time& t,
00278 const ros::Duration& wait) const
00279 {
00280 tf::StampedTransform tr;
00281 tf_->waitForTransform(fixed_frame_, base_frame_, t, wait);
00282 tf_->lookupTransform(fixed_frame_, base_frame_, t, tr);
00283 return tr;
00284 }
00285
00286
00287 void GraphMapper::addNodeAt (const ros::Time& t)
00288 {
00289 const unsigned n = graph_.addNode();
00290 optimize_flag_ = true;
00291 last_node_addition_time_ = t;
00292
00293 const tf::Pose ff_pose = fixedFramePoseAt(t, update_duration_ * 0.5);
00294 ff_poses_.set(n, util::toPose(ff_pose));
00295
00296 msg::ConstraintGraphDiff diff;
00297 diff.new_node_timestamps.push_back(t);
00298 diff.new_nodes.resize(1);
00299 diff.new_nodes[0].id = n;
00300 diff_pub_.addDiff(&diff);
00301
00302 ROS_DEBUG_STREAM_NAMED ("update_graph", "Added node " << n << " at " << t);
00303 }
00304
00306 bool GraphMapper::findNodeNear (const gm::PoseStamped& p) const
00307 {
00308 const unsigned ref = util::refNode(p);
00309 const tf::Pose ref_pose = graph_.getOptimizedPose(ref);
00310 const tf::Pose opt_pose = util::absolutePose(ref_pose, p.pose);
00311 pg::OptimizedDistancePredicate pred(graph_, opt_pose.getOrigin(),
00312 pos_threshold_*4);
00313 pg::NodeSet nodes = filterNearbyNodes(graph_, ref, pred);
00314 BOOST_FOREACH (const unsigned n, nodes) {
00315 const tf::Pose p = util::relativePose(graph_.getOptimizedPose(n), opt_pose);
00316 const double angle_displacement = fabs(tf::getYaw(p.getRotation()));
00317 const double pos_displacement = p.getOrigin().length();
00318 if (angle_displacement < angle_threshold_ &&
00319 pos_displacement < pos_threshold_) {
00320 return true;
00321 }
00322 }
00323 return false;
00324 }
00325
00326 void GraphMapper::updateGraph (const ros::TimerEvent& e)
00327 {
00328 bool add_new_node = false;
00329 if (!initialized_)
00330 return;
00331
00332
00333 {
00334 Lock l(mutex_);
00335
00336 add_new_node = (graph_.allNodes().size() == 0);
00337 if (!add_new_node) {
00338 const tf::Pose disp = update_odometer_->getDisplacement();
00339 if (disp.getOrigin().length() > pos_threshold_ ||
00340 fabs(tf::getYaw(disp.getRotation())) > angle_threshold_) {
00341 boost::optional<gm::PoseStamped> last_loc =
00342 localizations_.lastLocalization();
00343 add_new_node = true;
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356 }
00357 }
00358 }
00359
00360
00361 if (add_new_node && add_new_nodes_) {
00362
00363 MaybeTime t = newNodeTime();
00364 if (t) {
00365 Lock l(mutex_);
00366 if (graph_.allNodes().empty() || localizations_.hasLocalization(*t)) {
00367 addNodeAt(*t);
00368 update_odometer_->reset();
00369 }
00370 else {
00371 ROS_DEBUG_STREAM_NAMED ("update_graph", "Not adding node at time " << *t
00372 << " due to lack of localization");
00373 }
00374 }
00375 }
00376
00378 optimize();
00379 }
00380
00381
00382 MaybeTime GraphMapper::newNodeTime ()
00383 {
00384 return MaybeTime(ros::Time::now());
00385 }
00386
00387
00388
00389
00390
00391
00392 void GraphMapper::optimize ()
00393 {
00394 if (optimize_flag_) {
00395 boost::optional<gm::PoseStamped> last_loc =
00396 localizations_.lastLocalization();
00397 if (last_loc)
00398 optimizeGraphComponent(util::refNode(*last_loc));
00399 else
00400 ROS_DEBUG_STREAM_NAMED ("optimize", "Not optimizing since "
00401 "we don't have a last localization");
00402 }
00403 }
00404
00406 void GraphMapper::optimizeGraphComponent (const unsigned n)
00407 {
00408 pg::ConstraintGraph copy;
00409 pg::NodeSet comp;
00410 pg::NodePoseMap init;
00411 {
00412 Lock l(mutex_);
00413 copy = graph_;
00414 comp = componentContaining(graph_, n);
00415 BOOST_FOREACH (unsigned n, comp)
00416 init[n] = util::toPose(*ff_poses_.get(n));
00417 }
00418
00419 pg::NodePoseMap opt_poses;
00420 ROS_DEBUG_STREAM_NAMED ("optimize", "Optimizing component with "
00421 << comp.size() << " nodes");
00422 if (optimization_algorithm_ == "spa")
00423 opt_poses = pg::optimizeGraph2D(copy, init, comp);
00424 else {
00425 ROS_ASSERT_MSG (optimization_algorithm_ == "none", "Unknown optimization "
00426 "algorithm %s", optimization_algorithm_.c_str());
00427 opt_poses = init;
00428 }
00429
00430 Lock l(mutex_);
00431 graph_.setOptimizedPoses(opt_poses);
00432 last_optimized_comp_ = comp;
00433 last_optimization_response_ = opt_poses;
00434 saveGraph();
00435 ROS_DEBUG_STREAM_NAMED ("optimize", "Optimized poses of " <<
00436 opt_poses.size() << " nodes");
00437 optimize_flag_ = false;
00438 }
00439
00440
00441 bool GraphMapper::getPoses (msg::GetPoses::Request& req, msg::GetPoses::Response& resp)
00442 {
00443 pg::NodeSet comp;
00444 pg::ConstraintGraph graph_copy;
00445 pg::NodePoseMap init_estimates;
00446 pg::NodePoseMap opt_poses;
00447 bool optimize=false;
00448
00449 {
00450
00451 Lock l(mutex_);
00452 if (req.nodes.empty())
00453 {
00454 resp.error_code = msg::GetPoses::Response::EMPTY_NODE_SET;
00455 return true;
00456 }
00457
00458
00459
00460 const unsigned n(req.nodes[0]);
00461 comp = componentContaining(graph_, n);
00462 BOOST_FOREACH (const unsigned id, req.nodes)
00463 {
00464 if (!util::contains(comp, id))
00465 {
00466 resp.error_code = msg::GetPoses::Response::DISCONNECTED_NODES;
00467 return true;
00468 }
00469 }
00470
00471
00472
00473 if (comp == last_optimized_comp_)
00474 {
00475 opt_poses = last_optimization_response_;
00476 }
00477 else
00478 {
00479 BOOST_FOREACH (unsigned n, comp)
00480 init_estimates[n] = util::toPose(*ff_poses_.get(n));
00481 optimize = true;
00482 graph_copy = graph_;
00483 }
00484 }
00485
00486
00487 if (optimize)
00488 {
00489 ROS_DEBUG_STREAM_NAMED ("optimize", "Optimizing component with "
00490 << comp.size() << " nodes");
00491 opt_poses = pg::optimizeGraph2D(graph_copy, init_estimates, comp);
00492 }
00493
00494
00495
00496
00497 {
00498 Lock l(mutex_);
00499 if (optimize)
00500 {
00501 last_optimized_comp_ = comp;
00502 last_optimization_response_ = opt_poses;
00503 }
00504 }
00505
00506
00507 resp.poses.reserve(req.nodes.size());
00508 BOOST_FOREACH (const pg::NodePoseMap::value_type& e, opt_poses)
00509 resp.poses.push_back(util::toPose(e.second));
00510 resp.error_code = msg::GetPoses::Response::SUCCESS;
00511 return true;
00512 }
00513
00514
00515
00516
00517
00518
00519
00520 void GraphMapper::saveGraphTimed (const ros::TimerEvent& e)
00521 {
00522 Lock l(mutex_);
00523
00524 }
00525
00526 void GraphMapper::saveGraph ()
00527 {
00528 ROS_DEBUG_STREAM_NAMED ("save", "Saving graph with " <<
00529 graph_.allNodes().size() << " nodes");
00530 saved_graph_.publish(pg::constraintGraphToMessage(graph_));
00531 }
00532
00533 pg::ConstraintGraph GraphMapper::graphSnapshot () const
00534 {
00535 Lock l(mutex_);
00536 return graph_;
00537 }
00538
00539
00540
00541
00542 }