一、体素滤波:点云世界的“像素化”操作
体素滤波,英文叫 Voxel Grid Filter。说白了,就是把三维空间切成一个个小立方体,每个小立方体就叫一个体素(Voxel)。然后每个体素里只保留一个点,通常是这个体素内所有点的重心,或者直接取中心点。
我刚开始接触点云时,觉得这玩意儿不就是降采样嘛,有啥好讲的?后来做项目才发现,体素大小选不对,后面所有算法都得翻车。嗯,咱们今天就把这事彻底聊透。
1.1 体素滤波的数学原理
体素滤波的核心思想其实很简单:
- 第一步:确定点云的包围盒(Bounding Box),找到 x、y、z 三个方向的最小值和最大值
- 第二步:设定体素边长
leaf_size,把包围盒切成 N_x × N_y × N_z 个小格子 - 第三步:每个格子里的点,用一个点代替
数学表达是这样的:
设点云中任意一点 P(x, y, z),体素边长为 L
则 P 所属的体素索引为:
idx_x = floor((x - x_min) / L)
idx_y = floor((y - y_min) / L)
idx_z = floor((z - z_min) / L)
该体素内的代表点 = 所有点的平均值(重心法)
或者 = 离体素中心最近的点(中心法)
我个人习惯用重心法。为什么?因为中心法有时候会选到离群点,重心法更稳定。当然,如果你对速度要求极高,中心法会快一丢丢,毕竟不用算均值。
1.2 体素大小对点云密度的影响
这个才是真正的坑。体素大小直接影响点云密度,我给大家列个表:
| 体素大小 | 点云密度变化 | 典型场景 |
|---|---|---|
| 0.01m(1cm) | 几乎不变 | 高精度建模、缺陷检测 |
| 0.05m(5cm) | 减少约 80% | 室内 SLAM、机器人导航 |
| 0.10m(10cm) | 减少约 95% | 室外大场景、自动驾驶 |
| 0.50m(50cm) | 极度稀疏 | 地形建模、粗略配准 |
你想想看,如果你做的是室内 SLAM,激光雷达扫一圈可能有几十万个点。体素设成 1cm,降完还有十几万,计算量还是大。设成 5cm,可能就剩一两万了,跑起来飞快。
但这里有个陷阱——体素太大,会丢失细节。我曾经做过一个项目,用 10cm 体素处理一个椅子点云,结果椅腿直接没了,因为椅腿太细,体素里没几个点,直接被吞掉了。所以体素大小一定要小于你关心的最小物体尺寸。
1.3 PCL 实现与参数调优
PCL 里体素滤波的实现非常成熟,代码就几行:
#include <pcl/filters/voxel_grid.h>
pcl::VoxelGrid<pcl::PointXYZ> voxel_filter;
voxel_filter.setInputCloud(cloud_in);
voxel_filter.setLeafSize(0.05f, 0.05f, 0.05f); // 体素边长 5cm
voxel_filter.filter(*cloud_out);
就这么简单?对,就这么简单。但调参才是真正的技术活。
参数调优的几个关键点:
- leaf_size 三个方向可以不一样:比如地面点云,z 方向可以设大一点,因为地面高度变化小。我做过一个矿区项目,x、y 设 0.1m,z 设 0.3m,效果出奇的好。
- setMinimumPointsNumberPerVoxel:这个参数可以设置每个体素最少要几个点才保留。默认是 0,但如果你设成 3,可以过滤掉孤立的噪点。嗯,这个技巧很少人用,但很实用。
- downsample_all_data_:如果你有法线、颜色等字段,记得把这个设为 true,否则只降采样坐标,其他字段会丢失。
核心经验:体素大小 = 你关心的最小物体尺寸 × 0.3 ~ 0.5。比如你要识别一个 10cm 的障碍物,体素设 3~5cm 最合适。
1.4 避坑指南
我曾经踩过的坑:
- 体素滤波后点云数量不稳定?检查一下你的点云是不是有 NaN 点。PCL 的体素滤波不会自动处理 NaN,NaN 点会参与计算,导致结果异常。先做一步 NaN 去除。
- 降采样后点云位置偏移?大概率是你用了中心法,而体素内点分布不均匀。换成重心法就好了。
- 体素滤波后法线计算错误?因为降采样改变了局部点密度,法线估计的半径参数需要重新调整。我一般会把法线估计的搜索半径设为体素大小的 2~3 倍。
小技巧:如果你不确定体素大小怎么设,可以先用 0.01m、0.05m、0.1m 各跑一遍,看看点云数量和结构变化。我习惯写个脚本自动对比,省得每次手动调。
1.5 体素滤波的知识体系
下面这张图是我自己总结的体素滤波核心逻辑,你看一眼就能记住:
这张图把体素滤波的流程讲得很清楚:先切格子,再选代表点。选代表点有两种方式,我推荐你用重心法,稳定可靠。
1.6 什么时候该用体素滤波?
说实话,体素滤波不是万能的。我给大家总结几个场景:
- 必须用:点云数据量太大,内存扛不住,或者算法跑不动。比如 1000 万点的激光雷达数据,不降采样根本没法做实时处理。
- 建议用:做特征提取之前。点云太密会导致特征计算量爆炸,而且很多特征在密集区域反而会退化。
- 别用:如果你需要保留点云的精细几何结构,比如文物扫描、精密零件检测。这时候体素滤波会破坏细节,建议用更高级的降采样方法,比如 MLS(移动最小二乘)重采样。
我记得有一次做自动驾驶项目,同事把体素设成了 0.2m,结果路沿石直接消失了,因为路沿石只有 10cm 宽。后来我帮他调到 0.05m,路沿石回来了,但点云量翻了好几倍。这就是典型的精度和效率的 trade-off。
好了,体素滤波就聊到这儿。记住一句话:体素大小决定一切,选对了事半功倍,选错了事倍功半。