37 #include <sensor_msgs/LaserScan.h> 38 #include <sensor_msgs/PointCloud2.h> 63 scanCloudMaxPoints_(0),
64 scanDownsamplingStep_(1),
69 scanNormalRadius_(0.0),
70 scanNormalGroundUp_(0.0),
71 plugin_loader_(
"rtabmap_ros",
"rtabmap_ros::PluginInterface"),
89 pnh.
param(
"scan_cloud_max_points", scanCloudMaxPoints_, scanCloudMaxPoints_);
90 pnh.
param(
"scan_downsampling_step", scanDownsamplingStep_, scanDownsamplingStep_);
91 pnh.
param(
"scan_range_min", scanRangeMin_, scanRangeMin_);
92 pnh.
param(
"scan_range_max", scanRangeMax_, scanRangeMax_);
93 pnh.
param(
"scan_voxel_size", scanVoxelSize_, scanVoxelSize_);
94 pnh.
param(
"scan_normal_k", scanNormalK_, scanNormalK_);
95 pnh.
param(
"scan_normal_radius", scanNormalRadius_, scanNormalRadius_);
96 pnh.
param(
"scan_normal_ground_up", scanNormalGroundUp_, scanNormalGroundUp_);
101 pnh.
getParam(
"plugins", pluginsList);
105 std::string
pluginName =
static_cast<std::string
>(pluginsList[i][
"name"]);
106 std::string type =
static_cast<std::string
>(pluginsList[i][
"type"]);
107 NODELET_INFO(
"IcpOdometry: Using plugin %s of type \"%s\"", pluginName.c_str(), type.c_str());
110 plugins_.push_back(plugin);
111 plugin->initialize(pluginName, pnh);
112 if(!plugin->isEnabled())
114 NODELET_WARN(
"Plugin: %s is not enabled, filtering will not occur. \"enabled_\" member " 115 "should be managed in subclasses. This can be ignored if the " 116 "plugin should really be initialized as disabled.",
117 plugin->getName().c_str());
121 ROS_ERROR(
"Failed to load plugin %s. Error: %s", pluginName.c_str(), ex.what());
129 ROS_WARN(
"rtabmap: Parameter \"scan_cloud_normal_k\" has been renamed to \"scan_normal_k\". " 130 "The value is still used. Use \"scan_normal_k\" to avoid this warning.");
131 pnh.
param(
"scan_cloud_normal_k", scanNormalK_, scanNormalK_);
134 NODELET_INFO(
"IcpOdometry: scan_cloud_max_points = %d", scanCloudMaxPoints_);
135 NODELET_INFO(
"IcpOdometry: scan_downsampling_step = %d", scanDownsamplingStep_);
136 NODELET_INFO(
"IcpOdometry: scan_range_min = %f m", scanRangeMin_);
137 NODELET_INFO(
"IcpOdometry: scan_range_max = %f m", scanRangeMax_);
138 NODELET_INFO(
"IcpOdometry: scan_voxel_size = %f m", scanVoxelSize_);
139 NODELET_INFO(
"IcpOdometry: scan_normal_k = %d", scanNormalK_);
140 NODELET_INFO(
"IcpOdometry: scan_normal_radius = %f m", scanNormalRadius_);
141 NODELET_INFO(
"IcpOdometry: scan_normal_ground_up = %f", scanNormalGroundUp_);
143 scan_sub_ = nh.
subscribe(
"scan", 1, &ICPOdometry::callbackScan,
this);
144 cloud_sub_ = nh.
subscribe(
"scan_cloud", 1, &ICPOdometry::callbackCloud,
this);
146 filtered_scan_pub_ = nh.
advertise<sensor_msgs::PointCloud2>(
"odom_filtered_input_scan", 1);
152 ParametersMap::iterator iter = parameters.find(Parameters::kRegStrategy());
153 if(iter != parameters.end() && iter->second.compare(
"1") != 0)
155 ROS_WARN(
"ICP odometry works only with \"Reg/Strategy\"=1. Ignoring value %s.", iter->second.c_str());
160 iter = parameters.find(Parameters::kIcpDownsamplingStep());
161 if(iter != parameters.end())
166 if(!pnh.
hasParam(
"scan_downsampling_step"))
168 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());
169 scanDownsamplingStep_ = value;
174 ROS_WARN(
"IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_downsampling_step\" are set.", iter->first.c_str());
178 iter = parameters.find(Parameters::kIcpRangeMin());
179 if(iter != parameters.end())
186 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());
187 scanRangeMin_ = value;
192 ROS_WARN(
"IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_range_min\" are set.", iter->first.c_str());
196 iter = parameters.find(Parameters::kIcpRangeMax());
197 if(iter != parameters.end())
204 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());
205 scanRangeMax_ = value;
210 ROS_WARN(
"IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_range_max\" are set.", iter->first.c_str());
214 iter = parameters.find(Parameters::kIcpVoxelSize());
215 if(iter != parameters.end())
220 if(!pnh.
hasParam(
"scan_voxel_size"))
222 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());
223 scanVoxelSize_ = value;
228 ROS_WARN(
"IcpOdometry: Both parameter \"%s\" and ros parameter \"scan_voxel_size\" are set.", iter->first.c_str());
232 iter = parameters.find(Parameters::kIcpPointToPlaneK());
233 if(iter != parameters.end())
240 ROS_WARN(
"IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_normal_k\" for convenience.", iter->second.c_str(), iter->first.c_str());
241 scanNormalK_ = value;
245 iter = parameters.find(Parameters::kIcpPointToPlaneRadius());
246 if(iter != parameters.end())
251 if(!pnh.
hasParam(
"scan_normal_radius"))
253 ROS_WARN(
"IcpOdometry: Transferring value %s of \"%s\" to ros parameter \"scan_normal_radius\" for convenience.", iter->second.c_str(), iter->first.c_str());
254 scanNormalRadius_ = value;
258 iter = parameters.find(Parameters::kIcpPointToPlaneGroundNormalsUp());
259 if(iter != parameters.end())
264 if(!pnh.
hasParam(
"scan_normal_ground_up"))
266 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());
267 scanNormalGroundUp_ = value;
277 ROS_ERROR(
"%s is already receiving clouds on \"%s\", but also " 278 "just received a scan on \"%s\". Both subscribers cannot be " 279 "used at the same time! Disabling scan subscriber.",
280 this->
getName().c_str(), cloud_sub_.getTopic().c_str(), scan_sub_.getTopic().c_str());
281 scan_sub_.shutdown();
284 scanReceived_ =
true;
292 scanMsg->header.frame_id,
293 scanMsg->header.stamp +
ros::Duration().
fromSec(scanMsg->ranges.size()*scanMsg->time_increment));
294 if(localScanTransform.
isNull())
296 ROS_ERROR(
"TF of received laser scan topic at time %fs is not set, aborting odometry update.", scanMsg->header.stamp.toSec());
301 sensor_msgs::PointCloud2 scanOut;
305 bool hasIntensity =
false;
306 for(
unsigned int i=0; i<scanOut.fields.size(); ++i)
308 if(scanOut.fields[i].name.compare(
"intensity") == 0)
310 if(scanOut.fields[i].datatype == sensor_msgs::PointField::FLOAT32)
316 static bool warningShown =
false;
319 ROS_WARN(
"The input scan cloud has an \"intensity\" field " 320 "but the datatype (%d) is not supported. Intensity will be ignored. " 321 "This message is only shown once.", scanOut.fields[i].datatype);
328 pcl::PointCloud<pcl::PointXYZI>::Ptr pclScanI(
new pcl::PointCloud<pcl::PointXYZI>);
329 pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(
new pcl::PointCloud<pcl::PointXYZ>);
334 pclScanI->is_dense =
true;
339 pclScan->is_dense =
true;
343 int maxLaserScans = (int)scanMsg->ranges.size();
344 if(!pclScan->empty() || !pclScanI->empty())
346 if(scanDownsamplingStep_ > 1)
356 maxLaserScans /= scanDownsamplingStep_;
358 if(scanVoxelSize_ > 0.0
f)
360 float pointsBeforeFiltering;
361 float pointsAfterFiltering;
364 pointsBeforeFiltering = (float)pclScanI->size();
366 pointsAfterFiltering = (float)pclScanI->size();
370 pointsBeforeFiltering = (float)pclScan->size();
372 pointsAfterFiltering = (float)pclScan->size();
374 float ratio = pointsAfterFiltering / pointsBeforeFiltering;
375 maxLaserScans = int(
float(maxLaserScans) * ratio);
377 if(scanNormalK_ > 0 || scanNormalRadius_>0.0
f)
380 pcl::PointCloud<pcl::Normal>::Ptr normals;
381 if(scanVoxelSize_ > 0.0
f)
403 pcl::PointCloud<pcl::PointXYZINormal>::Ptr pclScanINormal;
404 pcl::PointCloud<pcl::PointXYZINormal>::Ptr pclScanNormal;
407 pclScanINormal.reset(
new pcl::PointCloud<pcl::PointXYZINormal>);
408 pcl::concatenateFields(*pclScanI, *normals, *pclScanINormal);
413 pclScanNormal.reset(
new pcl::PointCloud<pcl::PointXYZINormal>);
414 pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
418 if(filtered_scan_pub_.getNumSubscribers())
420 sensor_msgs::PointCloud2 msg;
429 msg.header = scanMsg->header;
430 filtered_scan_pub_.publish(msg);
444 if(filtered_scan_pub_.getNumSubscribers())
446 sensor_msgs::PointCloud2 msg;
455 msg.header = scanMsg->header;
456 filtered_scan_pub_.publish(msg);
462 LaserScan(scan, maxLaserScans, scanMsg->range_max,
473 this->processData(data, scanMsg->header.stamp,
"");
480 ROS_ERROR(
"%s is already receiving scans on \"%s\", but also " 481 "just received a cloud on \"%s\". Both subscribers cannot be " 482 "used at the same time! Disabling cloud subscriber.",
483 this->
getName().c_str(), scan_sub_.getTopic().c_str(), cloud_sub_.getTopic().c_str());
484 cloud_sub_.shutdown();
487 cloudReceived_ =
true;
493 sensor_msgs::PointCloud2 cloudMsg;
494 if (!plugins_.empty())
496 if (plugins_[0]->isEnabled())
498 cloudMsg = plugins_[0]->filterPointCloud(*pointCloudMsg);
502 cloudMsg = *pointCloudMsg;
505 if (plugins_.size() > 1)
507 for (
int i = 1; i < plugins_.size(); i++) {
508 if (plugins_[i]->isEnabled()) {
509 cloudMsg = plugins_[i]->filterPointCloud(cloudMsg);
516 cloudMsg = *pointCloudMsg;
520 bool hasNormals =
false;
521 bool hasIntensity =
false;
522 for(
unsigned int i=0; i<cloudMsg.fields.size(); ++i)
524 if(scanVoxelSize_ == 0.0
f && cloudMsg.fields[i].name.compare(
"normal_x") == 0)
528 if(cloudMsg.fields[i].name.compare(
"intensity") == 0)
530 if(cloudMsg.fields[i].datatype == sensor_msgs::PointField::FLOAT32)
536 static bool warningShown =
false;
539 ROS_WARN(
"The input scan cloud has an \"intensity\" field " 540 "but the datatype (%d) is not supported. Intensity will be ignored. " 541 "This message is only shown once.", cloudMsg.fields[i].datatype);
549 if(localScanTransform.
isNull())
551 ROS_ERROR(
"TF of received scan cloud at time %fs is not set, aborting rtabmap update.", cloudMsg.header.stamp.toSec());
554 if(scanCloudMaxPoints_ == 0 && cloudMsg.height > 1)
556 scanCloudMaxPoints_ = cloudMsg.height * cloudMsg.width;
557 NODELET_WARN(
"IcpOdometry: \"scan_cloud_max_points\" is not set but input " 558 "cloud is not dense, for convenience it will be set to %d (%dx%d)",
559 scanCloudMaxPoints_, cloudMsg.width, cloudMsg.height);
561 else if(cloudMsg.height > 1 && scanCloudMaxPoints_ != cloudMsg.height * cloudMsg.width)
563 NODELET_WARN(
"IcpOdometry: \"scan_cloud_max_points\" is set to %d but input " 564 "cloud is not dense and has a size of %d (%dx%d), setting to this later size.",
565 scanCloudMaxPoints_, cloudMsg.width *cloudMsg.height, cloudMsg.width, cloudMsg.height);
566 scanCloudMaxPoints_ = cloudMsg.width *cloudMsg.height;
568 int maxLaserScans = scanCloudMaxPoints_;
570 if(hasNormals && hasIntensity)
572 pcl::PointCloud<pcl::PointXYZINormal>::Ptr pclScan(
new pcl::PointCloud<pcl::PointXYZINormal>);
574 if(pclScan->size() && scanDownsamplingStep_ > 1)
577 if(pclScan->height>1)
579 maxLaserScans = pclScan->height * pclScan->width;
583 maxLaserScans /= scanDownsamplingStep_;
587 if(filtered_scan_pub_.getNumSubscribers())
589 sensor_msgs::PointCloud2 msg;
591 msg.header = cloudMsg.header;
592 filtered_scan_pub_.publish(msg);
597 pcl::PointCloud<pcl::PointNormal>::Ptr pclScan(
new pcl::PointCloud<pcl::PointNormal>);
599 if(pclScan->size() && scanDownsamplingStep_ > 1)
602 if(pclScan->height>1)
604 maxLaserScans = pclScan->height * pclScan->width;
608 maxLaserScans /= scanDownsamplingStep_;
612 if(filtered_scan_pub_.getNumSubscribers())
614 sensor_msgs::PointCloud2 msg;
616 msg.header = cloudMsg.header;
617 filtered_scan_pub_.publish(msg);
620 else if(hasIntensity)
622 pcl::PointCloud<pcl::PointXYZI>::Ptr pclScan(
new pcl::PointCloud<pcl::PointXYZI>);
624 if(pclScan->size() && scanDownsamplingStep_ > 1)
627 if(pclScan->height>1)
629 maxLaserScans = pclScan->height * pclScan->width;
633 maxLaserScans /= scanDownsamplingStep_;
636 if(!pclScan->is_dense)
643 if(scanVoxelSize_ > 0.0
f)
645 float pointsBeforeFiltering = (float)pclScan->size();
647 float ratio = float(pclScan->size()) / pointsBeforeFiltering;
648 maxLaserScans = int(
float(maxLaserScans) * ratio);
650 if(scanNormalK_ > 0 || scanNormalRadius_>0.0
f)
653 pcl::PointCloud<pcl::Normal>::Ptr normals =
util3d::computeNormals(pclScan, scanNormalK_, scanNormalRadius_);
654 pcl::PointCloud<pcl::PointXYZINormal>::Ptr pclScanNormal(
new pcl::PointCloud<pcl::PointXYZINormal>);
655 pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
658 if(filtered_scan_pub_.getNumSubscribers())
660 sensor_msgs::PointCloud2 msg;
662 msg.header = cloudMsg.header;
663 filtered_scan_pub_.publish(msg);
670 if(filtered_scan_pub_.getNumSubscribers())
672 sensor_msgs::PointCloud2 msg;
674 msg.header = cloudMsg.header;
675 filtered_scan_pub_.publish(msg);
682 pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(
new pcl::PointCloud<pcl::PointXYZ>);
684 if(pclScan->size() && scanDownsamplingStep_ > 1)
687 if(pclScan->height>1)
689 maxLaserScans = pclScan->height * pclScan->width;
693 maxLaserScans /= scanDownsamplingStep_;
696 if(!pclScan->is_dense)
703 if(scanVoxelSize_ > 0.0
f)
705 float pointsBeforeFiltering = (float)pclScan->size();
707 float ratio = float(pclScan->size()) / pointsBeforeFiltering;
708 maxLaserScans = int(
float(maxLaserScans) * ratio);
710 if(scanNormalK_ > 0 || scanNormalRadius_>0.0
f)
713 pcl::PointCloud<pcl::Normal>::Ptr normals =
util3d::computeNormals(pclScan, scanNormalK_, scanNormalRadius_);
714 pcl::PointCloud<pcl::PointNormal>::Ptr pclScanNormal(
new pcl::PointCloud<pcl::PointNormal>);
715 pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
718 if(filtered_scan_pub_.getNumSubscribers())
720 sensor_msgs::PointCloud2 msg;
722 msg.header = cloudMsg.header;
723 filtered_scan_pub_.publish(msg);
730 if(filtered_scan_pub_.getNumSubscribers())
732 sensor_msgs::PointCloud2 msg;
734 msg.header = cloudMsg.header;
735 filtered_scan_pub_.publish(msg);
741 LaserScan laserScan(scan, maxLaserScans, 0,
746 if(scanRangeMin_ > 0 || scanRangeMax_ > 0)
750 if(!laserScan.isEmpty() && laserScan.hasNormals() && !laserScan.is2d() && scanNormalGroundUp_)
763 this->processData(data, cloudMsg.header.
stamp, cloudMsg.header.frame_id);
784 std::vector<boost::shared_ptr<rtabmap_ros::PluginInterface> >
plugins_;
786 bool scanReceived_ =
false;
787 bool cloudReceived_ =
false;
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))
#define NODELET_WARN(...)
int scanDownsamplingStep_
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
double scanNormalGroundUp_
ros::Publisher filtered_scan_pub_
pcl::PointCloud< pcl::PointXYZ >::Ptr RTABMAP_EXP removeNaNFromPointCloud(const pcl::PointCloud< pcl::PointXYZ >::Ptr &cloud)
ros::Subscriber scan_sub_
std::string getName(void *handle)
void callbackCloud(const sensor_msgs::PointCloud2ConstPtr &pointCloudMsg)
void fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud< T > &pcl_cloud)
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))
pluginlib::ClassLoader< rtabmap_ros::PluginInterface > plugin_loader_
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)
Duration & fromSec(double t)
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val) const
cv::Mat RTABMAP_EXP computeNormals(const cv::Mat &laserScan, int searchK, float searchRadius)
QMap< QString, QVariant > ParametersMap
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
virtual void onOdomInit()
#define NODELET_INFO(...)
bool getParam(const std::string &key, std::string &s) const
virtual void updateParameters(ParametersMap ¶meters)
LaserScan RTABMAP_EXP rangeFiltering(const LaserScan &scan, float rangeMin, float rangeMax)
void toROSMsg(const sensor_msgs::PointCloud2 &cloud, sensor_msgs::Image &image)
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
std::vector< boost::shared_ptr< rtabmap_ros::PluginInterface > > plugins_
pcl::PointCloud< pcl::PointXYZ >::Ptr RTABMAP_EXP voxelize(const pcl::PointCloud< pcl::PointXYZ >::Ptr &cloud, const pcl::IndicesPtr &indices, float voxelSize)
void uInsert(std::map< K, V > &map, const std::pair< K, V > &pair)
LaserScan RTABMAP_EXP adjustNormalsToViewPoint(const LaserScan &scan, const Eigen::Vector3f &viewpoint=Eigen::Vector3f(0, 0, 0), float groundNormalsUp=0.0f)
LaserScan RTABMAP_EXP laserScanFromPointCloud(const pcl::PCLPointCloud2 &cloud, bool filterNaNs=true, bool is2D=false, const Transform &transform=Transform())