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 "nabo/nabo.h"
00015
00016 #include "pointmatcher_ros/point_cloud.h"
00017 #include "pointmatcher_ros/transform.h"
00018 #include "pointmatcher_ros/get_params_from_server.h"
00019 #include "pointmatcher_ros/ros_logger.h"
00020
00021 #include "nav_msgs/Odometry.h"
00022 #include "tf/transform_broadcaster.h"
00023 #include "tf_conversions/tf_eigen.h"
00024 #include "tf/transform_listener.h"
00025 #include "eigen_conversions/eigen_msg.h"
00026
00027
00028 #include "std_msgs/String.h"
00029 #include "std_srvs/Empty.h"
00030 #include "map_msgs/GetPointMap.h"
00031 #include "map_msgs/SaveMap.h"
00032 #include "ethzasl_icp_mapper/LoadMap.h"
00033 #include "ethzasl_icp_mapper/CorrectPose.h"
00034 #include "ethzasl_icp_mapper/SetMode.h"
00035 #include "ethzasl_icp_mapper/GetMode.h"
00036 #include "ethzasl_icp_mapper/GetBoundedMap.h"
00037
00038 using namespace std;
00039 using namespace PointMatcherSupport;
00040
00041 class Mapper
00042 {
00043 typedef PointMatcher<float> PM;
00044 typedef PM::DataPoints DP;
00045 typedef PM::Matches Matches;
00046
00047 typedef typename Nabo::NearestNeighbourSearch<float> NNS;
00048 typedef typename NNS::SearchType NNSearchType;
00049
00050 ros::NodeHandle& n;
00051 ros::NodeHandle& pn;
00052
00053
00054 ros::Subscriber scanSub;
00055 ros::Subscriber cloudSub;
00056
00057
00058 ros::Publisher mapPub;
00059 ros::Publisher outlierPub;
00060 ros::Publisher odomPub;
00061 ros::Publisher odomErrorPub;
00062
00063
00064 ros::ServiceServer getPointMapSrv;
00065 ros::ServiceServer saveMapSrv;
00066 ros::ServiceServer loadMapSrv;
00067 ros::ServiceServer resetSrv;
00068 ros::ServiceServer correctPoseSrv;
00069 ros::ServiceServer setModeSrv;
00070 ros::ServiceServer getModeSrv;
00071 ros::ServiceServer getBoundedMapSrv;
00072
00073
00074 ros::Time mapCreationTime;
00075 ros::Time lastPoinCloudTime;
00076
00077
00078 PM::DataPointsFilters inputFilters;
00079 PM::DataPointsFilters mapPreFilters;
00080 PM::DataPointsFilters mapPostFilters;
00081 PM::DataPoints *mapPointCloud;
00082 PM::ICPSequence icp;
00083 unique_ptr<PM::Transformation> transformation;
00084
00085
00086 #if BOOST_VERSION >= 104100
00087 typedef boost::packaged_task<PM::DataPoints*> MapBuildingTask;
00088 typedef boost::unique_future<PM::DataPoints*> MapBuildingFuture;
00089 boost::thread mapBuildingThread;
00090 MapBuildingTask mapBuildingTask;
00091 MapBuildingFuture mapBuildingFuture;
00092 bool mapBuildingInProgress;
00093 #endif // BOOST_VERSION >= 104100
00094 bool processingNewCloud;
00095
00096
00097 bool publishMapTf;
00098 bool useConstMotionModel;
00099 bool localizing;
00100 bool mapping;
00101 int minReadingPointCount;
00102 int minMapPointCount;
00103 int inputQueueSize;
00104 double minOverlap;
00105 double maxOverlapToMerge;
00106 double tfRefreshPeriod;
00107 string odomFrame;
00108 string mapFrame;
00109 string vtkFinalMapName;
00110
00111 const double mapElevation;
00112
00113
00114 const float priorStatic;
00115 const float priorDyn;
00116 const float maxAngle;
00117 const float eps_a;
00118 const float eps_d;
00119 const float alpha;
00120 const float beta;
00121 const float maxDyn;
00122 const float maxDistNewPoint;
00123
00124
00125
00126
00127 PM::TransformationParameters TOdomToMap;
00128 boost::thread publishThread;
00129 boost::mutex publishLock;
00130 ros::Time publishStamp;
00131
00132 tf::TransformListener tfListener;
00133 tf::TransformBroadcaster tfBroadcaster;
00134
00135 const float eps;
00136
00137 public:
00138 Mapper(ros::NodeHandle& n, ros::NodeHandle& pn);
00139 ~Mapper();
00140
00141 protected:
00142 void gotScan(const sensor_msgs::LaserScan& scanMsgIn);
00143 void gotCloud(const sensor_msgs::PointCloud2& cloudMsgIn);
00144 void processCloud(unique_ptr<DP> cloud, const std::string& scannerFrame, const ros::Time& stamp, uint32_t seq);
00145 void processNewMapIfAvailable();
00146 void setMap(DP* newPointCloud);
00147 DP* updateMap(DP* newPointCloud, const PM::TransformationParameters Ticp, bool updateExisting);
00148 void waitForMapBuildingCompleted();
00149
00150 void publishLoop(double publishPeriod);
00151 void publishTransform();
00152
00153
00154 bool getPointMap(map_msgs::GetPointMap::Request &req, map_msgs::GetPointMap::Response &res);
00155 bool saveMap(map_msgs::SaveMap::Request &req, map_msgs::SaveMap::Response &res);
00156 bool loadMap(ethzasl_icp_mapper::LoadMap::Request &req, ethzasl_icp_mapper::LoadMap::Response &res);
00157 bool reset(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res);
00158 bool correctPose(ethzasl_icp_mapper::CorrectPose::Request &req, ethzasl_icp_mapper::CorrectPose::Response &res);
00159 bool setMode(ethzasl_icp_mapper::SetMode::Request &req, ethzasl_icp_mapper::SetMode::Response &res);
00160 bool getMode(ethzasl_icp_mapper::GetMode::Request &req, ethzasl_icp_mapper::GetMode::Response &res);
00161 bool getBoundedMap(ethzasl_icp_mapper::GetBoundedMap::Request &req, ethzasl_icp_mapper::GetBoundedMap::Response &res);
00162 };
00163
00164 Mapper::Mapper(ros::NodeHandle& n, ros::NodeHandle& pn):
00165 n(n),
00166 pn(pn),
00167 mapPointCloud(0),
00168 transformation(PM::get().REG(Transformation).create("RigidTransformation")),
00169 #if BOOST_VERSION >= 104100
00170 mapBuildingInProgress(false),
00171 #endif
00172 processingNewCloud(false),
00173 publishMapTf(getParam<bool>("publishMapTf", true)),
00174 useConstMotionModel(getParam<bool>("useConstMotionModel", false)),
00175 localizing(getParam<bool>("localizing", true)),
00176 mapping(getParam<bool>("mapping", true)),
00177 minReadingPointCount(getParam<int>("minReadingPointCount", 2000)),
00178 minMapPointCount(getParam<int>("minMapPointCount", 500)),
00179 inputQueueSize(getParam<int>("inputQueueSize", 10)),
00180 minOverlap(getParam<double>("minOverlap", 0.5)),
00181 maxOverlapToMerge(getParam<double>("maxOverlapToMerge", 0.9)),
00182 tfRefreshPeriod(getParam<double>("tfRefreshPeriod", 0.01)),
00183 odomFrame(getParam<string>("odom_frame", "odom")),
00184 mapFrame(getParam<string>("map_frame", "map")),
00185 vtkFinalMapName(getParam<string>("vtkFinalMapName", "finalMap.vtk")),
00186 mapElevation(getParam<double>("mapElevation", 0)),
00187 priorStatic(getParam<double>("priorStatic", 0.5)),
00188 priorDyn(getParam<double>("priorDyn", 0.5)),
00189 maxAngle(getParam<double>("maxAngle", 0.02)),
00190 eps_a(getParam<double>("eps_a", 0.05)),
00191 eps_d(getParam<double>("eps_d", 0.02)),
00192 alpha(getParam<double>("alpha", 0.99)),
00193 beta(getParam<double>("beta", 0.99)),
00194 maxDyn(getParam<double>("maxDyn", 0.95)),
00195 maxDistNewPoint(pow(getParam<double>("maxDistNewPoint", 0.1),2)),
00196 TOdomToMap(PM::TransformationParameters::Identity(4, 4)),
00197 publishStamp(ros::Time::now()),
00198 tfListener(ros::Duration(30)),
00199 eps(0.0001)
00200 {
00201
00202
00203 if(localizing == false)
00204 mapping = false;
00205 if(mapping == true)
00206 localizing = true;
00207
00208
00209 if (getParam<bool>("useROSLogger", false))
00210 PointMatcherSupport::setLogger(new PointMatcherSupport::ROSLogger);
00211
00212
00213 string configFileName;
00214 if (ros::param::get("~icpConfig", configFileName))
00215 {
00216 ifstream ifs(configFileName.c_str());
00217 if (ifs.good())
00218 {
00219 icp.loadFromYaml(ifs);
00220 }
00221 else
00222 {
00223 ROS_ERROR_STREAM("Cannot load ICP config from YAML file " << configFileName);
00224 icp.setDefault();
00225 }
00226 }
00227 else
00228 {
00229 ROS_INFO_STREAM("No ICP config file given, using default");
00230 icp.setDefault();
00231 }
00232 if (getParam<bool>("useROSLogger", false))
00233 PointMatcherSupport::setLogger(new PointMatcherSupport::ROSLogger);
00234
00235 if (ros::param::get("~inputFiltersConfig", configFileName))
00236 {
00237 ifstream ifs(configFileName.c_str());
00238 if (ifs.good())
00239 {
00240 inputFilters = PM::DataPointsFilters(ifs);
00241 }
00242 else
00243 {
00244 ROS_ERROR_STREAM("Cannot load input filters config from YAML file " << configFileName);
00245 }
00246 }
00247 else
00248 {
00249 ROS_INFO_STREAM("No input filters config file given, not using these filters");
00250 }
00251
00252 if (ros::param::get("~mapPreFiltersConfig", configFileName))
00253 {
00254 ifstream ifs(configFileName.c_str());
00255 if (ifs.good())
00256 {
00257 mapPreFilters = PM::DataPointsFilters(ifs);
00258 }
00259 else
00260 {
00261 ROS_ERROR_STREAM("Cannot load map pre-filters config from YAML file " << configFileName);
00262 }
00263 }
00264 else
00265 {
00266 ROS_INFO_STREAM("No map pre-filters config file given, not using these filters");
00267 }
00268
00269 if (ros::param::get("~mapPostFiltersConfig", configFileName))
00270 {
00271 ifstream ifs(configFileName.c_str());
00272 if (ifs.good())
00273 {
00274 mapPostFilters = PM::DataPointsFilters(ifs);
00275 }
00276 else
00277 {
00278 ROS_ERROR_STREAM("Cannot load map post-filters config from YAML file " << configFileName);
00279 }
00280 }
00281 else
00282 {
00283 ROS_INFO_STREAM("No map post-filters config file given, not using these filters");
00284 }
00285
00286
00287 if (getParam<bool>("subscribe_scan", true))
00288 scanSub = n.subscribe("scan", inputQueueSize, &Mapper::gotScan, this);
00289 if (getParam<bool>("subscribe_cloud", true))
00290 cloudSub = n.subscribe("cloud_in", inputQueueSize, &Mapper::gotCloud, this);
00291 mapPub = n.advertise<sensor_msgs::PointCloud2>("point_map", 2, true);
00292 outlierPub = n.advertise<sensor_msgs::PointCloud2>("outliers", 2, true);
00293 odomPub = n.advertise<nav_msgs::Odometry>("icp_odom", 50, true);
00294 odomErrorPub = n.advertise<nav_msgs::Odometry>("icp_error_odom", 50, true);
00295 getPointMapSrv = n.advertiseService("dynamic_point_map", &Mapper::getPointMap, this);
00296 saveMapSrv = pn.advertiseService("save_map", &Mapper::saveMap, this);
00297 loadMapSrv = pn.advertiseService("load_map", &Mapper::loadMap, this);
00298 resetSrv = pn.advertiseService("reset", &Mapper::reset, this);
00299 correctPoseSrv = pn.advertiseService("correct_pose", &Mapper::correctPose, this);
00300 setModeSrv = pn.advertiseService("set_mode", &Mapper::setMode, this);
00301 getModeSrv = pn.advertiseService("get_mode", &Mapper::getMode, this);
00302 getBoundedMapSrv = pn.advertiseService("get_bounded_map", &Mapper::getBoundedMap, this);
00303
00304
00305 publishThread = boost::thread(boost::bind(&Mapper::publishLoop, this, tfRefreshPeriod));
00306
00307 }
00308
00309 Mapper::~Mapper()
00310 {
00311 #if BOOST_VERSION >= 104100
00312
00313 if (mapBuildingInProgress)
00314 {
00315 mapBuildingFuture.wait();
00316 if (mapBuildingFuture.has_value())
00317 delete mapBuildingFuture.get();
00318 }
00319 #endif // BOOST_VERSION >= 104100
00320
00321 publishThread.join();
00322
00323 if (mapPointCloud)
00324 {
00325 mapPointCloud->save(vtkFinalMapName);
00326 delete mapPointCloud;
00327 }
00328 }
00329
00330 void Mapper::gotScan(const sensor_msgs::LaserScan& scanMsgIn)
00331 {
00332 if(localizing)
00333 {
00334 const ros::Time endScanTime(scanMsgIn.header.stamp + ros::Duration(scanMsgIn.time_increment * (scanMsgIn.ranges.size() - 1)));
00335 unique_ptr<DP> cloud(new DP(PointMatcher_ros::rosMsgToPointMatcherCloud<float>(scanMsgIn, &tfListener, odomFrame)));
00336 processCloud(move(cloud), scanMsgIn.header.frame_id, endScanTime, scanMsgIn.header.seq);
00337 }
00338 }
00339
00340 void Mapper::gotCloud(const sensor_msgs::PointCloud2& cloudMsgIn)
00341 {
00342 if(localizing)
00343 {
00344 unique_ptr<DP> cloud(new DP(PointMatcher_ros::rosMsgToPointMatcherCloud<float>(cloudMsgIn)));
00345 processCloud(move(cloud), cloudMsgIn.header.frame_id, cloudMsgIn.header.stamp, cloudMsgIn.header.seq);
00346 }
00347 }
00348
00349 struct BoolSetter
00350 {
00351 public:
00352 bool toSetValue;
00353 BoolSetter(bool& target, bool toSetValue):
00354 toSetValue(toSetValue),
00355 target(target)
00356 {}
00357 ~BoolSetter()
00358 {
00359 target = toSetValue;
00360 }
00361 protected:
00362 bool& target;
00363 };
00364
00365 void Mapper::processCloud(unique_ptr<DP> newPointCloud, const std::string& scannerFrame, const ros::Time& stamp, uint32_t seq)
00366 {
00367 processingNewCloud = true;
00368 BoolSetter stopProcessingSetter(processingNewCloud, false);
00369
00370
00371 processNewMapIfAvailable();
00372 cerr << "received new map" << endl;
00373
00374
00375 timer t;
00376
00377
00378
00379
00380
00381 const size_t goodCount(newPointCloud->features.cols());
00382 if (goodCount == 0)
00383 {
00384 ROS_ERROR("I found no good points in the cloud");
00385 return;
00386 }
00387
00388
00389 const int dimp1(newPointCloud->features.rows());
00390
00391 if(!(newPointCloud->descriptorExists("stamps_Msec") && newPointCloud->descriptorExists("stamps_sec") && newPointCloud->descriptorExists("stamps_nsec")))
00392 {
00393 const float Msec = round(stamp.sec/1e6);
00394 const float sec = round(stamp.sec - Msec*1e6);
00395 const float nsec = round(stamp.nsec);
00396
00397 const PM::Matrix desc_Msec = PM::Matrix::Constant(1, goodCount, Msec);
00398 const PM::Matrix desc_sec = PM::Matrix::Constant(1, goodCount, sec);
00399 const PM::Matrix desc_nsec = PM::Matrix::Constant(1, goodCount, nsec);
00400 newPointCloud->addDescriptor("stamps_Msec", desc_Msec);
00401 newPointCloud->addDescriptor("stamps_sec", desc_sec);
00402 newPointCloud->addDescriptor("stamps_nsec", desc_nsec);
00403
00404 cout << "Adding time" << endl;
00405
00406 }
00407
00408 ROS_INFO("Processing new point cloud");
00409 {
00410 timer t;
00411
00412
00413 inputFilters.apply(*newPointCloud);
00414
00415 ROS_INFO_STREAM("Input filters took " << t.elapsed() << " [s]");
00416 }
00417
00418 string reason;
00419
00420 if(!icp.hasMap())
00421 {
00422
00423 publishLock.lock();
00424 TOdomToMap = PM::TransformationParameters::Identity(dimp1, dimp1);
00425 TOdomToMap(2,3) = mapElevation;
00426 publishLock.unlock();
00427 }
00428
00429 cout << "TOdomToMap" << endl << TOdomToMap << endl;
00430
00431
00432
00433 PM::TransformationParameters TOdomToScanner;
00434 try
00435 {
00436 TOdomToScanner = PointMatcher_ros::eigenMatrixToDim<float>(
00437 PointMatcher_ros::transformListenerToEigenMatrix<float>(
00438 tfListener,
00439 scannerFrame,
00440 odomFrame,
00441 stamp
00442 ), dimp1);
00443 }
00444 catch(tf::ExtrapolationException e)
00445 {
00446 ROS_ERROR_STREAM("Extrapolation Exception. stamp = "<< stamp << " now = " << ros::Time::now() << " delta = " << ros::Time::now() - stamp << endl << e.what() );
00447 return;
00448 }
00449
00450
00451 ROS_DEBUG_STREAM("TOdomToScanner(" << odomFrame<< " to " << scannerFrame << "):\n" << TOdomToScanner);
00452 ROS_DEBUG_STREAM("TOdomToMap(" << odomFrame<< " to " << mapFrame << "):\n" << TOdomToMap);
00453
00454 const PM::TransformationParameters TscannerToMap = TOdomToMap * TOdomToScanner.inverse();
00455 ROS_DEBUG_STREAM("TscannerToMap (" << scannerFrame << " to " << mapFrame << "):\n" << TscannerToMap);
00456
00457
00458 const int ptsCount = newPointCloud->features.cols();
00459 if(ptsCount < minReadingPointCount)
00460 {
00461 ROS_ERROR_STREAM("Not enough points in newPointCloud: only " << ptsCount << " pts.");
00462 return;
00463 }
00464
00465
00466 if(!icp.hasMap())
00467 {
00468 ROS_INFO_STREAM("Creating an initial map");
00469 mapCreationTime = stamp;
00470 setMap(updateMap(newPointCloud.release(), TscannerToMap, false));
00471
00472 return;
00473 }
00474
00475
00476 if (newPointCloud->features.rows() != icp.getInternalMap().features.rows())
00477 {
00478 ROS_ERROR_STREAM("Dimensionality missmatch: incoming cloud is " << newPointCloud->features.rows()-1 << " while map is " << icp.getInternalMap().features.rows()-1);
00479 return;
00480 }
00481
00482 try
00483 {
00484
00485 PM::TransformationParameters Ticp;
00486 Ticp = icp(*newPointCloud, TscannerToMap);
00487
00488
00489 {
00490
00491 PM::TransformationParameters Tdelta = Ticp * TscannerToMap.inverse();
00492
00493
00494 Tdelta(2,0) = 0;
00495 Tdelta(2,1) = 0;
00496 Tdelta(2,2) = 1;
00497 Tdelta(0,2) = 0;
00498 Tdelta(1,2) = 0;
00499 Tdelta(2,3) = 0;
00500 Tdelta.block(0,0,3,3) = transformation->correctParameters(Tdelta.block(0,0,3,3));
00501
00502 Ticp = Tdelta*TscannerToMap;
00503
00504 }
00505
00506 ROS_DEBUG_STREAM("Ticp:\n" << Ticp);
00507
00508
00509 const double estimatedOverlap = icp.errorMinimizer->getOverlap();
00510 ROS_INFO_STREAM("Overlap: " << estimatedOverlap);
00511 if (estimatedOverlap < minOverlap)
00512 {
00513 ROS_ERROR_STREAM("Estimated overlap too small, ignoring ICP correction!");
00514 return;
00515 }
00516
00517
00518 publishStamp = stamp;
00519 publishLock.lock();
00520 TOdomToMap = Ticp * TOdomToScanner;
00521
00522 if(publishMapTf == true)
00523 {
00524 tfBroadcaster.sendTransform(PointMatcher_ros::eigenMatrixToStampedTransform<float>(TOdomToMap, mapFrame, odomFrame, stamp));
00525 }
00526
00527 publishLock.unlock();
00528 processingNewCloud = false;
00529
00530 ROS_DEBUG_STREAM("TOdomToMap:\n" << TOdomToMap);
00531
00532
00533 if (odomPub.getNumSubscribers())
00534 odomPub.publish(PointMatcher_ros::eigenMatrixToOdomMsg<float>(Ticp, mapFrame, stamp));
00535
00536
00537 if (odomErrorPub.getNumSubscribers())
00538 odomErrorPub.publish(PointMatcher_ros::eigenMatrixToOdomMsg<float>(TOdomToMap, mapFrame, stamp));
00539
00540
00541
00542
00543 if (
00544 mapping &&
00545 ((estimatedOverlap < maxOverlapToMerge) || (icp.getInternalMap().features.cols() < minMapPointCount)) &&
00546 #if BOOST_VERSION >= 104100
00547 (!mapBuildingInProgress)
00548 #else
00549 true
00550 #endif
00551 )
00552 {
00553 cout << "map Creation..." << endl;
00554
00555 mapCreationTime = stamp;
00556 #if BOOST_VERSION >= 104100
00557 ROS_INFO("Adding new points to the map in background");
00558 mapBuildingTask = MapBuildingTask(boost::bind(&Mapper::updateMap, this, newPointCloud.release(), Ticp, true));
00559 mapBuildingFuture = mapBuildingTask.get_future();
00560 mapBuildingThread = boost::thread(boost::move(boost::ref(mapBuildingTask)));
00561 mapBuildingInProgress = true;
00562 #else // BOOST_VERSION >= 104100
00563 ROS_INFO("Adding new points to the map");
00564 setMap(updateMap( newPointCloud.release(), Ticp, true));
00565 #endif // BOOST_VERSION >= 104100
00566 }
00567 }
00568 catch (PM::ConvergenceError error)
00569 {
00570 ROS_ERROR_STREAM("ICP failed to converge: " << error.what());
00571 return;
00572 }
00573
00574
00575 int realTimeRatio = 100*t.elapsed() / (stamp.toSec()-lastPoinCloudTime.toSec());
00576 ROS_INFO_STREAM("[TIME] Total ICP took: " << t.elapsed() << " [s]");
00577 if(realTimeRatio < 80)
00578 ROS_INFO_STREAM("[TIME] Real-time capability: " << realTimeRatio << "%");
00579 else
00580 ROS_WARN_STREAM("[TIME] Real-time capability: " << realTimeRatio << "%");
00581
00582 lastPoinCloudTime = stamp;
00583 }
00584
00585 void Mapper::processNewMapIfAvailable()
00586 {
00587 #if BOOST_VERSION >= 104100
00588 if (mapBuildingInProgress && mapBuildingFuture.has_value())
00589 {
00590 ROS_INFO_STREAM("New map available");
00591 setMap(mapBuildingFuture.get());
00592 mapBuildingInProgress = false;
00593 }
00594 #endif // BOOST_VERSION >= 104100
00595 }
00596
00597 void Mapper::setMap(DP* newPointCloud)
00598 {
00599
00600 if (mapPointCloud)
00601 delete mapPointCloud;
00602
00603
00604 mapPointCloud = newPointCloud;
00605 cerr << "copying map to ICP" << endl;
00606 icp.setMap(*mapPointCloud);
00607
00608
00609 cerr << "publishing map" << endl;
00610
00611
00612 if (mapPub.getNumSubscribers())
00613 mapPub.publish(PointMatcher_ros::pointMatcherCloudToRosMsg<float>(*mapPointCloud, mapFrame, mapCreationTime));
00614 }
00615
00616 Mapper::DP* Mapper::updateMap(DP* newPointCloud, const PM::TransformationParameters Ticp, bool updateExisting)
00617 {
00618 timer t;
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628 if(newPointCloud->descriptorExists("probabilityStatic") == false)
00629 {
00630
00631 newPointCloud->addDescriptor("probabilityStatic", PM::Matrix::Constant(1, newPointCloud->features.cols(), priorStatic));
00632 }
00633
00634 if(newPointCloud->descriptorExists("probabilityDynamic") == false)
00635 {
00636
00637 newPointCloud->addDescriptor("probabilityDynamic", PM::Matrix::Constant(1, newPointCloud->features.cols(), priorDyn));
00638 }
00639
00640 if(newPointCloud->descriptorExists("dynamic_ratio") == false)
00641 {
00642 newPointCloud->addDescriptor("dynamic_ratio", PM::Matrix::Zero(1, newPointCloud->features.cols()));
00643 }
00644
00645 if (!updateExisting)
00646 {
00647
00648 cout << "Jumping map creation" << endl;
00649 *newPointCloud = transformation->compute(*newPointCloud, Ticp);
00650 mapPostFilters.apply(*newPointCloud);
00651 return newPointCloud;
00652 }
00653
00654
00655
00656 const int readPtsCount(newPointCloud->features.cols());
00657 const int mapPtsCount(mapPointCloud->features.cols());
00658
00659
00660 PM::Matrix radius_reading = newPointCloud->features.topRows(3).colwise().norm();
00661
00662 PM::Matrix angles_reading(2, readPtsCount);
00663
00664
00665 for(int i=0; i<readPtsCount; i++)
00666 {
00667 const float ratio = newPointCloud->features(2,i)/radius_reading(0,i);
00668
00669
00670
00671 angles_reading(0,i) = acos(ratio);
00672 angles_reading(1,i) = atan2(newPointCloud->features(1,i), newPointCloud->features(0,i));
00673 }
00674
00675 std::shared_ptr<NNS> featureNNS;
00676 featureNNS.reset( NNS::create(angles_reading));
00677
00678
00679
00680 DP mapLocalFrame = transformation->compute(*mapPointCloud, Ticp.inverse());
00681
00682
00683
00684 const float sensorMaxRange = 80.0;
00685 PM::Matrix globalId(1, mapPtsCount);
00686
00687 int mapCutPtsCount = 0;
00688 DP mapLocalFrameCut(mapLocalFrame.createSimilarEmpty());
00689 for (int i = 0; i < mapPtsCount; i++)
00690 {
00691 if (mapLocalFrame.features.col(i).head(3).norm() < sensorMaxRange)
00692 {
00693 mapLocalFrameCut.setColFrom(mapCutPtsCount, mapLocalFrame, i);
00694 globalId(0,mapCutPtsCount) = i;
00695 mapCutPtsCount++;
00696 }
00697 }
00698
00699 mapLocalFrameCut.conservativeResize(mapCutPtsCount);
00700
00701
00702
00703
00704
00705
00706
00707
00708 PM::Matrix radius_map = mapLocalFrameCut.features.topRows(3).colwise().norm();
00709
00710 PM::Matrix angles_map(2, mapCutPtsCount);
00711
00712
00713 for(int i=0; i<mapCutPtsCount; i++)
00714 {
00715 const float ratio = mapLocalFrameCut.features(2,i)/radius_map(0,i);
00716
00717
00718
00719 angles_map(0,i) = acos(ratio);
00720
00721 angles_map(1,i) = atan2(mapLocalFrameCut.features(1,i), mapLocalFrameCut.features(0,i));
00722 }
00723
00724
00725 Matches::Dists dists(1,mapCutPtsCount);
00726 Matches::Ids ids(1,mapCutPtsCount);
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741 featureNNS->knn(angles_map, ids, dists, 1, 0, NNS::ALLOW_SELF_MATCH, maxAngle);
00742
00743
00744
00745
00746 DP::View viewOn_Msec_overlap = newPointCloud->getDescriptorViewByName("stamps_Msec");
00747 DP::View viewOn_sec_overlap = newPointCloud->getDescriptorViewByName("stamps_sec");
00748 DP::View viewOn_nsec_overlap = newPointCloud->getDescriptorViewByName("stamps_nsec");
00749
00750 DP::View viewOnProbabilityStatic = mapPointCloud->getDescriptorViewByName("probabilityStatic");
00751 DP::View viewOnProbabilityDynamic = mapPointCloud->getDescriptorViewByName("probabilityDynamic");
00752 DP::View viewOnDynamicRatio = mapPointCloud->getDescriptorViewByName("dynamic_ratio");
00753
00754 DP::View viewOn_normals_map = mapPointCloud->getDescriptorViewByName("normals");
00755 DP::View viewOn_Msec_map = mapPointCloud->getDescriptorViewByName("stamps_Msec");
00756 DP::View viewOn_sec_map = mapPointCloud->getDescriptorViewByName("stamps_sec");
00757 DP::View viewOn_nsec_map = mapPointCloud->getDescriptorViewByName("stamps_nsec");
00758
00759
00760
00761 viewOnDynamicRatio = PM::Matrix::Zero(1, mapPtsCount);
00762 for(int i=0; i < mapCutPtsCount; i++)
00763 {
00764 if(dists(i) != numeric_limits<float>::infinity())
00765 {
00766 const int readId = ids(0,i);
00767 const int mapId = globalId(0,i);
00768
00769
00770 const Eigen::Vector3f readPt = newPointCloud->features.col(readId).head(3);
00771 const Eigen::Vector3f mapPt = mapLocalFrameCut.features.col(i).head(3);
00772 const Eigen::Vector3f mapPt_n = mapPt.normalized();
00773 const float delta = (readPt - mapPt).norm();
00774 const float d_max = eps_a * readPt.norm();
00775
00776
00777
00778
00779
00780 const Eigen::Vector3f normal_map = viewOn_normals_map.col(mapId);
00781
00782
00783
00784
00785
00786
00787 const float w_v = eps + (1 - eps)*fabs(normal_map.dot(mapPt_n));
00788
00789 const float w_d1 = eps + (1 - eps)*(1 - sqrt(dists(i))/maxAngle);
00790
00791
00792 const float offset = delta - eps_d;
00793 float w_d2 = 1;
00794 if(delta < eps_d || mapPt.norm() > readPt.norm())
00795 {
00796 w_d2 = eps;
00797 }
00798 else
00799 {
00800 if (offset < d_max)
00801 {
00802 w_d2 = eps + (1 - eps )*offset/d_max;
00803 }
00804 }
00805
00806 float w_p2 = eps;
00807 if(delta < eps_d)
00808 {
00809 w_p2 = 1;
00810 }
00811 else
00812 {
00813 if(offset < d_max)
00814 {
00815 w_p2 = eps + (1 - eps)*(1 - offset/d_max);
00816 }
00817 }
00818
00819
00820
00821
00822
00823 if((readPt.norm() + eps_d + d_max) >= mapPt.norm())
00824 {
00825 const float lastDyn = viewOnProbabilityDynamic(0,mapId);
00826 const float lastStatic = viewOnProbabilityStatic(0, mapId);
00827
00828 const float c1 = (1 - (w_v*(1 - w_d1)));
00829 const float c2 = w_v*(1 - w_d1);
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843 if(lastDyn < maxDyn)
00844 {
00845 viewOnProbabilityDynamic(0,mapId) = c1*lastDyn + c2*w_d2*((1 - alpha)*lastStatic + beta*lastDyn);
00846 viewOnProbabilityStatic(0, mapId) = c1*lastStatic + c2*w_p2*(alpha*lastStatic + (1 - beta)*lastDyn);
00847 }
00848 else
00849 {
00850 viewOnProbabilityStatic(0,mapId) = eps;
00851 viewOnProbabilityDynamic(0,mapId) = 1-eps;
00852 }
00853
00854
00855
00856
00857 const float sumZ = viewOnProbabilityDynamic(0,mapId) + viewOnProbabilityStatic(0, mapId);
00858 assert(sumZ >= eps);
00859
00860 viewOnProbabilityDynamic(0,mapId) /= sumZ;
00861 viewOnProbabilityStatic(0,mapId) /= sumZ;
00862
00863
00864 viewOnDynamicRatio(0,mapId) = w_d2;
00865
00866
00867
00868
00869 viewOn_Msec_map(0,mapId) = viewOn_Msec_overlap(0,readId);
00870 viewOn_sec_map(0,mapId) = viewOn_sec_overlap(0,readId);
00871 viewOn_nsec_map(0,mapId) = viewOn_nsec_overlap(0,readId);
00872 }
00873
00874
00875 }
00876 }
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886 DP tmp_map = mapLocalFrameCut;
00887 tmp_map.concatenate(*newPointCloud);
00888
00889
00890
00891
00892 cout << "build first kdtree with " << tmp_map.features.cols() << endl;
00893
00894 featureNNS.reset( NNS::create(tmp_map.features, tmp_map.features.rows() - 1, NNS::KDTREE_LINEAR_HEAP, NNS::TOUCH_STATISTICS));
00895
00896 PM::Matches matches_overlap(
00897 Matches::Dists(1, readPtsCount),
00898 Matches::Ids(1, readPtsCount)
00899 );
00900
00901 featureNNS->knn(newPointCloud->features, matches_overlap.ids, matches_overlap.dists, 1, 0);
00902
00903
00904 DP overlap(newPointCloud->createSimilarEmpty());
00905 DP no_overlap(newPointCloud->createSimilarEmpty());
00906
00907
00908
00909
00910
00911
00912
00913
00914 int ptsOut = 0;
00915 int ptsIn = 0;
00916 for (int i = 0; i < readPtsCount; ++i)
00917 {
00918 if (matches_overlap.dists(i) > maxDistNewPoint)
00919 {
00920 no_overlap.setColFrom(ptsOut, *newPointCloud, i);
00921 ptsOut++;
00922 }
00923 else
00924 {
00925 overlap.setColFrom(ptsIn, *newPointCloud, i);
00926 ptsIn++;
00927 }
00928 }
00929
00930 no_overlap.conservativeResize(ptsOut);
00931 overlap.conservativeResize(ptsIn);
00932
00933 cout << "ptsOut=" << ptsOut << ", ptsIn=" << ptsIn << endl;
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943 no_overlap.addDescriptor("probabilityStatic", PM::Matrix::Constant(1, no_overlap.features.cols(), priorStatic));
00944
00945 no_overlap.addDescriptor("probabilityDynamic", PM::Matrix::Constant(1, no_overlap.features.cols(), priorDyn));
00946
00947 no_overlap.addDescriptor("dynamic_ratio", PM::Matrix::Zero(1, no_overlap.features.cols()));
00948
00949
00950 *newPointCloud = no_overlap;
00951
00952
00953
00954
00955 *newPointCloud = transformation->compute(*newPointCloud, Ticp);
00956
00957
00958
00959
00960
00961 newPointCloud->concatenate(*mapPointCloud);
00962 mapPostFilters.apply(*newPointCloud);
00963
00964 cout << "... end map creation" << endl;
00965 ROS_INFO_STREAM("[TIME] New map available (" << newPointCloud->features.cols() << " pts), update took " << t.elapsed() << " [s]");
00966
00967 return newPointCloud;
00968 }
00969
00970 void Mapper::waitForMapBuildingCompleted()
00971 {
00972 #if BOOST_VERSION >= 104100
00973 if (mapBuildingInProgress)
00974 {
00975
00976 mapBuildingFuture.wait();
00977 mapBuildingInProgress = false;
00978 }
00979 #endif // BOOST_VERSION >= 104100
00980 }
00981
00982 void Mapper::publishLoop(double publishPeriod)
00983 {
00984 if(publishPeriod == 0)
00985 return;
00986 ros::Rate r(1.0 / publishPeriod);
00987 while(ros::ok())
00988 {
00989 publishTransform();
00990 r.sleep();
00991 }
00992 }
00993
00994 void Mapper::publishTransform()
00995 {
00996 if(processingNewCloud == false && publishMapTf == true)
00997 {
00998 publishLock.lock();
00999
01000 tfBroadcaster.sendTransform(PointMatcher_ros::eigenMatrixToStampedTransform<float>(TOdomToMap, mapFrame, odomFrame, ros::Time::now()));
01001 publishLock.unlock();
01002 }
01003 }
01004
01005 bool Mapper::getPointMap(map_msgs::GetPointMap::Request &req, map_msgs::GetPointMap::Response &res)
01006 {
01007 if (!mapPointCloud)
01008 return false;
01009
01010
01011 res.map = PointMatcher_ros::pointMatcherCloudToRosMsg<float>(*mapPointCloud, mapFrame, ros::Time::now());
01012 return true;
01013 }
01014
01015 bool Mapper::saveMap(map_msgs::SaveMap::Request &req, map_msgs::SaveMap::Response &res)
01016 {
01017 if (!mapPointCloud)
01018 return false;
01019
01020 try
01021 {
01022 mapPointCloud->save(req.filename.data);
01023 }
01024 catch (const std::runtime_error& e)
01025 {
01026 ROS_ERROR_STREAM("Unable to save: " << e.what());
01027 return false;
01028 }
01029
01030 ROS_INFO_STREAM("Map saved at " << req.filename.data << " with " << mapPointCloud->features.cols() << " points.");
01031 return true;
01032 }
01033
01034 bool Mapper::loadMap(ethzasl_icp_mapper::LoadMap::Request &req, ethzasl_icp_mapper::LoadMap::Response &res)
01035 {
01036 waitForMapBuildingCompleted();
01037
01038 DP* cloud(new DP(DP::load(req.filename.data)));
01039
01040 const int dim = cloud->features.rows();
01041 const int nbPts = cloud->features.cols();
01042 ROS_INFO_STREAM("Loading " << dim-1 << "D point cloud (" << req.filename.data << ") with " << nbPts << " points.");
01043
01044 publishLock.lock();
01045 TOdomToMap = PM::TransformationParameters::Identity(dim,dim);
01046
01047
01048 TOdomToMap(2,3) = mapElevation;
01049 publishLock.unlock();
01050
01051 setMap(cloud);
01052
01053 return true;
01054 }
01055
01056 bool Mapper::reset(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
01057 {
01058 waitForMapBuildingCompleted();
01059
01060
01061 publishLock.lock();
01062 TOdomToMap = PM::TransformationParameters::Identity(4,4);
01063 publishLock.unlock();
01064
01065 icp.clearMap();
01066
01067 return true;
01068 }
01069
01070 bool Mapper::correctPose(ethzasl_icp_mapper::CorrectPose::Request &req, ethzasl_icp_mapper::CorrectPose::Response &res)
01071 {
01072 publishLock.lock();
01073 TOdomToMap = PointMatcher_ros::odomMsgToEigenMatrix<float>(req.odom);
01074
01075
01076 {
01077
01078 TOdomToMap(2,0) = 0;
01079 TOdomToMap(2,1) = 0;
01080 TOdomToMap(2,2) = 1;
01081 TOdomToMap(0,2) = 0;
01082 TOdomToMap(1,2) = 0;
01083 TOdomToMap(2,3) = mapElevation;
01084 }
01085
01086 tfBroadcaster.sendTransform(PointMatcher_ros::eigenMatrixToStampedTransform<float>(TOdomToMap, mapFrame, odomFrame, ros::Time::now()));
01087 publishLock.unlock();
01088
01089 return true;
01090 }
01091
01092 bool Mapper::setMode(ethzasl_icp_mapper::SetMode::Request &req, ethzasl_icp_mapper::SetMode::Response &res)
01093 {
01094
01095 if(req.localize == false && req.map == true)
01096 return false;
01097
01098 localizing = req.localize;
01099 mapping = req.map;
01100
01101 return true;
01102 }
01103
01104 bool Mapper::getMode(ethzasl_icp_mapper::GetMode::Request &req, ethzasl_icp_mapper::GetMode::Response &res)
01105 {
01106 res.localize = localizing;
01107 res.map = mapping;
01108 return true;
01109 }
01110
01111
01112
01113 bool Mapper::getBoundedMap(ethzasl_icp_mapper::GetBoundedMap::Request &req, ethzasl_icp_mapper::GetBoundedMap::Response &res)
01114 {
01115 if (!mapPointCloud)
01116 return false;
01117
01118 const float max_x = req.topRightCorner.x;
01119 const float max_y = req.topRightCorner.y;
01120 const float max_z = req.topRightCorner.z;
01121
01122 const float min_x = req.bottomLeftCorner.x;
01123 const float min_y = req.bottomLeftCorner.y;
01124 const float min_z = req.bottomLeftCorner.z;
01125
01126 cerr << "min [" << min_x << ", " << min_y << ", " << min_z << "] " << endl;
01127 cerr << "max [" << max_x << ", " << max_y << ", " << max_z << "] " << endl;
01128
01129
01130
01131 tf::StampedTransform stampedTr;
01132
01133 Eigen::Affine3d eigenTr;
01134 tf::poseMsgToEigen(req.mapCenter, eigenTr);
01135 Eigen::MatrixXf T = eigenTr.matrix().inverse().cast<float>();
01136
01137
01138 cerr << "T:" << endl << T << endl;
01139 T = transformation->correctParameters(T);
01140
01141
01142
01143 const DP centeredPointCloud = transformation->compute(*mapPointCloud, T);
01144 DP cutPointCloud = centeredPointCloud.createSimilarEmpty();
01145
01146 cerr << centeredPointCloud.features.topLeftCorner(3, 10) << endl;
01147 cerr << T << endl;
01148
01149 int newPtCount = 0;
01150 for(int i=0; i < centeredPointCloud.features.cols(); i++)
01151 {
01152 const float x = centeredPointCloud.features(0,i);
01153 const float y = centeredPointCloud.features(1,i);
01154 const float z = centeredPointCloud.features(2,i);
01155
01156 if(x < max_x && x > min_x &&
01157 y < max_y && y > min_y &&
01158 z < max_z && z > min_z )
01159 {
01160 cutPointCloud.setColFrom(newPtCount, centeredPointCloud, i);
01161 newPtCount++;
01162 }
01163 }
01164
01165 cerr << "Extract " << newPtCount << " points from the map" << endl;
01166
01167 cutPointCloud.conservativeResize(newPtCount);
01168 cutPointCloud = transformation->compute(cutPointCloud, T.inverse());
01169
01170
01171
01172 res.boundedMap = PointMatcher_ros::pointMatcherCloudToRosMsg<float>(cutPointCloud, mapFrame, ros::Time::now());
01173 return true;
01174 }
01175
01176
01177 int main(int argc, char **argv)
01178 {
01179 ros::init(argc, argv, "mapper");
01180 ros::NodeHandle n;
01181 ros::NodeHandle pn("~");
01182 Mapper mapper(n, pn);
01183 ros::spin();
01184
01185 return 0;
01186 }