00001 #include <fstream>
00002
00003 #include <boost/version.hpp>
00004 #include <boost/thread.hpp>
00005 #if BOOST_VERSION >= 104100
00006 #include <boost/thread/future.hpp>
00007 #endif // BOOST_VERSION >= 104100
00008
00009 #include "ros/ros.h"
00010 #include "ros/console.h"
00011 #include "pointmatcher/PointMatcher.h"
00012 #include "pointmatcher/Timer.h"
00013
00014 #include "pointmatcher_ros/point_cloud.h"
00015 #include "pointmatcher_ros/transform.h"
00016 #include "pointmatcher_ros/get_params_from_server.h"
00017 #include "pointmatcher_ros/ros_logger.h"
00018
00019 #include "nav_msgs/Odometry.h"
00020 #include "tf/transform_broadcaster.h"
00021 #include "tf_conversions/tf_eigen.h"
00022 #include "tf/transform_listener.h"
00023 #include "eigen_conversions/eigen_msg.h"
00024
00025
00026 #include "std_msgs/String.h"
00027 #include "std_srvs/Empty.h"
00028 #include "map_msgs/GetPointMap.h"
00029 #include "map_msgs/SaveMap.h"
00030 #include "ethzasl_icp_mapper/LoadMap.h"
00031 #include "ethzasl_icp_mapper/CorrectPose.h"
00032 #include "ethzasl_icp_mapper/SetMode.h"
00033 #include "ethzasl_icp_mapper/GetMode.h"
00034 #include "ethzasl_icp_mapper/GetBoundedMap.h"
00035
00036 using namespace std;
00037 using namespace PointMatcherSupport;
00038
00039 class Mapper
00040 {
00041 typedef PointMatcher<float> PM;
00042 typedef PM::DataPoints DP;
00043
00044 ros::NodeHandle& n;
00045 ros::NodeHandle& pn;
00046
00047
00048 ros::Subscriber scanSub;
00049 ros::Subscriber cloudSub;
00050
00051
00052 ros::Publisher mapPub;
00053 ros::Publisher outlierPub;
00054 ros::Publisher odomPub;
00055 ros::Publisher odomErrorPub;
00056
00057
00058 ros::ServiceServer getPointMapSrv;
00059 ros::ServiceServer saveMapSrv;
00060 ros::ServiceServer loadMapSrv;
00061 ros::ServiceServer resetSrv;
00062 ros::ServiceServer correctPoseSrv;
00063 ros::ServiceServer setModeSrv;
00064 ros::ServiceServer getModeSrv;
00065 ros::ServiceServer getBoundedMapSrv;
00066
00067 ros::Time mapCreationTime;
00068 ros::Time lastPoinCloudTime;
00069
00070 PM::DataPointsFilters inputFilters;
00071 PM::DataPointsFilters mapPreFilters;
00072 PM::DataPointsFilters mapPostFilters;
00073 PM::DataPoints *mapPointCloud;
00074 PM::ICPSequence icp;
00075 unique_ptr<PM::Transformation> transformation;
00076
00077
00078 #if BOOST_VERSION >= 104100
00079 typedef boost::packaged_task<PM::DataPoints*> MapBuildingTask;
00080 typedef boost::unique_future<PM::DataPoints*> MapBuildingFuture;
00081 boost::thread mapBuildingThread;
00082 MapBuildingTask mapBuildingTask;
00083 MapBuildingFuture mapBuildingFuture;
00084 bool mapBuildingInProgress;
00085 #endif // BOOST_VERSION >= 104100
00086
00087
00088 bool useConstMotionModel;
00089 bool processingNewCloud;
00090 bool localizing;
00091 bool mapping;
00092 int minReadingPointCount;
00093 int minMapPointCount;
00094 int inputQueueSize;
00095 double minOverlap;
00096 double maxOverlapToMerge;
00097 double tfRefreshPeriod;
00098 string odomFrame;
00099 string mapFrame;
00100 string vtkFinalMapName;
00101
00102
00103 PM::TransformationParameters TOdomToMap;
00104 boost::thread publishThread;
00105 boost::mutex publishLock;
00106 ros::Time publishStamp;
00107
00108 tf::TransformListener tfListener;
00109 tf::TransformBroadcaster tfBroadcaster;
00110
00111 public:
00112 Mapper(ros::NodeHandle& n, ros::NodeHandle& pn);
00113 ~Mapper();
00114
00115 protected:
00116 void gotScan(const sensor_msgs::LaserScan& scanMsgIn);
00117 void gotCloud(const sensor_msgs::PointCloud2& cloudMsgIn);
00118 void processCloud(unique_ptr<DP> cloud, const std::string& scannerFrame, const ros::Time& stamp, uint32_t seq);
00119 void processNewMapIfAvailable();
00120 void setMap(DP* newPointCloud);
00121 DP* updateMap(DP* newPointCloud, const PM::TransformationParameters Ticp, bool updateExisting);
00122 void waitForMapBuildingCompleted();
00123
00124 void publishLoop(double publishPeriod);
00125 void publishTransform();
00126
00127
00128 bool getPointMap(map_msgs::GetPointMap::Request &req, map_msgs::GetPointMap::Response &res);
00129 bool saveMap(map_msgs::SaveMap::Request &req, map_msgs::SaveMap::Response &res);
00130 bool loadMap(ethzasl_icp_mapper::LoadMap::Request &req, ethzasl_icp_mapper::LoadMap::Response &res);
00131 bool reset(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res);
00132 bool correctPose(ethzasl_icp_mapper::CorrectPose::Request &req, ethzasl_icp_mapper::CorrectPose::Response &res);
00133 bool setMode(ethzasl_icp_mapper::SetMode::Request &req, ethzasl_icp_mapper::SetMode::Response &res);
00134 bool getMode(ethzasl_icp_mapper::GetMode::Request &req, ethzasl_icp_mapper::GetMode::Response &res);
00135 bool getBoundedMap(ethzasl_icp_mapper::GetBoundedMap::Request &req, ethzasl_icp_mapper::GetBoundedMap::Response &res);
00136 };
00137
00138 Mapper::Mapper(ros::NodeHandle& n, ros::NodeHandle& pn):
00139 n(n),
00140 pn(pn),
00141 mapPointCloud(0),
00142 transformation(PM::get().REG(Transformation).create("RigidTransformation")),
00143 #if BOOST_VERSION >= 104100
00144 mapBuildingInProgress(false),
00145 #endif
00146 useConstMotionModel(getParam<bool>("useConstMotionModel", false)),
00147 processingNewCloud(false),
00148 localizing(getParam<bool>("localizing", true)),
00149 mapping(getParam<bool>("mapping", true)),
00150 minReadingPointCount(getParam<int>("minReadingPointCount", 2000)),
00151 minMapPointCount(getParam<int>("minMapPointCount", 500)),
00152 inputQueueSize(getParam<int>("inputQueueSize", 10)),
00153 minOverlap(getParam<double>("minOverlap", 0.5)),
00154 maxOverlapToMerge(getParam<double>("maxOverlapToMerge", 0.9)),
00155 tfRefreshPeriod(getParam<double>("tfRefreshPeriod", 0.01)),
00156 odomFrame(getParam<string>("odom_frame", "odom")),
00157 mapFrame(getParam<string>("map_frame", "map")),
00158 vtkFinalMapName(getParam<string>("vtkFinalMapName", "finalMap.vtk")),
00159 TOdomToMap(PM::TransformationParameters::Identity(4, 4)),
00160 publishStamp(ros::Time::now()),
00161 tfListener(ros::Duration(30))
00162 {
00163
00164
00165 if(localizing == false)
00166 mapping = false;
00167 if(mapping == true)
00168 localizing = true;
00169
00170
00171 if (getParam<bool>("useROSLogger", false))
00172 PointMatcherSupport::setLogger(new PointMatcherSupport::ROSLogger);
00173
00174
00175 string configFileName;
00176 if (ros::param::get("~icpConfig", configFileName))
00177 {
00178 ifstream ifs(configFileName.c_str());
00179 if (ifs.good())
00180 {
00181 icp.loadFromYaml(ifs);
00182 }
00183 else
00184 {
00185 ROS_ERROR_STREAM("Cannot load ICP config from YAML file " << configFileName);
00186 icp.setDefault();
00187 }
00188 }
00189 else
00190 {
00191 ROS_INFO_STREAM("No ICP config file given, using default");
00192 icp.setDefault();
00193 }
00194 if (getParam<bool>("useROSLogger", false))
00195 PointMatcherSupport::setLogger(new PointMatcherSupport::ROSLogger);
00196
00197 if (ros::param::get("~inputFiltersConfig", configFileName))
00198 {
00199 ifstream ifs(configFileName.c_str());
00200 if (ifs.good())
00201 {
00202 inputFilters = PM::DataPointsFilters(ifs);
00203 }
00204 else
00205 {
00206 ROS_ERROR_STREAM("Cannot load input filters config from YAML file " << configFileName);
00207 }
00208 }
00209 else
00210 {
00211 ROS_INFO_STREAM("No input filters config file given, not using these filters");
00212 }
00213
00214 if (ros::param::get("~mapPreFiltersConfig", configFileName))
00215 {
00216 ifstream ifs(configFileName.c_str());
00217 if (ifs.good())
00218 {
00219 mapPreFilters = PM::DataPointsFilters(ifs);
00220 }
00221 else
00222 {
00223 ROS_ERROR_STREAM("Cannot load map pre-filters config from YAML file " << configFileName);
00224 }
00225 }
00226 else
00227 {
00228 ROS_INFO_STREAM("No map pre-filters config file given, not using these filters");
00229 }
00230
00231 if (ros::param::get("~mapPostFiltersConfig", configFileName))
00232 {
00233 ifstream ifs(configFileName.c_str());
00234 if (ifs.good())
00235 {
00236 mapPostFilters = PM::DataPointsFilters(ifs);
00237 }
00238 else
00239 {
00240 ROS_ERROR_STREAM("Cannot load map post-filters config from YAML file " << configFileName);
00241 }
00242 }
00243 else
00244 {
00245 ROS_INFO_STREAM("No map post-filters config file given, not using these filters");
00246 }
00247
00248
00249 if (getParam<bool>("subscribe_scan", true))
00250 scanSub = n.subscribe("scan", inputQueueSize, &Mapper::gotScan, this);
00251 if (getParam<bool>("subscribe_cloud", true))
00252 cloudSub = n.subscribe("cloud_in", inputQueueSize, &Mapper::gotCloud, this);
00253 mapPub = n.advertise<sensor_msgs::PointCloud2>("point_map", 2, true);
00254 outlierPub = n.advertise<sensor_msgs::PointCloud2>("outliers", 2, true);
00255 odomPub = n.advertise<nav_msgs::Odometry>("icp_odom", 50, true);
00256 odomErrorPub = n.advertise<nav_msgs::Odometry>("icp_error_odom", 50, true);
00257 getPointMapSrv = n.advertiseService("dynamic_point_map", &Mapper::getPointMap, this);
00258 saveMapSrv = pn.advertiseService("save_map", &Mapper::saveMap, this);
00259 loadMapSrv = pn.advertiseService("load_map", &Mapper::loadMap, this);
00260 resetSrv = pn.advertiseService("reset", &Mapper::reset, this);
00261 correctPoseSrv = pn.advertiseService("correct_pose", &Mapper::correctPose, this);
00262 setModeSrv = pn.advertiseService("set_mode", &Mapper::setMode, this);
00263 getModeSrv = pn.advertiseService("get_mode", &Mapper::getMode, this);
00264 getBoundedMapSrv = pn.advertiseService("get_bounded_map", &Mapper::getBoundedMap, this);
00265
00266
00267 publishThread = boost::thread(boost::bind(&Mapper::publishLoop, this, tfRefreshPeriod));
00268 }
00269
00270 Mapper::~Mapper()
00271 {
00272 #if BOOST_VERSION >= 104100
00273
00274 if (mapBuildingInProgress)
00275 {
00276 mapBuildingFuture.wait();
00277 if (mapBuildingFuture.has_value())
00278 delete mapBuildingFuture.get();
00279 }
00280 #endif // BOOST_VERSION >= 104100
00281
00282 publishThread.join();
00283
00284 if (mapPointCloud)
00285 {
00286 mapPointCloud->save(vtkFinalMapName);
00287 delete mapPointCloud;
00288 }
00289 }
00290
00291 void Mapper::gotScan(const sensor_msgs::LaserScan& scanMsgIn)
00292 {
00293 if(localizing)
00294 {
00295 const ros::Time endScanTime(scanMsgIn.header.stamp + ros::Duration(scanMsgIn.time_increment * (scanMsgIn.ranges.size() - 1)));
00296 unique_ptr<DP> cloud(new DP(PointMatcher_ros::rosMsgToPointMatcherCloud<float>(scanMsgIn, &tfListener, odomFrame)));
00297 processCloud(move(cloud), scanMsgIn.header.frame_id, endScanTime, scanMsgIn.header.seq);
00298 }
00299 }
00300
00301 void Mapper::gotCloud(const sensor_msgs::PointCloud2& cloudMsgIn)
00302 {
00303 if(localizing)
00304 {
00305 unique_ptr<DP> cloud(new DP(PointMatcher_ros::rosMsgToPointMatcherCloud<float>(cloudMsgIn)));
00306 processCloud(move(cloud), cloudMsgIn.header.frame_id, cloudMsgIn.header.stamp, cloudMsgIn.header.seq);
00307 }
00308 }
00309
00310 struct BoolSetter
00311 {
00312 public:
00313 bool toSetValue;
00314 BoolSetter(bool& target, bool toSetValue):
00315 toSetValue(toSetValue),
00316 target(target)
00317 {}
00318 ~BoolSetter()
00319 {
00320 target = toSetValue;
00321 }
00322 protected:
00323 bool& target;
00324 };
00325
00326 void Mapper::processCloud(unique_ptr<DP> newPointCloud, const std::string& scannerFrame, const ros::Time& stamp, uint32_t seq)
00327 {
00328 processingNewCloud = true;
00329 BoolSetter stopProcessingSetter(processingNewCloud, false);
00330
00331
00332 processNewMapIfAvailable();
00333
00334
00335 timer t;
00336
00337
00338 const size_t goodCount(newPointCloud->features.cols());
00339 if (goodCount == 0)
00340 {
00341 ROS_ERROR("I found no good points in the cloud");
00342 return;
00343 }
00344
00345
00346 const int dimp1(newPointCloud->features.rows());
00347
00348 ROS_INFO("Processing new point cloud");
00349 {
00350 timer t;
00351
00352
00353 inputFilters.apply(*newPointCloud);
00354
00355 ROS_INFO_STREAM("Input filters took " << t.elapsed() << " [s]");
00356 }
00357
00358 string reason;
00359
00360 if(!icp.hasMap())
00361 {
00362
00363 publishLock.lock();
00364 TOdomToMap = PM::TransformationParameters::Identity(dimp1, dimp1);
00365 publishLock.unlock();
00366 }
00367
00368
00369
00370 PM::TransformationParameters TOdomToScanner;
00371 try
00372 {
00373 TOdomToScanner = PointMatcher_ros::eigenMatrixToDim<float>(
00374 PointMatcher_ros::transformListenerToEigenMatrix<float>(
00375 tfListener,
00376 scannerFrame,
00377 odomFrame,
00378 stamp
00379 ), dimp1);
00380 }
00381 catch(tf::ExtrapolationException e)
00382 {
00383 ROS_ERROR_STREAM("Extrapolation Exception. stamp = "<< stamp << " now = " << ros::Time::now() << " delta = " << ros::Time::now() - stamp);
00384 return;
00385 }
00386
00387
00388
00389 TOdomToMap = PointMatcher_ros::eigenMatrixToDim<float>(TOdomToMap, dimp1);
00390
00391 ROS_DEBUG_STREAM("TOdomToScanner(" << odomFrame<< " to " << scannerFrame << "):\n" << TOdomToScanner);
00392 ROS_DEBUG_STREAM("TOdomToMap(" << odomFrame<< " to " << mapFrame << "):\n" << TOdomToMap);
00393
00394 const PM::TransformationParameters TscannerToMap = transformation->correctParameters(TOdomToMap * TOdomToScanner.inverse());
00395 ROS_DEBUG_STREAM("TscannerToMap (" << scannerFrame << " to " << mapFrame << "):\n" << TscannerToMap);
00396
00397
00398 const int ptsCount = newPointCloud->features.cols();
00399 if(ptsCount < minReadingPointCount)
00400 {
00401 ROS_ERROR_STREAM("Not enough points in newPointCloud: only " << ptsCount << " pts.");
00402 return;
00403 }
00404
00405
00406 if(!icp.hasMap())
00407 {
00408 ROS_INFO_STREAM("Creating an initial map");
00409 mapCreationTime = stamp;
00410 setMap(updateMap(newPointCloud.release(), TscannerToMap, false));
00411
00412 return;
00413 }
00414
00415
00416 if (newPointCloud->features.rows() != icp.getInternalMap().features.rows())
00417 {
00418 ROS_ERROR_STREAM("Dimensionality missmatch: incoming cloud is " << newPointCloud->features.rows()-1 << " while map is " << icp.getInternalMap().features.rows()-1);
00419 return;
00420 }
00421
00422 try
00423 {
00424
00425 PM::TransformationParameters Ticp;
00426
00427 Ticp = icp(*newPointCloud, TscannerToMap);
00428
00429 ROS_DEBUG_STREAM("Ticp:\n" << Ticp);
00430
00431
00432 const double estimatedOverlap = icp.errorMinimizer->getOverlap();
00433 ROS_INFO_STREAM("Overlap: " << estimatedOverlap);
00434 if (estimatedOverlap < minOverlap)
00435 {
00436 ROS_ERROR_STREAM("Estimated overlap too small, ignoring ICP correction!");
00437 return;
00438 }
00439
00440
00441 publishStamp = stamp;
00442 publishLock.lock();
00443 TOdomToMap = Ticp * TOdomToScanner;
00444
00445 tfBroadcaster.sendTransform(PointMatcher_ros::eigenMatrixToStampedTransform<float>(TOdomToMap, mapFrame, odomFrame, stamp));
00446 publishLock.unlock();
00447 processingNewCloud = false;
00448
00449 ROS_DEBUG_STREAM("TOdomToMap:\n" << TOdomToMap);
00450
00451
00452 if (odomPub.getNumSubscribers())
00453 odomPub.publish(PointMatcher_ros::eigenMatrixToOdomMsg<float>(Ticp, mapFrame, stamp));
00454
00455
00456 if (odomErrorPub.getNumSubscribers())
00457 odomErrorPub.publish(PointMatcher_ros::eigenMatrixToOdomMsg<float>(TOdomToMap, mapFrame, stamp));
00458
00459
00460 if (outlierPub.getNumSubscribers())
00461 {
00462
00463
00464 }
00465
00466
00467 if (
00468 mapping &&
00469 ((estimatedOverlap < maxOverlapToMerge) || (icp.getInternalMap().features.cols() < minMapPointCount)) &&
00470 #if BOOST_VERSION >= 104100
00471 (!mapBuildingInProgress)
00472 #else
00473 true
00474 #endif
00475 )
00476 {
00477
00478 mapCreationTime = stamp;
00479 #if BOOST_VERSION >= 104100
00480 ROS_INFO("Adding new points to the map in background");
00481 mapBuildingTask = MapBuildingTask(boost::bind(&Mapper::updateMap, this, newPointCloud.release(), Ticp, true));
00482 mapBuildingFuture = mapBuildingTask.get_future();
00483 mapBuildingThread = boost::thread(boost::move(boost::ref(mapBuildingTask)));
00484 mapBuildingInProgress = true;
00485 #else // BOOST_VERSION >= 104100
00486 ROS_INFO("Adding new points to the map");
00487 setMap(updateMap( newPointCloud.release(), Ticp, true));
00488 #endif // BOOST_VERSION >= 104100
00489 }
00490 }
00491 catch (PM::ConvergenceError error)
00492 {
00493 ROS_ERROR_STREAM("ICP failed to converge: " << error.what());
00494 return;
00495 }
00496
00497
00498 int realTimeRatio = 100*t.elapsed() / (stamp.toSec()-lastPoinCloudTime.toSec());
00499 ROS_INFO_STREAM("[TIME] Total ICP took: " << t.elapsed() << " [s]");
00500 if(realTimeRatio < 80)
00501 ROS_INFO_STREAM("[TIME] Real-time capability: " << realTimeRatio << "%");
00502 else
00503 ROS_WARN_STREAM("[TIME] Real-time capability: " << realTimeRatio << "%");
00504
00505 lastPoinCloudTime = stamp;
00506 }
00507
00508 void Mapper::processNewMapIfAvailable()
00509 {
00510 #if BOOST_VERSION >= 104100
00511 if (mapBuildingInProgress && mapBuildingFuture.has_value())
00512 {
00513 ROS_INFO_STREAM("New map available");
00514 setMap(mapBuildingFuture.get());
00515 mapBuildingInProgress = false;
00516 }
00517 #endif // BOOST_VERSION >= 104100
00518 }
00519
00520 void Mapper::setMap(DP* newPointCloud)
00521 {
00522
00523 if (mapPointCloud)
00524 delete mapPointCloud;
00525
00526
00527 mapPointCloud = newPointCloud;
00528 icp.setMap(*mapPointCloud);
00529
00530
00531
00532 if (mapPub.getNumSubscribers())
00533 mapPub.publish(PointMatcher_ros::pointMatcherCloudToRosMsg<float>(*mapPointCloud, mapFrame, mapCreationTime));
00534 }
00535
00536 Mapper::DP* Mapper::updateMap(DP* newPointCloud, const PM::TransformationParameters Ticp, bool updateExisting)
00537 {
00538 timer t;
00539
00540
00541 *newPointCloud = transformation->compute(*newPointCloud, Ticp);
00542
00543
00544 mapPreFilters.apply(*newPointCloud);
00545
00546
00547 if (updateExisting)
00548 newPointCloud->concatenate(*mapPointCloud);
00549
00550
00551 if(newPointCloud->features.cols() > minMapPointCount)
00552 mapPostFilters.apply(*newPointCloud);
00553
00554 ROS_INFO_STREAM("[TIME] New map available (" << newPointCloud->features.cols() << " pts), update took " << t.elapsed() << " [s]");
00555
00556 return newPointCloud;
00557 }
00558
00559 void Mapper::waitForMapBuildingCompleted()
00560 {
00561 #if BOOST_VERSION >= 104100
00562 if (mapBuildingInProgress)
00563 {
00564
00565 mapBuildingFuture.wait();
00566 mapBuildingInProgress = false;
00567 }
00568 #endif // BOOST_VERSION >= 104100
00569 }
00570
00571 void Mapper::publishLoop(double publishPeriod)
00572 {
00573 if(publishPeriod == 0)
00574 return;
00575 ros::Rate r(1.0 / publishPeriod);
00576 while(ros::ok())
00577 {
00578 publishTransform();
00579 r.sleep();
00580 }
00581 }
00582
00583 void Mapper::publishTransform()
00584 {
00585 if(processingNewCloud == false)
00586 {
00587 publishLock.lock();
00588
00589 tfBroadcaster.sendTransform(PointMatcher_ros::eigenMatrixToStampedTransform<float>(TOdomToMap, mapFrame, odomFrame, ros::Time::now()));
00590 publishLock.unlock();
00591 }
00592 }
00593
00594 bool Mapper::getPointMap(map_msgs::GetPointMap::Request &req, map_msgs::GetPointMap::Response &res)
00595 {
00596 if (!mapPointCloud)
00597 return false;
00598
00599
00600 res.map = PointMatcher_ros::pointMatcherCloudToRosMsg<float>(*mapPointCloud, mapFrame, ros::Time::now());
00601 return true;
00602 }
00603
00604 bool Mapper::saveMap(map_msgs::SaveMap::Request &req, map_msgs::SaveMap::Response &res)
00605 {
00606 if (!mapPointCloud)
00607 return false;
00608
00609 try
00610 {
00611 mapPointCloud->save(req.filename.data);
00612 }
00613 catch (const std::runtime_error& e)
00614 {
00615 ROS_ERROR_STREAM("Unable to save: " << e.what());
00616 return false;
00617 }
00618
00619 ROS_INFO_STREAM("Map saved at " << req.filename.data << "with " << mapPointCloud->features.cols() << " points.");
00620 return true;
00621 }
00622
00623 bool Mapper::loadMap(ethzasl_icp_mapper::LoadMap::Request &req, ethzasl_icp_mapper::LoadMap::Response &res)
00624 {
00625 waitForMapBuildingCompleted();
00626
00627 DP* cloud(new DP(DP::load(req.filename.data)));
00628
00629 const int dim = cloud->features.rows();
00630 const int nbPts = cloud->features.cols();
00631 ROS_INFO_STREAM("Loading " << dim-1 << "D point cloud (" << req.filename.data << ") with " << nbPts << " points.");
00632
00633 publishLock.lock();
00634 TOdomToMap = PM::TransformationParameters::Identity(dim,dim);
00635 publishLock.unlock();
00636
00637 setMap(cloud);
00638
00639 return true;
00640 }
00641
00642 bool Mapper::reset(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
00643 {
00644 waitForMapBuildingCompleted();
00645
00646
00647 publishLock.lock();
00648 TOdomToMap = PM::TransformationParameters::Identity(4,4);
00649 publishLock.unlock();
00650
00651 icp.clearMap();
00652
00653 return true;
00654 }
00655
00656 bool Mapper::correctPose(ethzasl_icp_mapper::CorrectPose::Request &req, ethzasl_icp_mapper::CorrectPose::Response &res)
00657 {
00658 publishLock.lock();
00659 TOdomToMap = PointMatcher_ros::odomMsgToEigenMatrix<float>(req.odom);
00660 tfBroadcaster.sendTransform(PointMatcher_ros::eigenMatrixToStampedTransform<float>(TOdomToMap, mapFrame, odomFrame, ros::Time::now()));
00661 publishLock.unlock();
00662
00663 return true;
00664 }
00665
00666 bool Mapper::setMode(ethzasl_icp_mapper::SetMode::Request &req, ethzasl_icp_mapper::SetMode::Response &res)
00667 {
00668
00669 if(req.localize == false && req.map == true)
00670 return false;
00671
00672 localizing = req.localize;
00673 mapping = req.map;
00674
00675 return true;
00676 }
00677
00678 bool Mapper::getMode(ethzasl_icp_mapper::GetMode::Request &req, ethzasl_icp_mapper::GetMode::Response &res)
00679 {
00680 res.localize = localizing;
00681 res.map = mapping;
00682 return true;
00683 }
00684
00685
00686
00687 bool Mapper::getBoundedMap(ethzasl_icp_mapper::GetBoundedMap::Request &req, ethzasl_icp_mapper::GetBoundedMap::Response &res)
00688 {
00689 if (!mapPointCloud)
00690 return false;
00691
00692 const float max_x = req.topRightCorner.x;
00693 const float max_y = req.topRightCorner.y;
00694 const float max_z = req.topRightCorner.z;
00695
00696 const float min_x = req.bottomLeftCorner.x;
00697 const float min_y = req.bottomLeftCorner.y;
00698 const float min_z = req.bottomLeftCorner.z;
00699
00700 cerr << "min [" << min_x << ", " << min_y << ", " << min_z << "] " << endl;
00701 cerr << "max [" << max_x << ", " << max_y << ", " << max_z << "] " << endl;
00702
00703
00704
00705 tf::StampedTransform stampedTr;
00706
00707 Eigen::Affine3d eigenTr;
00708 tf::poseMsgToEigen(req.mapCenter, eigenTr);
00709 Eigen::MatrixXf T = eigenTr.matrix().inverse().cast<float>();
00710
00711
00712 cerr << "T:" << endl << T << endl;
00713 T = transformation->correctParameters(T);
00714
00715
00716
00717 const DP centeredPointCloud = transformation->compute(*mapPointCloud, T);
00718 DP cutPointCloud = centeredPointCloud.createSimilarEmpty();
00719
00720 cerr << centeredPointCloud.features.topLeftCorner(3, 10) << endl;
00721 cerr << T << endl;
00722
00723 int newPtCount = 0;
00724 for(int i=0; i < centeredPointCloud.features.cols(); i++)
00725 {
00726 const float x = centeredPointCloud.features(0,i);
00727 const float y = centeredPointCloud.features(1,i);
00728 const float z = centeredPointCloud.features(2,i);
00729
00730 if(x < max_x && x > min_x &&
00731 y < max_y && y > min_y &&
00732 z < max_z && z > min_z )
00733 {
00734 cutPointCloud.setColFrom(newPtCount, centeredPointCloud, i);
00735 newPtCount++;
00736 }
00737 }
00738
00739 cerr << "Extract " << newPtCount << " points from the map" << endl;
00740
00741 cutPointCloud.conservativeResize(newPtCount);
00742 cutPointCloud = transformation->compute(cutPointCloud, T.inverse());
00743
00744
00745
00746 res.boundedMap = PointMatcher_ros::pointMatcherCloudToRosMsg<float>(cutPointCloud, mapFrame, ros::Time::now());
00747 return true;
00748 }
00749
00750
00751 int main(int argc, char **argv)
00752 {
00753 ros::init(argc, argv, "mapper");
00754 ros::NodeHandle n;
00755 ros::NodeHandle pn("~");
00756 Mapper mapper(n, pn);
00757 ros::spin();
00758
00759 return 0;
00760 }