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


ethzasl_icp_mapper
Author(s): François Pomerleau and Stéphane Magnenat
autogenerated on Mon Oct 6 2014 10:28:23