第四节 体素滤波(VoxelGrid):下采样原理、体素大小选择、代码实现与效果对比

体素滤波,说白了就是给点云“打格子”。

你想想看,一帧激光雷达数据动不动就十几万个点。这么多点,很多还是重复的、冗余的。比如一面墙,你扫出10万个点,其实1000个点就能描述清楚它的位置。那剩下的99000个点,除了占内存、拖慢算法,还有什么用?

我刚开始做点云处理时,就吃过这个亏。那时候直接拿原始点云做配准,结果算法跑了整整两分钟还没收敛。后来同事提醒我:“你先降采样啊!”从那以后,体素滤波就成了我工具箱里的常客。

1. 体素滤波的核心原理

体素(Voxel)这个词,是体积(Volume)和像素(Pixel)的组合。你可以把它想象成一个三维的小立方体盒子。

体素滤波的流程其实就三步:

  1. 建格子:把整个点云空间划分成一个个固定大小的三维网格(体素)。
  2. 装点:每个点根据它的三维坐标,落入对应的体素中。
  3. 取代表:每个体素内,只保留一个点(通常是体素内所有点的重心,或者中心点)。

嗯,这里要注意:重心和中心点是两码事。重心是体素内所有点坐标的平均值,中心点是体素几何中心。我个人习惯用重心,因为它更能代表原始点云的分布特征。

核心公式:

体素索引 = floor(点坐标 / 体素大小)

重心坐标 = (1/n) * Σ(体素内所有点的坐标)

说白了,体素滤波就是一个“以少胜多”的策略。用更少的点,保留原始点云的主要几何结构。

2. 体素大小怎么选?

这是最让人头疼的问题。体素大小选大了,点云细节全丢;选小了,降采样效果不明显。

我根据项目经验,总结了一个参考表:

应用场景 推荐体素大小 说明
快速可视化 0.5m - 1.0m 只要看个大概轮廓,细节不重要
SLAM前端配准 0.1m - 0.3m 需要保留足够特征点,又不能太多
目标检测 0.05m - 0.1m 小目标(行人、自行车)需要精细点云
高精度地图构建 0.02m - 0.05m 厘米级精度,体素不能太大

我的经验法则:

如果你不确定选多大,先试试0.2m。这个值在大多数场景下表现都不错。然后根据结果微调——点云太密就加大,太稀疏就减小。

3. 代码实现

我用PCL库来实现体素滤波。PCL里已经封装好了,我们直接调用就行。但理解背后的逻辑更重要。

#include <pcl/filters/voxel_grid.h>

// 输入点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 输出点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

// 创建体素滤波器对象
pcl::VoxelGrid<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);

// 设置体素大小 —— 这里我设成0.2m
sor.setLeafSize(0.2f, 0.2f, 0.2f);

// 执行滤波
sor.filter(*cloud_filtered);

std::cout << "原始点数: " << cloud->size() << std::endl;
std::cout << "滤波后点数: " << cloud_filtered->size() << std::endl;

代码就这么几行。但我想强调一点:setLeafSize的三个参数,一般设成相同值。如果你设成不同的,比如x方向0.2,y方向0.5,那体素就变成了长方体。除非你有特殊需求,否则不建议这么做。

4. 效果对比

我曾经在一个项目里做过对比测试。原始点云有12万个点,用不同体素大小处理,结果如下:

体素大小 滤波后点数 压缩比 处理时间 效果评价
0.05m 85,000 1.4:1 0.8s 细节保留很好,但压缩不明显
0.1m 32,000 3.8:1 0.5s 平衡点,推荐
0.2m 8,500 14:1 0.3s 轮廓清晰,细节有损失
0.5m 1,200 100:1 0.1s 只能看个形状了

避坑指南:

我曾经遇到过一个问题:体素滤波后,点云出现了“锯齿”现象。后来发现是因为体素大小设置得太大,导致原本平滑的曲面被离散化了。解决办法很简单——把体素大小减小一半。

另外,体素滤波会改变点的精确位置(因为用的是重心)。如果你做的是高精度测量,这点要特别注意。

5. 体素滤波的变体

除了标准的体素滤波,还有几个变体值得了解:

  • 近似体素滤波:不计算重心,直接取体素中心点。速度快,但精度略低。
  • 自适应体素滤波:根据点云密度自动调整体素大小。密度大的区域用小体素,密度小的区域用大体素。
  • 体素中心采样:每个体素只保留离中心最近的点。适合需要保持点云原始位置的应用。

我个人在大多数场景下都用标准体素滤波。只有在实时性要求极高的场合,才会换成近似体素滤波。

6. 核心逻辑流程图

下面这张图,是我自己总结的体素滤波决策流程。你可以把它当作一个快速参考:

体素滤波核心流程 输入原始点云 设置体素大小(关键参数) 划分体素网格 计算体素重心 → 输出 关键决策点 体素大小 ↓ → 细节保留多 → 压缩比小 体素大小 ↑ → 细节丢失多 → 压缩比大 推荐起始值: 0.1m - 0.2m 根据结果微调

这张图把整个流程串起来了。你从输入开始,设置体素大小,然后划分网格,最后计算重心输出。每一步都很清晰。

7. 总结

体素滤波是我用得最多的降采样方法。它简单、高效、可控。你只需要调一个参数——体素大小,就能控制点云的密度。

记住几个要点:

  • 体素大小是核心参数,没有万能值,要根据场景调
  • 重心比中心点更能代表原始分布
  • 体素滤波会改变点的精确位置,高精度应用要谨慎
  • 实时性要求高时,可以用近似体素滤波

嗯,体素滤波就讲到这里。这个工具用好了,能帮你省下不少计算资源。