HDL_graph_slam 点云预处理 prefiltering_nodelete
收到点云原始数据后第一步是对点云进行预处理,降低样本数量加快计算速度。hdl这里用到的主要有体素降采样,离群点移除(可选择使用半径或者统计学离群点移除法),还有一个简单的最大距离滤波。算法基本构建于pcl库,本篇分析一下预处理节点源码。
- 整体结构,变量,函数
#include <string>
#include <ros/ros.h>
#include <ros/time.h>
#include <pcl_ros/transforms.h>
#include <pcl_ros/point_cloud.h>
#include <tf/transform_listener.h>
#include <sensor_msgs/Imu.h>
#include <sensor_msgs/PointCloud2.h>
#include <nodelet/nodelet.h>
#include <pluginlib/class_list_macros.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/filters/passthrough.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/approximate_voxel_grid.h>
#include <pcl/filters/radius_outlier_removal.h>
#include <pcl/filters/statistical_outlier_removal.h>
namespace hdl_graph_slam {
class PrefilteringNodelet: public:: Nodelet{ //预处理类继承nodelet类
public: //公有成员
typedef pcl::PointXYZI PointT; //数据类型定义
PrefilteringNodelet() {} //构造函数
virtual ~PrefilteringNodelet() {} //虚析构函数
virtual void onInit() {} //虚初始化函数
private: //私有成员
ros::NodeHandle nh; //ROS公共句柄
ros::NodeHandle private_nh; //ROS私有句柄
ros::Subscriber imu_sub; //imu消息订阅器
std::vector<sensor_msgs::ImuConstPtr> imu_queue; //IMU队列向量
ros::Subscriber points_sub; //点云消息订阅器
ros::Publisher points_pub; //点云消息发布器
ros::Publisher colored_pub; //色彩消息发布器
tf::TransformListener tf_listener; //tf监听器
std::string base_link_frame; //baselink坐标系名称
bool use_distance_filter; //是否使用距离滤波
double distance_near_thresh; //距离滤波近点阈值
double distance_far_thresh; //距离滤波远点阈值
pcl::Filter<PointT>::Ptr downsample_filter; //降采样滤波器
pcl::Filter<PointT>::Ptr outlier_removal_filter; //离群点去除滤波器
void initialize_params(){} //参数初始化函数
void imu_callback(pcl::PointCloud<PointT>::ConstPtr src_cloud) //imu消息回调函数
void cloud_callback(pcl::PointCloud<PointT>::ConstPtr src_cloud){} //点云消息回调函数
pcl::PointCloud<PointT>::ConstPtr downsample(const pcl::PointCloud<PointT>::ConstPtr& cloud) const{} //降采样函数
pcl::PointCloud<PointT>::ConstPtr outlier_removal(const pcl::PointCloud<PointT>::ConstPtr& cloud) const{} //离群点去除函数
pcl::PointCloud<PointT>::ConstPtr distance_filter(const pcl::PointCloud<PointT>::ConstPtr& cloud) const{} //距离滤波器函数
pcl::PointCloud<PointT>::ConstPtr deskewing(const pcl::PointCloud<PointT>::ConstPtr& cloud) {} //点云去畸变函数
}
}
- 参数初始化 initialize_params
void initialize_params() {
std::string downsample_method = private_nh.param<std::string>("downsample_method", "VOXELGRID"); //ros借口读取降采样方法参数
double downsample_resolution = private_nh.param<double>("downsample_resolution", 0.1); //ros借口读取降采样方法分辨率(leafsize)
if(downsample_method == "VOXELGRID") { //体素降采样法具体设置
std::cout << "downsample: VOXELGRID " << downsample_resolution << std::endl;
// create a shared pointer filter using pcl voxel grid
boost::shared_ptr<pcl::VoxelGrid<PointT>> voxelgrid(new pcl::VoxelGrid<PointT>()); //新建体素滤波器对象
voxelgrid->setLeafSize(downsample_resolution, downsample_resolution, downsample_resolution); //设置体素滤波器分辨率
downsample_filter = voxelgrid; //命名滤波器
} else if(downsample_method == "APPROX_VOXELGRID") { //设置为近似体素滤波器
std::cout << "downsample: APPROX_VOXELGRID " << downsample_resolution << std::endl;
boost::shared_ptr<pcl::ApproximateVoxelGrid<PointT>> approx_voxelgrid(new pcl::ApproximateVoxelGrid<PointT>());
approx_voxelgrid->setLeafSize(downsample_resolution, downsample_resolution, downsample_resolution);
downsample_filter = approx_voxelgrid;
} else {
if(downsample_method != "NONE") {
std::cerr << "warning: unknown downsampling type (" << downsample_method << ")" << std::endl;
std::cerr << " : use passthrough filter" << std::endl;
}
std::cout << "downsample: NONE" << std::endl;
}
std::string outlier_removal_method = private_nh.param<std::string>("outlier_removal_method", "STATISTICAL"); //离群点滤波器方法参数读取
if(outlier_removal_method == "STATISTICAL") {
int mean_k = private_nh.param<int>("statistical_mean_k", 20); //读取统计滤波k均值
double stddev_mul_thresh = private_nh.param<double>("statistical_stddev", 1.0); //读取标准差
std::cout << "outlier_removal: STATISTICAL " << mean_k << " - " << stddev_mul_thresh << std::endl;
pcl::StatisticalOutlierRemoval<PointT>::Ptr sor(new pcl::StatisticalOutlierRemoval<PointT>()); //新建滤波器对象
sor->setMeanK(mean_k); //设置均值
sor->setStddevMulThresh(stddev_mul_thresh); //设置标准差
outlier_removal_filter = sor; //命名滤波器
} else if(outlier_removal_method == "RADIUS") { //设置半径离群点滤波器
double radius = private_nh.param<double>("radius_radius", 0.8);
int min_neighbors = private_nh.param<int>("radius_min_neighbors", 2);
std::cout << "outlier_removal: RADIUS " << radius << " - " << min_neighbors << std::endl;
pcl::RadiusOutlierRemoval<PointT>::Ptr rad(new pcl::RadiusOutlierRemoval<PointT>());
rad->setRadiusSearch(radius);
rad->setMinNeighborsInRadius(min_neighbors);
outlier_removal_filter = rad;
} else {
std::cout << "outlier_removal: NONE" << std::endl;
}
use_distance_filter = private_nh.param<bool>("use_distance_filter", true);
distance_near_thresh = private_nh.param<double>("distance_near_thresh", 1.0);
distance_far_thresh = private_nh.param<double>("distance_far_thresh", 100.0);
base_link_frame = private_nh.param<std::string>("base_link_frame", "");
}
-
点云回调函数 cloud_callback
void cloud_callback(pcl::PointCloud<PointT>::ConstPtr src_cloud) { //输入原始点云指针 if(src_cloud->empty()) { //判断点云不为空 return; } src_cloud = deskewing(src_cloud); //点云去畸变 // if base_link_frame is defined, transform the input cloud to the frame if(!base_link_frame.empty()) { //判断并转换点云到baselink坐标系 if(!tf_listener.canTransform(base_link_frame, src_cloud->header.frame_id, ros::Time(0))) { std::cerr << "failed to find transform between " << base_link_frame << " and " << src_cloud->header.frame_id << std::endl; } tf::StampedTransform transform; tf_listener.waitForTransform(base_link_frame, src_cloud->header.frame_id, ros::Time(0), ros::Duration(2.0)); tf_listener.lookupTransform(base_link_frame, src_cloud->header.frame_id, ros::Time(0), transform); pcl::PointCloud<PointT>::Ptr transformed(new pcl::PointCloud<PointT>()); pcl_ros::transformPointCloud(*src_cloud, *transformed, transform); transformed->header.frame_id = base_link_frame; transformed->header.stamp = src_cloud->header.stamp; src_cloud = transformed; } pcl::PointCloud<PointT>::ConstPtr filtered = distance_filter(src_cloud); //应用距离滤波函数 filtered = downsample(filtered); //应用降采样函数降采样 filtered = outlier_removal(filtered); //应用离群点函数去除离群点 points_pub.publish(filtered); //发布预处理过后的点云 }
-
降采样函数 downsample
pcl::PointCloud<PointT>::ConstPtr downsample(const pcl::PointCloud<PointT>::ConstPtr& cloud) const { if(!downsample_filter) { //确定降采样滤波器已被设置成功 return cloud; } pcl::PointCloud<PointT>::Ptr filtered(new pcl::PointCloud<PointT>()); downsample_filter->setInputCloud(cloud); //输入点云 downsample_filter->filter(*filtered); //点云滤波 filtered->header = cloud->header; //header复制 return filtered; //返回过滤后的点云 }
-
离群点去除函数 outlier_removal
pcl::PointCloud<PointT>::ConstPtr outlier_removal(const pcl::PointCloud<PointT>::ConstPtr& cloud) const { if(!outlier_removal_filter) { 确定离群点滤波器已被设置成功 return cloud; } pcl::PointCloud<PointT>::Ptr filtered(new pcl::PointCloud<PointT>()); outlier_removal_filter->setInputCloud(cloud); //输入点云 outlier_removal_filter->filter(*filtered); //滤波 filtered->header = cloud->header; return filtered; //返回过滤后的点云 }
-
距离滤波函数 distance_filter
pcl::PointCloud<PointT>::ConstPtr distance_filter(const pcl::PointCloud<PointT>::ConstPtr& cloud) const { pcl::PointCloud<PointT>::Ptr filtered(new pcl::PointCloud<PointT>()); filtered->reserve(cloud->size()); std::copy_if(cloud->begin(), cloud->end(), std::back_inserter(filtered->points), [&](const PointT& p) { double d = p.getVector3fMap().norm(); return d > distance_near_thresh && d < distance_far_thresh; }); //筛选在距离阈值范围内点 filtered->width = filtered->size(); filtered->height = 1; filtered->is_dense = false; filtered->header = cloud->header; return filtered; }
-
去畸变函数 deskewing
pcl::PointCloud<PointT>::ConstPtr deskewing(const pcl::PointCloud<PointT>::ConstPtr& cloud) { ros::Time stamp = pcl_conversions::fromPCL(cloud->header.stamp); //获得点云时间戳数据 if(imu_queue.empty()) { return cloud; //查看队列是否有数据 } // the color encodes the point number in the point sequence if(colored_pub.getNumSubscribers()) { //发布带颜色的点云数据 pcl::PointCloud<pcl::PointXYZRGB>::Ptr colored(new pcl::PointCloud<pcl::PointXYZRGB>()); colored->header = cloud->header; colored->is_dense = cloud->is_dense; colored->width = cloud->width; colored->height = cloud->height; colored->resize(cloud->size()); for(int i = 0; i < cloud->size(); i++) { double t = static_cast<double>(i) / cloud->size(); colored->at(i).getVector4fMap() = cloud->at(i).getVector4fMap(); colored->at(i).r = 255 * t; colored->at(i).g = 128; colored->at(i).b = 255 * (1 - t); } colored_pub.publish(colored); } sensor_msgs::ImuConstPtr imu_msg = imu_queue.front(); auto loc = imu_queue.begin(); for(; loc != imu_queue.end(); loc++) { imu_msg = (*loc); if((*loc)->header.stamp > stamp) { break; } } imu_queue.erase(imu_queue.begin(), loc); Eigen::Vector3f ang_v(imu_msg->angular_velocity.x, imu_msg->angular_velocity.y, imu_msg->angular_velocity.z); //获取imu角速度vector ang_v *= -1; pcl::PointCloud<PointT>::Ptr deskewed(new pcl::PointCloud<PointT>()); //初始化定义的去畸变点云 deskewed->header = cloud->header; deskewed->is_dense = cloud->is_dense; deskewed->width = cloud->width; deskewed->height = cloud->height; deskewed->resize(cloud->size()); double scan_period = private_nh.param<double>("scan_period", 0.1); for(int i = 0; i < cloud->size(); i++) { const auto& pt = cloud->at(i); // TODO: transform IMU data into the LIDAR frame double delta_t = scan_period * static_cast<double>(i) / cloud->size(); Eigen::Quaternionf delta_q(1, delta_t / 2.0 * ang_v[0], delta_t / 2.0 * ang_v[1], delta_t / 2.0 * ang_v[2]); //通过角速度和扫描时间差计算畸变四元数 Eigen::Vector3f pt_ = delta_q.inverse() * pt.getVector3fMap(); //计算校正后的点云 deskewed->at(i) = cloud->at(i); //获得校正后的点云 deskewed->at(i).getVector3fMap() = pt_; // } return deskewed; }