一、点云滤波概述

1.1 点云噪声从哪来?

做SLAM这些年,我见过太多被噪声坑惨的项目了。说白了,点云噪声就是传感器采集时混入的「脏数据」。你想想看,激光雷达扫一圈,总有些不该出现的东西混进来。

常见的噪声来源,我归纳为三类:

  • 传感器自身噪声——激光雷达的测量误差、多路径反射、边缘效应。我记得有一次调试Velodyne,墙角处总出现一些飘忽的点,后来发现是激光打到玻璃上产生了二次反射。
  • 环境干扰——雨雪雾天、阳光直射、动态物体(行人、车辆)。我在做园区无人车时,夏天正午的强光直接让雷达数据炸了,满屏都是飞点。
  • 运动畸变——扫描过程中传感器或目标在动,导致点云变形。这个在手持SLAM里特别常见,你边走边扫,墙都给你扫歪了。

核心认知:噪声不是「有没有」的问题,而是「有多少」的问题。滤波的目的不是消除所有噪声——那不可能——而是把信噪比提升到算法能接受的程度。

1.2 滤波在SLAM中到底多重要?

说实话,早期我做SLAM时对滤波不太上心。觉得算法够强就行,噪声让后端优化去扛。结果呢?前端匹配经常崩,回环检测误报一堆。

后来我总结出滤波的三个关键作用:

  1. 提升配准精度——ICP、NDT这类算法对离群点极其敏感。一个飞点就能把变换矩阵带偏几厘米。你想想看,几厘米的误差累积起来,地图不飘才怪。
  2. 减少计算量——原始点云动不动几十万点,全量处理太慢了。滤波后保留关键点,速度能快一个数量级。我习惯在预处理阶段先降采样,后面所有环节都受益。
  3. 改善地图质量——滤波能去掉漂浮点、平滑表面、填补小空洞。最终建出来的地图干净整洁,做导航也好,做可视化也罢,都舒服得多。

我的经验:滤波不是越狠越好。过度滤波会丢失细节特征,比如墙角、边缘这些对SLAM至关重要的结构。找到那个「恰到好处」的平衡点,才是真功夫。

1.3 常见滤波算法分类

滤波算法五花八门,但按思路分,其实就三大流派。我画了张图帮你理清脉络:

点云滤波算法分类 点云滤波算法 基于统计的滤波 基于邻域的滤波 基于投影的滤波 统计滤波 (SOR) 半径滤波 体素滤波 (VoxelGrid) 均匀采样滤波 直通滤波 (PassThrough) 条件滤波 特点对比 统计滤波:去离群点强,适合稀疏噪声 体素滤波:降采样快,适合预处理 | 直通滤波:简单粗暴,适合裁剪ROI

下面我逐个说说每种算法的核心思路:

基于统计的滤波

这类算法的核心假设是:噪声点周围的点密度明显低于正常点。说白了,好点都是扎堆的,坏点都是孤零零的。

统计滤波(SOR)——我最常用的去噪方法。它计算每个点到k个最近邻的平均距离,然后假设这个距离服从高斯分布。距离超过均值±n倍标准差的,直接干掉。

// PCL中的统计滤波示例
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(50);          // 近邻点数
sor.setStddevMulThresh(1.0); // 标准差倍数
sor.filter(*cloud_filtered);

避坑指南:我曾经在稀疏点云上用过大的MeanK值,结果把正常点全滤掉了。后来学乖了——稠密场景用50-100,稀疏场景用10-20。另外,stddevMulThresh设1.0比较激进,设2.0更保守,看你的容忍度。

半径滤波——更直观的思路:在指定半径内,如果邻居点少于某个阈值,就认为是噪声。它比SOR更「硬核」,适合处理那些明显孤立的飞点。

基于邻域的滤波

这类算法不关心「是不是噪声」,而是关心「怎么用周围点来代表这个区域」。说白了就是降采样,保留结构,减少数据量。

体素滤波(VoxelGrid)——SLAM预处理的标准操作。把空间切成小立方体(体素),每个体素内所有点取重心或中心。体素大小是关键参数:设大了细节丢失,设小了降采样效果不明显。

// PCL体素滤波
pcl::VoxelGrid<pcl::PointXYZ> vg;
vg.setInputCloud(cloud);
vg.setLeafSize(0.05f, 0.05f, 0.05f); // 5cm体素
vg.filter(*cloud_filtered);

我的习惯:室内场景用2-5cm体素,室外场景用10-20cm。体素大小最好和地图分辨率匹配,这样后续处理更顺畅。

均匀采样滤波——和体素滤波类似,但它保证每个体素内最多保留一个点。适合对点云密度均匀性有要求的场景。

基于投影的滤波

这类算法最简单,也最容易被忽视。它的思路是:根据某个条件直接「切一刀」。

直通滤波(PassThrough)——指定一个轴的范围,范围内的保留,范围外的扔掉。比如我只关心前方10米内的点,那就把x轴限制在0-10米。

// 直通滤波:只保留z轴在-1到1米之间的点
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud);
pass.setFilterFieldName("z");
pass.setFilterLimits(-1.0, 1.0);
pass.filter(*cloud_filtered);

条件滤波——直通滤波的升级版,可以组合多个条件。比如「x在0-10米且z在-1到1米且反射强度大于50」。适合做精细的ROI提取。

总结一下:滤波没有银弹。我通常的流程是:先用直通滤波裁剪ROI,再用体素滤波降采样,最后用统计滤波去噪。三步走下来,数据质量基本就稳了。

嗯,这一章先聊到这儿。滤波这东西,光看理论不够,得动手调参数才能找到感觉。后面几章我会带大家逐个算法深入实战,到时候咱们再细聊。

可可精品资料库