激光雷达点云滤波算法与处理管线 - 第3节

一、体素滤波:点云世界的“像素化”降维

体素滤波,英文叫 VoxelGrid。说白了,就是把三维空间切成一个个小立方体。

每个小立方体,就是一个体素(Voxel)。

然后,在每个体素里,只保留一个点。其他点,全部丢掉。

嗯,就这么简单粗暴。

我刚开始接触点云时,觉得这操作太“暴力”了。后来发现,这恰恰是点云预处理中最优雅的一步。为什么?你想想看,激光雷达一帧数据,动辄十几万甚至上百万个点。这么多点,很多都是冗余的。比如一面墙,你扫出10万个点,和扫出1000个点,对后续的配准、建图来说,差别不大。

体素滤波的核心思想,就是用空间分辨率换计算效率。它不关心点的密度,只关心点云在空间中的“骨架”。

1.1 体素滤波到底在做什么?

我习惯用一个比喻来理解它:

你有一堆沙子(原始点云)。你拿一个筛子(体素网格)去筛。筛子的每个格子大小固定。每个格子里,只留一粒沙子。其他沙子,全部筛掉。

留下来的那粒沙子,通常是这个格子内所有点的重心(质心),或者直接取格子中心点。

所以,体素滤波有两个关键步骤:

  • 划分体素网格:根据你设定的体素大小,把整个点云空间切成无数个小立方体。
  • 体素内降采样:每个小立方体内,只保留一个代表点。

这里有个细节,我踩过坑——体素内的代表点怎么选?

PCL 默认用的是体素内所有点的重心(质心)。这个做法比较稳,能保留局部几何特征。但如果你追求极致速度,也可以直接用体素的中心点。不过,我建议你用默认的质心方式,精度会好一些。

核心结论:体素滤波的本质,是用一个点代表一个局部空间。体素越小,保留的细节越多;体素越大,降采样越狠,但信息损失也越大。

1.2 体素大小对滤波效果的影响

这是体素滤波里最关键的参数。没有之一。

我见过很多新手,上来就设一个 0.1m 的体素,结果点云还是几十万个点,滤波了个寂寞。也有人设 1m,结果一堵墙变成了几个孤零零的点,后续配准直接崩了。

体素大小的影响,我总结成一张表:

体素大小 效果 适用场景
很小(如 0.01m) 几乎不降采样,保留全部细节 高精度建模、精细测量
适中(如 0.05m - 0.1m) 降采样明显,保留主要几何结构 SLAM前端、配准、一般建图
较大(如 0.5m - 1m) 严重降采样,细节丢失 快速预览、粗配准、大场景概览

我个人习惯,在 SLAM 前端做配准时,体素大小设在 0.05m 到 0.1m 之间。这个范围既能保证配准精度,又能把点云数量压到几千到几万点,计算量刚刚好。

但要注意,体素大小不是固定的。它跟你的场景尺度有关。室内小场景,0.02m 可能都嫌大。室外大场景,0.2m 可能都嫌小。我建议你根据点云的平均点间距来设定。一般取平均点间距的 2-5 倍,效果比较稳妥。

避坑指南:我曾经在一个项目中,把体素设得太大,结果点云变得稀疏无比,导致后续的 ICP 配准直接发散。后来花了半天排查,才发现是体素滤波把关键特征点都滤掉了。所以,体素大小一定要跟你的应用场景匹配。宁小勿大,小了只是慢一点,大了可能直接导致算法失效。

二、PCL 代码实现:手把手教你调参

理论讲完了,咱们直接上代码。PCL 的体素滤波实现非常成熟,核心就几个 API。

下面是我常用的一个代码模板:

#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>

// 输入点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out(new pcl::PointCloud<pcl::PointXYZ>);

// 创建体素滤波器对象
pcl::VoxelGrid<pcl::PointXYZ> voxel_filter;

// 设置输入点云
voxel_filter.setInputCloud(cloud_in);

// 设置体素大小(单位:米)
voxel_filter.setLeafSize(0.1f, 0.1f, 0.1f);

// 执行滤波
voxel_filter.filter(*cloud_out);

// 输出结果
std::cout << "滤波前点数: " << cloud_in->size() << std::endl;
std::cout << "滤波后点数: " << cloud_out->size() << std::endl;

代码很简单,对吧?但有几个参数细节,我得跟你聊聊。

2.1 setLeafSize 的三个值

setLeafSize 接受三个参数,分别是 x、y、z 方向的体素大小。大多数情况下,三个值设成一样就行,也就是立方体体素。

但有一种情况例外——如果你的点云在某个方向特别稀疏,比如无人机激光雷达扫出来的点云,Z 方向变化不大,你可以把 Z 方向的体素设大一点,减少计算量。

我一般保持三个值一致,省心。

2.2 滤波后的点云数量控制

体素滤波不会直接告诉你输出多少个点。你需要通过调整体素大小来控制。

我有个经验公式:

输出点数 ≈ 点云包围盒体积 / 体素体积

比如,你的点云包围盒是 10m x 10m x 5m = 500 立方米。体素大小 0.1m,体素体积 0.001 立方米。那么输出点数大约 500 / 0.001 = 500,000 个点。

当然,这是理想情况。实际会有空体素,所以点数会少一些。但这个公式能帮你快速估算。

调参技巧:如果你想要输出大约 1 万个点,可以先估算包围盒体积,然后反推体素大小。比如包围盒 500 立方米,想要 1 万个点,体素体积 ≈ 500 / 10000 = 0.05 立方米,体素边长 ≈ 0.37m。然后在这个值附近微调即可。

2.3 进阶用法:保留点云颜色或强度

如果你的点云带有颜色或强度信息,记得用对应的点类型。PCL 支持得很全:

// 带颜色的点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_color(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::VoxelGrid<pcl::PointXYZRGB> voxel_filter_color;
voxel_filter_color.setInputCloud(cloud_color);
voxel_filter_color.setLeafSize(0.1f, 0.1f, 0.1f);
voxel_filter_color.filter(*cloud_out_color);

注意,体素滤波在计算重心时,会同时平均颜色或强度值。所以输出点的颜色是体素内所有点的平均色。这个效果在视觉上很平滑,我个人挺喜欢的。

三、体素滤波的核心逻辑图

为了让你更直观地理解整个过程,我画了一张流程图:

体素滤波(VoxelGrid)核心流程 输入原始点云 设定体素大小(LeafSize) 划分体素网格 体素内取重心/中心点 关键参数说明 体素大小:0.01m ~ 1m 体素越小,保留细节越多 体素越大,降采样越狠 代表点:重心(默认) 或体素中心点 输出点数 ≈ 包围盒体积 / 体素体积 适用:SLAM前端、配准 建图、预处理

这张图把整个流程串起来了。从输入点云开始,设定体素大小,然后划分网格,最后在每个体素内取一个代表点。每一步都很清晰。

四、参数调优:我的实战经验

调参这件事,光看理论没用。我分享几个实际项目中的经验。

4.1 场景不同,体素不同

  • 室内走廊:点云密集,体素 0.02m - 0.05m 即可。太大会丢失墙角等细节。
  • 室外道路:点云稀疏,体素 0.1m - 0.2m 合适。太小了滤波效果不明显。
  • 大范围地图:比如整个园区,体素 0.5m - 1m 都行。主要看你要保留多少细节。

4.2 体素滤波的“副作用”

体素滤波会改变点云的局部密度。原本密集的区域,滤波后依然密集(因为体素小)。原本稀疏的区域,滤波后可能直接消失(因为体素内没点)。

所以,如果你的点云密度分布极不均匀,体素滤波可能会把稀疏区域的点全部滤掉。这时候,我建议你先做一次半径滤波或统计滤波,把离群点处理一下,再做体素滤波。

注意:体素滤波不是万能的。它适合降采样,但不适合去噪。如果你需要去除离群点,请用统计滤波或半径滤波。体素滤波只管“降采样”,不管“去噪”。

4.3 性能优化小技巧

体素滤波的计算量跟点云数量成正比。对于百万级点云,PCL 的体素滤波大概需要几十到几百毫秒。如果你需要实时处理,可以考虑:

  • 先对点云做下采样(比如每隔几个点取一个),减少输入点数。
  • 使用多线程版本的体素滤波(PCL 1.8+ 支持)。
  • 如果体素大小固定,可以缓存体素网格,避免重复划分。

我一般在实时 SLAM 系统中,把体素滤波放在单独的线程里跑,不影响主循环的帧率。

五、总结

体素滤波是点云预处理中最基础、最常用的工具。它的核心就一句话:用空间换效率。

体素大小是唯一的参数,也是最重要的参数。调好了,你的 SLAM 系统跑得飞快。调不好,要么点云太密跑不动,要么点云太稀算法失效。

我建议你从 0.05m 开始试,然后根据输出点数和效果微调。记住,宁小勿大。

好了,体素滤波就讲到这里。下一章,我们聊聊统计滤波——那个专门用来干掉离群点的家伙。

课后练习:找一帧激光雷达数据,分别用 0.01m、0.05m、0.1m、0.5m 的体素大小做滤波,观察输出点数和点云形态的变化。你会发现,体素大小对结果的影响,比你想的还要大。

可可精品资料库