4、点云预处理:离群点滤除、体素滤波、直通滤波
各位同学,咱们今天聊聊点云预处理。说实话,很多做飞控的朋友拿到激光雷达数据,第一反应就是直接往上怼算法。我当年也这么干过,结果嘛……嗯,炸机了。后来才明白,原始点云就像一块刚从矿里挖出来的石头,杂质太多,不经过打磨根本没法用。
预处理做得好,后面的定位、建图、避障才能稳。今天我就把三个最常用的滤波手段掰开揉碎了讲给你听。
4.1 离群点滤除:把“野点”揪出来
什么叫离群点?说白了就是那些明显不合理的点。比如你扫描一面墙,结果墙外面突然飘着一个孤零零的点——这大概率是传感器噪声或者多路径反射造成的。
我在项目中遇到过最典型的情况:无人机在树林里飞行,树叶晃动导致雷达打到半空中的水滴或者昆虫,这些点如果不去掉,飞控会以为前方有障碍物,然后莫名其妙地急刹车。
常用的方法叫统计滤波。思路很简单:
- 对每个点,计算它到周围K个邻居的平均距离
- 如果这个平均距离超出了全局均值的某个标准差范围,就干掉它
代码实现也不复杂,我贴一段我常用的PCL代码:
// 统计离群点滤除
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(50); // 邻居点数
sor.setStddevMulThresh(1.0); // 标准差倍数
sor.filter(*cloud_filtered);
4.2 体素滤波:给点云“减肥”
你想想看,一个16线激光雷达每秒能产生30万个点,32线、64线就更夸张了。这么多点直接扔给飞控,算力根本扛不住。体素滤波就是用来降采样的。
它的原理特别直观:把空间划分成一个个小立方体(体素),每个立方体里只保留一个点(通常是重心点)。体素越大,点云越稀疏,计算量越小,但细节损失也越大。
我习惯用0.1m的体素尺寸做预处理,既能保留环境轮廓,又能把点云量压缩到原来的十分之一左右。代码长这样:
// 体素滤波降采样
pcl::VoxelGrid<pcl::PointXYZ> vg;
vg.setInputCloud(cloud_filtered);
vg.setLeafSize(0.1f, 0.1f, 0.1f); // 体素边长0.1米
vg.filter(*cloud_downsampled);
4.3 直通滤波:只保留感兴趣的区域
这个最简单,也最实用。直通滤波就是沿着某个坐标轴切一刀,只保留范围内的点。
比如无人机在2米高度飞行,你根本不需要关心地面以下1米的点,也不需要关心头顶10米以上的点。把范围限定在高度1~3米,既能减少计算量,又能排除地面反射和天空噪声的干扰。
我一般在飞控里同时做X、Y、Z三个方向的直通滤波:
// 直通滤波 - 只保留前方10米,左右5米,高度1~3米
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud_downsampled);
pass.setFilterFieldName("z");
pass.setFilterLimits(1.0, 3.0); // 高度范围
pass.filter(*cloud_z);
pass.setInputCloud(cloud_z);
pass.setFilterFieldName("x");
pass.setFilterLimits(0.0, 10.0); // 前方范围
pass.filter(*cloud_x);
pass.setInputCloud(cloud_x);
pass.setFilterFieldName("y");
pass.setFilterLimits(-5.0, 5.0); // 左右范围
pass.filter(*cloud_final);
核心逻辑总结:
离群点滤除 → 体素滤波 → 直通滤波,这个顺序我建议不要乱。先去掉噪声,再降采样,最后切出感兴趣区域。每一步都在为下一步减轻负担。
4.4 三种滤波的对比
| 滤波方法 | 主要作用 | 关键参数 | 计算开销 |
|---|---|---|---|
| 统计离群点滤除 | 去除噪声点、飞点 | MeanK, StddevMulThresh | 中 |
| 体素滤波 | 降采样、减少数据量 | LeafSize | 低 |
| 直通滤波 | 限定空间范围 | FilterLimits | 极低 |
你看,这三种滤波各有各的用处。离群点滤除是“清垃圾”,体素滤波是“压缩文件”,直通滤波是“划重点”。三者配合好了,你的点云数据才能又快又准地喂给飞控。
好了,点云预处理这块我就讲这么多。记住一句话:预处理做得好,飞控稳得像老狗;预处理偷懒,炸机就在下一秒。你自己琢磨琢磨是不是这个理儿。