dynamic_mapper.cpp
Go to the documentation of this file.
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 // Services
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" // FIXME: should that be moved to map_msgs?
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         // Subscribers
00054         ros::Subscriber scanSub;
00055         ros::Subscriber cloudSub;
00056         
00057         // Publishers
00058         ros::Publisher mapPub;
00059         ros::Publisher outlierPub;
00060         ros::Publisher odomPub;
00061         ros::Publisher odomErrorPub;
00062         
00063         // Services
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         // Time
00074         ros::Time mapCreationTime;
00075         ros::Time lastPoinCloudTime;
00076 
00077         // libpointmatcher
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         // multi-threading mapper
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         // Parameters
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; // initial correction on z-axis //FIXME: handle the full matrix
00112         
00113         // Parameters for dynamic filtering
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         // Services
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 // BOOST_VERSION >= 104100
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         // Ensure proper states
00203         if(localizing == false)
00204                 mapping = false;
00205         if(mapping == true)
00206                 localizing = true;
00207 
00208         // set logger
00209         if (getParam<bool>("useROSLogger", false))
00210                 PointMatcherSupport::setLogger(new PointMatcherSupport::ROSLogger);
00211 
00212         // load configs
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         // topics and services initialization
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         // refreshing tf transform thread
00305         publishThread = boost::thread(boost::bind(&Mapper::publishLoop, this, tfRefreshPeriod));
00306 
00307 }
00308 
00309 Mapper::~Mapper()
00310 {
00311         #if BOOST_VERSION >= 104100
00312         // wait for map-building thread
00313         if (mapBuildingInProgress)
00314         {
00315                 mapBuildingFuture.wait();
00316                 if (mapBuildingFuture.has_value())
00317                         delete mapBuildingFuture.get();
00318         }
00319         #endif // BOOST_VERSION >= 104100
00320         // wait for publish thread
00321         publishThread.join();
00322         // save point cloud
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         // if the future has completed, use the new map
00371         processNewMapIfAvailable();
00372         cerr << "received new map" << endl;
00373         
00374         // IMPORTANT:  We need to receive the point clouds in local coordinates (scanner or robot)
00375         timer t;
00376 
00377 
00378         
00379         
00380         // Convert point cloud
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         // Dimension of the point cloud, important since we handle 2D and 3D
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; // Print how long take the algo
00411                 
00412                 // Apply filters to incoming cloud, in scanner coordinates
00413                 inputFilters.apply(*newPointCloud);
00414                 
00415                 ROS_INFO_STREAM("Input filters took " << t.elapsed() << " [s]");
00416         }
00417         
00418         string reason;
00419         // Initialize the transformation to identity if empty
00420         if(!icp.hasMap())
00421         {
00422                 // we need to know the dimensionality of the point cloud to initialize properly
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         // Fetch transformation from scanner to odom
00432         // Note: we don't need to wait for transform. It is already called in transformListenerToEigenMatrix()
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         // Ensure a minimum amount of point after filtering
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         // Initialize the map if empty
00466         if(!icp.hasMap())
00467         {
00468                 ROS_INFO_STREAM("Creating an initial map");
00469                 mapCreationTime = stamp;
00470                 setMap(updateMap(newPointCloud.release(), TscannerToMap, false));
00471                 // we must not delete newPointCloud because we just stored it in the mapPointCloud
00472                 return;
00473         }
00474         
00475         // Check dimension
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                 // Apply ICP
00485                 PM::TransformationParameters Ticp;
00486                 Ticp = icp(*newPointCloud, TscannerToMap);
00487 
00488                 // ISER
00489                 {
00490                 // extract corrections
00491                 PM::TransformationParameters Tdelta = Ticp * TscannerToMap.inverse();
00492                 
00493                 // remove roll and pitch
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; //z
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                 // Ensure minimum overlap between scans
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                 // Compute tf
00518                 publishStamp = stamp;
00519                 publishLock.lock();
00520                 TOdomToMap = Ticp * TOdomToScanner;
00521                 // Publish tf
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                 // Publish odometry
00533                 if (odomPub.getNumSubscribers())
00534                         odomPub.publish(PointMatcher_ros::eigenMatrixToOdomMsg<float>(Ticp, mapFrame, stamp));
00535         
00536                 // Publish error on odometry
00537                 if (odomErrorPub.getNumSubscribers())
00538                         odomErrorPub.publish(PointMatcher_ros::eigenMatrixToOdomMsg<float>(TOdomToMap, mapFrame, stamp));
00539 
00540                 
00541 
00542                 // check if news points should be added to the map
00543                 if (
00544                         mapping &&
00545                         ((estimatedOverlap < maxOverlapToMerge) || (icp.getInternalMap().features.cols() < minMapPointCount)) &&
00546                         #if BOOST_VERSION >= 104100
00547                         (!mapBuildingInProgress)
00548                         #else // BOOST_VERSION >= 104100
00549                         true
00550                         #endif // BOOST_VERSION >= 104100
00551                 )
00552                 {
00553                         cout << "map Creation..." << endl;
00554                         // make sure we process the last available map
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         //Statistics about time and real-time capability
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         // delete old map
00600         if (mapPointCloud)
00601                 delete mapPointCloud;
00602         
00603         // set new map
00604         mapPointCloud = newPointCloud;
00605         cerr << "copying map to ICP" << endl;
00606         icp.setMap(*mapPointCloud);
00607         
00608         
00609         cerr << "publishing map" << endl;
00610         // Publish map point cloud
00611         // FIXME this crash when used without descriptor
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         // FIXME: those are parameters
00621         //const float priorStatic = 0.8; // ICRA 2014
00622         //const float priorDyn = 0.2; // ICRA 2014
00623         //const float priorStatic = 0.4; // ISER 2014
00624         //const float priorDyn = 0.6; // ISER 2014
00625 
00626 
00627         // Prepare empty field if not existing
00628         if(newPointCloud->descriptorExists("probabilityStatic") == false)
00629         {
00630                 //newPointCloud->addDescriptor("probabilityStatic", PM::Matrix::Zero(1, newPointCloud->features.cols()));
00631                 newPointCloud->addDescriptor("probabilityStatic", PM::Matrix::Constant(1, newPointCloud->features.cols(), priorStatic));
00632         }
00633         
00634         if(newPointCloud->descriptorExists("probabilityDynamic") == false)
00635         {
00636                 //newPointCloud->addDescriptor("probabilityDynamic", PM::Matrix::Zero(1, newPointCloud->features.cols()));
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                 // FIXME: correct that, ugly
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         // Build a range image of the reading point cloud (local coordinates)
00660         PM::Matrix radius_reading = newPointCloud->features.topRows(3).colwise().norm();
00661 
00662         PM::Matrix angles_reading(2, readPtsCount); // 0=inclination, 1=azimuth
00663 
00664         // No atan in Eigen, so we are for to loop through it...
00665         for(int i=0; i<readPtsCount; i++)
00666         {
00667                 const float ratio = newPointCloud->features(2,i)/radius_reading(0,i);
00668                 //if(ratio < -1 || ratio > 1)
00669                         //cout << "Error angle!" << endl;
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         // Transform the global map in local coordinates
00680         DP mapLocalFrame = transformation->compute(*mapPointCloud, Ticp.inverse());
00681 
00682         // Remove points out of sensor range
00683         // FIXME: this is a parameter
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         //PM::Parameters params({{"maxDist", toParam(sensorMaxRange)}});
00701         //PM::DataPointsFilter* maxDistFilter = 
00702         //              PM::get().DataPointsFilterRegistrar.create("MaxDistDataPointsFilter", params);
00703 
00704         //mapLocalFrame = maxDistFilter->filter(mapLocalFrame);
00705 
00706 
00707         
00708         PM::Matrix radius_map = mapLocalFrameCut.features.topRows(3).colwise().norm();
00709 
00710         PM::Matrix angles_map(2, mapCutPtsCount); // 0=inclination, 1=azimuth
00711 
00712         // No atan in Eigen, so we are for to loop through it...
00713         for(int i=0; i<mapCutPtsCount; i++)
00714         {
00715                 const float ratio = mapLocalFrameCut.features(2,i)/radius_map(0,i);
00716                 //if(ratio < -1 || ratio > 1)
00717                         //cout << "Error angle!" << endl;
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         // Look for NN in spherical coordinates
00725         Matches::Dists dists(1,mapCutPtsCount);
00726         Matches::Ids ids(1,mapCutPtsCount);
00727         
00728         // FIXME: those are parameters
00729         // note: 0.08 rad is 5 deg
00730         //const float maxAngle = 0.04; // in rad (ICRA 14)
00731         //const float eps_a = 0.2; // ratio of distance (ICRA 14)
00732         //const float eps_d = 0.1; // in meters (ICRA 14)
00733         //const float maxAngle = 0.02; // in rad (ISER 14)
00734         //const float eps_a = 0.05; // ratio of distance (ISER 14)
00735         //const float eps_d = 0.02; // in meters (ISER 14)
00736         //const float alpha = 0.99;
00737         //const float beta = 0.99;
00738 
00739         
00740         
00741         featureNNS->knn(angles_map, ids, dists, 1, 0, NNS::ALLOW_SELF_MATCH, maxAngle);
00742 
00743         // Define views on descriptors
00744         //DP::View viewOn_normals_overlap = newPointCloud->getDescriptorViewByName("normals");
00745         //DP::View viewOn_obsDir_overlap = newPointCloud->getDescriptorViewByName("observationDirections");
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                         // in local coordinates
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                         //const double timeMap = viewOn_Msec_map(0,mapId)*10e5 + viewOn_sec_map(0,mapId) + viewOn_nsec_map(0,mapId)*10e-10;
00777                         //const double timeRead = viewOn_Msec_overlap(0,readId)*10e5 + viewOn_sec_overlap(0,readId) + viewOn_nsec_overlap(0,readId)*10e-10;
00778                         //const double deltaTime = timeRead - timeMap;
00779 
00780                         const Eigen::Vector3f normal_map = viewOn_normals_map.col(mapId);
00781                         //const Eigen::Vector3f normal_read = viewOn_normals_overlap.col(readId);
00782                         //const Eigen::Vector3f obsDir = viewOn_obsDir_overlap.col(readId);
00783                         //const float viewAngle = acos(normal_map.normalized().dot(obsDir.normalized()));
00784                         //const float normalAngle = acos(normal_map.normalized().dot(normal_read.normalized()));
00785                         
00786                         // Weight for dynamic elements
00787                         const float w_v = eps + (1 - eps)*fabs(normal_map.dot(mapPt_n));
00788                         //const float w_d1 = 1 + eps - acos(readPt.normalized().dot(mapPt_n))/maxAngle;
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                         //cerr << "readPt.norm(): "<< readPt.norm()  << "mapPt.norm(): "<< mapPt.norm() << ", w_p2: " << w_p2 << ", w_d2: " << w_d2 << endl;
00820                 
00821 
00822                         // We don't update point behind the reading
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                                 //viewOnProbabilityDynamic(0,mapId) += (w_v + w_d2) * w_d1/2;
00833                                 //viewOnProbabilityDynamic(0,mapId) += (w_v * w_d2);
00834                                 
00835                                 //viewOnProbabilityStatic(0, mapId) += (w_p2) * w_d1;
00836                                 //viewOnProbabilityStatic(0, mapId) += w_p2;
00837 
00838                                 // FIXME: this is a parameter
00839                                 //const float maxDyn = 0.9; // ICRA 14
00840                                 //const float maxDyn = 0.98; // ISER 14
00841 
00842                                 //Lock dynamic point to stay dynamic under a threshold
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                                 // normalization
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                                 //viewOnDynamicRatio(0,mapId) =viewOnProbabilityDynamic(0, mapId);
00864                                 viewOnDynamicRatio(0,mapId) = w_d2;
00865 
00866                                 //viewOnDynamicRatio(0,mapId) = w_d2;
00867 
00868                                 // Refresh time
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         // Correct new points using ICP result
00882         //*newPointCloud = transformation->compute(*newPointCloud, Ticp); 
00883 
00884         // Generate temporary map for density computation
00885         //DP tmp_map = (*mapPointCloud); // FIXME: this should be on mapLocalFrameCut
00886         DP tmp_map = mapLocalFrameCut; // FIXME: this should be on mapLocalFrameCut
00887         tmp_map.concatenate(*newPointCloud);
00888 
00889         // Compute density
00890         //std::shared_ptr<NNS> featureNNS;
00891 
00892         cout << "build first kdtree with " << tmp_map.features.cols() << endl;
00893         // build and populate NNS
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         // FIXME: this is a parameter
00910   //const float maxDist = pow(0.3, 2); // ICRA 2014
00911         //const float maxDist = pow(0.1, 2); // ISER 2014
00912         //const float maxDist = pow(0.1, 2);
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         // Publish outliers
00936         //if (outlierPub.getNumSubscribers())
00937         //{
00938                 //outlierPub.publish(PointMatcher_ros::pointMatcherCloudToRosMsg<float>(no_overlap, mapFrame, mapCreationTime));
00939         //}
00940 
00941         // Initialize descriptors
00942         //no_overlap.addDescriptor("probabilityStatic", PM::Matrix::Zero(1, no_overlap.features.cols()));
00943         no_overlap.addDescriptor("probabilityStatic", PM::Matrix::Constant(1, no_overlap.features.cols(), priorStatic));
00944         //no_overlap.addDescriptor("probabilityDynamic", PM::Matrix::Zero(1, no_overlap.features.cols()));
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         // shrink the newPointCloud to the new information
00950         *newPointCloud = no_overlap;
00951         
00952 
00953         
00954         // Correct new points using ICP result
00955         *newPointCloud = transformation->compute(*newPointCloud, Ticp);
00956         
00957         
00958         
00959         
00960         // Merge point clouds to map
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                 // we wait for now, in future we should kill it
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                 // Note: we use now as timestamp to refresh the tf and avoid other buffer to be empty
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         // FIXME: do we need a mutex here?
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         //ISER
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         // note: no need for locking as we do ros::spin(), to update if we go for multi-threading
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         //ISER
01076         {
01077         // remove roll and pitch
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; //z
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         // Impossible states
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         //const Eigen::MatrixXf T = eigenTr.matrix().cast<float>();
01137 
01138         cerr << "T:" << endl << T << endl;
01139         T = transformation->correctParameters(T);
01140 
01141                 
01142         // FIXME: do we need a mutex here?
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         // Send the resulting point cloud in ROS format
01172         res.boundedMap = PointMatcher_ros::pointMatcherCloudToRosMsg<float>(cutPointCloud, mapFrame, ros::Time::now());
01173         return true;
01174 }
01175 
01176 // Main function supporting the Mapper class
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 }


ethzasl_icp_mapper
Author(s): François Pomerleau and Stéphane Magnenat
autogenerated on Tue Mar 3 2015 15:28:44