mirror of
https://github.com/introlab/rtabmap_ros.git
synced 2026-10-09 03:07:45 +08:00
535 lines
19 KiB
C++
535 lines
19 KiB
C++
/*
|
|
Copyright (c) 2010-2019, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the Universite de Sherbrooke nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <rtabmap_util/point_cloud_assembler.hpp>
|
|
|
|
#include <pcl/point_cloud.h>
|
|
#include <pcl/point_types.h>
|
|
#include <pcl_conversions/pcl_conversions.h>
|
|
#include <pcl/io/pcd_io.h>
|
|
#include <pcl/filters/voxel_grid.h>
|
|
#include <pcl/filters/radius_outlier_removal.h>
|
|
|
|
#include <rtabmap_conversions/MsgConversion.h>
|
|
#include <rtabmap_msgs/msg/odom_info.hpp>
|
|
#include <rtabmap/core/util3d.h>
|
|
#include <rtabmap/core/util3d_filtering.h>
|
|
#include <rtabmap/core/Version.h>
|
|
|
|
namespace rtabmap_util
|
|
{
|
|
|
|
PointCloudAssembler::PointCloudAssembler(const rclcpp::NodeOptions & options) :
|
|
Node("point_cloud_assembler", options),
|
|
warningThread_(0),
|
|
callbackCalled_(false),
|
|
exactSync_(0),
|
|
exactInfoSync_(0),
|
|
maxClouds_(0),
|
|
skipClouds_(0),
|
|
cloudsSkipped_(0),
|
|
circularBuffer_(false),
|
|
linearUpdate_(0),
|
|
angularUpdate_(0),
|
|
assemblingTime_(0),
|
|
waitForTransform_(0.1),
|
|
rangeMin_(0),
|
|
rangeMax_(0),
|
|
voxelSize_(0),
|
|
noiseRadius_(0),
|
|
noiseMinNeighbors_(5),
|
|
removeZ_(false),
|
|
fixedFrameId_("odom"),
|
|
frameId_("")
|
|
{
|
|
tfBuffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
|
|
//auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
|
|
// this->get_node_base_interface(),
|
|
// this->get_node_timers_interface());
|
|
//tfBuffer_->setCreateTimerInterface(timer_interface);
|
|
tfListener_ = std::make_shared<tf2_ros::TransformListener>(*tfBuffer_);
|
|
|
|
int topicQueueSize = 10;
|
|
int syncQueueSize = 10;
|
|
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
|
bool subscribeOdomInfo = false;
|
|
|
|
topicQueueSize = this->declare_parameter("topic_queue_size", topicQueueSize);
|
|
int queueSize = this->declare_parameter("queue_size", -1);
|
|
if(queueSize != -1)
|
|
{
|
|
syncQueueSize = queueSize;
|
|
RCLCPP_WARN(this->get_logger(), "Parameter \"queue_size\" has been renamed "
|
|
"to \"sync_queue_size\" and will be removed "
|
|
"in future versions! The value (%d) is copied to "
|
|
"\"sync_queue_size\".", syncQueueSize);
|
|
}
|
|
syncQueueSize = this->declare_parameter("sync_queue_size", syncQueueSize);
|
|
qos = this->declare_parameter("qos", qos);
|
|
int qosOdom = this->declare_parameter("qos_odom", qos);
|
|
fixedFrameId_ = this->declare_parameter("fixed_frame_id", fixedFrameId_);
|
|
frameId_ = this->declare_parameter("frame_id", frameId_);
|
|
maxClouds_ = this->declare_parameter("max_clouds", maxClouds_);
|
|
assemblingTime_ = this->declare_parameter("assembling_time", assemblingTime_);
|
|
skipClouds_ = this->declare_parameter("skip_clouds", skipClouds_);
|
|
circularBuffer_ = this->declare_parameter("circular_buffer", circularBuffer_);
|
|
linearUpdate_ = this->declare_parameter("linear_update", linearUpdate_);
|
|
angularUpdate_ = this->declare_parameter("angular_update", angularUpdate_);
|
|
waitForTransform_ = this->declare_parameter("wait_for_transform", waitForTransform_);
|
|
rangeMin_ = this->declare_parameter("range_min", rangeMin_);
|
|
rangeMax_ = this->declare_parameter("range_max", rangeMax_);
|
|
voxelSize_ = this->declare_parameter("voxel_size", voxelSize_);
|
|
noiseRadius_ = this->declare_parameter("noise_radius", noiseRadius_);
|
|
noiseMinNeighbors_ = this->declare_parameter("noise_min_neighbors", noiseMinNeighbors_);
|
|
removeZ_ = this->declare_parameter("remove_z", removeZ_);
|
|
subscribeOdomInfo = this->declare_parameter("subscribe_odom_info", subscribeOdomInfo);
|
|
|
|
RCLCPP_INFO(this->get_logger(), "%s: topic_queue_size=%d", get_name(), topicQueueSize);
|
|
RCLCPP_INFO(this->get_logger(), "%s: sync_queue_size=%d", get_name(), syncQueueSize);
|
|
RCLCPP_INFO(this->get_logger(), "%s: qos=%d", get_name(), qos);
|
|
RCLCPP_INFO(this->get_logger(), "%s: qos_odom=%d", get_name(), qosOdom);
|
|
RCLCPP_INFO(this->get_logger(), "%s: fixed_frame_id=%s", get_name(), fixedFrameId_.c_str());
|
|
RCLCPP_INFO(this->get_logger(), "%s: frame_id=%s", get_name(), frameId_.c_str());
|
|
RCLCPP_INFO(this->get_logger(), "%s: max_clouds=%d", get_name(), maxClouds_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: assembling_time=%fs", get_name(), assemblingTime_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: skip_clouds=%d", get_name(), skipClouds_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: circular_buffer=%s", get_name(), circularBuffer_?"true":"false");
|
|
RCLCPP_INFO(this->get_logger(), "%s: linear_update=%f m", get_name(), linearUpdate_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: angular_update=%f rad", get_name(), angularUpdate_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: wait_for_transform=%f", get_name(), waitForTransform_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: range_min=%f", get_name(), rangeMin_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: range_max=%f", get_name(), rangeMax_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: voxel_size=%fm", get_name(), voxelSize_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: noise_radius=%fm", get_name(), noiseRadius_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: noise_min_neighbors=%d", get_name(), noiseMinNeighbors_);
|
|
RCLCPP_INFO(this->get_logger(), "%s: remove_z=%s", get_name(), removeZ_?"true":"false");
|
|
|
|
if(maxClouds_==0 && assemblingTime_ ==0.0)
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "point_cloud_assembler: max_clouds or assembling_time parameters should be set!");
|
|
exit(-1);
|
|
}
|
|
|
|
cloudsSkipped_ = skipClouds_;
|
|
|
|
cloudPub_ = create_publisher<sensor_msgs::msg::PointCloud2>("assembled_cloud", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
|
|
|
if(!fixedFrameId_.empty())
|
|
{
|
|
cloudSub_ = create_subscription<sensor_msgs::msg::PointCloud2>("cloud", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&PointCloudAssembler::callbackCloud, this, std::placeholders::_1));
|
|
subscribedTopicsMsg_ = uFormat("\n%s subscribed to %s",
|
|
get_name(),
|
|
cloudSub_->get_topic_name());
|
|
}
|
|
else if(subscribeOdomInfo)
|
|
{
|
|
syncCloudSub_.subscribe(this, "cloud", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
|
syncOdomSub_.subscribe(this, "odom", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosOdom).get_rmw_qos_profile());
|
|
syncOdomInfoSub_.subscribe(this, "odom_info", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosOdom).get_rmw_qos_profile());
|
|
exactInfoSync_ = new message_filters::Synchronizer<syncInfoPolicy>(syncInfoPolicy(syncQueueSize), syncCloudSub_, syncOdomSub_, syncOdomInfoSub_);
|
|
exactInfoSync_->registerCallback(std::bind(&rtabmap_util::PointCloudAssembler::callbackCloudOdomInfo, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
subscribedTopicsMsg_ = uFormat("\n%s subscribed to (exact sync):\n %s,\n %s",
|
|
get_name(),
|
|
syncCloudSub_.getSubscriber()->get_topic_name(),
|
|
syncOdomSub_.getSubscriber()->get_topic_name(),
|
|
syncOdomInfoSub_.getSubscriber()->get_topic_name());
|
|
}
|
|
else
|
|
{
|
|
syncCloudSub_.subscribe(this, "cloud", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
|
syncOdomSub_.subscribe(this, "odom", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosOdom).get_rmw_qos_profile());
|
|
exactSync_ = new message_filters::Synchronizer<syncPolicy>(syncPolicy(syncQueueSize), syncCloudSub_, syncOdomSub_);
|
|
exactSync_->registerCallback(std::bind(&rtabmap_util::PointCloudAssembler::callbackCloudOdom, this, std::placeholders::_1, std::placeholders::_2));
|
|
subscribedTopicsMsg_ = uFormat("\n%s subscribed to (exact sync):\n %s,\n %s",
|
|
get_name(),
|
|
syncCloudSub_.getSubscriber()->get_topic_name(),
|
|
syncOdomSub_.getSubscriber()->get_topic_name());
|
|
}
|
|
|
|
warningThread_ = new std::thread([&](){
|
|
rclcpp::Rate r(1.0/5.0);
|
|
while(!callbackCalled_)
|
|
{
|
|
r.sleep();
|
|
if(!callbackCalled_)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(),
|
|
"%s: Did not receive data since 5 seconds! Make sure the input topics are "
|
|
"published (\"$ ros2 topic hz my_topic\") and the timestamps in their "
|
|
"header are set. %s",
|
|
get_name(),
|
|
subscribedTopicsMsg_.c_str());
|
|
}
|
|
}
|
|
});
|
|
|
|
RCLCPP_INFO(this->get_logger(), "%s", subscribedTopicsMsg_.c_str());
|
|
}
|
|
|
|
PointCloudAssembler::~PointCloudAssembler()
|
|
{
|
|
delete exactSync_;
|
|
delete exactInfoSync_;
|
|
|
|
if(warningThread_)
|
|
{
|
|
callbackCalled_=true;
|
|
warningThread_->join();
|
|
delete warningThread_;
|
|
}
|
|
}
|
|
|
|
void PointCloudAssembler::callbackCloudOdom(
|
|
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg,
|
|
const nav_msgs::msg::Odometry::ConstSharedPtr odomMsg)
|
|
{
|
|
callbackCalled_ = true;
|
|
rtabmap::Transform odom = rtabmap_conversions::transformFromPoseMsg(odomMsg->pose.pose);
|
|
if(!odom.isNull())
|
|
{
|
|
fixedFrameId_ = odomMsg->header.frame_id;
|
|
callbackCloud(cloudMsg);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Reseting point cloud assembler as null odometry has been received.");
|
|
clouds_.clear();
|
|
}
|
|
}
|
|
|
|
sensor_msgs::msg::PointCloud2 removeField(const sensor_msgs::msg::PointCloud2 & input, const std::string & field)
|
|
{
|
|
sensor_msgs::msg::PointCloud2 output;
|
|
int offset = 0;
|
|
std::vector<int> inputFieldIndex;
|
|
for(size_t i=0; i<input.fields.size(); ++i)
|
|
{
|
|
if(input.fields[i].name.compare(field) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
sensor_msgs::msg::PointField outputField = input.fields[i];
|
|
outputField.offset = offset;
|
|
offset += outputField.count * rtabmap_conversions::sizeOfPointField(outputField.datatype);
|
|
output.fields.push_back(outputField);
|
|
inputFieldIndex.push_back(i);
|
|
}
|
|
}
|
|
output.header = input.header;
|
|
output.height = input.height;
|
|
output.width = input.width;
|
|
output.is_bigendian = input.is_bigendian;
|
|
output.is_dense = input.is_dense;
|
|
output.point_step = offset;
|
|
output.row_step = output.width * output.point_step;
|
|
output.data.resize(output.height*output.row_step);
|
|
int total = output.height*output.width;
|
|
for(int i=0; i<total; ++i)
|
|
{
|
|
// for each point, copy fields
|
|
int oi = i*output.point_step;
|
|
int pi = i*input.point_step;
|
|
for(size_t j=0;j<output.fields.size(); ++j)
|
|
{
|
|
memcpy(&output.data[oi + output.fields[j].offset],
|
|
&input.data[pi + input.fields[inputFieldIndex[j]].offset],
|
|
output.fields[j].count * rtabmap_conversions::sizeOfPointField(output.fields[j].datatype));
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
void PointCloudAssembler::callbackCloudOdomInfo(
|
|
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg,
|
|
const nav_msgs::msg::Odometry::ConstSharedPtr odomMsg,
|
|
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr odomInfoMsg)
|
|
{
|
|
callbackCalled_ = true;
|
|
rtabmap::Transform odom = rtabmap_conversions::transformFromPoseMsg(odomMsg->pose.pose);
|
|
if(!odom.isNull())
|
|
{
|
|
if(odomInfoMsg->key_frame_added)
|
|
{
|
|
fixedFrameId_ = odomMsg->header.frame_id;
|
|
callbackCloud(cloudMsg);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Skipping non keyframe...");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Resetting point cloud assembler as null odometry has been received.");
|
|
clouds_.clear();
|
|
}
|
|
}
|
|
|
|
void PointCloudAssembler::callbackCloud(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg)
|
|
{
|
|
callbackCalled_ = true;
|
|
if(cloudPub_->get_subscription_count())
|
|
{
|
|
UASSERT_MSG(cloudMsg->data.size() == cloudMsg->row_step*cloudMsg->height,
|
|
uFormat("data=%d row_step=%d height=%d", cloudMsg->data.size(), cloudMsg->row_step, cloudMsg->height).c_str());
|
|
|
|
if(skipClouds_<=0 || cloudsSkipped_ >= skipClouds_)
|
|
{
|
|
cloudsSkipped_ = 0;
|
|
|
|
rtabmap::Transform pose = rtabmap_conversions::getTransform(
|
|
fixedFrameId_, //fromFrame
|
|
cloudMsg->header.frame_id, //toFrame
|
|
cloudMsg->header.stamp,
|
|
*tfBuffer_,
|
|
waitForTransform_);
|
|
|
|
if(pose.isNull())
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "Cloud not transform all clouds! Resetting...");
|
|
clouds_.clear();
|
|
return;
|
|
}
|
|
|
|
bool isMoving = true;
|
|
if(!previousPose_.isNull() && (linearUpdate_>0 || angularUpdate_>0))
|
|
{
|
|
rtabmap::Transform delta = previousPose_.inverse()*pose;
|
|
float roll, pitch, yaw;
|
|
delta.getEulerAngles(roll, pitch, yaw);
|
|
isMoving = fabs(delta.x()) > linearUpdate_ ||
|
|
fabs(delta.y()) > linearUpdate_ ||
|
|
fabs(delta.z()) > linearUpdate_ ||
|
|
(angularUpdate_>0.0f && (
|
|
fabs(roll) > angularUpdate_ ||
|
|
fabs(pitch) > angularUpdate_ ||
|
|
fabs(yaw) > angularUpdate_));
|
|
}
|
|
|
|
pcl::PCLPointCloud2::Ptr newCloud(new pcl::PCLPointCloud2);
|
|
if(rangeMin_ > 0.0 || rangeMax_ > 0.0 || voxelSize_ > 0.0f)
|
|
{
|
|
pcl_conversions::toPCL(*cloudMsg, *newCloud);
|
|
rtabmap::LaserScan scan = rtabmap::util3d::laserScanFromPointCloud(*newCloud);
|
|
scan = rtabmap::util3d::commonFiltering(scan, 1, rangeMin_, rangeMax_, voxelSize_);
|
|
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
|
std::uint64_t stamp = newCloud->header.stamp;
|
|
#else
|
|
pcl::uint64_t stamp = newCloud->header.stamp;
|
|
#endif
|
|
newCloud = rtabmap::util3d::laserScanToPointCloud2(scan, pose);
|
|
newCloud->header.stamp = stamp;
|
|
}
|
|
else
|
|
{
|
|
sensor_msgs::msg::PointCloud2 output;
|
|
rtabmap_conversions::transformPointCloud(pose.toEigen4f(), *cloudMsg, output);
|
|
pcl_conversions::toPCL(output, *newCloud);
|
|
}
|
|
|
|
if(!newCloud->is_dense)
|
|
{
|
|
// remove nans
|
|
newCloud = rtabmap::util3d::removeNaNFromPointCloud(newCloud);
|
|
}
|
|
|
|
clouds_.push_back(newCloud);
|
|
|
|
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
|
bool reachedMaxSize =
|
|
((int)clouds_.size() >= maxClouds_ && maxClouds_ > 0)
|
|
||
|
|
((*newCloud).header.stamp >= clouds_.front()->header.stamp + static_cast<std::uint64_t>(assemblingTime_*1000000.0) && assemblingTime_ > 0.0);
|
|
#else
|
|
bool reachedMaxSize =
|
|
((int)clouds_.size() >= maxClouds_ && maxClouds_ > 0)
|
|
||
|
|
((*newCloud).header.stamp >= clouds_.front()->header.stamp + static_cast<pcl::uint64_t>(assemblingTime_*1000000.0) && assemblingTime_ > 0.0);
|
|
#endif
|
|
|
|
if( circularBuffer_ || reachedMaxSize )
|
|
{
|
|
pcl::PCLPointCloud2Ptr assembled(new pcl::PCLPointCloud2);
|
|
for(std::list<pcl::PCLPointCloud2::Ptr>::iterator iter=clouds_.begin(); iter!=clouds_.end(); ++iter)
|
|
{
|
|
if(assembled->data.empty())
|
|
{
|
|
*assembled = *(*iter);
|
|
}
|
|
else
|
|
{
|
|
pcl::PCLPointCloud2Ptr assembledTmp(new pcl::PCLPointCloud2);
|
|
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
|
pcl::concatenate(*assembled, *(*iter), *assembledTmp);
|
|
#else
|
|
pcl::concatenatePointCloud(*assembled, *(*iter), *assembledTmp);
|
|
#endif
|
|
//Make sure row_step is the sum of both
|
|
assembledTmp->row_step = assembled->row_step + (*iter)->row_step;
|
|
assembled = assembledTmp;
|
|
}
|
|
}
|
|
|
|
sensor_msgs::msg::PointCloud2 rosCloud;
|
|
if(voxelSize_>0.0)
|
|
{
|
|
// estimate if there would be an overflow
|
|
int x_idx=-1, y_idx=-1, z_idx=-1;
|
|
for (std::size_t d = 0; d < assembled->fields.size (); ++d)
|
|
{
|
|
if (assembled->fields[d].name.compare("x")==0)
|
|
x_idx = d;
|
|
if (assembled->fields[d].name.compare("y")==0)
|
|
y_idx = d;
|
|
if (assembled->fields[d].name.compare("z")==0)
|
|
z_idx = d;
|
|
}
|
|
bool overflow = false;
|
|
if(x_idx>=0 && y_idx>=0 && z_idx>=0) {
|
|
Eigen::Vector4f min_p, max_p;
|
|
pcl::getMinMax3D(assembled, x_idx, y_idx, z_idx, min_p, max_p);
|
|
float inverseVoxelSize = 1.0f/voxelSize_;
|
|
std::int64_t dx = static_cast<std::int64_t>((max_p[0] - min_p[0]) * inverseVoxelSize)+1;
|
|
std::int64_t dy = static_cast<std::int64_t>((max_p[1] - min_p[1]) * inverseVoxelSize)+1;
|
|
std::int64_t dz = static_cast<std::int64_t>((max_p[2] - min_p[2]) * inverseVoxelSize)+1;
|
|
|
|
if ((dx*dy*dz) > static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()))
|
|
{
|
|
overflow = true;
|
|
}
|
|
}
|
|
if(overflow)
|
|
{
|
|
rtabmap::LaserScan scan = rtabmap::util3d::laserScanFromPointCloud(*assembled);
|
|
scan = rtabmap::util3d::commonFiltering(scan, 1, 0, 0, voxelSize_);
|
|
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
|
std::uint64_t stamp = assembled->header.stamp;
|
|
#else
|
|
pcl::uint64_t stamp = assembled->header.stamp;
|
|
#endif
|
|
assembled = rtabmap::util3d::laserScanToPointCloud2(scan);
|
|
assembled->header.stamp = stamp;
|
|
}
|
|
else
|
|
{
|
|
pcl::VoxelGrid<pcl::PCLPointCloud2> filter;
|
|
filter.setLeafSize(voxelSize_, voxelSize_, voxelSize_);
|
|
filter.setInputCloud(assembled);
|
|
pcl::PCLPointCloud2Ptr output(new pcl::PCLPointCloud2);
|
|
filter.filter(*output);
|
|
assembled = output;
|
|
}
|
|
}
|
|
if(noiseRadius_>0.0 && noiseMinNeighbors_>0)
|
|
{
|
|
pcl::RadiusOutlierRemoval<pcl::PCLPointCloud2> filter;
|
|
filter.setRadiusSearch(noiseRadius_);
|
|
filter.setMinNeighborsInRadius(noiseMinNeighbors_);
|
|
filter.setInputCloud(assembled);
|
|
pcl::PCLPointCloud2Ptr output(new pcl::PCLPointCloud2);
|
|
filter.filter(*output);
|
|
assembled = output;
|
|
}
|
|
pcl_conversions::moveFromPCL(*assembled, rosCloud);
|
|
rtabmap::Transform t = pose;
|
|
if(!frameId_.empty())
|
|
{
|
|
// transform in target frame_id instead of sensor frame
|
|
t = rtabmap_conversions::getTransform(
|
|
fixedFrameId_, //fromFrame
|
|
frameId_, //toFrame
|
|
cloudMsg->header.stamp,
|
|
*tfBuffer_,
|
|
waitForTransform_);
|
|
if(t.isNull())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Cloud not transform back assembled clouds in target frame \"%s\"! Resetting...", frameId_.c_str());
|
|
clouds_.clear();
|
|
return;
|
|
}
|
|
}
|
|
rtabmap_conversions::transformPointCloud(t.toEigen4f().inverse(), rosCloud, rosCloud);
|
|
|
|
if(removeZ_)
|
|
{
|
|
rosCloud = removeField(rosCloud, "z");
|
|
}
|
|
|
|
rosCloud.header = cloudMsg->header;
|
|
if(!frameId_.empty())
|
|
{
|
|
rosCloud.header.frame_id = frameId_;
|
|
}
|
|
cloudPub_->publish(rosCloud);
|
|
if(circularBuffer_)
|
|
{
|
|
if(!isMoving)
|
|
{
|
|
clouds_.pop_back();
|
|
}
|
|
else
|
|
{
|
|
previousPose_ = pose;
|
|
if(reachedMaxSize)
|
|
{
|
|
clouds_.pop_front();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
clouds_.clear();
|
|
previousPose_.setNull();
|
|
}
|
|
}
|
|
else if(!isMoving)
|
|
{
|
|
clouds_.pop_back();
|
|
}
|
|
else
|
|
{
|
|
previousPose_ = pose;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++cloudsSkipped_;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#include "rclcpp_components/register_node_macro.hpp"
|
|
|
|
// Register the component with class_loader.
|
|
// This acts as a sort of entry point, allowing the component to be discoverable when its library
|
|
// is being loaded into a running process.
|
|
RCLCPP_COMPONENTS_REGISTER_NODE(rtabmap_util::PointCloudAssembler)
|