第四节:直通滤波(PassThrough)—— 最朴素的点云“剪刀”
各位同学,今天我们来聊一个非常基础、但我在实际项目中几乎天天用到的滤波方法——直通滤波。
说实话,直通滤波的原理简单到让人怀疑人生:就是沿着X、Y或者Z轴,设定一个范围,把范围外的点全部砍掉。嗯,就这么简单。但你别小看它,我做过那么多激光雷达项目,预处理的第一步,十有八九就是直通滤波。
4.1 直通滤波的核心思想
你想想看,我们拿到的原始点云,往往包含大量无用信息。比如一个室外场景,激光雷达可能打到几百米外的建筑物,或者扫到头顶的树枝。这些点对我们当前的任务来说,就是噪声。
直通滤波说白了就是一把“剪刀”。你告诉它:
- 沿着哪个轴剪(X/Y/Z)
- 剪掉哪一段(设置最小值和最大值)
剩下的点,就是你要的ROI(感兴趣区域)。
核心公式(其实不算公式):
保留点 = { p ∈ 点云 | min ≤ p.axis ≤ max }
就这么一行,没有比这更简单的滤波了。
4.2 PCL中的实现:pcl::PassThrough
PCL给我们封装好了现成的类,叫 pcl::PassThrough<T>。模板参数T就是点云类型,一般用 pcl::PointXYZ 就够了。
我给大家写一个最常用的例子:提取Z轴0到2米范围内的点。为什么是这个范围?因为我在做移动机器人导航时,地面通常在0米左右,而2米以上一般是天花板或者树枝,对导航没什么用。
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>
int main()
{
// 1. 加载原始点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("input.pcd", *cloud);
// 2. 创建直通滤波对象
pcl::PassThrough<pcl::PointXYZ> pass;
// 3. 设置输入点云
pass.setInputCloud(cloud);
// 4. 设置滤波轴和范围
pass.setFilterFieldName("z"); // 沿Z轴
pass.setFilterLimits(0.0, 2.0); // 保留0到2米
// 5. 执行滤波
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
pass.filter(*cloud_filtered);
// 6. 保存结果
pcl::io::savePCDFile("output.pcd", *cloud_filtered);
return 0;
}
代码就这么几行。你可能会问:这也太简单了吧?嗯,确实简单。但越简单的东西,越容易用错。下面我讲讲几个坑。
4.3 避坑指南(我踩过的雷)
坑一:默认是保留范围内的点,但你可以反过来
我曾经在一个项目里,想去除地面上的点(Z轴0到0.3米),结果写成了保留0到0.3米,把地面留下了,上面的点全丢了。后来才发现,setFilterLimitsNegative(true) 可以取反,变成保留范围外的点。
记住:setFilterLimitsNegative(false) 是默认行为,保留范围内的点。
技巧:多轴组合滤波
直通滤波一次只能处理一个轴。如果你想同时限制X、Y、Z三个轴,需要连续调用三次。我个人习惯写一个循环或者封装成函数,避免重复代码。
坑二:点云类型必须匹配
如果你用的是 pcl::PointXYZRGB 或者 pcl::PointXYZI,模板参数也要对应改。我见过有人用 pcl::PointXYZ 去滤波带颜色的点云,编译报错后一脸懵。
4.4 应用场景:什么时候用直通滤波?
我总结了一下,直通滤波最常用的三个场景:
| 场景 | 说明 | 示例 |
|---|---|---|
| 去除远处背景 | 激光雷达打到远处建筑物或天空,这些点对近处目标识别是干扰 | 沿Z轴保留0-50米 |
| 提取ROI | 只关注某个区域内的点,比如车辆前方的道路 | 沿X轴保留-10到10米,沿Y轴保留0到30米 |
| 去除地面/天花板 | 在机器人导航中,地面点通常需要单独处理 | 沿Z轴保留0.3米以上 |
举个例子。我在做自动驾驶的障碍物检测时,第一步就是直通滤波。把激光雷达前方100米以外的点全部去掉。为什么?因为100米外的点,稀疏得跟鬼一样,根本没法做聚类。去掉之后,计算量直接减少一半。
4.5 直通滤波的流程图
下面这张图,是我自己画的一个直通滤波的完整流程。你可以看到,从原始点云到最终输出,中间只有几个步骤,非常清晰。
4.6 性能与注意事项
直通滤波的时间复杂度是O(n),n是点云中的点数。说白了,就是遍历一遍所有点,判断一下坐标是否在范围内。所以即使点云有几百万个点,处理起来也很快。
但我还是要提醒你几点:
- 注意坐标系:你的点云是在什么坐标系下?如果是激光雷达坐标系,Z轴通常朝上。如果是相机坐标系,Z轴可能朝前。搞错了轴,滤波结果就完全不对了。
- 注意单位:PCL默认单位是米。如果你从别的系统导入数据,单位可能是毫米或者厘米,记得转换。
- 注意点云密度:直通滤波不会改变点云的密度,它只是裁剪范围。如果你觉得点太稀疏,后面可以再接一个体素滤波。
我的个人习惯:
每次做直通滤波之前,我都会先用 pcl::visualization::CloudViewer 看一眼原始点云,确认一下目标物体的坐标范围。这样设置参数的时候心里有数,不会瞎猜。
好了,直通滤波就讲到这里。内容不多,但很实用。你可以在自己的数据集上试试,看看效果如何。记住,滤波不是目的,目的是让后续的处理更高效、更准确。
可可精品资料库