00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00027 #include "ros/ros.h"
00028 #include "ros/console.h"
00029 #include "message_filters/subscriber.h"
00030 #include "tf/transform_broadcaster.h"
00031 #include "tf/transform_listener.h"
00032 #include "tf/message_filter.h"
00033 #include "visualization_msgs/MarkerArray.h"
00034
00035 #include "nav_msgs/MapMetaData.h"
00036 #include "sensor_msgs/LaserScan.h"
00037 #include "nav_msgs/GetMap.h"
00038
00039 #include "karto/Mapper.h"
00040
00041 #include "spa_solver.h"
00042
00043 #include <boost/thread.hpp>
00044
00045 #include <string>
00046 #include <map>
00047 #include <vector>
00048
00049
00050 #define MAP_IDX(sx, i, j) ((sx) * (j) + (i))
00051
00052 class SlamKarto
00053 {
00054 public:
00055 SlamKarto();
00056 ~SlamKarto();
00057
00058 void laserCallback(const sensor_msgs::LaserScan::ConstPtr& scan);
00059 bool mapCallback(nav_msgs::GetMap::Request &req,
00060 nav_msgs::GetMap::Response &res);
00061
00062 private:
00063 bool getOdomPose(karto::Pose2& karto_pose, const ros::Time& t);
00064 karto::LaserRangeFinder* getLaser(const sensor_msgs::LaserScan::ConstPtr& scan);
00065 bool addScan(karto::LaserRangeFinder* laser,
00066 const sensor_msgs::LaserScan::ConstPtr& scan,
00067 karto::Pose2& karto_pose);
00068 bool updateMap();
00069 void publishTransform();
00070 void publishLoop(double transform_publish_period);
00071 void publishGraphVisualization();
00072
00073
00074 ros::NodeHandle node_;
00075 tf::TransformListener tf_;
00076 tf::TransformBroadcaster* tfB_;
00077 message_filters::Subscriber<sensor_msgs::LaserScan>* scan_filter_sub_;
00078 tf::MessageFilter<sensor_msgs::LaserScan>* scan_filter_;
00079 ros::Publisher sst_;
00080 ros::Publisher marker_publisher_;
00081 ros::Publisher sstm_;
00082 ros::ServiceServer ss_;
00083
00084
00085 nav_msgs::GetMap::Response map_;
00086
00087
00088 std::string odom_frame_;
00089 std::string map_frame_;
00090 std::string base_frame_;
00091 int throttle_scans_;
00092 ros::Duration map_update_interval_;
00093 double resolution_;
00094 boost::mutex map_mutex_;
00095 boost::mutex map_to_odom_mutex_;
00096
00097
00098 karto::Mapper* mapper_;
00099 karto::Dataset* dataset_;
00100 SpaSolver* solver_;
00101 std::map<std::string, karto::LaserRangeFinder*> lasers_;
00102 std::map<std::string, bool> lasers_inverted_;
00103
00104
00105 bool got_map_;
00106 int laser_count_;
00107 boost::thread* transform_thread_;
00108 tf::Transform map_to_odom_;
00109 unsigned marker_count_;
00110 bool inverted_laser_;
00111 };
00112
00113 SlamKarto::SlamKarto() :
00114 got_map_(false),
00115 laser_count_(0),
00116 transform_thread_(NULL),
00117 marker_count_(0)
00118 {
00119 map_to_odom_.setIdentity();
00120
00121 ros::NodeHandle private_nh_("~");
00122 if(!private_nh_.getParam("odom_frame", odom_frame_))
00123 odom_frame_ = "odom";
00124 if(!private_nh_.getParam("map_frame", map_frame_))
00125 map_frame_ = "map";
00126 if(!private_nh_.getParam("base_frame", base_frame_))
00127 base_frame_ = "base_link";
00128 if(!private_nh_.getParam("throttle_scans", throttle_scans_))
00129 throttle_scans_ = 1;
00130 double tmp;
00131 if(!private_nh_.getParam("map_update_interval", tmp))
00132 tmp = 5.0;
00133 map_update_interval_.fromSec(tmp);
00134 if(!private_nh_.getParam("resolution", resolution_))
00135 {
00136
00137
00138 if(!private_nh_.getParam("delta", resolution_))
00139 resolution_ = 0.05;
00140 }
00141 double transform_publish_period;
00142 private_nh_.param("transform_publish_period", transform_publish_period, 0.05);
00143
00144
00145 tfB_ = new tf::TransformBroadcaster();
00146 sst_ = node_.advertise<nav_msgs::OccupancyGrid>("map", 1, true);
00147 sstm_ = node_.advertise<nav_msgs::MapMetaData>("map_metadata", 1, true);
00148 ss_ = node_.advertiseService("dynamic_map", &SlamKarto::mapCallback, this);
00149 scan_filter_sub_ = new message_filters::Subscriber<sensor_msgs::LaserScan>(node_, "scan", 5);
00150 scan_filter_ = new tf::MessageFilter<sensor_msgs::LaserScan>(*scan_filter_sub_, tf_, odom_frame_, 5);
00151 scan_filter_->registerCallback(boost::bind(&SlamKarto::laserCallback, this, _1));
00152 marker_publisher_ = node_.advertise<visualization_msgs::MarkerArray>("visualization_marker_array",1);
00153
00154
00155
00156
00157 transform_thread_ = new boost::thread(boost::bind(&SlamKarto::publishLoop, this, transform_publish_period));
00158
00159
00160 mapper_ = new karto::Mapper();
00161 dataset_ = new karto::Dataset();
00162
00163
00164 solver_ = new SpaSolver();
00165 mapper_->SetScanSolver(solver_);
00166 }
00167
00168 SlamKarto::~SlamKarto()
00169 {
00170 if(transform_thread_)
00171 {
00172 transform_thread_->join();
00173 delete transform_thread_;
00174 }
00175 if (scan_filter_)
00176 delete scan_filter_;
00177 if (scan_filter_sub_)
00178 delete scan_filter_sub_;
00179 if (solver_)
00180 delete solver_;
00181 if (mapper_)
00182 delete mapper_;
00183 if (dataset_)
00184 delete dataset_;
00185
00186
00187 }
00188
00189 void
00190 SlamKarto::publishLoop(double transform_publish_period)
00191 {
00192 if(transform_publish_period == 0)
00193 return;
00194
00195 ros::Rate r(1.0 / transform_publish_period);
00196 while(ros::ok())
00197 {
00198 publishTransform();
00199 r.sleep();
00200 }
00201 }
00202
00203 void
00204 SlamKarto::publishTransform()
00205 {
00206 boost::mutex::scoped_lock(map_to_odom_mutex_);
00207 ros::Time tf_expiration = ros::Time::now() + ros::Duration(0.05);
00208 tfB_->sendTransform(tf::StampedTransform (map_to_odom_, ros::Time::now(), map_frame_, odom_frame_));
00209 }
00210
00211 karto::LaserRangeFinder*
00212 SlamKarto::getLaser(const sensor_msgs::LaserScan::ConstPtr& scan)
00213 {
00214
00215 if(lasers_.find(scan->header.frame_id) == lasers_.end())
00216 {
00217
00218
00219
00220 tf::Stamped<tf::Pose> ident;
00221 tf::Stamped<btTransform> laser_pose;
00222 ident.setIdentity();
00223 ident.frame_id_ = scan->header.frame_id;
00224 ident.stamp_ = scan->header.stamp;
00225 try
00226 {
00227 tf_.transformPose(base_frame_, ident, laser_pose);
00228 }
00229 catch(tf::TransformException e)
00230 {
00231 ROS_WARN("Failed to compute laser pose, aborting initialization (%s)",
00232 e.what());
00233 return NULL;
00234 }
00235
00236 double yaw = tf::getYaw(laser_pose.getRotation());
00237
00238 ROS_INFO("laser %s's pose wrt base: %.3f %.3f %.3f",
00239 scan->header.frame_id.c_str(),
00240 laser_pose.getOrigin().x(),
00241 laser_pose.getOrigin().y(),
00242 yaw);
00243
00244
00245 tf::Quaternion q;
00246 q.setRPY(0.0, 0.0, scan->angle_min);
00247 tf::Stamped<tf::Quaternion> min_q(q, scan->header.stamp,
00248 scan->header.frame_id);
00249 q.setRPY(0.0, 0.0, scan->angle_max);
00250 tf::Stamped<tf::Quaternion> max_q(q, scan->header.stamp,
00251 scan->header.frame_id);
00252 try
00253 {
00254 tf_.transformQuaternion(base_frame_, min_q, min_q);
00255 tf_.transformQuaternion(base_frame_, max_q, max_q);
00256 }
00257 catch(tf::TransformException& e)
00258 {
00259 ROS_WARN("Unable to transform min/max laser angles into base frame: %s",
00260 e.what());
00261 return false;
00262 }
00263
00264 double angle_min = tf::getYaw(min_q);
00265 double angle_max = tf::getYaw(max_q);
00266 bool inverse = lasers_inverted_[scan->header.frame_id] = angle_max < angle_min;
00267 if (inverse)
00268 ROS_INFO("laser is mounted upside-down");
00269
00270
00271
00272
00273 std::string name = scan->header.frame_id;
00274 karto::LaserRangeFinder* laser =
00275 karto::LaserRangeFinder::CreateLaserRangeFinder(karto::LaserRangeFinder_Custom, karto::Name(name));
00276 laser->SetOffsetPose(karto::Pose2(laser_pose.getOrigin().x(),
00277 laser_pose.getOrigin().y(),
00278 yaw));
00279 laser->SetMinimumRange(scan->range_min);
00280 laser->SetMaximumRange(scan->range_max);
00281 laser->SetMinimumAngle(scan->angle_min);
00282 laser->SetMaximumAngle(scan->angle_max);
00283 laser->SetAngularResolution(scan->angle_increment);
00284
00285
00286
00287
00288 lasers_[scan->header.frame_id] = laser;
00289
00290
00291 dataset_->Add(laser);
00292 }
00293
00294 return lasers_[scan->header.frame_id];
00295 }
00296
00297 bool
00298 SlamKarto::getOdomPose(karto::Pose2& karto_pose, const ros::Time& t)
00299 {
00300
00301 tf::Stamped<tf::Pose> ident (btTransform(tf::createQuaternionFromRPY(0,0,0),
00302 btVector3(0,0,0)), t, base_frame_);
00303 tf::Stamped<btTransform> odom_pose;
00304 try
00305 {
00306 tf_.transformPose(odom_frame_, ident, odom_pose);
00307 }
00308 catch(tf::TransformException e)
00309 {
00310 ROS_WARN("Failed to compute odom pose, skipping scan (%s)", e.what());
00311 return false;
00312 }
00313 double yaw = tf::getYaw(odom_pose.getRotation());
00314
00315 karto_pose =
00316 karto::Pose2(odom_pose.getOrigin().x(),
00317 odom_pose.getOrigin().y(),
00318 yaw);
00319 return true;
00320 }
00321
00322 void
00323 SlamKarto::publishGraphVisualization()
00324 {
00325 std::vector<float> graph;
00326 solver_->getGraph(graph);
00327
00328 visualization_msgs::MarkerArray marray;
00329
00330 visualization_msgs::Marker m;
00331 m.header.frame_id = "map";
00332 m.header.stamp = ros::Time::now();
00333 m.id = 0;
00334 m.ns = "karto";
00335 m.type = visualization_msgs::Marker::SPHERE;
00336 m.pose.position.x = 0.0;
00337 m.pose.position.y = 0.0;
00338 m.pose.position.z = 0.0;
00339 m.scale.x = 0.1;
00340 m.scale.y = 0.1;
00341 m.scale.z = 0.1;
00342 m.color.r = 1.0;
00343 m.color.g = 0;
00344 m.color.b = 0.0;
00345 m.color.a = 1.0;
00346 m.lifetime = ros::Duration(0);
00347
00348 visualization_msgs::Marker edge;
00349 edge.header.frame_id = "map";
00350 edge.header.stamp = ros::Time::now();
00351 edge.action = visualization_msgs::Marker::ADD;
00352 edge.ns = "karto";
00353 edge.id = 0;
00354 edge.type = visualization_msgs::Marker::LINE_STRIP;
00355 edge.scale.x = 0.1;
00356 edge.scale.y = 0.1;
00357 edge.scale.z = 0.1;
00358 edge.color.a = 1.0;
00359 edge.color.r = 0.0;
00360 edge.color.g = 0.0;
00361 edge.color.b = 1.0;
00362
00363 m.action = visualization_msgs::Marker::ADD;
00364 uint id = 0;
00365 for (uint i=0; i<graph.size()/2; i++)
00366 {
00367 m.id = id;
00368 m.pose.position.x = graph[2*i];
00369 m.pose.position.y = graph[2*i+1];
00370 marray.markers.push_back(visualization_msgs::Marker(m));
00371 id++;
00372
00373 if(i>0)
00374 {
00375 edge.points.clear();
00376
00377 geometry_msgs::Point p;
00378 p.x = graph[2*(i-1)];
00379 p.y = graph[2*(i-1)+1];
00380 edge.points.push_back(p);
00381 p.x = graph[2*i];
00382 p.y = graph[2*i+1];
00383 edge.points.push_back(p);
00384 edge.id = id;
00385
00386 marray.markers.push_back(visualization_msgs::Marker(edge));
00387 id++;
00388 }
00389 }
00390
00391 m.action = visualization_msgs::Marker::DELETE;
00392 for (; id < marker_count_; id++)
00393 {
00394 m.id = id;
00395 marray.markers.push_back(visualization_msgs::Marker(m));
00396 }
00397
00398 marker_count_ = marray.markers.size();
00399
00400 marker_publisher_.publish(marray);
00401 }
00402
00403 void
00404 SlamKarto::laserCallback(const sensor_msgs::LaserScan::ConstPtr& scan)
00405 {
00406 laser_count_++;
00407 if ((laser_count_ % throttle_scans_) != 0)
00408 return;
00409
00410 static ros::Time last_map_update(0,0);
00411
00412
00413 karto::LaserRangeFinder* laser = getLaser(scan);
00414
00415 if(!laser)
00416 {
00417 ROS_WARN("Failed to create laser device for %s; discarding scan",
00418 scan->header.frame_id.c_str());
00419 return;
00420 }
00421
00422 karto::Pose2 odom_pose;
00423 if(addScan(laser, scan, odom_pose))
00424 {
00425 ROS_DEBUG("added scan at pose: %.3f %.3f %.3f",
00426 odom_pose.GetX(),
00427 odom_pose.GetY(),
00428 odom_pose.GetHeading());
00429
00430 publishGraphVisualization();
00431
00432 if(!got_map_ ||
00433 (scan->header.stamp - last_map_update) > map_update_interval_)
00434 {
00435 if(updateMap())
00436 {
00437 last_map_update = scan->header.stamp;
00438 got_map_ = true;
00439 ROS_DEBUG("Updated the map");
00440 }
00441 }
00442 }
00443 }
00444
00445 bool
00446 SlamKarto::updateMap()
00447 {
00448 boost::mutex::scoped_lock(map_mutex_);
00449
00450 karto::OccupancyGrid* occ_grid =
00451 karto::OccupancyGrid::CreateFromScans(mapper_->GetAllProcessedScans(), resolution_);
00452
00453 if(!occ_grid)
00454 return false;
00455
00456 if(!got_map_) {
00457 map_.map.info.resolution = resolution_;
00458 map_.map.info.origin.position.x = 0.0;
00459 map_.map.info.origin.position.y = 0.0;
00460 map_.map.info.origin.position.z = 0.0;
00461 map_.map.info.origin.orientation.x = 0.0;
00462 map_.map.info.origin.orientation.y = 0.0;
00463 map_.map.info.origin.orientation.z = 0.0;
00464 map_.map.info.origin.orientation.w = 1.0;
00465 }
00466
00467
00468 kt_int32s width = occ_grid->GetWidth();
00469 kt_int32s height = occ_grid->GetHeight();
00470 karto::Vector2<kt_double> offset = occ_grid->GetCoordinateConverter()->GetOffset();
00471
00472 if(map_.map.info.width != (unsigned int) width ||
00473 map_.map.info.height != (unsigned int) height ||
00474 map_.map.info.origin.position.x != offset.GetX() ||
00475 map_.map.info.origin.position.y != offset.GetY())
00476 {
00477 map_.map.info.origin.position.x = offset.GetX();
00478 map_.map.info.origin.position.y = offset.GetY();
00479 map_.map.info.width = width;
00480 map_.map.info.height = height;
00481 map_.map.data.resize(map_.map.info.width * map_.map.info.height);
00482 }
00483
00484 for (kt_int32s y=0; y<height; y++)
00485 {
00486 for (kt_int32s x=0; x<width; x++)
00487 {
00488
00489 kt_int8u value = occ_grid->GetValue(karto::Vector2<kt_int32s>(x, y));
00490
00491 switch (value)
00492 {
00493 case karto::GridStates_Unknown:
00494 map_.map.data[MAP_IDX(map_.map.info.width, x, y)] = -1;
00495 break;
00496 case karto::GridStates_Occupied:
00497 map_.map.data[MAP_IDX(map_.map.info.width, x, y)] = 100;
00498 break;
00499 case karto::GridStates_Free:
00500 map_.map.data[MAP_IDX(map_.map.info.width, x, y)] = 0;
00501 break;
00502 default:
00503 ROS_WARN("Encountered unknown cell value at %d, %d", x, y);
00504 break;
00505 }
00506 }
00507 }
00508
00509
00510 map_.map.header.stamp = ros::Time::now();
00511 map_.map.header.frame_id = map_frame_;
00512
00513 sst_.publish(map_.map);
00514 sstm_.publish(map_.map.info);
00515
00516 delete occ_grid;
00517
00518 return true;
00519 }
00520
00521 bool
00522 SlamKarto::addScan(karto::LaserRangeFinder* laser,
00523 const sensor_msgs::LaserScan::ConstPtr& scan,
00524 karto::Pose2& karto_pose)
00525 {
00526 if(!getOdomPose(karto_pose, scan->header.stamp))
00527 return false;
00528
00529
00530 std::vector<kt_double> readings;
00531
00532 if (lasers_inverted_[scan->header.frame_id]) {
00533 for(std::vector<float>::const_reverse_iterator it = scan->ranges.rbegin();
00534 it != scan->ranges.rend();
00535 ++it)
00536 {
00537 readings.push_back(*it);
00538 }
00539 } else {
00540 for(std::vector<float>::const_iterator it = scan->ranges.begin();
00541 it != scan->ranges.end();
00542 ++it)
00543 {
00544 readings.push_back(*it);
00545 }
00546 }
00547
00548
00549 karto::LocalizedRangeScan* range_scan =
00550 new karto::LocalizedRangeScan(laser->GetName(), readings);
00551 range_scan->SetOdometricPose(karto_pose);
00552 range_scan->SetCorrectedPose(karto_pose);
00553
00554
00555 bool processed;
00556 if((processed = mapper_->Process(range_scan)))
00557 {
00558
00559
00560 karto::Pose2 corrected_pose = range_scan->GetCorrectedPose();
00561
00562
00563 tf::Stamped<tf::Pose> odom_to_map;
00564 try
00565 {
00566 tf_.transformPose(odom_frame_,tf::Stamped<tf::Pose> (btTransform(tf::createQuaternionFromRPY(0, 0, corrected_pose.GetHeading()),
00567 btVector3(corrected_pose.GetX(), corrected_pose.GetY(), 0.0)).inverse(),
00568 scan->header.stamp, base_frame_),odom_to_map);
00569 }
00570 catch(tf::TransformException e)
00571 {
00572 ROS_ERROR("Transform from base_link to odom failed\n");
00573 odom_to_map.setIdentity();
00574 }
00575
00576 map_to_odom_mutex_.lock();
00577 map_to_odom_ = tf::Transform(tf::Quaternion( odom_to_map.getRotation() ),
00578 tf::Point( odom_to_map.getOrigin() ) ).inverse();
00579 map_to_odom_mutex_.unlock();
00580
00581
00582
00583 dataset_->Add(range_scan);
00584 }
00585 else
00586 delete range_scan;
00587
00588 return processed;
00589 }
00590
00591 bool
00592 SlamKarto::mapCallback(nav_msgs::GetMap::Request &req,
00593 nav_msgs::GetMap::Response &res)
00594 {
00595 boost::mutex::scoped_lock(map_mutex_);
00596 if(got_map_ && map_.map.info.width && map_.map.info.height)
00597 {
00598 res = map_;
00599 return true;
00600 }
00601 else
00602 return false;
00603 }
00604
00605 int
00606 main(int argc, char** argv)
00607 {
00608 ros::init(argc, argv, "slam_karto");
00609
00610 SlamKarto kn;
00611
00612 ros::spin();
00613
00614 return 0;
00615 }