icp_odometry.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
00003 All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007     * Redistributions of source code must retain the above copyright
00008       notice, this list of conditions and the following disclaimer.
00009     * Redistributions in binary form must reproduce the above copyright
00010       notice, this list of conditions and the following disclaimer in the
00011       documentation and/or other materials provided with the distribution.
00012     * Neither the name of the Universite de Sherbrooke nor the
00013       names of its contributors may be used to endorse or promote products
00014       derived from this software without specific prior written permission.
00015 
00016 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00017 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
00020 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00021 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00022 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00023 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00024 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00025 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026 */
00027 
00028 #include <rtabmap_ros/OdometryROS.h>
00029 
00030 #include <pluginlib/class_list_macros.h>
00031 #include <nodelet/nodelet.h>
00032 
00033 #include <laser_geometry/laser_geometry.h>
00034 
00035 #include <sensor_msgs/LaserScan.h>
00036 #include <sensor_msgs/PointCloud2.h>
00037 #include <pcl_conversions/pcl_conversions.h>
00038 
00039 #include "rtabmap_ros/MsgConversion.h"
00040 
00041 #include <rtabmap/core/util3d.h>
00042 #include <rtabmap/core/util3d_surface.h>
00043 #include <rtabmap/core/util3d_transforms.h>
00044 #include <rtabmap/core/util3d_filtering.h>
00045 #include <rtabmap/core/util2d.h>
00046 #include <rtabmap/utilite/ULogger.h>
00047 #include <rtabmap/utilite/UConversion.h>
00048 #include <rtabmap/utilite/UStl.h>
00049 
00050 using namespace rtabmap;
00051 
00052 namespace rtabmap_ros
00053 {
00054 
00055 class ICPOdometry : public rtabmap_ros::OdometryROS
00056 {
00057 public:
00058         ICPOdometry() :
00059                 OdometryROS(false, false, true),
00060                 scanCloudMaxPoints_(0),
00061                 scanDownsamplingStep_(1),
00062                 scanVoxelSize_(0.0),
00063                 scanNormalK_(0),
00064                 scanNormalRadius_(0.0)
00065         {
00066         }
00067 
00068         virtual ~ICPOdometry()
00069         {
00070         }
00071 
00072 private:
00073 
00074         virtual void onOdomInit()
00075         {
00076                 ros::NodeHandle & nh = getNodeHandle();
00077                 ros::NodeHandle & pnh = getPrivateNodeHandle();
00078 
00079                 pnh.param("scan_cloud_max_points",  scanCloudMaxPoints_, scanCloudMaxPoints_);
00080                 pnh.param("scan_downsampling_step", scanDownsamplingStep_, scanDownsamplingStep_);
00081                 pnh.param("scan_voxel_size", scanVoxelSize_, scanVoxelSize_);
00082                 pnh.param("scan_normal_k", scanNormalK_, scanNormalK_);
00083                 if(pnh.hasParam("scan_cloud_normal_k") && !pnh.hasParam("scan_normal_k"))
00084                 {
00085                         ROS_WARN("rtabmap: Parameter \"scan_cloud_normal_k\" has been renamed to \"scan_normal_k\". "
00086                                         "The value is still used. Use \"scan_normal_k\" to avoid this warning.");
00087                         pnh.param("scan_cloud_normal_k", scanNormalK_, scanNormalK_);
00088                 }
00089                 pnh.param("scan_normal_radius", scanNormalRadius_, scanNormalRadius_);
00090 
00091                 NODELET_INFO("IcpOdometry: scan_cloud_max_points  = %d", scanCloudMaxPoints_);
00092                 NODELET_INFO("IcpOdometry: scan_downsampling_step = %d", scanDownsamplingStep_);
00093                 NODELET_INFO("IcpOdometry: scan_voxel_size        = %f", scanVoxelSize_);
00094                 NODELET_INFO("IcpOdometry: scan_normal_k          = %d", scanNormalK_);
00095                 NODELET_INFO("IcpOdometry: scan_normal_radius     = %f", scanNormalRadius_);
00096 
00097                 scan_sub_ = nh.subscribe("scan", 1, &ICPOdometry::callbackScan, this);
00098                 cloud_sub_ = nh.subscribe("scan_cloud", 1, &ICPOdometry::callbackCloud, this);
00099         }
00100 
00101         virtual void updateParameters(ParametersMap & parameters)
00102         {
00103                 //make sure we are using Reg/Strategy=0
00104                 ParametersMap::iterator iter = parameters.find(Parameters::kRegStrategy());
00105                 if(iter != parameters.end() && iter->second.compare("1") != 0)
00106                 {
00107                         ROS_WARN("ICP odometry works only with \"Reg/Strategy\"=1. Ignoring value %s.", iter->second.c_str());
00108                 }
00109                 uInsert(parameters, ParametersPair(Parameters::kRegStrategy(), "1"));
00110 
00111                 ros::NodeHandle & pnh = getPrivateNodeHandle();
00112                 iter = parameters.find(Parameters::kIcpDownsamplingStep());
00113                 if(iter != parameters.end())
00114                 {
00115                         int value = uStr2Int(iter->second);
00116                         if(value > 1)
00117                         {
00118                                 if(!pnh.hasParam("scan_downsampling_step"))
00119                                 {
00120                                         ROS_WARN("IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_downsampling_step\" for convenience. \"%s\" is set to 0.", iter->second.c_str(), iter->first.c_str(), iter->first.c_str());
00121                                         scanDownsamplingStep_ = value;
00122                                         iter->second = "1";
00123                                 }
00124                                 else
00125                                 {
00126                                         ROS_WARN("IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_downsampling_step\" are set.", iter->first.c_str());
00127                                 }
00128                         }
00129                 }
00130                 iter = parameters.find(Parameters::kIcpVoxelSize());
00131                 if(iter != parameters.end())
00132                 {
00133                         float value = uStr2Float(iter->second);
00134                         if(value != 0.0f)
00135                         {
00136                                 if(!pnh.hasParam("scan_voxel_size"))
00137                                 {
00138                                         ROS_WARN("IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_voxel_size\" for convenience. \"%s\" is set to 0.", iter->second.c_str(), iter->first.c_str(), iter->first.c_str());
00139                                         scanVoxelSize_ = value;
00140                                         iter->second = "0";
00141                                 }
00142                                 else
00143                                 {
00144                                         ROS_WARN("IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_voxel_size\" are set.", iter->first.c_str());
00145                                 }
00146                         }
00147                 }
00148                 iter = parameters.find(Parameters::kIcpPointToPlaneK());
00149                 if(iter != parameters.end())
00150                 {
00151                         int value = uStr2Int(iter->second);
00152                         if(value != 0)
00153                         {
00154                                 if(!pnh.hasParam("scan_normal_k"))
00155                                 {
00156                                         ROS_WARN("IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_normal_k\" for convenience.", iter->second.c_str(), iter->first.c_str());
00157                                         scanNormalK_ = value;
00158                                 }
00159                         }
00160                 }
00161                 iter = parameters.find(Parameters::kIcpPointToPlaneRadius());
00162                 if(iter != parameters.end())
00163                 {
00164                         float value = uStr2Float(iter->second);
00165                         if(value != 0.0f)
00166                         {
00167                                 if(!pnh.hasParam("scan_normal_radius"))
00168                                 {
00169                                         ROS_WARN("IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_normal_radius\" for convenience.", iter->second.c_str(), iter->first.c_str());
00170                                         scanNormalRadius_ = value;
00171                                 }
00172                         }
00173                 }
00174         }
00175 
00176         void callbackScan(const sensor_msgs::LaserScanConstPtr& scanMsg)
00177         {
00178                 // make sure the frame of the laser is updated too
00179                 Transform localScanTransform = getTransform(this->frameId(),
00180                                 scanMsg->header.frame_id,
00181                                 scanMsg->header.stamp + ros::Duration().fromSec(scanMsg->ranges.size()*scanMsg->time_increment));
00182                 if(localScanTransform.isNull())
00183                 {
00184                         ROS_ERROR("TF of received laser scan topic at time %fs is not set, aborting odometry update.", scanMsg->header.stamp.toSec());
00185                         return;
00186                 }
00187 
00188                 //transform in frameId_ frame
00189                 sensor_msgs::PointCloud2 scanOut;
00190                 laser_geometry::LaserProjection projection;
00191                 projection.transformLaserScanToPointCloud(scanMsg->header.frame_id, *scanMsg, scanOut, this->tfListener());
00192                 pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(new pcl::PointCloud<pcl::PointXYZ>);
00193                 pcl::fromROSMsg(scanOut, *pclScan);
00194                 pclScan->is_dense = true;
00195 
00196                 cv::Mat scan;
00197                 int maxLaserScans = (int)scanMsg->ranges.size();
00198                 if(pclScan->size())
00199                 {
00200                         if(scanDownsamplingStep_ > 1)
00201                         {
00202                                 pclScan = util3d::downsample(pclScan, scanDownsamplingStep_);
00203                                 maxLaserScans /= scanDownsamplingStep_;
00204                         }
00205                         if(scanVoxelSize_ > 0.0f)
00206                         {
00207                                 float pointsBeforeFiltering = (float)pclScan->size();
00208                                 pclScan = util3d::voxelize(pclScan, scanVoxelSize_);
00209                                 float ratio = float(pclScan->size()) / pointsBeforeFiltering;
00210                                 maxLaserScans = int(float(maxLaserScans) * ratio);
00211                         }
00212                         if(scanNormalK_ > 0 || scanNormalRadius_>0.0f)
00213                         {
00214                                 //compute normals
00215                                 pcl::PointCloud<pcl::Normal>::Ptr normals;
00216                                 if(scanVoxelSize_ > 0.0f)
00217                                 {
00218                                         normals = util3d::computeNormals2D(pclScan, scanNormalK_, scanNormalRadius_);
00219                                 }
00220                                 else
00221                                 {
00222                                         normals = util3d::computeFastOrganizedNormals2D(pclScan, scanNormalK_, scanNormalRadius_);
00223                                 }
00224                                 pcl::PointCloud<pcl::PointNormal>::Ptr pclScanNormal(new pcl::PointCloud<pcl::PointNormal>);
00225                                 pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
00226                                 scan = util3d::laserScan2dFromPointCloud(*pclScanNormal);
00227                         }
00228                         else
00229                         {
00230                                 scan = util3d::laserScan2dFromPointCloud(*pclScan);
00231                         }
00232                 }
00233 
00234                 rtabmap::SensorData data(
00235                                 LaserScan::backwardCompatibility(scan, maxLaserScans, scanMsg->range_max, localScanTransform),
00236                                 cv::Mat(),
00237                                 cv::Mat(),
00238                                 CameraModel(),
00239                                 0,
00240                                 rtabmap_ros::timestampFromROS(scanMsg->header.stamp));
00241 
00242                 this->processData(data, scanMsg->header.stamp);
00243         }
00244 
00245         void callbackCloud(const sensor_msgs::PointCloud2ConstPtr& cloudMsg)
00246         {
00247                 cv::Mat scan;
00248                 bool containNormals = false;
00249                 if(scanVoxelSize_ == 0.0f)
00250                 {
00251                         for(unsigned int i=0; i<cloudMsg->fields.size(); ++i)
00252                         {
00253                                 if(cloudMsg->fields[i].name.compare("normal_x") == 0)
00254                                 {
00255                                         containNormals = true;
00256                                         break;
00257                                 }
00258                         }
00259                 }
00260 
00261                 Transform localScanTransform = getTransform(this->frameId(), cloudMsg->header.frame_id, cloudMsg->header.stamp);
00262                 if(localScanTransform.isNull())
00263                 {
00264                         ROS_ERROR("TF of received scan cloud at time %fs is not set, aborting rtabmap update.", cloudMsg->header.stamp.toSec());
00265                         return;
00266                 }
00267 
00268                 int maxLaserScans = scanCloudMaxPoints_;
00269                 if(containNormals)
00270                 {
00271                         pcl::PointCloud<pcl::PointNormal>::Ptr pclScan(new pcl::PointCloud<pcl::PointNormal>);
00272                         pcl::fromROSMsg(*cloudMsg, *pclScan);
00273                         if(pclScan->size() && scanDownsamplingStep_ > 1)
00274                         {
00275                                 pclScan = util3d::downsample(pclScan, scanDownsamplingStep_);
00276                                 maxLaserScans /= scanDownsamplingStep_;
00277                         }
00278                         scan = util3d::laserScanFromPointCloud(*pclScan);
00279                 }
00280                 else
00281                 {
00282                         pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(new pcl::PointCloud<pcl::PointXYZ>);
00283                         pcl::fromROSMsg(*cloudMsg, *pclScan);
00284                         if(pclScan->size() && scanDownsamplingStep_ > 1)
00285                         {
00286                                 pclScan = util3d::downsample(pclScan, scanDownsamplingStep_);
00287                                 maxLaserScans /= scanDownsamplingStep_;
00288                         }
00289                         if(!pclScan->is_dense)
00290                         {
00291                                 pclScan = util3d::removeNaNFromPointCloud(pclScan);
00292                         }
00293 
00294                         if(pclScan->size())
00295                         {
00296                                 if(scanVoxelSize_ > 0.0f)
00297                                 {
00298                                         float pointsBeforeFiltering = (float)pclScan->size();
00299                                         pclScan = util3d::voxelize(pclScan, scanVoxelSize_);
00300                                         float ratio = float(pclScan->size()) / pointsBeforeFiltering;
00301                                         maxLaserScans = int(float(maxLaserScans) * ratio);
00302                                 }
00303                                 if(scanNormalK_ > 0 || scanNormalRadius_>0.0f)
00304                                 {
00305                                         //compute normals
00306                                         pcl::PointCloud<pcl::Normal>::Ptr normals = util3d::computeNormals(pclScan, scanNormalK_, scanNormalRadius_);
00307                                         pcl::PointCloud<pcl::PointNormal>::Ptr pclScanNormal(new pcl::PointCloud<pcl::PointNormal>);
00308                                         pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
00309                                         scan = util3d::laserScanFromPointCloud(*pclScanNormal);
00310                                 }
00311                                 else
00312                                 {
00313                                         scan = util3d::laserScanFromPointCloud(*pclScan);
00314                                 }
00315                         }
00316                 }
00317 
00318                 rtabmap::SensorData data(
00319                                 LaserScan::backwardCompatibility(scan, maxLaserScans, 0, localScanTransform),
00320                                 cv::Mat(),
00321                                 cv::Mat(),
00322                                 CameraModel(),
00323                                 0,
00324                                 rtabmap_ros::timestampFromROS(cloudMsg->header.stamp));
00325 
00326                 this->processData(data, cloudMsg->header.stamp);
00327         }
00328 
00329 protected:
00330         virtual void flushCallbacks()
00331         {
00332                 // flush callbacks
00333         }
00334 
00335 private:
00336         ros::Subscriber scan_sub_;
00337         ros::Subscriber cloud_sub_;
00338         int scanCloudMaxPoints_;
00339         int scanDownsamplingStep_;
00340         double scanVoxelSize_;
00341         int scanNormalK_;
00342         double scanNormalRadius_;
00343 };
00344 
00345 PLUGINLIB_EXPORT_CLASS(rtabmap_ros::ICPOdometry, nodelet::Nodelet);
00346 
00347 }


rtabmap_ros
Author(s): Mathieu Labbe
autogenerated on Thu Jun 6 2019 21:30:49