赞
踩
1 PassThrough 直通滤波
官方教程:Filtering a PointCloud using a PassThrough filter
1.class pcl::PassThrough< PointT >
2.class pcl::PassThrough< pcl::PCLPointCloud2 >
pcl::PassThrough<pcl::PointXYZ> pass; // 创建直通滤波器对象
pass.setInputCloud (cloud); // 输入点云
pass.setFilterFieldName ("z"); // 设置过滤字段
pass.setFilterLimits (0.0, 1.0); // 设置范围
//pass.setFilterLimitsNegative (true); // 设置字段范围内的是保留(false)还是过滤掉(true)
pass.filter (*cloud_filtered); // 执行滤波,并存储结果
2.VoxelGrid 点云下采样
官方教程:Downsampling a PointCloud using a VoxelGrid filter
1.class pcl::ApproximateVoxelGrid< PointT >
2.class pcl::VoxelGrid< PointT >
3.class pcl::VoxelGrid< pcl::PCLPointCloud2 >
VoxelGrid类根据 输入的点云数据创建一个三维体素栅格,然后将每个体素内所有的点都用该体素内的点集的重心(centroid)来近似;ApproximateVoxelGrid类与VoxelGrid类基本相同,唯一不同在于其利用每一个体素的中心(center)来近似该体素内的点,相比于 VoxelGrid,计算速度稍快,但也损失了原始点云局部形态的精细度。
// 创建对象
pcl::VoxelGrid<pcl::PointXYZ> voxel;
// 输入点云
voxel.setInputCloud (cloud);
// 设置体素大小
voxel.setLeafSize (0.01f, 0.01f, 0.01f);
// 输出点云
voxel.filter (*cloud_filtered);
关键成员函数:
// 通过向量设置体素栅格 leaf_size
void setLeafSize (const Eigen::Vector4f &leaf_size)
// 通过 lx, ly, lz 分别设置体素栅格在 XYZ 3个方向上的尺寸
void setLeafSize (float lx, float ly, float lz)
// 设置是否对全部字段进行下采样
// 若需要在全部字段(包括颜色、强度等)下采样则设置参数 downsample 为 True
// 仅对 XYZ 字段下采样则设置为 False
void setDownsampleAllData ( bool downsample)
3. 去除 outliers
(1)统计分析 StatisticalOutlierRemoval
官方教程:Removing outliers using a StatisticalOutlierRemoval filter
class pcl::StatisticalOutlierRemoval< PointT >
class pcl::StatisticalOutlierRemoval< pcl::PCLPointCloud2 >
对每个点的邻域进行统计分析,计算所有邻点的平均距离,假如得到的分布为高斯分布,就可以得到均值和标准差,平均距离在区间 [公式] + std_mul * [公式] 之外的点视为离群点,std_mul为表示标准差倍数的阈值。
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud (cloud);
sor.setMeanK (50); // 设置临近点个数
sor.setStddevMulThresh (1.0); // 设置阈值,判断是否为离群点
sor.filter (*cloud_filtered);
结果:右图中红色与绿色分别表示滤波前后 mean k-nearest neighbor distances,可见离群点(outliers)变少。
(2)半径范围 RadiusOutlierRemoval
官方教程:Removing outliers using a Conditional or RadiusOutlier removal
class pcl::RadiusOutlierRemoval< PointT >
class pcl::RadiusOutlierRemoval< pcl::PCLPointCloud2 >
通过指定空间半径范围内的邻点数量来判定是否为离群点,如下图所示,若设定搜索半径为 d,最少的邻点数量为 1,则黄点将被视为离群点,若最少的邻点数量为2,则黄点和绿点都被视为离群点。
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
outrem.setInputCloud(cloud);
outrem.setRadiusSearch(0.8); // 设置搜索半径
outrem.setMinNeighborsInRadius (2); // 设置最少的邻点数量
outrem.filter (*cloud_filtered);
4. 通过点云索引提取点云子集
官方教程:Extracting indices from a PointCloud
class pcl::ExtractIndices< PointT >
class pcl::ExtractIndices< pcl::PCLPointCloud2 >
// 下采样 pcl::VoxelGrid<pcl::PCLPointCloud2> sor; sor.setInputCloud (cloud_blob); sor.setLeafSize (0.01f, 0.01f, 0.01f); sor.filter (*cloud_filtered_blob); // 参数化分割 pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients ()); pcl::PointIndices::Ptr inliers (new pcl::PointIndices ()); // Create the segmentation object pcl::SACSegmentation<pcl::PointXYZ> seg; // Optional seg.setOptimizeCoefficients (true); // Mandatory seg.setModelType (pcl::SACMODEL_PLANE); seg.setMethodType (pcl::SAC_RANSAC); seg.setMaxIterations (1000); seg.setDistanceThreshold (0.01); // 提取索引 pcl::ExtractIndices<pcl::PointXYZ> extract; // Extract the inliers extract.setInputCloud (cloud_filtered); extract.setIndices (inliers); // 设置分割后的内点为需要提取的点集 extract.setNegative (false); // 设置提取内点 extract.filter (*cloud_p); // 提取并保存至 cloud_p
ROS、PCL点云过滤模块整理
前言
点云滤波模块常见的模块包括:Pass Through直通滤波、VoxelGrid过滤、StatisticalOutlierRemoval过滤、点云投影、提取索引等;今天介绍前三种过滤模块:直通滤波、体素滤波、统计滤波。
直通滤波:快速过滤掉用户自定义区间范围内的点云
体素滤波:在分割、配准前,如果点云数量太多会影响后续时间。此时,需要对点云进行下采样处理,体素滤波为采用体素网格方法采样,减少点云数量。
统计滤波:统计滤波往往去除离群点,利用统计分析技术删除噪声异常值等。
下面对原始点云分别进行三种滤波处理;
一、直通滤波
pcl::PassThrough<pcl::PointXYZ> pass; //创建滤波器
pass.setInputCloud (cloud); //设置点云输入
pass.setFilterFieldName ("z"); //设置滤波的方向,z轴
pass.setFilterLimits (0.0, 1.0); //设置滤波的范围
//pass.setFilterLimitsNegative (true); //设置范围内点云保留还是过滤,默认保留
pass.filter (*cloud_filtered); //执行滤波
/********************************************* *滤波器:直通滤波器 * 功能 :快速去除不在规定区间范围内的点; * 时间 :2019.5.19 * 明天5.20,还在整理代码,单身狗的悲哀; *********************************************/ #include <iostream> #include <pcl/filters/passthrough.h> #include <pcl/point_cloud.h> #include <pcl/point_types.h> #include <pcl/visualization/pcl_visualizer.h> #include <pcl/io/pcd_io.h> using namespace std; int main(int argc, char** argv) { //读取点云 pcl::PCDReader reader; pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>); pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_fz(new pcl::PointCloud<pcl::PointXYZRGB>); pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_fy(new pcl::PointCloud<pcl::PointXYZRGB>); reader.read("E:/code/date/coal_final.pcd", *cloud); //直通滤波 pcl::PassThrough<pcl::PointXYZRGB> pass_z;//设置滤波器对象 //参数设置 pass_z.setInputCloud(cloud); pass_z.setFilterFieldName("z"); //z轴区间设置 pass_z.setFilterLimits(2.3,6); //设置为保留还是去除 pass_z.setFilterLimitsNegative(true); pass_z.filter(*cloud_fz); //直通滤波 pcl::PassThrough<pcl::PointXYZRGB> pass_y;//设置滤波器对象 //参数设置 pass_y.setInputCloud(cloud_fz); pass_y.setFilterFieldName("y"); //z轴区间设置 pass_y.setFilterLimits(-1.5, 0.9); //pass_y.setFilterLimitsNegative(false); pass_y.filter(*cloud_fy); pcl::PCDWriter writer; writer.write("E:/code/date/coal_520.pcd",*cloud_fy); //可视化 pcl::visualization::PCLVisualizer viewer("cloud_viewer"); viewer.setBackgroundColor(255, 255, 255); viewer.addPointCloud(cloud_fy); //添加坐标轴 //viewer.addCoordinateSystem(); while (!viewer.wasStopped()) { viewer.spinOnce(); } return(0); }
可以看出,经过直通滤波后,大量杂乱的点云被直接去除。下面,对处理好的点云进行一次统计滤波。
二、统计滤波
#include <iostream> #include <pcl/filters/statistical_outlier_removal.h>//统计滤波 头文件 #include <pcl/point_cloud.h> #include <pcl/point_types.h> #include <pcl/visualization/pcl_visualizer.h> #include <pcl/io/pcd_io.h> using namespace std; int main(int argc, char** argv) { //读取点云 pcl::PCDReader reader; pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>); pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_f(new pcl::PointCloud<pcl::PointXYZRGB>); reader.read("E:/code/date/coal_520.pcd", *cloud); //统计滤波 pcl::StatisticalOutlierRemoval<pcl::PointXYZRGB> outrem;//创建统计滤波对象 //参数设置 outrem.setInputCloud(cloud); outrem.setMeanK(200);//附近邻近点数 outrem.setStddevMulThresh(1);//判断是否离群点 outrem.filter(*cloud_f); //储存 pcl::PCDWriter writer; writer.write("E:/code/cpp/filter/coal_.pcd", *cloud); //可视化 pcl::visualization::PCLVisualizer viewer("cloud_viewer"); viewer.setBackgroundColor(255, 255, 255); viewer.addPointCloud(cloud_f); //viewer.addCoordinateSystem(); while (!viewer.wasStopped()) { viewer.spinOnce(); } return(0); }
经过统计滤波后,更多离群点或噪声被过滤。下面对点云进行下采样。
三、体素滤波
pcl::VoxelGrid<pcl::PCLPointCloud2> sor; //创建体素滤波器
sor.setInputCloud (cloud); //设置点云输入
sor.setLeafSize (0.01f, 0.01f, 0.01f); //设置滤波的体素大小,0.01m立方体
sor.filter (*cloud_filtered); //执行滤波
#include <iostream> #include <pcl/point_types.h> #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> #include <pcl/filters/voxel_grid.h> #include <pcl/visualization/pcl_visualizer.h> using namespace std; using namespace pcl; int main(int argc, char** argv) { //读取点云 pcl::PCDReader reader; PointCloud<pcl::PointXYZRGB>::Ptr cloud(new PointCloud<pcl::PointXYZRGB>); PointCloud<pcl::PointXYZRGB>::Ptr cloud_f(new PointCloud<pcl::PointXYZRGB>); reader.read("E:/code/cpp/filter/coal_.pcd",*cloud); cout << "before_filter :"<<cloud->points.size()<<endl; //体素滤波 pcl::VoxelGrid<pcl::PointXYZRGB> vox; vox.setInputCloud(cloud); vox.setLeafSize(0.01f,0.01f,0.01f);//体素网格大小 vox.filter(*cloud_f); cout << "after filter :" << cloud_f->points.size()<<endl; //点云可视化 pcl::visualization::PCLVisualizer viewer("cloud_viewer"); viewer.setBackgroundColor(255, 255, 255); viewer.addPointCloud(cloud_f); viewer.addCoordinateSystem(); while (!viewer.wasStopped()) { viewer.spinOnce(); } return(0); }
可见,体素滤波下采样后,点云结构不变,故配准、分割前为了减少时间,往往对点云进行下采样处理。
一、点云投影
#include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/ModelCoefficients.h> #include <pcl/filters/project_inliers.h> #include <pcl/visualization/pcl_visualizer.h> using namespace std; using namespace pcl; int main(int argc, char** argv) { //读取文件 PCDReader reader; PCDWriter writer; PointCloud<pcl::PointXYZRGB>::Ptr cloud(new PointCloud<pcl::PointXYZRGB>); PointCloud<pcl::PointXYZRGB>::Ptr cloud_f(new PointCloud<pcl::PointXYZRGB>); reader.read("E:/code/date/Coal.pcd", *cloud); //我们使用平面模型,其中ax + by + cz + d = 0,其中a = b = d = 0,并且c = 1,或者换句话说,XY平面。 pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients()); coefficients->values.resize(4); coefficients->values[0] = coefficients->values[1] = 0; coefficients->values[2] = 1.0; coefficients->values[3] = 0; //我们创建投影对象,并使用上面定义的模型作为投影模型 pcl::ProjectInliers<pcl::PointXYZRGB> proj; proj.setModelType(pcl::SACMODEL_PLANE); proj.setInputCloud(cloud); proj.setModelCoefficients(coefficients); proj.filter(*cloud_f); //可视化 pcl::visualization::PCLVisualizer viewer("cloud_viewer"); viewer.setBackgroundColor(255, 255, 255); viewer.addPointCloud(cloud_f); //viewer.addCoordinateSystem(); while (!viewer.wasStopped()) { viewer.spinOnce(); } return(0); }
原始点云:
投影后的点云:
【补充】
ROS下的PCL
官方教程:http://wiki.ros.org/pcl/Tutorials#pcl.2BAC8-Tutorials.2BAC8-hydro.Create_a_ROS_package
创建ROS package
cd ~/catkin_ws/src
catkin_create_pkg my_pcl_tutorial pcl_conversions pcl_ros roscpp sensor_msgs
打开package.xml,在文本后添加
<build_depend>libpcl-all-dev</build_depend>
<exec_depend>libpcl-all</exec_depend>
示例程序(新手上路)
这是原始的示例程序:
#include <ros/ros.h> // PCL specific includes #include <sensor_msgs/PointCloud2.h> #include <pcl_conversions/pcl_conversions.h> #include <pcl/point_cloud.h> #include <pcl/point_types.h> ros::Publisher pub; void cloud_cb (const sensor_msgs::PointCloud2ConstPtr& input) { // Create a container for the data. sensor_msgs::PointCloud2 output; // Do data processing here... output = *input; // Publish the data. pub.publish (output); } int main (int argc, char** argv) { // Initialize ROS ros::init (argc, argv, "my_pcl_tutorial"); ros::NodeHandle nh; // Create a ROS subscriber for the input point cloud ros::Subscriber sub = nh.subscribe ("input", 1, cloud_cb); // Create a ROS publisher for the output point cloud pub = nh.advertise<sensor_msgs::PointCloud2> ("output", 1); // Spin ros::spin (); }
在CMakeLists.txt下添加新创建的包(每个新创建的包都要在CMakeLists.txt下添加路径):
add_executable(example src/example.cpp)
target_link_libraries(example ${catkin_LIBRARIES})
这个示例程序并没有进行任何滤波。
接下来是三种ROS下的滤波。
框架可以参考ROS下的example_voxelgrid.cpp:
http://wiki.ros.org/pcl/Tutorials/hydro?action=AttachFile&do=view&target=example_voxelgrid.cpp
直通滤波
#include <ros/ros.h> // PCL specific includes #include <sensor_msgs/PointCloud2.h> #include <pcl_conversions/pcl_conversions.h> #include <pcl/point_cloud.h> #include <pcl/point_types.h> //添加引用 #include <pcl/PCLPointCloud2.h> #include <pcl/filters/passthrough.h> ros::Publisher pub; void cloud_cb (const sensor_msgs::PointCloud2ConstPtr& cloud_msg) { // Container for original & filtered data pcl::PCLPointCloud2* cloud = new pcl::PCLPointCloud2; pcl::PCLPointCloud2ConstPtr cloudPtr(cloud); pcl::PCLPointCloud2 cloud_filtered; // Convert to PCL data type pcl_conversions::toPCL(*cloud_msg, *cloud); // Perform the actual filtering pcl::PassThrough<pcl::PCLPointCloud2> pass; // build the filter pass.setInputCloud (cloudPtr); pass.setFilterFieldName ("z"); pass.setFilterLimits (0.0, 1.3); // apply filter pass.filter (cloud_filtered); // Convert to ROS data type sensor_msgs::PointCloud2 cloud_pt; pcl_conversions::moveFromPCL(cloud_filtered, cloud_pt); // Publish the data pub.publish (cloud_pt); } int main (int argc, char** argv) { // Initialize ROS ros::init (argc, argv, "PassThrough"); ros::NodeHandle nh; // Create a ROS subscriber for the input point cloud 输入 ros::Subscriber sub = nh.subscribe<sensor_msgs::PointCloud2> ("/cloud_input", 1, cloud_cb); // Create a ROS publisher for the output point cloud 输出 pub = nh.advertise<sensor_msgs::PointCloud2> ("/filtered_PassThrough", 1); // Spin ros::spin (); }
体素滤波
#include <ros/ros.h> // PCL specific includes #include <sensor_msgs/PointCloud2.h> #include <pcl_conversions/pcl_conversions.h> #include <pcl/point_cloud.h> #include <pcl/point_types.h> //添加 #include <pcl/PCLPointCloud2.h> #include <pcl/filters/voxel_grid.h> ros::Publisher pub; void cloud_cb (const sensor_msgs::PointCloud2ConstPtr& cloud_msg) { // Container for original & filtered data pcl::PCLPointCloud2* cloud = new pcl::PCLPointCloud2; pcl::PCLPointCloud2ConstPtr cloudPtr(cloud); pcl::PCLPointCloud2 cloud_filtered; // Convert to PCL data type pcl_conversions::toPCL(*cloud_msg, *cloud); // Perform the actual filtering pcl::VoxelGrid<pcl::PCLPointCloud2> sor; // build the filter sor.setInputCloud (cloudPtr); sor.setLeafSize (0.005, 0.005, 0.005); // apply filter sor.filter (cloud_filtered); // Convert to ROS data type sensor_msgs::PointCloud2 cloud_vog; pcl_conversions::moveFromPCL(cloud_filtered, cloud_vog); // Publish the data pub.publish (cloud_vog); } int main (int argc, char** argv) { // Initialize ROS ros::init (argc, argv, "VoxelGrid"); ros::NodeHandle nh; // Create a ROS subscriber for the input point cloud ros::Subscriber sub = nh.subscribe<sensor_msgs::PointCloud2> ("/filtered_PassThrough", 1, cloud_cb); // Create a ROS publisher for the output point cloud pub = nh.advertise<sensor_msgs::PointCloud2> ("/filtered_VoxelGrid", 1); // Spin ros::spin (); }
半径滤波
#include <ros/ros.h> // PCL specific includes #include <sensor_msgs/PointCloud2.h> #include <pcl_conversions/pcl_conversions.h> #include <pcl/point_cloud.h> #include <pcl/point_types.h> //添加 #include <pcl/PCLPointCloud2.h> #include <pcl/filters/radius_outlier_removal.h> #include <pcl/filters/conditional_removal.h> ros::Publisher pub; void cloud_cb (const sensor_msgs::PointCloud2ConstPtr& cloud_msg) { // Container for original & filtered data pcl::PCLPointCloud2* cloud = new pcl::PCLPointCloud2; pcl::PCLPointCloud2ConstPtr cloudPtr(cloud); pcl::PCLPointCloud2 cloud_filtered; // Convert to PCL data type pcl_conversions::toPCL(*cloud_msg, *cloud); // Perform the actual filtering pcl::RadiusOutlierRemoval<pcl::PCLPointCloud2> outrem; // build the filter outrem.setInputCloud(cloudPtr); outrem.setRadiusSearch(0.08); outrem.setMinNeighborsInRadius (60); // apply filter outrem.filter (cloud_filtered); // Convert to ROS data type sensor_msgs::PointCloud2 cloud_rad; pcl_conversions::moveFromPCL(cloud_filtered, cloud_rad); // Publish the data pub.publish (cloud_rad); } int main (int argc, char** argv) { // Initialize ROS ros::init (argc, argv, "RadiusOutlierRemoval"); ros::NodeHandle nh; // Create a ROS subscriber for the input point cloud ros::Subscriber sub = nh.subscribe<sensor_msgs::PointCloud2> ("/filtered_VoxelGrid", 1, cloud_cb); // Create a ROS publisher for the output point cloud pub = nh.advertise<sensor_msgs::PointCloud2> ("/filtered_RadiusOutlierRemoval", 1); // Spin ros::spin (); }
最后在CMakeLists.txt下添加三种滤波方式
add_executable(passthrough src/passthrough.cpp)
target_link_libraries(passthrough ${catkin_LIBRARIES})
add_executable(radius_outlier_removal src/radius_outlier_removal.cpp)
target_link_libraries(radius_outlier_removal ${catkin_LIBRARIES})
add_executable(voxel_grid src/voxel_grid.cpp)
target_link_libraries(voxel_grid ${catkin_LIBRARIES})
注:(1)根据自己的滤波顺序,调整在滤波中的输入和输出。
(2)如果是在rviz中显示滤波图像,建议不要把体素滤波放在最后。
(3)个人测试:octomap支持的体素滤波的最小为0.001。
(4)体素滤波值太小而半径滤波的值太大会导致无法显示(全部过滤掉了)。
现在已经把三种滤波的.cpp写完了,接下来就是如何在ROS中调用。
3、将滤波加入ROS
这里主要实现滤波后的图像在rviz下显示。
修改octomap
参考:https://blog.csdn.net/zhang970187013/article/details/81098175
第三部分对sensors_kinect.yaml修改,主要修改point_cloud_topic,实现octomap调用滤波后的输出(最后一个滤波的output)。
sensors:
- sensor_plugin: occupancy_map_monitor/PointCloudOctomapUpdater
point_cloud_topic: /filtered_RadiusOutlierRemoval
max_range: 2
point_subsample: 1
padding_offset: 0.1
padding_scale: 1.0
filtered_cloud_topic: filtered_cloud
在sensor_manager.launch.xml文件中修改参数控制显示的精度:
<param name="octomap_resolution" type="double" value="0.05" />
修改启动launch文件
之前一步虽然可以调用输出,但是并没有执行滤波处理,所以在启动文件上加入滤波处理。
这一部分也是参考前一步的博文,在launch中加入:
<node pkg="my_pcl_tutorial" type="passthrough" name="Pass_Through" respawn="false">
<remap from="/cloud_input" to="/kinect2/qhd/points" />
</node>
<node pkg="my_pcl_tutorial" type="radius_outlier_removal" name="Radius_OutlierRemoval" respawn="false"/>
<node pkg="my_pcl_tutorial" type="voxel_grid" name="Voxel_Grid" respawn="false"/>
启动node和remap的格式要求参考:http://wiki.ros.org/roslaunch/XML/node和http://wiki.ros.org/roslaunch/XML/remap
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。