37 #include <sensor_msgs/LaserScan.h>
38 #include <sensor_msgs/PointCloud2.h>
64 scanCloudMaxPoints_(-1),
65 scanCloudIs2d_(
false),
66 scanDownsamplingStep_(1),
71 scanNormalRadius_(0.0),
72 scanNormalGroundUp_(0.0),
74 deskewingSlerp_(
false),
75 plugin_loader_(
"rtabmap_odom",
"rtabmap_odom::PluginInterface"),
94 pnh.
param(
"queue_size", queueSize, queueSize);
95 pnh.
param(
"scan_cloud_max_points", scanCloudMaxPoints_, scanCloudMaxPoints_);
96 pnh.
param(
"scan_cloud_is_2d", scanCloudIs2d_, scanCloudIs2d_);
97 pnh.
param(
"scan_downsampling_step", scanDownsamplingStep_, scanDownsamplingStep_);
98 pnh.
param(
"scan_range_min", scanRangeMin_, scanRangeMin_);
99 pnh.
param(
"scan_range_max", scanRangeMax_, scanRangeMax_);
100 pnh.
param(
"scan_voxel_size", scanVoxelSize_, scanVoxelSize_);
101 pnh.
param(
"scan_normal_k", scanNormalK_, scanNormalK_);
102 pnh.
param(
"scan_normal_radius", scanNormalRadius_, scanNormalRadius_);
103 pnh.
param(
"scan_normal_ground_up", scanNormalGroundUp_, scanNormalGroundUp_);
104 pnh.
param(
"deskewing", deskewing_, deskewing_);
105 pnh.
param(
"deskewing_slerp", deskewingSlerp_, deskewingSlerp_);
110 pnh.
getParam(
"plugins", pluginsList);
114 std::string pluginName =
static_cast<std::string
>(pluginsList[
i][
"name"]);
115 std::string
type =
static_cast<std::string
>(pluginsList[
i][
"type"]);
116 NODELET_INFO(
"IcpOdometry: Using plugin %s of type \"%s\"", pluginName.c_str(),
type.c_str());
119 plugins_.push_back(plugin);
120 plugin->initialize(pluginName, pnh);
121 if(!plugin->isEnabled())
123 NODELET_WARN(
"Plugin: %s is not enabled, filtering will not occur. \"enabled_\" member "
124 "should be managed in subclasses. This can be ignored if the "
125 "plugin should really be initialized as disabled.",
126 plugin->getName().c_str());
130 ROS_ERROR(
"Failed to load plugin %s. Error: %s", pluginName.c_str(), ex.what());
138 ROS_WARN(
"rtabmap: Parameter \"scan_cloud_normal_k\" has been renamed to \"scan_normal_k\". "
139 "The value is still used. Use \"scan_normal_k\" to avoid this warning.");
140 pnh.
param(
"scan_cloud_normal_k", scanNormalK_, scanNormalK_);
143 NODELET_INFO(
"IcpOdometry: queue_size = %d", queueSize);
144 NODELET_INFO(
"IcpOdometry: scan_cloud_max_points = %d", scanCloudMaxPoints_);
145 NODELET_INFO(
"IcpOdometry: scan_cloud_is_2d = %s", scanCloudIs2d_?
"true":
"false");
146 NODELET_INFO(
"IcpOdometry: scan_downsampling_step = %d", scanDownsamplingStep_);
147 NODELET_INFO(
"IcpOdometry: scan_range_min = %f m", scanRangeMin_);
148 NODELET_INFO(
"IcpOdometry: scan_range_max = %f m", scanRangeMax_);
149 NODELET_INFO(
"IcpOdometry: scan_voxel_size = %f m", scanVoxelSize_);
150 NODELET_INFO(
"IcpOdometry: scan_normal_k = %d", scanNormalK_);
151 NODELET_INFO(
"IcpOdometry: scan_normal_radius = %f m", scanNormalRadius_);
152 NODELET_INFO(
"IcpOdometry: scan_normal_ground_up = %f", scanNormalGroundUp_);
153 NODELET_INFO(
"IcpOdometry: deskewing = %s", deskewing_?
"true":
"false");
154 NODELET_INFO(
"IcpOdometry: deskewing_slerp = %s", deskewingSlerp_?
"true":
"false");
156 scan_sub_ = nh.
subscribe(
"scan", queueSize, &ICPOdometry::callbackScan,
this);
157 cloud_sub_ = nh.
subscribe(
"scan_cloud", queueSize, &ICPOdometry::callbackCloud,
this);
159 filtered_scan_pub_ = nh.
advertise<sensor_msgs::PointCloud2>(
"odom_filtered_input_scan", 1);
161 initDiagnosticMsg(
uFormat(
"\n%s subscribed to %s and %s (make sure only one of this topic is published, otherwise remap one to a dummy topic name).",
163 scan_sub_.getTopic().c_str(),
164 cloud_sub_.getTopic().c_str()),
true);
170 ParametersMap::iterator
iter = parameters.find(Parameters::kRegStrategy());
171 if(
iter != parameters.end() &&
iter->second.compare(
"1") != 0)
173 ROS_WARN(
"ICP odometry works only with \"Reg/Strategy\"=1. Ignoring value %s.",
iter->second.c_str());
178 iter = parameters.find(Parameters::kIcpDownsamplingStep());
179 if(
iter != parameters.end())
184 if(!pnh.
hasParam(
"scan_downsampling_step"))
186 ROS_WARN(
"IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_downsampling_step\" for convenience. \"%s\" is set to 1.",
iter->second.c_str(),
iter->first.c_str(),
iter->first.c_str());
187 scanDownsamplingStep_ =
value;
192 ROS_WARN(
"IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_downsampling_step\" are set.",
iter->first.c_str());
196 iter = parameters.find(Parameters::kIcpRangeMin());
197 if(
iter != parameters.end())
204 ROS_WARN(
"IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_range_min\" for convenience. \"%s\" is set to 0.",
iter->second.c_str(),
iter->first.c_str(),
iter->first.c_str());
205 scanRangeMin_ =
value;
210 ROS_WARN(
"IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_range_min\" are set.",
iter->first.c_str());
214 iter = parameters.find(Parameters::kIcpRangeMax());
215 if(
iter != parameters.end())
222 ROS_WARN(
"IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_range_max\" for convenience. \"%s\" is set to 0.",
iter->second.c_str(),
iter->first.c_str(),
iter->first.c_str());
223 scanRangeMax_ =
value;
228 ROS_WARN(
"IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_range_max\" are set.",
iter->first.c_str());
232 iter = parameters.find(Parameters::kIcpVoxelSize());
233 if(
iter != parameters.end())
238 if(!pnh.
hasParam(
"scan_voxel_size"))
240 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());
241 scanVoxelSize_ =
value;
246 ROS_WARN(
"IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_voxel_size\" are set.",
iter->first.c_str());
250 else if(pnh.
hasParam(
"scan_voxel_size"))
252 NODELET_INFO(
"IcpOdometry: scan_voxel_size is set (%f), setting %s to 0", scanVoxelSize_, Parameters::kIcpVoxelSize().
c_str());
253 parameters.insert(
ParametersPair(Parameters::kIcpVoxelSize(),
"0"));
255 iter = parameters.find(Parameters::kIcpPointToPlaneK());
256 if(
iter != parameters.end())
263 ROS_WARN(
"IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_normal_k\" for convenience.",
iter->second.c_str(),
iter->first.c_str());
264 scanNormalK_ =
value;
268 NODELET_INFO(
"IcpOdometry: scan_normal_k is set (%d), setting %s to same value.", scanNormalK_, Parameters::kIcpPointToPlaneK().
c_str());
273 else if(pnh.
hasParam(
"scan_normal_k"))
275 NODELET_INFO(
"IcpOdometry: scan_normal_k is set (%d), setting %s to same value.", scanNormalK_, Parameters::kIcpPointToPlaneK().
c_str());
278 iter = parameters.find(Parameters::kIcpPointToPlaneRadius());
279 if(
iter != parameters.end())
284 if(!pnh.
hasParam(
"scan_normal_radius"))
286 ROS_WARN(
"IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_normal_radius\" for convenience.",
iter->second.c_str(),
iter->first.c_str());
287 scanNormalRadius_ =
value;
291 NODELET_INFO(
"IcpOdometry: scan_normal_radius is set (%f), setting %s to same value.", scanNormalRadius_, Parameters::kIcpPointToPlaneRadius().
c_str());
296 else if(pnh.
hasParam(
"scan_normal_radius"))
298 NODELET_INFO(
"IcpOdometry: scan_normal_radius is set (%f), setting %s to same value.", scanNormalRadius_, Parameters::kIcpPointToPlaneRadius().
c_str());
301 iter = parameters.find(Parameters::kIcpPointToPlaneGroundNormalsUp());
302 if(
iter != parameters.end())
307 if(!pnh.
hasParam(
"scan_normal_ground_up"))
309 ROS_WARN(
"IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_normal_ground_up\" for convenience.",
iter->second.c_str(),
iter->first.c_str());
310 scanNormalGroundUp_ =
value;
314 NODELET_INFO(
"IcpOdometry: scan_normal_ground_up is set (%f), setting %s to same value.", scanNormalGroundUp_, Parameters::kIcpPointToPlaneGroundNormalsUp().
c_str());
319 else if(pnh.
hasParam(
"scan_normal_ground_up"))
321 NODELET_INFO(
"IcpOdometry: scan_normal_ground_up is set (%f), setting %s to same value.", scanNormalGroundUp_, Parameters::kIcpPointToPlaneGroundNormalsUp().
c_str());
330 ROS_ERROR(
"%s is already receiving clouds on \"%s\", but also "
331 "just received a scan on \"%s\". Both subscribers cannot be "
332 "used at the same time! Disabling scan subscriber.",
333 this->
getName().
c_str(), cloud_sub_.getTopic().c_str(), scan_sub_.getTopic().c_str());
334 scan_sub_.shutdown();
338 scanReceived_ =
true;
346 scanMsg->header.frame_id,
347 scanMsg->header.stamp,
349 this->waitForTransformDuration());
350 if(localScanTransform.
isNull())
352 ROS_ERROR(
"TF of received laser scan topic at time %fs is not set, aborting odometry update.", scanMsg->header.stamp.toSec());
357 sensor_msgs::PointCloud2 scanOut;
360 if(deskewing_ && (!guessFrameId().
empty() || (
frameId().
compare(scanMsg->header.frame_id) != 0)))
364 scanMsg->header.frame_id,
366 scanMsg->header.stamp,
367 scanMsg->header.stamp +
ros::Duration().
fromSec(scanMsg->ranges.size()*scanMsg->time_increment),
369 this->waitForTransformDuration());
383 if(guessFrameId().
empty() && previousStamp() > 0 && !velocityGuess().
isNull())
386 sensor_msgs::PointCloud2 scanOutDeskewed;
389 ROS_ERROR(
"Failed to deskew input cloud, aborting odometry update!");
392 scanOut = scanOutDeskewed;
395 sensor_msgs::PointCloud2 scanOutDeskewed;
398 ROS_ERROR(
"Cannot transform back projected scan from \"%s\" frame to \"%s\" frame at time %fs.",
399 (guessFrameId().
empty()?
frameId():guessFrameId()).
c_str(), scanMsg->header.frame_id.c_str(), scanMsg->header.stamp.toSec());
402 scanOut = scanOutDeskewed;
408 if(deskewing_ && previousStamp() > 0 && !velocityGuess().
isNull())
411 sensor_msgs::PointCloud2 scanOutDeskewed;
414 ROS_ERROR(
"Failed to deskew input cloud, aborting odometry update!");
417 scanOut = scanOutDeskewed;
421 bool hasIntensity =
false;
422 for(
unsigned int i=0;
i<scanOut.fields.size(); ++
i)
424 if(scanOut.fields[
i].name.compare(
"intensity") == 0)
426 if(scanOut.fields[
i].datatype == sensor_msgs::PointField::FLOAT32)
432 static bool warningShown =
false;
435 ROS_WARN(
"The input scan cloud has an \"intensity\" field "
436 "but the datatype (%d) is not supported. Intensity will be ignored. "
437 "This message is only shown once.", scanOut.fields[
i].datatype);
444 pcl::PointCloud<pcl::PointXYZI>::Ptr pclScanI(
new pcl::PointCloud<pcl::PointXYZI>);
445 pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(
new pcl::PointCloud<pcl::PointXYZ>);
450 pclScanI->is_dense =
true;
455 pclScan->is_dense =
true;
459 int maxLaserScans = (
int)scanMsg->ranges.
size();
460 if(!pclScan->empty() || !pclScanI->empty())
462 if(scanDownsamplingStep_ > 1)
472 maxLaserScans /= scanDownsamplingStep_;
474 if(scanVoxelSize_ > 0.0
f)
476 float pointsBeforeFiltering;
477 float pointsAfterFiltering;
480 pointsBeforeFiltering = (
float)pclScanI->size();
482 pointsAfterFiltering = (
float)pclScanI->size();
486 pointsBeforeFiltering = (
float)pclScan->size();
488 pointsAfterFiltering = (
float)pclScan->size();
490 float ratio = pointsAfterFiltering / pointsBeforeFiltering;
491 maxLaserScans =
int(
float(maxLaserScans) *
ratio);
493 if(scanNormalK_ > 0 || scanNormalRadius_>0.0
f)
496 pcl::PointCloud<pcl::Normal>::Ptr normals;
497 if(scanVoxelSize_ > 0.0
f)
519 pcl::PointCloud<pcl::PointXYZINormal>::Ptr pclScanINormal;
520 pcl::PointCloud<pcl::PointNormal>::Ptr pclScanNormal;
523 pclScanINormal.reset(
new pcl::PointCloud<pcl::PointXYZINormal>);
524 pcl::concatenateFields(*pclScanI, *normals, *pclScanINormal);
529 pclScanNormal.reset(
new pcl::PointCloud<pcl::PointNormal>);
530 pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
547 if(scanRangeMin_ > 0 || scanRangeMax_ > 0)
555 scanRangeMax_>0&&scanRangeMax_<scanMsg->range_max?scanRangeMax_:scanMsg->range_max,
563 this->processData(
data, scanMsg->header);
568 UASSERT_MSG(pointCloudMsg->data.size() == pointCloudMsg->row_step*pointCloudMsg->height,
569 uFormat(
"data=%d row_step=%d height=%d", pointCloudMsg->data.size(), pointCloudMsg->row_step, pointCloudMsg->height).c_str());
573 ROS_ERROR(
"%s is already receiving scans on \"%s\", but also "
574 "just received a cloud on \"%s\". Both subscribers cannot be "
575 "used at the same time! Disabling cloud subscriber.",
576 this->
getName().
c_str(), scan_sub_.getTopic().c_str(), cloud_sub_.getTopic().c_str());
577 cloud_sub_.shutdown();
581 cloudReceived_ =
true;
587 sensor_msgs::PointCloud2::Ptr cloudMsg(
new sensor_msgs::PointCloud2);
588 if (!plugins_.empty())
590 if (plugins_[0]->isEnabled())
592 *cloudMsg = plugins_[0]->filterPointCloud(*pointCloudMsg);
596 *cloudMsg = *pointCloudMsg;
599 if (plugins_.size() > 1)
601 for (
int i = 1;
i < plugins_.size();
i++) {
602 if (plugins_[
i]->isEnabled()) {
603 *cloudMsg = plugins_[
i]->filterPointCloud(*cloudMsg);
610 *cloudMsg = *pointCloudMsg;
614 if(localScanTransform.
isNull())
616 ROS_ERROR(
"TF of received scan cloud at time %fs is not set, aborting rtabmap update.", cloudMsg->header.stamp.toSec());
622 if(!guessFrameId().
empty())
625 if(!
rtabmap_conversions::deskew(*pointCloudMsg, *cloudMsg, guessFrameId(), tfListener(), waitForTransformDuration(), deskewingSlerp_))
627 ROS_ERROR(
"Failed to deskew input cloud, aborting odometry update!");
631 else if(previousStamp() > 0 && !velocityGuess().
isNull())
634 bool alreadyInBaseFrame =
frameId().compare(pointCloudMsg->header.frame_id) == 0;
635 sensor_msgs::PointCloud2Ptr cloudInBaseFrame;
636 sensor_msgs::PointCloud2Ptr cloudPtr = cloudMsg;
637 if(!alreadyInBaseFrame)
640 cloudInBaseFrame.reset(
new sensor_msgs::PointCloud2);
643 ROS_ERROR(
"Cannot transform back projected scan from \"%s\" frame to \"%s\" frame at time %fs.",
644 pointCloudMsg->header.frame_id.c_str(),
frameId().
c_str(), pointCloudMsg->header.stamp.toSec());
647 cloudPtr = cloudInBaseFrame;
650 sensor_msgs::PointCloud2::Ptr cloudDeskewed(
new sensor_msgs::PointCloud2);
653 ROS_ERROR(
"Failed to deskew input cloud, aborting odometry update!");
657 if(!alreadyInBaseFrame)
662 ROS_ERROR(
"Cannot transform back projected scan from \"%s\" frame to \"%s\" frame at time %fs.",
663 frameId().
c_str(), pointCloudMsg->header.frame_id.c_str(), pointCloudMsg->header.stamp.toSec());
669 cloudMsg = cloudDeskewed;
675 bool hasNormals =
false;
676 bool hasIntensity =
false;
678 for(
unsigned int i=0;
i<cloudMsg->fields.size(); ++
i)
680 if(scanVoxelSize_ == 0.0
f && cloudMsg->fields[
i].name.compare(
"normal_x") == 0)
684 if(cloudMsg->fields[
i].name.compare(
"z") == 0 && !scanCloudIs2d_)
688 if(cloudMsg->fields[
i].name.compare(
"intensity") == 0)
690 if(cloudMsg->fields[
i].datatype == sensor_msgs::PointField::FLOAT32)
696 static bool warningShown =
false;
699 ROS_WARN(
"The input scan cloud has an \"intensity\" field "
700 "but the datatype (%d) is not supported. Intensity will be ignored. "
701 "This message is only shown once.", cloudMsg->fields[
i].datatype);
708 if(cloudMsg->height > 1)
710 if(scanCloudMaxPoints_ == -1)
712 scanCloudMaxPoints_ = cloudMsg->height * cloudMsg->width;
713 NODELET_WARN(
"IcpOdometry: \"scan_cloud_max_points\" is not set but input "
714 "cloud is not dense, for convenience it will be set to %d (%dx%d)",
715 scanCloudMaxPoints_, cloudMsg->width, cloudMsg->height);
717 else if(scanCloudMaxPoints_ > 0 && scanCloudMaxPoints_ < cloudMsg->
height * cloudMsg->width)
719 NODELET_WARN(
"IcpOdometry: \"scan_cloud_max_points\" is set to %d but input "
720 "cloud is not dense and has a size of %d (%dx%d), setting to this later size.",
721 scanCloudMaxPoints_, cloudMsg->width *cloudMsg->height, cloudMsg->width, cloudMsg->height);
722 scanCloudMaxPoints_ = cloudMsg->width *cloudMsg->height;
725 if(scanCloudMaxPoints_ == -1)
727 scanCloudMaxPoints_ = 0;
729 int maxLaserScans = scanCloudMaxPoints_;
731 if(hasNormals && hasIntensity)
733 pcl::PointCloud<pcl::PointXYZINormal>::Ptr pclScan(
new pcl::PointCloud<pcl::PointXYZINormal>);
735 if(pclScan->size() && scanDownsamplingStep_ > 1)
738 if(pclScan->height>1)
740 maxLaserScans = pclScan->height * pclScan->width;
744 maxLaserScans /= scanDownsamplingStep_;
751 pcl::PointCloud<pcl::PointNormal>::Ptr pclScan(
new pcl::PointCloud<pcl::PointNormal>);
753 if(pclScan->size() && scanDownsamplingStep_ > 1)
756 if(pclScan->height>1)
758 maxLaserScans = pclScan->height * pclScan->width;
762 maxLaserScans /= scanDownsamplingStep_;
767 else if(hasIntensity)
769 pcl::PointCloud<pcl::PointXYZI>::Ptr pclScan(
new pcl::PointCloud<pcl::PointXYZI>);
771 if(pclScan->size() && scanDownsamplingStep_ > 1)
774 if(pclScan->height>1)
776 maxLaserScans = pclScan->height * pclScan->width;
780 maxLaserScans /= scanDownsamplingStep_;
783 if(!pclScan->is_dense)
790 if(scanVoxelSize_ > 0.0
f)
792 float pointsBeforeFiltering = (
float)pclScan->size();
794 float ratio =
float(pclScan->size()) / pointsBeforeFiltering;
795 maxLaserScans =
int(
float(maxLaserScans) *
ratio);
797 if(scanNormalK_ > 0 || scanNormalRadius_>0.0
f)
800 pcl::PointCloud<pcl::Normal>::Ptr normals = is3D?
803 pcl::PointCloud<pcl::PointXYZINormal>::Ptr pclScanNormal(
new pcl::PointCloud<pcl::PointXYZINormal>);
804 pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
815 pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(
new pcl::PointCloud<pcl::PointXYZ>);
817 if(pclScan->size() && scanDownsamplingStep_ > 1)
820 if(pclScan->height>1)
822 maxLaserScans = pclScan->height * pclScan->width;
826 maxLaserScans /= scanDownsamplingStep_;
829 if(!pclScan->is_dense)
836 if(scanVoxelSize_ > 0.0
f)
838 float pointsBeforeFiltering = (
float)pclScan->size();
840 float ratio =
float(pclScan->size()) / pointsBeforeFiltering;
841 maxLaserScans =
int(
float(maxLaserScans) *
ratio);
843 if(scanNormalK_ > 0 || scanNormalRadius_>0.0
f)
846 pcl::PointCloud<pcl::Normal>::Ptr normals = is3D?
849 pcl::PointCloud<pcl::PointNormal>::Ptr pclScanNormal(
new pcl::PointCloud<pcl::PointNormal>);
850 pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
864 if(scanRangeMin_ > 0 || scanRangeMax_ > 0)
881 this->processData(
data, cloudMsg->header);
892 if(filtered_scan_pub_.getNumSubscribers())
894 sensor_msgs::PointCloud2
msg;
897 filtered_scan_pub_.publish(
msg);
916 std::vector<boost::shared_ptr<rtabmap_odom::PluginInterface> >
plugins_;
918 bool scanReceived_ =
false;
919 bool cloudReceived_ =
false;