00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
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
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
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
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
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
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
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 }