3、直通滤波:原理与实现、在仿真中过滤无效范围数据、代码实战(Python + Open3D)
3.1 直通滤波到底在干什么?
直通滤波,名字听着挺唬人。其实说白了,就是一刀切。
你想象一下,你面前有一堆点云数据。这些数据里,有些点离你太远了,远到根本看不清;有些点又太近了,近到贴在你的传感器镜头上。这些点,对后续处理来说,基本就是噪音。
直通滤波要做的,就是沿着某个坐标轴(比如 X、Y、Z),设定一个范围。在这个范围里的点,我留下;不在这个范围里的,我直接扔掉。
嗯,就这么简单。
我在做自动驾驶仿真的时候,经常遇到这种情况。激光雷达扫出来的点云,动不动就几百米开外。但我的仿真场景,可能就关注车前 50 米内的障碍物。那些远处的点,不仅没用,还会拖慢计算速度。所以,我习惯上来就先做一个直通滤波,把范围卡死。
核心思想: 直通滤波是一种基于坐标轴范围的快速点云裁剪方法。它不关心点的颜色、强度,只看点的空间位置。
3.2 原理:其实就是设定一个“框”
直通滤波的原理,用一句话就能讲清楚:
对于点云中的每一个点,检查它在指定维度上的坐标值。如果这个值在设定的最小值和最大值之间,就保留;否则,就删除。
举个例子。你有一堆点,分布在 X 轴上从 -100 到 100 的位置。你只想保留 X 在 -10 到 10 之间的点。那么直通滤波就会把 X < -10 和 X > 10 的点全部过滤掉。
为什么会这样?因为很多传感器,尤其是激光雷达,边缘区域的数据质量很差。我记得有一次做室内建图仿真,墙壁边缘的点总是飘忽不定。后来发现,是雷达在水平视角边缘的测量误差太大。我用直通滤波把水平角度对应的 X 范围一缩,问题就解决了。
直通滤波可以单独对 X、Y、Z 轴操作,也可以组合使用。比如先过滤 X 轴,再过滤 Y 轴,最后过滤 Z 轴。这样就能得到一个立方体形状的感兴趣区域。
| 维度 | 典型应用场景 | 我常用的范围 |
|---|---|---|
| X 轴 | 过滤左右两侧的无效点 | -50m ~ 50m |
| Y 轴 | 过滤前后方向的远距离点 | 0m ~ 80m(车前) |
| Z 轴 | 过滤地面和过高物体 | -1.5m ~ 3.0m |
3.3 在仿真中,它为什么这么重要?
仿真环境里,数据往往是“完美”的。但完美不等于有用。
你想想看,仿真场景可能生成一个巨大的城市模型。激光雷达模拟器会忠实地把几公里外的建筑轮廓都扫出来。这些数据,对于验证感知算法来说,完全是多余的。
直通滤波在仿真中的核心作用,我总结为三点:
- 减少计算量: 去掉无效范围的数据,后续的聚类、分割算法能快不少。我做过测试,一个 100 万点的点云,经过直通滤波后可能只剩 30 万点,处理速度提升 3 倍以上。
- 提高算法稳定性: 远处的点稀疏且噪声大,容易让算法产生误检。把范围卡死,算法只关注有效区域,误报率明显下降。
- 模拟真实传感器限制: 真实的激光雷达都有最大探测距离和视场角。仿真中如果不做直通滤波,算法可能会“看到”现实中根本看不到的东西,导致仿真结果过于乐观。
一个小技巧: 在仿真中,我通常会把直通滤波的参数和传感器的型号绑定。比如模拟 Velodyne HDL-64E,我就把 Y 轴范围设为 0~120m;模拟 Livox Horizon,就设为 0~90m。这样仿真结果更贴近实际。
3.4 代码实战:Python + Open3D
光说不练假把式。咱们直接上代码。
Open3D 里实现直通滤波,其实就一行核心函数。但我还是习惯把整个流程写清楚,方便调试。
import open3d as o3d
import numpy as np
# 1. 读取点云数据(这里用 Open3D 自带的示例数据)
print("加载点云数据...")
pcd = o3d.io.read_point_cloud("sample.pcd")
print(f"原始点云点数: {len(pcd.points)}")
# 2. 创建直通滤波器对象
# 我个人习惯先创建一个副本,避免修改原始数据
pcd_filtered = o3d.geometry.PointCloud()
pcd_filtered.points = o3d.utility.Vector3dVector(np.asarray(pcd.points))
# 3. 定义过滤范围
# 这里我过滤 Z 轴,只保留高度在 -0.5 到 1.5 米之间的点
# 这个范围在仿真中常用于过滤地面和低矮障碍物
z_min = -0.5
z_max = 1.5
# 4. 执行直通滤波
# Open3D 的 crop 方法配合 AxisAlignedBoundingBox 可以实现直通滤波效果
bbox = o3d.geometry.AxisAlignedBoundingBox(
min_bound=(-np.inf, -np.inf, z_min),
max_bound=(np.inf, np.inf, z_max)
)
pcd_filtered = pcd_filtered.crop(bbox)
print(f"直通滤波后点云点数: {len(pcd_filtered.points)}")
# 5. 可视化对比
# 原始点云用红色,滤波后点云用绿色
pcd.paint_uniform_color([1, 0, 0]) # 红色
pcd_filtered.paint_uniform_color([0, 1, 0]) # 绿色
# 显示在一起,方便对比
o3d.visualization.draw_geometries([pcd, pcd_filtered],
window_name="直通滤波效果对比",
width=800, height=600)
注意: 上面的代码用了 AxisAlignedBoundingBox 来实现直通滤波。这种方法的好处是可以一次性过滤多个维度。如果你只想过滤单个维度,也可以用更直接的方式:遍历点云,手动判断每个点的坐标值。但 Open3D 的 crop 方法效率更高,我推荐用这个。
3.5 我曾经踩过的坑
讲两个我亲身经历的问题,希望能帮你避开。
第一个坑:坐标系搞反了。
有一次我在仿真中过滤点云,明明设了 Y 轴范围 0~50 米,结果点云全没了。查了半天,发现仿真场景的坐标系是 Z 轴朝前,而不是常见的 Y 轴朝前。所以,拿到点云数据后,第一件事就是确认坐标系方向。别想当然。
第二个坑:范围设得太死。
直通滤波虽然好用,但别把范围卡得太极限。比如你设 Z 轴范围 -1.0 到 2.0 米,刚好把车顶切掉一部分。在仿真中,这种边界上的点很容易因为传感器噪声而抖动。我建议留出 10%~20% 的余量,让算法有缓冲空间。
3.6 本章知识体系
下面这张图,是我自己梳理的直通滤波知识结构。你可以对照着回顾一下。
直通滤波虽然简单,但它是点云预处理的第一道防线。用好了,后面的工作会轻松很多。我个人建议,在仿真验证流程中,把直通滤波作为标准步骤固定下来,不要省略。
可可精品资料库