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 #include <laser_slam/util.h>
00032 #include <laser_slam/scan_match_localization.h>
00033 #include <graph_mapping_utils/utils.h>
00034 #include <pose_graph/graph_search.h>
00035 #include <kidnapped_robot/SavePlace.h>
00036 #include <kidnapped_robot/MatchRequest.h>
00037 #include <boost/foreach.hpp>
00038
00039
00040 namespace laser_slam
00041 {
00042
00043 namespace pg=pose_graph;
00044 namespace util=graph_mapping_utils;
00045 namespace msg=graph_mapping_msgs;
00046 namespace gm=geometry_msgs;
00047 namespace sm=sensor_msgs;
00048 namespace ksm=karto_scan_matcher;
00049 namespace kr=kidnapped_robot;
00050
00051 using std::string;
00052
00053 struct Lock
00054 {
00055
00056 Lock(boost::mutex& m)
00057 {
00058 ROS_DEBUG_STREAM_NAMED ("lock", "Waiting for lock");
00059 l = boost::mutex::scoped_lock(m);
00060 ROS_DEBUG_STREAM_NAMED ("lock", "Acquired lock");
00061 }
00062
00063 ~Lock()
00064 {
00065 ROS_DEBUG_STREAM_NAMED ("lock", "Releasing lock");
00066 }
00067
00068 boost::mutex::scoped_lock l;
00069 };
00070
00071
00072
00073
00074
00075
00076
00077
00078 ScanMatchLocalizer::ScanMatchLocalizer(const ros::NodeHandle param_nh,
00079 boost::shared_ptr<tf::TransformListener> tf) :
00080 param_nh_(param_nh), update_rate_(util::getParam<double>(param_nh_, "update_rate", 5.0)),
00081 scan_match_proportion_(util::getParam<double>(param_nh_, "scan_match_proportion", 1.0)),
00082 max_nbd_size_(util::getParam<double>(param_nh_, "scan_match_nbd_size", 20)),
00083 fixed_frame_(util::searchParam<string>(param_nh_, "fixed_frame")),
00084 base_frame_(util::searchParam<string>(param_nh_, "base_frame")),
00085 opt_frame_(util::searchParam<string>(param_nh_, "optimization_frame", "graph_optimization")),
00086 window_size_(util::getParam<double>(param_nh_, "window_size", 5.0)),
00087 odom_noise_(util::getParam<double>(param_nh_, "odom_noise", 0.0)),
00088 match_radius_(util::getParam<double>(param_nh_, "match_radius", 0.3)),
00089 match_resolution_(util::getParam<double>(param_nh_, "match_resolution", 0.01)),
00090 angular_resolution_(util::getParam<double>(param_nh_, "angular_resolution", 0.5)),
00091 do_global_localization_(util::getParam<bool>(param_nh_, "do_global_localization", false)),
00092 initialized_(false), optimize_flag_(true), match_count_(0), tf_(tf), db_("graph_mapping"),
00093 scans_(&db_, "scans"), ff_poses_(&db_, "fixed_frame_poses"),
00094 loc_pub_(nh_.advertise<msg::LocalizationDistribution>("graph_localization", 10)),
00095 image_save_pub_(nh_.advertise<kr::SavePlace>("save_image", 10)),
00096 match_req_pub_(nh_.advertise<kr::MatchRequest>("match", 10)),
00097 init_pose_sub_(nh_.subscribe("initialpose", 5, &ScanMatchLocalizer::setInitialPose, this)),
00098 scan_sub_(nh_.subscribe("scan", 1, &ScanMatchLocalizer::storeScan, this)),
00099 match_sub_(nh_.subscribe("transform_matches", 100, &ScanMatchLocalizer::matchCB, this)),
00100 diff_sub_(boost::bind(&ScanMatchLocalizer::diffCB, this, _1, _2)),
00101 get_poses_client_(nh_.serviceClient<msg::GetPoses>("get_node_poses")),
00102 update_timer_(nh_.createTimer(util::duration(update_rate_), &ScanMatchLocalizer::update, this))
00103 {
00104 Lock l(mutex_);
00105
00106
00107 const string laser_frame = util::searchParam<string>(param_nh_, "laser_frame");
00108 while (ros::ok()) {
00109 ros::Time t = ros::Time::now();
00110 if (tf_->waitForTransform(base_frame_, laser_frame, t, ros::Duration(5.0))) {
00111 gm::PoseStamped id, laser_pose;
00112 id.pose.orientation.w = 1.0;
00113 id.header.stamp = t;
00114 id.header.frame_id = laser_frame;
00115 tf_->transformPose(base_frame_, id, laser_pose);
00116 laser_offset_ = util::toPose(laser_pose.pose);
00117 break;
00118 }
00119 ROS_INFO_STREAM ("Waiting for transform between " << base_frame_ << " and " << laser_frame);
00120 }
00121
00122 initialized_ = ros::ok();
00123 ROS_DEBUG_NAMED ("init", "Initialized scan match localizer");
00124 }
00125
00126
00127
00128
00129
00130
00131 bool ScanMatchLocalizer::closerTo (const pg::ConstraintGraph& g,
00132 const btVector3& barycenter,
00133 const unsigned n1, const unsigned n2) const
00134 {
00135 if (!g.hasOptimizedPose(n2))
00136 return true;
00137 else if (!g.hasOptimizedPose(n1))
00138 return false;
00139
00140 const tf::Pose p1 = g.getOptimizedPose(n1);
00141 const tf::Pose p2 = g.getOptimizedPose(n2);
00142 const btVector3 b1 = p1*util::toPoint(scans_.get(n1)->barycenter);
00143 const btVector3 b2 = p2*util::toPoint(scans_.get(n2)->barycenter);
00144 return (barycenter.distance2(b1) < barycenter.distance2(b2));
00145 }
00146
00147
00148 unsigned ScanMatchLocalizer::closestNode (const pg::ConstraintGraph& g,
00149 const pg::NodeSet& nodes,
00150 const btVector3& p) const
00151 {
00152 ROS_DEBUG_STREAM_NAMED ("localization_matching",
00153 "Looking for closest node among nodes " <<
00154 util::toString(nodes));
00155 pg::NodeSet::const_iterator closest =
00156 min_element(nodes.begin(), nodes.end(),
00157 boost::bind(&ScanMatchLocalizer::closerTo, this,
00158 boost::ref(g), boost::ref(p), _1, _2));
00159 return *closest;
00160 }
00161
00162 void ScanMatchLocalizer::recomputeOptimizedPoses (pg::ConstraintGraph* g, const pg::NodeSet& comp)
00163 {
00164 BOOST_FOREACH (const unsigned n, comp)
00165 {
00166 if (!util::contains(opt_poses_, n))
00167 optimize_flag_ = true;
00168 }
00169 if (optimize_flag_)
00170 {
00171 optimizeGraph(g, &opt_poses_, comp, get_poses_client_);
00172 optimize_flag_ = false;
00173 }
00174 g->setOptimizedPoses(opt_poses_);
00175 }
00176
00177
00178
00179
00180
00181
00182
00183 void ScanMatchLocalizer::storeScan (sm::LaserScan::ConstPtr scan)
00184 {
00185 Lock l(mutex_);
00186 if (initialized_) {
00187 last_scan_ = scan;
00188 initialization_scan_ = scan;
00189 }
00190 }
00191
00192 void ScanMatchLocalizer::diffCB (boost::optional<const msg::ConstraintGraphDiff&> diff,
00193 const pg::ConstraintGraph& graph)
00194 {
00195 Lock l(mutex_);
00196 if (!diff || (diff && diff->new_edges.size() > 0))
00197 optimize_flag_ = true;
00198 else {
00199 ROS_ASSERT (diff->new_node_timestamps.size()==1 && diff->new_nodes.size()==1);
00200 kr::SavePlace m;
00201 m.stamp = diff->new_node_timestamps[0];
00202 m.id = diff->new_nodes[0].id;
00203 image_save_pub_.publish(m);
00204 ROS_DEBUG_STREAM_NAMED ("place_rec", "Saving image: " << m);
00205 }
00206 }
00207
00208
00209 void ScanMatchLocalizer::matchCB (const kr::MatchResult& match)
00210 {
00211 Lock l(mutex_);
00212
00213
00214 if (!match_result_) {
00215 match_result_ = match;
00216 ROS_INFO_STREAM ("Match result: " << match);
00217 const tf::Pose p(btQuaternion(match.transform.rotation.x, match.transform.rotation.y,
00218 match.transform.rotation.z, match.transform.rotation.w),
00219 btVector3(match.transform.translation.x, match.transform.translation.y,
00220 match.transform.translation.z));
00221 const unsigned n(match.match_id);
00222 pg::ConstraintGraph g = diff_sub_.getCurrentGraph();
00223 const pg::NodeSet comp = pg::componentContaining(g, n);
00224 recomputeOptimizedPoses(&g, comp);
00225 const tf::Pose init_estimate = g.getOptimizedPose(n)*p;
00226 adjustInitialPose(init_estimate, g, comp, 2.0);
00227 }
00228 }
00229
00231 void ScanMatchLocalizer::setInitialPose (const gm::PoseWithCovarianceStamped& m)
00232 {
00233 Lock l(mutex_);
00234 while (!tf_->waitForTransform(opt_frame_, m.header.frame_id, ros::Time(), ros::Duration(3.0)))
00235 ROS_INFO_STREAM ("Waiting for transform between " << opt_frame_ << " and " << m.header.frame_id);
00236 gm::PoseStamped pose, opt_pose;
00237 pose.header = m.header;
00238 pose.pose = m.pose.pose;
00239 tf_->transformPose(opt_frame_, pose, opt_pose);
00240 const tf::Pose init_estimate = util::toPose(opt_pose.pose);
00241 pg::ConstraintGraph g = diff_sub_.getCurrentGraph();
00242 const pg::NodeSet comp = largestComp(g);
00243 ROS_INFO_STREAM ("Largest component has size " << comp.size());
00244
00245 recomputeOptimizedPoses(&g, comp);
00246 adjustInitialPose(init_estimate, g, comp, 5.0);
00247 }
00248
00249
00250 void ScanMatchLocalizer::adjustInitialPose (const tf::Pose& init_estimate,
00251 const pg::ConstraintGraph& g, const pg::NodeSet& comp,
00252 const double global_loc_radius)
00253 {
00254
00255
00256 ros::Time t;
00257 tf::Pose corrected_pose;
00258 if (initialization_scan_) {
00259 MatcherPtr matcher(new ksm::KartoScanMatcher(*initialization_scan_, util::projectToPose2D(laser_offset_), 5.0, 0.05));
00260 pg::NodeSet nearby;
00261 const btVector3& pos = init_estimate.getOrigin();
00262 BOOST_FOREACH (const unsigned n, comp) {
00263 if (g.getOptimizedPose(n).getOrigin().distance(pos) < 5.0)
00264 nearby.insert(n);
00265 }
00266 boost::optional<tf::StampedTransform> prev_ff_pose =
00267 fixedFramePoseAt(initialization_scan_->header.stamp);
00268 const ksm::ScanMatchResult res = globalLocalization(g, matcher, nearby, scans_,
00269 *initialization_scan_, laser_offset_, global_loc_radius, 0.2,
00270 pos.x(), pos.y(), pos.x(), pos.y());
00271
00272
00273 t = ros::Time::now();
00274 boost::optional<tf::StampedTransform> current_ff_pose =
00275 fixedFramePoseAt(t);
00276 if (prev_ff_pose && current_ff_pose)
00277 corrected_pose = util::toPose(res.pose)*util::relativePose(*current_ff_pose, *prev_ff_pose);
00278 else
00279 corrected_pose = util::toPose(res.pose);
00280 }
00281 else {
00282 corrected_pose = init_estimate;
00283 t = initialization_scan_->header.stamp;
00284 ROS_WARN ("Don't have an initialization scan; using init estimate without correcting.");
00285 }
00286
00287 const unsigned closest = closestNode(g, g.allNodes(), corrected_pose.getOrigin());
00288 last_loc_.reset(new msg::LocalizationDistribution());
00289 last_loc_->stamp = t;
00290 last_loc_->samples.resize(1);
00291 last_loc_->samples[0].header.frame_id = util::nodeFrame(closest);
00292 last_loc_->samples[0].pose = util::toPose(util::relativePose(corrected_pose, g.getOptimizedPose(closest)));
00293 ROS_DEBUG_STREAM_NAMED ("localization", "Initialized localization to " << last_loc_->samples[0]);
00294 }
00295
00296
00297
00298
00299
00300
00301 void ScanMatchLocalizer::updateUsingFixedFrame ()
00302 {
00303 const ros::Time t = ros::Time::now();
00304 const ros::Time& t_prev = last_loc_->stamp;
00305 const ros::Time t_loc = t_prev + (t-t_prev)*0.5;
00306 ROS_ASSERT_MSG (last_loc_->samples.size()==1, "Assumption of deterministic localization violated.");
00307 const gm::PoseStamped& l = last_loc_->samples[0];
00308 const unsigned ref = util::refNode(l);
00309
00310
00311 gm::PoseStamped current;
00312 current.pose.orientation.w = 1.0;
00313 current.header.stamp = t_loc;
00314 current.header.frame_id = base_frame_;
00315 const util::MaybePose disp =
00316 util::waitAndTransform(*tf_, current, base_frame_, t_prev, fixed_frame_,
00317 util::duration(update_rate_)*0.5);
00318 if (!disp)
00319 {
00320 ROS_WARN_STREAM ("Skipping localization as couldn't get transform between "
00321 << t_loc << " and " << t_prev);
00322 return;
00323 }
00324
00325 const tf::Pose prev_offset = util::toPose(last_loc_->samples[0].pose);
00326
00327
00328 last_loc_.reset(new msg::LocalizationDistribution());
00329
00330 last_loc_->stamp = t_loc;
00331 last_loc_->samples.resize(1);
00332 last_loc_->samples[0].pose = util::transformPose(prev_offset, disp->pose);
00333 last_loc_->samples[0].header.frame_id = util::nodeFrame(ref);
00334
00335 loc_pub_.publish(last_loc_);
00336
00337 }
00338
00339
00340
00341 void ScanMatchLocalizer::update (const ros::TimerEvent& e)
00342 {
00343 Lock l(mutex_);
00344 if (!initialized_) {
00345 ROS_INFO_THROTTLE_NAMED (3, "localization", "Waiting for initialization");
00346 return;
00347 }
00348
00349 if (!last_scan_ || match_count_++ < 1/scan_match_proportion_) {
00350
00351
00352 if (!last_loc_)
00353 ROS_INFO_THROTTLE_NAMED(1, "localization", "Waiting for initial scan");
00354
00355
00356 else
00357 updateUsingFixedFrame();
00358 }
00359
00360 else {
00361 const ros::Time t = last_scan_->header.stamp;
00362
00363 if (!matcher_) {
00364 gm::Pose2D offset = util::projectToPose2D(laser_offset_);
00365 matcher_.reset(new ksm::KartoScanMatcher(*last_scan_, offset,
00366 match_radius_,
00367 match_resolution_));
00368 }
00369 pg::ConstraintGraph g = diff_sub_.getCurrentGraph();
00370
00371
00372 if (!last_loc_) {
00373
00374 if (g.allNodes().empty()) {
00375 ROS_INFO_THROTTLE_NAMED (2, "localization",
00376 "Waiting for nonempty graph");
00377 return;
00378 }
00379
00380
00381 else if (g.allNodes().size() == 1) {
00382 last_loc_.reset(new msg::LocalizationDistribution());
00383 last_loc_->stamp = t;
00384 last_loc_->samples.resize(1);
00385 last_loc_->samples[0].pose.orientation.w = 1.0;
00386 last_loc_->samples[0].header.frame_id = util::nodeFrame(1);
00387 loc_pub_.publish(last_loc_);
00388 }
00389
00390 else {
00391 ROS_INFO_THROTTLE_NAMED (2, "localization",
00392 "Waiting for initial localization for graph "
00393 " with %zu nodes", g.allNodes().size());
00394 const ros::Time t = ros::Time::now();
00395 if (!last_match_request_time_ ||
00396 t > *last_match_request_time_ + ros::Duration(3.0)) {
00397 ROS_DEBUG_STREAM_NAMED ("place_rec", "Sending match request");
00398 last_match_request_time_ = t;
00399 kr::MatchRequest req;
00400 req.stamp = t;
00401 match_req_pub_.publish(req);
00402 match_result_.reset();
00403 }
00404 }
00405 }
00406
00407 else {
00408 const gm::PoseStamped& l = last_loc_->samples[0];
00409 const unsigned ref = util::refNode(l);
00410
00411
00412 recomputeOptimizedPoses(&g, pg::componentContaining(g, ref));
00413
00414
00415 const ros::Time& t_prev = last_loc_->stamp;
00416 msg::LocalizationDistribution::Ptr new_loc(new msg::LocalizationDistribution());
00417
00418
00419 gm::PoseStamped current;
00420 current.pose.orientation.w = 1.0;
00421 current.header.stamp = t;
00422 current.header.frame_id = base_frame_;
00423 util::MaybePose disp =
00424 util::waitAndTransform(*tf_, current, base_frame_, t_prev, fixed_frame_,
00425 util::duration(update_rate_)*0.5);
00426 if (!disp)
00427 {
00428 ROS_WARN_STREAM ("Skipping localization as couldn't get transform between time " << t << " and " << t_prev);
00429 return;
00430 }
00431
00432 ROS_ASSERT_MSG (last_loc_->samples.size()==1, "Assumption of deterministic localization violated.");
00433 new_loc->stamp = t;
00434 new_loc->samples.resize(1);
00435
00436 ROS_ASSERT (g.hasOptimizedPose(ref));
00437 const tf::Pose old_pose =
00438 util::absolutePose(g.getOptimizedPose(ref), l.pose);
00439 const tf::Pose init_estimate = old_pose*util::toPose(disp->pose);
00440 const pg::OptimizedDistancePredicate
00441 pred(g, init_estimate.getOrigin(), window_size_);
00442 const pg::NodeSet nearby_nodes = pg::filterNearbyNodes(g, ref, pred);
00443 if (nearby_nodes.empty())
00444 {
00445 ROS_WARN ("Reverting to fixed frame update because "
00446 "currently too far from any graph nodes");
00447 updateUsingFixedFrame();
00448 }
00449 else
00450 {
00451
00452
00453
00454 const pg::NodeSet nbd =
00455 util::sampleSubset(nearby_nodes, max_nbd_size_);
00456 const ksm::ScanMatchResult res =
00457 scanMatchNodes(g, matcher_, nbd, scans_, *last_scan_,
00458 init_estimate, laser_offset_);
00459 match_count_=0;
00460
00461 const tf::Pose new_pose = util::toPose(res.pose);
00462
00463
00464 const btVector3& b = new_pose*util::barycenter(*last_scan_);
00465 const unsigned closest = closestNode(g, nearby_nodes, b);
00466
00467 new_loc->samples[0].header.frame_id = util::nodeFrame(closest);
00468 const tf::Pose closest_pose = g.getOptimizedPose(closest);
00469 new_loc->samples[0].pose =
00470 util::toPose(util::relativePose(new_pose, closest_pose));
00471 last_loc_ = new_loc;
00472 loc_pub_.publish(last_loc_);
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483 }
00484 }
00485
00486 last_scan_.reset();
00487 }
00488
00489 }
00490
00491
00492
00493 boost::optional<tf::StampedTransform>
00494 ScanMatchLocalizer::fixedFramePoseAt (const ros::Time& t)
00495 {
00496 return util::getTransform(*tf_, fixed_frame_, base_frame_,
00497 t, util::duration(update_rate_));
00498 }
00499
00500
00501 }
00502