第四节:直通滤波(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 直通滤波的流程图

下面这张图,是我自己画的一个直通滤波的完整流程。你可以看到,从原始点云到最终输出,中间只有几个步骤,非常清晰。

直通滤波流程图 输入原始点云 设置滤波轴(X/Y/Z) 设置范围(min, max) 执行滤波 输出滤波后点云 注意: 可设置取反模式 保留范围外点

4.6 性能与注意事项

直通滤波的时间复杂度是O(n),n是点云中的点数。说白了,就是遍历一遍所有点,判断一下坐标是否在范围内。所以即使点云有几百万个点,处理起来也很快。

但我还是要提醒你几点:

  • 注意坐标系:你的点云是在什么坐标系下?如果是激光雷达坐标系,Z轴通常朝上。如果是相机坐标系,Z轴可能朝前。搞错了轴,滤波结果就完全不对了。
  • 注意单位:PCL默认单位是米。如果你从别的系统导入数据,单位可能是毫米或者厘米,记得转换。
  • 注意点云密度:直通滤波不会改变点云的密度,它只是裁剪范围。如果你觉得点太稀疏,后面可以再接一个体素滤波。

我的个人习惯:

每次做直通滤波之前,我都会先用 pcl::visualization::CloudViewer 看一眼原始点云,确认一下目标物体的坐标范围。这样设置参数的时候心里有数,不会瞎猜。

好了,直通滤波就讲到这里。内容不多,但很实用。你可以在自己的数据集上试试,看看效果如何。记住,滤波不是目的,目的是让后续的处理更高效、更准确。

可可精品资料库