第3章 · 点云预处理
体素滤波、直通滤波、半径滤波、统计滤波,实战代码与效果对比。
点云预处理,说白了就是给原始点云“洗个澡”。激光雷达一帧扫下来,少则几万,多则上百万个点。这里头有噪声、有离群点、有远距离的稀疏点,还有大量冗余。如果不处理,后面的检测和跟踪根本跑不动。我刚开始做自动驾驶感知时,就吃过这个亏——直接把原始点云塞进网络,结果显存爆了,精度还一塌糊涂。所以,预处理不是可选项,是必选项。
1. 体素滤波 —— 降采样主力
体素滤波,就是把三维空间切成一个个小立方体(体素),每个体素内只保留一个重心点。说白了就是用分辨率换速度。我习惯把体素大小设在 0.1m~0.5m 之间,具体看场景。高速公路上 0.2m 就够,城区复杂场景我可能会用 0.1m。
import open3d as o3d
pcd = o3d.io.read_point_cloud("lidar_frame.pcd")
# 体素滤波,leaf_size = 0.2m
voxel_down = pcd.voxel_down_sample(voxel_size=0.2)
o3d.visualization.draw_geometries([voxel_down])
我在项目里经常先做体素滤波,把 10 万点降到 2 万左右,后续处理快很多。但注意——体素滤波会丢失精细结构,比如行人轮廓。所以后面通常还要接别的滤波。
2. 直通滤波 —— 切出 ROI
直通滤波,就是沿着某个坐标轴切一刀。比如我只关心车前 50 米内的障碍物,那就把 x 轴范围设在 [0, 50]。你想想看,远距离的点又稀疏又没用,直接砍掉能省不少计算。
import open3d as o3d
import numpy as np
pcd = o3d.io.read_point_cloud("lidar_frame.pcd")
points = np.asarray(pcd.points)
# 保留 x 在 0~50m, y 在 -20~20m, z 在 -2~3m
mask = (points[:, 0] > 0) & (points[:, 0] < 50) & \
(points[:, 1] > -20) & (points[:, 1] < 20) & \
(points[:, 2] > -2) & (points[:, 2] < 3)
filtered = pcd.select_by_index(np.where(mask)[0])
o3d.visualization.draw_geometries([filtered])
3. 半径滤波 —— 干掉孤立点
半径滤波的思路很简单:以每个点为中心画个球,如果球内邻居数少于阈值,就认为是离群点,删掉。我在处理高速公路点云时,经常遇到一些“飞点”——可能是飞虫或者灰尘。半径滤波一搞,基本就干净了。
# 半径滤波:半径0.5m内至少要有3个点
cl, ind = pcd.remove_radius_outlier(nb_points=3, radius=0.5)
filtered = pcd.select_by_index(ind)
o3d.visualization.draw_geometries([filtered])
4. 统计滤波 —— 更平滑的离群点去除
统计滤波和半径滤波有点像,但它基于全局统计。它计算每个点到最近 k 个邻居的平均距离,如果这个距离超过全局均值 + 若干倍标准差,就干掉。说白了,它更适合去除稀疏的、离散的噪声。
# 统计滤波:每个点考虑20个邻居,标准差倍数设为2.0
cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
filtered = pcd.select_by_index(ind)
o3d.visualization.draw_geometries([filtered])
我个人经验:统计滤波对高斯噪声效果很好,但遇到成片的离群点(比如反射板造成的鬼影)就有点力不从心。这时候我会先用半径滤波粗筛,再用统计滤波精修。
5. 效果对比与选型建议
| 滤波方法 | 主要用途 | 参数敏感度 | 速度 | 我常用的场景 |
|---|---|---|---|---|
| 体素滤波 | 降采样,减少点数 | 中等 | ⭐⭐⭐⭐ | 所有场景的第一步 |
| 直通滤波 | 裁剪ROI,去除远区/地面 | 低 | ⭐⭐⭐⭐⭐ | 快速限定检测范围 |
| 半径滤波 | 去除稀疏离群点 | 较高 | ⭐⭐⭐ | 城区、隧道等密集点云 |
| 统计滤波 | 去除高斯噪声/均匀离群点 | 中等 | ⭐⭐⭐ | 高速公路、远距离稀疏区 |
你看,没有万能滤波。我通常的管线是:直通滤波 → 体素滤波 → 半径滤波 → 统计滤波。当然,如果实时性要求极高,可以只做直通+体素。但要是做高精度检测,后面两个滤波别省。
🔧 实战小贴士:用 Open3D 的 visualization.draw_geometries 对比滤波前后效果。我习惯把原始点云标为红色,滤波后标为绿色,叠在一起看差异。
嗯,预处理这块其实不难,但坑不少。我记得有一次在雨夜场景,统计滤波把雨滴当成有效点保留了,后来我加了基于强度的直通滤波才搞定。所以,多试试组合,别死磕单一方法。