3. 体素滤波:体素网格构建、下采样原理、参数选择对点云密度的影响
体素滤波,说白了就是给点云数据“做减法”。
你想想看,激光雷达扫一圈,动辄几十万个点。这么多点,很多是冗余的。比如一面平整的墙,可能几千个点都在描述同一个平面。全留着?没必要,还拖慢算法速度。
体素滤波就是干这个的——在保持物体几何形状的前提下,大幅降低点云密度。我最早接触它是在做室外大场景重建时,原始点云几百万个点,算法跑得跟蜗牛一样。加了体素滤波后,数据量降到十分之一,效果几乎没变。嗯,从那时起,我就把这步当成标配了。
3.1 体素网格构建
体素,就是“体积像素”。你可以把它想象成三维空间里的小方块。
构建过程很简单:
- 确定包围盒:找到点云在X、Y、Z三个方向上的最小和最大值,形成一个长方体。
- 设定体素大小:比如每个小方块边长0.1米。这个参数很关键,后面会细说。
- 划分网格:把包围盒切成一个个小立方体。每个立方体就是一个体素。
- 分配点云:遍历所有点,根据坐标计算它落在哪个体素里。
举个例子。假设点云X范围是0到10米,体素大小设为1米。那X方向就有10个体素。每个点进来,用 floor(x / 体素大小) 就能算出它在哪个格子。
核心思想:体素网格把连续空间离散化了。每个体素是一个“桶”,装着一堆空间上邻近的点。
3.2 下采样原理
体素建好了,怎么“下采样”?
方法其实就两种主流做法:
- 体素重心法:计算每个体素内所有点的重心(坐标平均值),用这个重心点代表整个体素。这是最常用的方法,精度高。
- 体素中心法:直接用体素的几何中心点。速度快,但会丢失一些细节。
我个人习惯用重心法。为什么?因为它在降采样时保留了原始点云的局部分布特征。我在做室内SLAM时对比过,重心法建出来的地图,墙面更平整,边缘更清晰。
代码实现其实不复杂。PCL里一行就搞定:
// PCL 体素滤波示例
pcl::VoxelGrid<pcl::PointXYZ> voxel_filter;
voxel_filter.setInputCloud(cloud_input);
voxel_filter.setLeafSize(0.1f, 0.1f, 0.1f); // 体素大小
voxel_filter.filter(*cloud_filtered);
如果你自己实现,核心逻辑就是:
// 伪代码:体素下采样
for each point in cloud:
idx_x = floor(point.x / leaf_size_x)
idx_y = floor(point.y / leaf_size_y)
idx_z = floor(point.z / leaf_size_z)
voxel_key = (idx_x, idx_y, idx_z)
voxel_map[voxel_key].push_back(point)
for each voxel in voxel_map:
centroid = mean(voxel.points)
downsampled_cloud.push_back(centroid)
你看,逻辑很直白。但实际工程中,坑都在参数选择上。
3.3 参数选择对点云密度的影响
体素滤波最核心的参数就是体素大小(也叫叶子大小)。
体素大小直接影响输出点云的密度:
| 体素大小 | 输出点云密度 | 适用场景 |
|---|---|---|
| 0.01m (1cm) | 极高密度 | 精细建模、小物体扫描 |
| 0.05m (5cm) | 中等密度 | 室内SLAM、一般场景 |
| 0.10m (10cm) | 较低密度 | 室外大场景、快速建图 |
| 0.20m (20cm) | 稀疏 | 远距离感知、粗略定位 |
体素设得太小,比如1cm,那降采样效果微乎其微。点云还是那么密,算法该慢还是慢。
体素设得太大,比如50cm,那一个体素可能覆盖了墙面、桌角、地面。重心点算出来,细节全丢了。我曾经在项目里吃过这个亏——体素设太大,结果墙角都变圆了,特征匹配直接失败。
我的经验:体素大小一般取场景中最小物体尺寸的1/5到1/10。比如室内场景,最小物体是椅子腿(约5cm粗),那体素设1cm到2cm比较合适。
除了体素大小,还有两个参数值得注意:
- 最小点数阈值:有些体素里只有一两个点,可能是噪声。可以设置一个阈值,点数少于N的体素直接丢弃。
- 是否保留空体素:默认不保留。但如果你做的是占据网格地图,空体素也有意义。
避坑指南:我曾经在动态场景中用过体素滤波,结果移动的人被“平均”到了体素里,产生了一堆幽灵点。后来我加了运动检测,先滤掉动态物体再做体素滤波,问题就解决了。
3.4 体素滤波的变种与进阶
标准的体素滤波虽然好用,但有些场景下不够灵活。这里提几个变种:
- 自适应体素滤波:体素大小不固定。点云密集的地方用小体素,稀疏的地方用大体素。适合远近距离混合的场景。
- 近似体素滤波:不精确计算重心,而是用体素内第一个点或随机一个点。速度更快,适合实时性要求高的场景。
- 体素化+法线估计:先体素滤波,再在每个体素内估计法线。这样降采样后的点云还保留了法线信息,方便后续配准。
我个人在自动驾驶项目中用过自适应体素滤波。近处车体用小体素保留细节,远处路面用大体素减少计算量。效果比固定体素好不少。
3.5 本章知识体系
下面这张图总结了体素滤波的核心逻辑:
体素滤波看似简单,但用好它需要你对场景有理解。体素大小怎么选?下采样策略用哪个?这些没有标准答案,全靠经验积累。
我建议你从0.05m开始试,看看效果。如果点云还是太密,就加大体素;如果细节丢了,就减小体素。多调几次,你就能找到感觉。
小技巧:调试时可以把原始点云和降采样后的点云叠加显示。一眼就能看出体素滤波有没有“伤到”关键结构。
好了,体素滤波就讲到这里。记住它的核心:用空间换时间,用精度换效率。在工程中,这往往是性价比最高的预处理步骤。