icp_odometry.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of the Universite de Sherbrooke nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
29 
31 #include <nodelet/nodelet.h>
32 
34 
35 #include <sensor_msgs/LaserScan.h>
36 #include <sensor_msgs/PointCloud2.h>
38 
40 
41 #include <rtabmap/core/util3d.h>
45 #include <rtabmap/core/util2d.h>
48 #include <rtabmap/utilite/UStl.h>
49 
50 using namespace rtabmap;
51 
52 namespace rtabmap_ros
53 {
54 
56 {
57 public:
60  scanCloudMaxPoints_(0),
61  scanDownsamplingStep_(1),
62  scanVoxelSize_(0.0),
63  scanNormalK_(0),
64  scanNormalRadius_(0.0)
65  {
66  }
67 
68  virtual ~ICPOdometry()
69  {
70  }
71 
72 private:
73 
74  virtual void onOdomInit()
75  {
76  ros::NodeHandle & nh = getNodeHandle();
77  ros::NodeHandle & pnh = getPrivateNodeHandle();
78 
79  pnh.param("scan_cloud_max_points", scanCloudMaxPoints_, scanCloudMaxPoints_);
80  pnh.param("scan_downsampling_step", scanDownsamplingStep_, scanDownsamplingStep_);
81  pnh.param("scan_voxel_size", scanVoxelSize_, scanVoxelSize_);
82  pnh.param("scan_normal_k", scanNormalK_, scanNormalK_);
83  if(pnh.hasParam("scan_cloud_normal_k") && !pnh.hasParam("scan_normal_k"))
84  {
85  ROS_WARN("rtabmap: Parameter \"scan_cloud_normal_k\" has been renamed to \"scan_normal_k\". "
86  "The value is still used. Use \"scan_normal_k\" to avoid this warning.");
87  pnh.param("scan_cloud_normal_k", scanNormalK_, scanNormalK_);
88  }
89  pnh.param("scan_normal_radius", scanNormalRadius_, scanNormalRadius_);
90 
91  NODELET_INFO("IcpOdometry: scan_cloud_max_points = %d", scanCloudMaxPoints_);
92  NODELET_INFO("IcpOdometry: scan_downsampling_step = %d", scanDownsamplingStep_);
93  NODELET_INFO("IcpOdometry: scan_voxel_size = %f", scanVoxelSize_);
94  NODELET_INFO("IcpOdometry: scan_normal_k = %d", scanNormalK_);
95  NODELET_INFO("IcpOdometry: scan_normal_radius = %f", scanNormalRadius_);
96 
97  scan_sub_ = nh.subscribe("scan", 1, &ICPOdometry::callbackScan, this);
98  cloud_sub_ = nh.subscribe("scan_cloud", 1, &ICPOdometry::callbackCloud, this);
99  }
100 
101  virtual void updateParameters(ParametersMap & parameters)
102  {
103  //make sure we are using Reg/Strategy=0
104  ParametersMap::iterator iter = parameters.find(Parameters::kRegStrategy());
105  if(iter != parameters.end() && iter->second.compare("1") != 0)
106  {
107  ROS_WARN("ICP odometry works only with \"Reg/Strategy\"=1. Ignoring value %s.", iter->second.c_str());
108  }
109  uInsert(parameters, ParametersPair(Parameters::kRegStrategy(), "1"));
110 
111  ros::NodeHandle & pnh = getPrivateNodeHandle();
112  iter = parameters.find(Parameters::kIcpDownsamplingStep());
113  if(iter != parameters.end())
114  {
115  int value = uStr2Int(iter->second);
116  if(value > 1)
117  {
118  if(!pnh.hasParam("scan_downsampling_step"))
119  {
120  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());
121  scanDownsamplingStep_ = value;
122  iter->second = "1";
123  }
124  else
125  {
126  ROS_WARN("IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_downsampling_step\" are set.", iter->first.c_str());
127  }
128  }
129  }
130  iter = parameters.find(Parameters::kIcpVoxelSize());
131  if(iter != parameters.end())
132  {
133  float value = uStr2Float(iter->second);
134  if(value != 0.0f)
135  {
136  if(!pnh.hasParam("scan_voxel_size"))
137  {
138  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());
139  scanVoxelSize_ = value;
140  iter->second = "0";
141  }
142  else
143  {
144  ROS_WARN("IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_voxel_size\" are set.", iter->first.c_str());
145  }
146  }
147  }
148  iter = parameters.find(Parameters::kIcpPointToPlaneK());
149  if(iter != parameters.end())
150  {
151  int value = uStr2Int(iter->second);
152  if(value != 0)
153  {
154  if(!pnh.hasParam("scan_normal_k"))
155  {
156  ROS_WARN("IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_normal_k\" for convenience.", iter->second.c_str(), iter->first.c_str());
157  scanNormalK_ = value;
158  }
159  }
160  }
161  iter = parameters.find(Parameters::kIcpPointToPlaneRadius());
162  if(iter != parameters.end())
163  {
164  float value = uStr2Float(iter->second);
165  if(value != 0.0f)
166  {
167  if(!pnh.hasParam("scan_normal_radius"))
168  {
169  ROS_WARN("IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_normal_radius\" for convenience.", iter->second.c_str(), iter->first.c_str());
170  scanNormalRadius_ = value;
171  }
172  }
173  }
174  }
175 
176  void callbackScan(const sensor_msgs::LaserScanConstPtr& scanMsg)
177  {
178  // make sure the frame of the laser is updated too
179  Transform localScanTransform = getTransform(this->frameId(),
180  scanMsg->header.frame_id,
181  scanMsg->header.stamp + ros::Duration().fromSec(scanMsg->ranges.size()*scanMsg->time_increment));
182  if(localScanTransform.isNull())
183  {
184  ROS_ERROR("TF of received laser scan topic at time %fs is not set, aborting odometry update.", scanMsg->header.stamp.toSec());
185  return;
186  }
187 
188  //transform in frameId_ frame
189  sensor_msgs::PointCloud2 scanOut;
191  projection.transformLaserScanToPointCloud(scanMsg->header.frame_id, *scanMsg, scanOut, this->tfListener());
192  pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(new pcl::PointCloud<pcl::PointXYZ>);
193  pcl::fromROSMsg(scanOut, *pclScan);
194  pclScan->is_dense = true;
195 
196  cv::Mat scan;
197  int maxLaserScans = (int)scanMsg->ranges.size();
198  if(pclScan->size())
199  {
200  if(scanDownsamplingStep_ > 1)
201  {
202  pclScan = util3d::downsample(pclScan, scanDownsamplingStep_);
203  maxLaserScans /= scanDownsamplingStep_;
204  }
205  if(scanVoxelSize_ > 0.0f)
206  {
207  float pointsBeforeFiltering = (float)pclScan->size();
208  pclScan = util3d::voxelize(pclScan, scanVoxelSize_);
209  float ratio = float(pclScan->size()) / pointsBeforeFiltering;
210  maxLaserScans = int(float(maxLaserScans) * ratio);
211  }
212  if(scanNormalK_ > 0 || scanNormalRadius_>0.0f)
213  {
214  //compute normals
215  pcl::PointCloud<pcl::Normal>::Ptr normals;
216  if(scanVoxelSize_ > 0.0f)
217  {
218  normals = util3d::computeNormals2D(pclScan, scanNormalK_, scanNormalRadius_);
219  }
220  else
221  {
222  normals = util3d::computeFastOrganizedNormals2D(pclScan, scanNormalK_, scanNormalRadius_);
223  }
224  pcl::PointCloud<pcl::PointNormal>::Ptr pclScanNormal(new pcl::PointCloud<pcl::PointNormal>);
225  pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
226  scan = util3d::laserScan2dFromPointCloud(*pclScanNormal);
227  }
228  else
229  {
230  scan = util3d::laserScan2dFromPointCloud(*pclScan);
231  }
232  }
233 
234  rtabmap::SensorData data(
235  LaserScan::backwardCompatibility(scan, maxLaserScans, scanMsg->range_max, localScanTransform),
236  cv::Mat(),
237  cv::Mat(),
238  CameraModel(),
239  0,
240  rtabmap_ros::timestampFromROS(scanMsg->header.stamp));
241 
242  this->processData(data, scanMsg->header.stamp);
243  }
244 
245  void callbackCloud(const sensor_msgs::PointCloud2ConstPtr& cloudMsg)
246  {
247  cv::Mat scan;
248  bool containNormals = false;
249  if(scanVoxelSize_ == 0.0f)
250  {
251  for(unsigned int i=0; i<cloudMsg->fields.size(); ++i)
252  {
253  if(cloudMsg->fields[i].name.compare("normal_x") == 0)
254  {
255  containNormals = true;
256  break;
257  }
258  }
259  }
260 
261  Transform localScanTransform = getTransform(this->frameId(), cloudMsg->header.frame_id, cloudMsg->header.stamp);
262  if(localScanTransform.isNull())
263  {
264  ROS_ERROR("TF of received scan cloud at time %fs is not set, aborting rtabmap update.", cloudMsg->header.stamp.toSec());
265  return;
266  }
267 
268  int maxLaserScans = scanCloudMaxPoints_;
269  if(containNormals)
270  {
271  pcl::PointCloud<pcl::PointNormal>::Ptr pclScan(new pcl::PointCloud<pcl::PointNormal>);
272  pcl::fromROSMsg(*cloudMsg, *pclScan);
273  if(pclScan->size() && scanDownsamplingStep_ > 1)
274  {
275  pclScan = util3d::downsample(pclScan, scanDownsamplingStep_);
276  maxLaserScans /= scanDownsamplingStep_;
277  }
278  scan = util3d::laserScanFromPointCloud(*pclScan);
279  }
280  else
281  {
282  pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(new pcl::PointCloud<pcl::PointXYZ>);
283  pcl::fromROSMsg(*cloudMsg, *pclScan);
284  if(pclScan->size() && scanDownsamplingStep_ > 1)
285  {
286  pclScan = util3d::downsample(pclScan, scanDownsamplingStep_);
287  maxLaserScans /= scanDownsamplingStep_;
288  }
289  if(!pclScan->is_dense)
290  {
291  pclScan = util3d::removeNaNFromPointCloud(pclScan);
292  }
293 
294  if(pclScan->size())
295  {
296  if(scanVoxelSize_ > 0.0f)
297  {
298  float pointsBeforeFiltering = (float)pclScan->size();
299  pclScan = util3d::voxelize(pclScan, scanVoxelSize_);
300  float ratio = float(pclScan->size()) / pointsBeforeFiltering;
301  maxLaserScans = int(float(maxLaserScans) * ratio);
302  }
303  if(scanNormalK_ > 0 || scanNormalRadius_>0.0f)
304  {
305  //compute normals
306  pcl::PointCloud<pcl::Normal>::Ptr normals = util3d::computeNormals(pclScan, scanNormalK_, scanNormalRadius_);
307  pcl::PointCloud<pcl::PointNormal>::Ptr pclScanNormal(new pcl::PointCloud<pcl::PointNormal>);
308  pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
309  scan = util3d::laserScanFromPointCloud(*pclScanNormal);
310  }
311  else
312  {
313  scan = util3d::laserScanFromPointCloud(*pclScan);
314  }
315  }
316  }
317 
318  rtabmap::SensorData data(
319  LaserScan::backwardCompatibility(scan, maxLaserScans, 0, localScanTransform),
320  cv::Mat(),
321  cv::Mat(),
322  CameraModel(),
323  0,
324  rtabmap_ros::timestampFromROS(cloudMsg->header.stamp));
325 
326  this->processData(data, cloudMsg->header.stamp);
327  }
328 
329 protected:
330  virtual void flushCallbacks()
331  {
332  // flush callbacks
333  }
334 
335 private:
343 };
344 
346 
347 }
LaserScan RTABMAP_EXP laserScanFromPointCloud(const pcl::PCLPointCloud2 &cloud, bool filterNaNs=true)
virtual void flushCallbacks()
pcl::PointCloud< pcl::Normal >::Ptr RTABMAP_EXP computeNormals2D(const pcl::PointCloud< pcl::PointXYZ >::Ptr &cloud, int searchK=5, float searchRadius=0.0f, const Eigen::Vector3f &viewPoint=Eigen::Vector3f(0, 0, 0))
f
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
std::pair< std::string, std::string > ParametersPair
pcl::PointCloud< pcl::PointXYZ >::Ptr RTABMAP_EXP removeNaNFromPointCloud(const pcl::PointCloud< pcl::PointXYZ >::Ptr &cloud)
ros::Subscriber scan_sub_
void fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud< T > &pcl_cloud)
#define ROS_WARN(...)
rtabmap::Transform getTransform(const std::string &fromFrameId, const std::string &toFrameId, const ros::Time &stamp, tf::TransformListener &listener, double waitForTransform)
ros::Subscriber cloud_sub_
pcl::PointCloud< pcl::Normal >::Ptr RTABMAP_EXP computeFastOrganizedNormals2D(const pcl::PointCloud< pcl::PointXYZ >::Ptr &cloud, int searchK=5, float searchRadius=0.0f, const Eigen::Vector3f &viewPoint=Eigen::Vector3f(0, 0, 0))
void callbackScan(const sensor_msgs::LaserScanConstPtr &scanMsg)
LaserScan RTABMAP_EXP downsample(const LaserScan &cloud, int step)
cv::Mat RTABMAP_EXP laserScan2dFromPointCloud(const pcl::PointCloud< pcl::PointXYZ > &cloud, const Transform &transform=Transform(), bool filterNaNs=true)
PLUGINLIB_EXPORT_CLASS(rtabmap_ros::ICPOdometry, nodelet::Nodelet)
int uStr2Int(const std::string &str)
void callbackCloud(const sensor_msgs::PointCloud2ConstPtr &cloudMsg)
#define true
bool isNull() const
Duration & fromSec(double t)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
cv::Mat RTABMAP_EXP computeNormals(const cv::Mat &laserScan, int searchK, float searchRadius)
QMap< QString, QVariant > ParametersMap
double stamp() const
#define false
virtual void onOdomInit()
#define NODELET_INFO(...)
virtual void updateParameters(ParametersMap &parameters)
double timestampFromROS(const ros::Time &stamp)
float uStr2Float(const std::string &str)
void transformLaserScanToPointCloud(const std::string &target_frame, const sensor_msgs::LaserScan &scan_in, sensor_msgs::PointCloud &cloud_out, tf::Transformer &tf, double range_cutoff, int channel_options=channel_option::Default)
bool hasParam(const std::string &key) const
pcl::PointCloud< pcl::PointXYZ >::Ptr RTABMAP_EXP voxelize(const pcl::PointCloud< pcl::PointXYZ >::Ptr &cloud, const pcl::IndicesPtr &indices, float voxelSize)
#define ROS_ERROR(...)
static LaserScan backwardCompatibility(const cv::Mat &oldScanFormat, int maxPoints=0, int maxRange=0, const Transform &localTransform=Transform::getIdentity())
void uInsert(std::map< K, V > &map, const std::pair< K, V > &pair)


rtabmap_ros
Author(s): Mathieu Labbe
autogenerated on Fri Jun 7 2019 21:55:03