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
00037 #include <pose_graph/pose_graph.h>
00038 #include <graph_slam/global_map.h>
00039 #include <graph_slam/constraint_generator.h>
00040 #include <pose_graph/utils.h>
00041 #include <graph_slam/scan_match_localization.h>
00042 #include <pose_graph/spa_conversion.h>
00043 #include <pose_graph/spa_2d_conversion.h>
00044 #include <pose_graph/transforms.h>
00045 #include <pose_graph/pose_graph_message.h>
00046 #include <pose_graph/Edge.h>
00047 #include <pose_graph/exception.h>
00048 #include <graph_slam/diff_publisher.h>
00049 #include <pose_graph/SaveGraph.h>
00050 #include <graph_slam/GenerateGlobalMap.h>
00051 #include <occupancy_grid_utils/ray_tracer.h>
00052 #include <ros/ros.h>
00053 #include <ros/time.h>
00054 #include <rosbag/view.h>
00055 #include <tf/transform_datatypes.h>
00056 #include <tf/transform_listener.h>
00057 #include <tf/transform_broadcaster.h>
00058 #include <sensor_msgs/LaserScan.h>
00059 #include <graph_slam/GraphLocalization.h>
00060 #include <laser_geometry/laser_geometry.h>
00061 #include <geometry_msgs/PoseStamped.h>
00062 #include <visualization_msgs/MarkerArray.h>
00063 #include <boost/thread.hpp>
00064 #include <boost/foreach.hpp>
00065 #include <boost/shared_ptr.hpp>
00066 #include <boost/optional.hpp>
00067 #include <string>
00068 #include <algorithm>
00069 #include <std_msgs/Int64.h>
00070 #include <std_msgs/UInt64.h>
00071 #include <queue>
00072
00073
00074 namespace graph_slam
00075 {
00076
00077 namespace gu=occupancy_grid_utils;
00078 namespace sm=sensor_msgs;
00079 namespace gm=geometry_msgs;
00080 namespace nm=nav_msgs;
00081 namespace vm=visualization_msgs;
00082 namespace gs=graph_slam;
00083
00084 using namespace pose_graph;
00085
00086
00087 using boost::format;
00088 using XmlRpc::XmlRpcValue;
00089 using std::string;
00090 using std::map;
00091 using Eigen3::Vector3d;
00092 using Eigen3::Quaterniond;
00093 using Eigen3::Affine3d;
00094 using Eigen3::Translation3d;
00095 using ros::NodeHandle;
00096 using ros::WallTimerEvent;
00097 using ros::WallDuration;
00098 using ros::Duration;
00099 using ros::Time;
00100 using std_msgs::Int64;
00101 using std_msgs::UInt64;
00102 using boost::optional;
00103 using gm::Pose;
00104 using gm::PoseStamped;
00105 using gu::LocalizedCloud;
00106
00107 typedef boost::mutex::scoped_lock Lock;
00108 typedef set<NodeId> NodeSet;
00109 typedef boost::shared_ptr<nm::OccupancyGrid> GridPtr;
00110 typedef boost::shared_ptr<nm::OccupancyGrid const> GridConstPtr;
00111
00112
00113
00114
00115
00116 const double DEFAULT_POS_THRESHOLD = 2.0;
00117 const double DEFAULT_ANGLE_THRESHOLD = 1.57;
00118 const double STATUS_CHECK_INT = 5.0;
00119
00120
00121
00122
00123
00124 class GraphMapper
00125 {
00126 public:
00127
00129 GraphMapper();
00130
00131 private:
00132
00133
00134
00135
00136
00137
00138 void initializeFromFile (const string& fname);
00139
00140
00141 void setupConstraintGenerators () ;
00142
00143
00144 void updateGraph (const WallTimerEvent& e);
00145
00146
00147
00148 void updateGrid ();
00149
00150
00151 void updateFixedFramePose (const Time& t);
00152
00153
00154 void addNewNode ();
00155
00156
00157 bool sufficientlyFarApart (const Pose& p1, const Pose& p2) const;
00158
00159
00160 void scanCallback (const sm::LaserScan::ConstPtr& scan);
00161
00162
00163 void visualizeGridCallback (const UInt64& n);
00164
00165
00166 bool saveGraph (SaveGraph::Request& req, SaveGraph::Response& resp);
00167
00168
00169 bool generateMap (GenerateGlobalMap::Request& req, GenerateGlobalMap::Response& resp);
00170
00171
00172 void visualize (const WallTimerEvent& e);
00173
00174
00175 void optimizePosesTimed (const WallTimerEvent& e);
00176
00177
00178 void optimizePoses ();
00179
00180
00181 void publishLocalization (const WallTimerEvent& e);
00182
00183
00184 PoseStamped currentFixedFramePose () const;
00185
00186
00187 void statusCheck (const WallTimerEvent& e);
00188
00189
00190 optional<NodeId> getGridNode (const NodeId n) const;
00191
00192
00193
00194 void updateConstraintLocalizations ();
00195
00196
00197
00198
00199
00200
00201 const string sensor_frame_;
00202
00203
00204 const string base_frame_;
00205
00206
00207 const string fixed_frame_;
00208
00209
00210 const double pos_threshold_;
00211
00212
00213 const double angle_threshold_;
00214
00215
00216 const bool optimize_;
00217
00218
00219 const bool use_spa_3d_;
00220
00221
00222 const double local_grid_size_;
00223
00224
00225 const double local_grid_resolution_;
00226
00227
00228
00229
00230
00231
00232 bool initialized_;
00233
00234
00235 LaserPtr last_scan_;
00236
00237
00238 optional<PoseStamped> last_fixed_frame_pose_;
00239
00240
00241 NodePoseMap node_fixed_frame_poses_;
00242
00243
00244 optional<NodeId> last_node_;
00245
00246
00247 PoseGraph graph_;
00248
00249
00250
00251 NodePoseMap optimized_node_poses_;
00252
00253
00254 map<NodeId, NodeId> node_to_grid_node_;
00255
00256
00257 map<NodeId, NodeSet> grid_node_to_nodes_;
00258
00259
00260 optional<NodeId> current_grid_node_;
00261
00262
00263 optional<LocalizationState> localization_;
00264
00265
00266 std::queue<PoseGraphDiff> diffs_;
00267
00268
00269 optional<NodeId> node_to_visualize_;
00270
00271
00272 unsigned current_graph_id_;
00273
00274
00275
00276
00277
00278 mutable boost::mutex mutex_;
00279 NodeHandle nh_;
00280 tf::TransformListener tf_;
00281 ros::WallTimer update_timer_, visualization_timer_, optimization_timer_, graph_publication_timer_,
00282 status_check_timer_;
00283 ros::Subscriber scan_sub_, visualize_grid_sub_;
00284 ros::Publisher vis_marker_pub_, marker_array_pub_, vis_scan_pub_, current_frame_pub_, local_grid_pub_;
00285 ros::ServiceServer graph_save_srv_, generate_map_srv_;
00286 tf::TransformBroadcaster tf_broadcaster_;
00287 vector<ConstraintGenPtr> constraint_generators_;
00288 boost::shared_ptr<ScanMatchLocalization> localizer_;
00289 DiffPublisher diff_pub_;
00290
00291
00292 };
00293
00294
00295
00296
00297
00298
00299 unsigned getUniqueId ()
00300 {
00301 static unsigned id=0;
00302 return id++;
00303 }
00304
00305
00306
00307
00308
00309
00310 template <class T>
00311 T getPrivateParam (const string& name, const T& default_val)
00312 {
00313 T val;
00314 NodeHandle nh("~");
00315 nh.param(name, val, default_val);
00316 ROS_DEBUG_STREAM_NAMED ("init", "Set param " << name << " to value "
00317 << val << " (default was " << default_val << ")");
00318 return val;
00319 }
00320
00321
00322 void GraphMapper::setupConstraintGenerators ()
00323 {
00324 NodeHandle private_nh("~");
00325 XmlRpcValue constraint_sources;
00326 if (!private_nh.getParam("constraint_sources", constraint_sources))
00327 throw NonexistentParamException("~constraint_sources");
00328 const XmlRpcValue::Type type = constraint_sources.getType();
00329 if (type != XmlRpcValue::TypeArray)
00330 throw PoseGraphException(format("~constraint_sources parameter was not an array (type was %1%)") % type);
00331 for (int i=0; i<constraint_sources.size(); i++) {
00332 XmlRpcValue source = constraint_sources[i];
00333 const XmlRpcValue::Type type = source.getType();
00334 if (type != XmlRpcValue::TypeString)
00335 throw PoseGraphException(format("~constraint_sources[%1%] was not a string (type was %1%)") % type);
00336
00337 const NodeHandle source_nh(private_nh, (string) source);
00338 ROS_DEBUG_STREAM_NAMED ("init", "Initializing constraint " << (string) source);
00339 constraint_generators_.push_back(createConstraintGenerator(source_nh));
00340 }
00341 }
00342
00343
00344 void GraphMapper::initializeFromFile (const string& fname)
00345 {
00346 Lock lock(mutex_);
00347 ROS_DEBUG_STREAM_NAMED ("init", "Reading graph from " << fname);
00348 graph_ = readFromFile(fname);
00349 const NodeSet nodes = graph_.allNodes();
00350 BOOST_FOREACH (const NodeId n, nodes) {
00351 node_fixed_frame_poses_[n] = graph_.getInitialPoseEstimate(n);
00352 ROS_ASSERT_MSG (graph_.hasScan(n), "Graph doesn't have scan for node %lu", n.getId());
00353 localizer_->addNode(n, graph_.getScan(n));
00354 }
00355
00356 const NodeId n1 = *nodes.begin();
00357 last_node_ = n1;
00358 vector<NodeId> seq;
00359 seq.push_back(n1);
00360 BOOST_FOREACH (ConstraintGenPtr gen, constraint_generators_) {
00361 gen->initializeFromGraph(graph_, seq);
00362 }
00363 ROS_DEBUG_STREAM_NAMED ("init", "Read graph with " << nodes.size() << " nodes; assuming we're at " << n1);
00364 optimizePoses();
00365 localization_ = LocalizationState(n1, poseToWorldTransform(node_fixed_frame_poses_[n1]),
00366 convertToPose(makePose2D(0, 0, 0)), ros::Time::now());
00367 while (!last_fixed_frame_pose_)
00368 updateFixedFramePose(ros::Time::now());
00369 updateConstraintLocalizations();
00370 diff_pub_.setGraph(graph_);
00371 }
00372
00373 void GraphMapper::updateConstraintLocalizations ()
00374 {
00375 gs::GraphLocalization loc;
00376 loc.node = localization_->ref_node.getId();
00377 loc.offset = localization_->node_frame_pose;
00378 loc.fixed_frame_pose = last_fixed_frame_pose_->pose;
00379
00380 BOOST_FOREACH (ConstraintGenPtr gen, constraint_generators_)
00381 gen->updateLocalization(loc);
00382 }
00383
00384 double getLocalizationRate ()
00385 {
00386 return getPrivateParam("localization_pub_rate", 5.0);
00387 }
00388
00389 unsigned getLocalizationsPerScanMatch ()
00390 {
00391 unsigned l = ceil(getLocalizationRate()/getPrivateParam("localization_scan_match_rate", 5.0));
00392 ROS_DEBUG_STREAM_NAMED ("init", "We'll do scan matching every " << l << " localizations");
00393 return l;
00394 }
00395
00396
00397
00398
00399 GraphMapper::GraphMapper () :
00400 sensor_frame_(getPrivateParam<string>("sensor_frame", "base_laser_link")),
00401 base_frame_(getPrivateParam<string>("base_frame", "base_footprint")),
00402 fixed_frame_(getPrivateParam<string>("fixed_frame", "odom")),
00403 pos_threshold_(getPrivateParam<double>("position_displacement_threshold", DEFAULT_POS_THRESHOLD)),
00404 angle_threshold_(getPrivateParam<double>("angle_displacement_threshold", DEFAULT_ANGLE_THRESHOLD)),
00405 optimize_(getPrivateParam<bool>("optimize", true)),
00406 use_spa_3d_(getPrivateParam<bool>("use_spa_3d", true)),
00407 local_grid_size_(getPrivateParam("local_grid_size", 20.0)),
00408 local_grid_resolution_(getPrivateParam("local_grid_resolution", 0.05)),
00409 initialized_(false), update_timer_(nh_.createWallTimer(WallDuration(0.5), &GraphMapper::updateGraph, this)),
00410 visualization_timer_(nh_.createWallTimer(WallDuration(2.0), &GraphMapper::visualize, this)),
00411 optimization_timer_(nh_.createWallTimer(WallDuration(1.0), &GraphMapper::optimizePosesTimed, this)),
00412 graph_publication_timer_(nh_.createWallTimer(WallDuration(1/getLocalizationRate()), &GraphMapper::publishLocalization, this)),
00413 status_check_timer_(nh_.createWallTimer(WallDuration(STATUS_CHECK_INT), &GraphMapper::statusCheck, this)),
00414 scan_sub_(nh_.subscribe("scan", 1, &GraphMapper::scanCallback, this)),
00415 visualize_grid_sub_(nh_.subscribe("visualize_grid", 10, &GraphMapper::visualizeGridCallback, this)),
00416 vis_marker_pub_(nh_.advertise<vm::Marker> ("visualization_marker", 10)),
00417 marker_array_pub_(nh_.advertise<vm::MarkerArray> ("visualization_marker_array", 1000)),
00418 vis_scan_pub_(nh_.advertise<sm::PointCloud> ("pose_graph_scans", 100)),
00419 current_frame_pub_(nh_.advertise<Int64> ("current_node", 10)),
00420 local_grid_pub_(nh_.advertise<nm::OccupancyGrid> ("graph_mapper_local_grid", 10)),
00421 graph_save_srv_(nh_.advertiseService("save_graph", &GraphMapper::saveGraph, this)),
00422 generate_map_srv_(nh_.advertiseService("generate_map", &GraphMapper::generateMap, this)),
00423 diff_pub_(nh_)
00424 {
00425
00426 tf::Stamped<tf::Pose> ident;
00427 tf::Stamped<tf::Transform> laser_to_base;
00428 ident.setIdentity();
00429 ident.frame_id_ = sensor_frame_;
00430 ident.stamp_ = ros::Time();
00431
00432 {
00433 bool received = false;
00434 while (!received) {
00435 if (!tf_.waitForTransform(base_frame_, ident.frame_id_, ros::Time::now(), ros::Duration(10.0))) {
00436 ROS_WARN_STREAM ("Still waiting for transformn betwee " << sensor_frame_ << " and " << base_frame_ <<
00437 " during graph mapper initialization");
00438 ros::WallDuration(1.0).sleep();
00439 }
00440 else
00441 received = true;
00442 }
00443 }
00444
00445 tf_.transformPose(base_frame_, ident, laser_to_base);
00446 gm::Pose2D laser_pose;
00447 laser_pose.x = laser_to_base.getOrigin().x();
00448 laser_pose.y = laser_to_base.getOrigin().y();
00449 laser_pose.theta = tf::getYaw(laser_to_base.getRotation());
00450 localizer_.reset(new ScanMatchLocalization(laser_pose, 5.0, getLocalizationsPerScanMatch()));
00451
00452 string input_file = getPrivateParam("input_file", string());
00453 setupConstraintGenerators();
00454 if (!input_file.empty())
00455 initializeFromFile (input_file);
00456 initialized_ = true;
00457 }
00458
00459
00460
00461
00462
00463
00464 void GraphMapper::scanCallback (const LaserPtr& scan)
00465 {
00466 Lock lock(mutex_);
00467 ROS_DEBUG_COND_NAMED (!last_scan_, "pose_graph_node", "First scan received");
00468 last_scan_ = scan;
00469 updateFixedFramePose(scan->header.stamp);
00470 }
00471
00472
00473 void GraphMapper::visualizeGridCallback (const UInt64& n)
00474 {
00475 node_to_visualize_ = NodeId(n.data);
00476 }
00477
00478 bool GraphMapper::saveGraph (SaveGraph::Request& req, SaveGraph::Response& resp)
00479 {
00480 if (initialized_) {
00481 Lock lock(mutex_);
00482 rosbag::Bag bag(req.filename, rosbag::bagmode::Write);
00483 bag.write("graph", ros::Time::now(), poseGraphToRos(graph_));
00484 resp.succeeded = true;
00485 }
00486 else {
00487 ROS_WARN ("Can't save graph yet as it's not initialized");
00488 resp.succeeded = false;
00489 }
00490 return true;
00491 }
00492
00493 bool GraphMapper::generateMap (GenerateGlobalMap::Request& req, GenerateGlobalMap::Response& resp)
00494 {
00495 boost::shared_ptr<PoseGraph> copy;
00496 NodePoseMap opt_poses;
00497
00498
00499 {
00500 Lock lock(mutex_);
00501 copy.reset(new PoseGraph(graph_));
00502 opt_poses = optimized_node_poses_;
00503 }
00504
00505 if (initialized_ && !opt_poses.empty()) {
00506 resp.map = *generateGlobalMap(*copy, opt_poses, req.resolution);
00507 resp.succeeded = true;
00508 local_grid_pub_.publish(resp.map);
00509 }
00510 else {
00511 ROS_WARN ("Can't generate global map as not yet fully initialized");
00512 resp.succeeded = false;
00513 }
00514
00515 return true;
00516 }
00517
00518
00519
00520
00521
00522
00523
00524
00525 void GraphMapper::updateGraph (const WallTimerEvent& e)
00526 {
00527 Pose id;
00528 id.orientation.w = 1.0;
00529 Lock lock(mutex_);
00530 if (!initialized_ || !last_scan_)
00531 return;
00532 if (!last_fixed_frame_pose_)
00533 return;
00534 else if (Time::now() - last_fixed_frame_pose_->header.stamp > Duration(0.2))
00535 ROS_WARN_STREAM ("Fixed frame pose " << *last_fixed_frame_pose_ << " out of date. Skipping graph update.");
00536
00537 if (!localization_ || sufficientlyFarApart(id, localization_->node_frame_pose)) {
00538
00539 optimizePoses();
00540 addNewNode();
00541
00542
00543 PoseGraphDiff diff;
00544 diff.id = current_graph_id_++;
00545 if (graph_.hasCloud(*last_node_)) {
00546 diff.cloud_ids.push_back(last_node_->getId());
00547 diff.clouds.push_back(*graph_.getCloud(*last_node_));
00548 }
00549 Node diff_node;
00550 diff_node.id = last_node_->getId();
00551 diff_node.initial_pose_estimate = graph_.getInitialPoseEstimate(*last_node_);
00552 diff.new_nodes.push_back(diff_node);
00553
00554 BOOST_FOREACH (ConstraintGenPtr gen, constraint_generators_) {
00555 NodeConstraintVector constraints = gen->getConstraints(graph_, *last_node_, optimized_node_poses_);
00556 BOOST_FOREACH (const NodeConstraint& c, constraints) {
00557 const EdgeId e = graph_.addEdge(c.target, *last_node_, c.constraint);
00558
00559
00560 Edge diff_edge;
00561 diff_edge.src = c.target.getId();
00562 diff_edge.dest = last_node_->getId();
00563 diff_edge.constraint = constraintToRos(c.constraint);
00564 diff_edge.id = e.getId();
00565 diff.new_edges.push_back(diff_edge);
00566 }
00567 }
00568 diff_pub_.addDiff(&diff);
00569
00570
00571 updateGrid();
00572 }
00573 }
00574
00575
00576
00577
00578 void GraphMapper::updateFixedFramePose (const Time& t)
00579 {
00580 if (tf_.waitForTransform(fixed_frame_, base_frame_, t, ros::Duration(1.0))) {
00581 PoseStamped p;
00582 p.pose.position.x = p.pose.position.y = p.pose.position.z = 0.0;
00583 p.pose.orientation = tf::createQuaternionMsgFromYaw(0.0);
00584 p.header.frame_id = base_frame_;
00585 p.header.stamp = t;
00586 tf::Stamped<tf::Pose> base_pose, fixed_pose;
00587 tf::poseStampedMsgToTF(p, base_pose);
00588
00590 tf_.transformPose(fixed_frame_, base_pose, fixed_pose);
00591
00592 gm::PoseStamped fixed_pose_msg;
00593 tf::poseStampedTFToMsg(fixed_pose, fixed_pose_msg);
00594 last_fixed_frame_pose_ = fixed_pose_msg;
00595
00596 ROS_DEBUG_STREAM_NAMED ("pose_graph_update_fixed_frame_pose", "Updated fixed frame pose to "
00597 << toString(last_fixed_frame_pose_->pose) << " with stamp "
00598 << last_fixed_frame_pose_->header.stamp.toSec());
00599
00600 }
00601 else {
00602 ROS_WARN_STREAM ("Skipping update of fixed frame pose due to lack of transform from " <<
00603 base_frame_ << " to " << fixed_frame_);
00604 }
00605 }
00606
00607
00608
00609 bool GraphMapper::sufficientlyFarApart (const Pose& p1, const Pose& p2) const
00610 {
00611 const double dx = p1.position.x - p2.position.x;
00612 const double dy = p1.position.y - p2.position.y;
00613 const double dtheta = abs(tf::getYaw(p1.orientation) - tf::getYaw(p2.orientation));
00614 return (dx*dx + dy*dy > pos_threshold_*pos_threshold_) || (dtheta > angle_threshold_);
00615 }
00616
00617
00618 void GraphMapper::addNewNode ()
00619 {
00620 ROS_ASSERT (last_scan_ && last_fixed_frame_pose_);
00621 ROS_ASSERT_MSG (last_scan_->header.frame_id == sensor_frame_,
00622 "Code currently assumes scans are in default sensor frame");
00623
00624
00625
00626
00627
00628 NodeId n = graph_.addNode();
00629 graph_.attachScan(n, last_scan_);
00630 graph_.setInitialPoseEstimate(n, last_fixed_frame_pose_->pose);
00631 node_fixed_frame_poses_[n] = last_fixed_frame_pose_->pose;
00632 ros::Time node_stamp = last_fixed_frame_pose_->header.stamp;
00633 ROS_DEBUG_NAMED ("pose_graph_node_update",
00634 "Added node %lu with stamp %.2f, at [%.2f, %.2f, %.2f].",
00635 n.getId(), node_stamp.toSec(), last_fixed_frame_pose_->pose.position.x,
00636 last_fixed_frame_pose_->pose.position.y,
00637 tf::getYaw(last_fixed_frame_pose_->pose.orientation));
00638 last_node_ = n;
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651 try {
00652 const ros::Time start = last_scan_->header.stamp;
00653 const ros::Time end = start + ros::Duration(last_scan_->ranges.size()*last_scan_->time_increment);
00654 const ros::Duration wait(2.0);
00655
00656 if (!tf_.waitForTransform(fixed_frame_, sensor_frame_, start, wait)) {
00657 ROS_WARN_STREAM ("Timed out waiting for transform from sensor to fixed frame at start time " <<
00658 start << " of laser scan");
00659 }
00660 else if (!tf_.waitForTransform(fixed_frame_, sensor_frame_, end, ros::Duration(2.0))) {
00661 ROS_WARN_STREAM ("Timed out waiting for transform from sensor to fixed frame at end time " <<
00662 end << " of laser scan");
00663 }
00664
00665
00666 else {
00667
00668 sm::PointCloud fixed_frame_cloud;
00669 laser_geometry::LaserProjection projector_;
00670 projector_.transformLaserScanToPointCloud (fixed_frame_, *last_scan_, fixed_frame_cloud, tf_);
00671
00672 if (!tf_.waitForTransform(sensor_frame_, fixed_frame_, node_stamp, wait)) {
00673 ROS_WARN_STREAM ("Timed out waiting for transform from " << fixed_frame_ << " to " << sensor_frame_
00674 << " at " << node_stamp);
00675 }
00676
00677
00678 else {
00679
00680 LocalizedCloud::Ptr sensor_frame_cloud(new LocalizedCloud());
00681 tf_.transformPointCloud (sensor_frame_, node_stamp, fixed_frame_cloud,
00682 fixed_frame_, sensor_frame_cloud->cloud);
00683 geometry_msgs::PoseStamped id, sensor_pose;
00684 id.header.stamp = ros::Time();
00685 id.header.frame_id = sensor_frame_;
00686 id.pose.orientation.w = 1.0;
00687 tf_.transformPose(base_frame_, id, sensor_pose);
00688 sensor_frame_cloud->sensor_pose = sensor_pose;
00689
00690 graph_.attachCloud(n, sensor_frame_cloud);
00691 }
00692 }
00693 }
00694
00695
00696 catch (tf::TransformException& e) {
00697 ROS_WARN_STREAM ("Received tf exception '" << e.what() <<
00698 "' when trying to transform scan to cloud. Skipping");
00699 }
00700
00701
00702
00703
00704
00705 const Eigen3::Affine3d node_to_fixed_frame = poseToWorldTransform(node_fixed_frame_poses_[n]);
00706 localizer_->addNode(n, last_scan_);
00707 Pose identity;
00708 identity.orientation.w = 1.0;
00709 localization_ = LocalizationState(n, node_to_fixed_frame, identity, last_scan_->header.stamp);
00710
00711
00712
00713 ROS_DEBUG_STREAM_NAMED ("localization_switch", "Switching to newly added node " << n);
00714
00715 }
00716
00717
00718 bool withinDistance (const PoseGraph& g, const NodeId n1, const NodeId n2, const double d)
00719 {
00720 ShortestPathResult res=g.shortestPath(n1, n2);
00721 return res.path_found && res.cost < d;
00722 }
00723
00724
00725 void GraphMapper::updateGrid ()
00726 {
00727
00728 if (!(current_grid_node_ &&
00729 withinDistance(graph_, *current_grid_node_, *last_node_, local_grid_size_/3))) {
00730 current_grid_node_ = *last_node_;
00731 grid_node_to_nodes_[*last_node_] = NodeSet();
00732 }
00733
00734
00735 node_to_grid_node_[*last_node_] = *current_grid_node_;
00736 grid_node_to_nodes_[*current_grid_node_].insert(*last_node_);
00737 }
00738
00739
00740
00741
00742
00743
00744
00745
00746 void GraphMapper::optimizePosesTimed (const WallTimerEvent& e)
00747 {
00748 if (initialized_) {
00749 Lock lock(mutex_);
00750 optimizePoses();
00751 }
00752 }
00753
00754 void GraphMapper::optimizePoses ()
00755 {
00756 if (optimize_) {
00757 optimized_node_poses_ =
00758 use_spa_3d_ ?
00759 optimizeGraph(graph_) :
00760 optimizeGraph2D(graph_);
00761 }
00762 else
00763 optimized_node_poses_ = node_fixed_frame_poses_;
00764 }
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775 Pose applyConstraint (const Pose& p, const PoseConstraint& c)
00776 {
00777 const Affine3d trans = poseToWorldTransform(p);
00778 const Pose rel_pose = getPose(c);
00779 return applyTransform(trans, rel_pose);
00780 }
00781
00782
00783 void addTransformedPoints (LocalizedCloud::ConstPtr cloud, const Pose& pose, sm::PointCloud* cloud_out)
00784 {
00785 const Affine3d base_to_world = poseToWorldTransform(pose);
00786 const Affine3d sensor_to_base = poseToWorldTransform(cloud->sensor_pose.pose);
00787 const Affine3d sensor_to_world = base_to_world*sensor_to_base;
00788
00789 BOOST_FOREACH (const gm::Point32 pt, cloud->cloud.points)
00790 cloud_out->points.push_back(transformPoint(sensor_to_world, pt));
00791 }
00792
00793
00794
00795 void GraphMapper::visualize (const WallTimerEvent& e)
00796 {
00797 if (!initialized_)
00798 return;
00799
00800 const unsigned NODE_MARKER_ID_BASE = 1000;
00801
00802 Lock lock(mutex_);
00803 vm::Marker edges, loc;
00804 vm::Marker node_prototype;
00805 vm::MarkerArray nodes;
00806 sm::PointCloud cloud;
00807
00808 unsigned next_ind=0;
00809 edges.header.frame_id = node_prototype.header.frame_id = loc.header.frame_id = OPTIMIZED_FRAME;
00810 node_prototype.id = -42;
00811 edges.id = next_ind++;
00812 loc.id = next_ind++;
00813 node_prototype.header.stamp = edges.header.stamp = loc.header.stamp = Time::now();
00814 edges.ns = node_prototype.ns = loc.ns = "pose_graph";
00815 node_prototype.type = vm::Marker::SPHERE;
00816 edges.type = vm::Marker::LINE_LIST;
00817 loc.type = vm::Marker::ARROW;
00818 edges.action = node_prototype.action = loc.action = vm::Marker::ADD;
00819 edges.scale.x = 0.05;
00820 node_prototype.scale.x = 0.1;
00821 node_prototype.scale.y = 0.1;
00822 node_prototype.scale.z = 0.1;
00823 node_prototype.pose.orientation.w = 1.0;
00824 loc.scale.x = 0.1;
00825 loc.scale.y = 0.2;
00826 edges.color.b = node_prototype.color.r = 1.0;
00827 loc.color.r = .64;
00828 loc.color.g = .1;
00829 loc.color.b = .9;
00830 edges.color.a = loc.color.a = node_prototype.color.a = 1.0;
00831
00832
00833 if (optimized_node_poses_.size() > 0) {
00834
00835
00836 BOOST_FOREACH (const NodePoseMap::value_type& entry, optimized_node_poses_) {
00837
00838
00839 const NodeId n = entry.first;
00840 const Pose pose = entry.second;
00841 vm::Marker node = node_prototype;
00842 const size_t node_ind = nodes.markers.size();
00843 nodes.markers.resize(node_ind+1);
00844 nodes.markers[node_ind] = node_prototype;
00845 nodes.markers[node_ind].pose.position = pose.position;
00846 nodes.markers[node_ind].id = n.getId() + NODE_MARKER_ID_BASE;
00847
00848
00849 if (graph_.hasCloud(n))
00850 addTransformedPoints(graph_.getCloud(n), pose, &cloud);
00851
00852 BOOST_FOREACH (const EdgeId e, graph_.incidentEdges(n)) {
00853 if (graph_.incidentNodes(e).first == n) {
00854 edges.points.push_back(pose.position);
00855 const Pose pose2 = applyConstraint(entry.second, graph_.getConstraint(e));
00856 edges.points.push_back(pose2.position);
00857 }
00858 }
00859 }
00860
00861
00862
00863 if (localization_) {
00864 NodePoseMap::const_iterator iter=optimized_node_poses_.find(localization_->ref_node);
00865 if (iter!=optimized_node_poses_.end()) {
00866 loc.points.push_back(iter->second.position);
00867 Eigen3::Affine3d node_to_opt = poseToWorldTransform(iter->second);
00868 loc.points.push_back(applyTransform(node_to_opt, localization_->node_frame_pose).position);
00869 }
00870 }
00871 else
00872 loc.action = vm::Marker::DELETE;
00873 }
00874
00875 marker_array_pub_.publish(nodes);
00876 vis_marker_pub_.publish(edges);
00877 vis_marker_pub_.publish(loc);
00878
00879 cloud.header.frame_id = OPTIMIZED_FRAME;
00880 cloud.header.stamp = ros::Time::now();
00881 vis_scan_pub_.publish(cloud);
00882
00883 }
00884
00885 optional<NodeId> GraphMapper::getGridNode (const NodeId n) const
00886 {
00887 optional<NodeId> g;
00888 typedef map<NodeId, set<NodeId> > GridMap;
00889 BOOST_FOREACH (const GridMap::value_type& e, grid_node_to_nodes_) {
00890 if (contains(e.second, n)) {
00891 g = e.first;
00892 break;
00893 }
00894 }
00895 return g;
00896 }
00897
00898
00899
00900
00901
00902
00903
00904
00905 void TransformEigenToTF(const Eigen3::Affine3d &k, tf::Transform &t)
00906 {
00907 t.setOrigin(tf::Vector3(k.matrix()(0,3), k.matrix()(1,3), k.matrix()(2,3)));
00908 t.setBasis(btMatrix3x3(k.matrix()(0,0), k.matrix()(0,1),k.matrix()(0,2),k.matrix()(1,0), k.matrix()(1,1),k.matrix()(1,2),k.matrix()(2,0), k.matrix()(2,1),k.matrix()(2,2)));
00909 }
00910
00911 void GraphMapper::publishLocalization (const WallTimerEvent& e)
00912 {
00913 if (!initialized_)
00914 return;
00915 Lock lock(mutex_);
00916
00917 if (localization_ && last_fixed_frame_pose_) {
00918
00919 if (!contains(optimized_node_poses_, localization_->ref_node))
00920 optimizePoses();
00921
00922 const NodeId old_ref_node = localization_->ref_node;
00923 localization_ = localizer_->nextState(*localization_, last_fixed_frame_pose_->pose,
00924 last_scan_, graph_, optimized_node_poses_);
00925
00926 if (old_ref_node != localization_->ref_node)
00927 updateConstraintLocalizations();
00928
00929
00930 ROS_DEBUG_STREAM_NAMED ("localization", "Localization is now " << toString2D(localization_->node_frame_pose)
00931 << " w.r.t. " << localization_->ref_node);
00932
00933
00934 Int64 frame_msg;
00935 frame_msg.data = localization_->ref_node.getId();
00936 current_frame_pub_.publish(frame_msg);
00937
00938 std::vector<tf::StampedTransform> transforms;
00939
00940
00941 {
00942 tf::Transform opt_to_fixed;
00943 Affine3d opt_to_node(poseToWorldTransform(keyValue(optimized_node_poses_, localization_->ref_node)).inverse());
00944 TransformEigenToTF(localization_->node_to_fixed_frame*opt_to_node, opt_to_fixed);
00945 tf::StampedTransform stamped(opt_to_fixed, localization_->stamp, fixed_frame_, OPTIMIZED_FRAME);
00946 transforms.push_back(stamped);
00947 }
00948
00949
00950 const ros::Time now = ros::Time::now();
00951 BOOST_FOREACH (const NodePoseMap::value_type& e, optimized_node_poses_) {
00952 tf::Transform pose_to_opt;
00953 tf::poseMsgToTF(e.second, pose_to_opt);
00954 tf::StampedTransform stamped(pose_to_opt, now, OPTIMIZED_FRAME, PoseGraph::nodeFrameName(e.first));
00955 transforms.push_back(stamped);
00956 }
00957
00958
00959 tf_broadcaster_.sendTransform(transforms);
00960 }
00961 }
00962
00963
00964
00965
00966
00967
00968 void GraphMapper::statusCheck (const WallTimerEvent& e)
00969 {
00970 static unsigned count=0;
00971 if (count++) {
00972 if (!last_node_ || !localization_ || !initialized_) {
00973 ROS_WARN_COND_NAMED (!last_node_, "graph_mapper_status_check", "No nodes added yet.");
00974 ROS_WARN_COND_NAMED (!localization_ && last_node_, "graph_mapper_status_check", "No localization");
00975 ROS_WARN_STREAM_NAMED ("graph_mapper_status_check", "scan_received = " << (last_scan_.get()!=0)
00976 << ", fixed_frame_pose_valid_ = " << (bool) last_fixed_frame_pose_ <<
00977 ", initialized_ = " << initialized_);
00978 }
00979 }
00980 }
00981
00982 }
00983
00984
00985
00986
00987
00988 int main (int argc, char** argv)
00989 {
00990 ros::init(argc, argv, "graph_mapper");
00991 ros::AsyncSpinner spinner(3);
00992 spinner.start();
00993 graph_slam::GraphMapper node;
00994 ROS_INFO ("Initialization complete");
00995 ros::waitForShutdown();
00996 return 0;
00997 }