刚开始接触点云的人,十有八九都会被原始数据的“脏乱差”劝退。一帧64线激光雷达的点云,动辄一两万个点,里面既有远处建筑物墙面上密密麻麻的反射点,也有空中漂浮的飞点、车体近旁的杂散噪声,偶尔还有几根像“鬼影”一样的细长亮线。如果不做任何处理直接送去配准或分割,轻则算法运行慢得让人怀疑人生,重则结果直接崩坏。这就是“点云滤波”存在的意义——它是点云处理流水线里最不起眼、却决定上下游成败的一环。
这篇文章是点云处理系列的第二篇,专门讲滤波。我会从“噪声到底从哪来”讲起,把PCL(Point Cloud Library)里常用的几种滤波算法逐个拆开,结合我在车载激光雷达数据处理里的实际经验,给出能直接抄作业的参数配置、滤波顺序和问题排查方法。无论你是刚入门的学生,还是已经在搞三维重建、SLAM、自动驾驶感知的工程师,这篇都能帮你少踩几个坑。
1. 为什么要滤波:点云里的“脏乱差”从哪来
很多人觉得滤波就是“把没用的点删掉”,这个理解没错,但太粗了。想把滤波做好,你得先搞清楚点云里的噪声和无效点到底是怎么产生的,才能针对性地选对滤波器。
1.1 分清“稀疏离群点”和“大尺度噪声”
点云里的噪声,按尺度和来源大致可以分成三类。
第一类是稀疏离群点,典型的像激光雷达打到玻璃幕墙、水面、雨雾这些地方产生的飞点。这类点通常位置随机、分布稀疏、和周围点没有明显的几何关联。它们对点云配准的杀伤力尤其大——很多配准算法(比如ICP)是按最近邻匹配算误差的,几个远离真实表面的飞点会把误差项彻底带歪,让迭代收敛到错误位置。
第二类是低频的大尺度噪声,常见于机械式雷达的近场区域。雷达底座附近的点、车辆自身反光部件产生的回波,会形成一团团位置漂移的低质量点。这类点体积大、数量多,光靠“去离群点”的思路搞不定,得用直通滤波或者条件滤波先把空间范围卡住。
第三类是重叠冗余点,严格说不是噪声,却会拖垮后续算法。比如同一面墙,不同线束的激光打上去会产生密集的点云簇,几万个点挤在一起,几何信息却只有那么点。这类冗余点对配准、分割没帮助,反而让计算量成倍增长,需要靠降采样来处理。
我在实际项目里见过不少新手拿着统计滤波的默认参数去处理所有场景,结果该去的飞点没去掉,反而把墙面边缘的细节点给误删了。原因很简单:不知道点云噪声的来源构成,自然没法判断该用哪种滤波器。
1.2 滤波在点云处理流水线中的位置
滤波不是终点,它是给后续步骤“打底”的。完整的点云处理流程通常是:传感器采集原始点云 → 滤波清洗 → 配准/分割/特征提取 → 建图或目标识别。滤波做得好不好,直接决定了后面配准能不能收敛,分割能不能把地面和障碍物干净地分开。
我在做地形配准的时候,一开始不重视滤波,直接用原始点云跑配准,结果迭代了上百次都不收敛。后来先把离群点去掉,再用体素降采样把点数压到十分之一,配准一次搞定,耗时从十几秒降到了两秒以内。这就是滤波的价值——它不是可有可无的预处理,而是直接决定上下游算法成败的环节。
2. 六种常用滤波方法逐个拆解
PCL里封装的滤波方法不少,但真正高频使用的,每天日常就是直通、体素、统计、半径这“四大金刚”,再加条件和双边两个特殊场景工具。下面我把它们逐一说透。
2.1 直通滤波:最快的“一刀切”
直通滤波(PassThrough Filter)的原理最简单:指定一个坐标轴(比如Z轴),设定一个最小值和一个最大值,凡是落在范围外的点直接删除。说得直白点,就是拿一把“大铡刀”,沿着某个方向把点云切一刀。
这把铡刀什么时候用?场景通常非常固定。我做一个室外扫描项目,雷达装在地面以上1.5米的位置,那么地面以下和天空以上的点基本都可以不要。Z轴方向卡一个范围,比如-2到30米,所有不可能有用的点全被切掉,计算量瞬间减半。处理厂区道路时,雷达后方和侧面的点很多是车身反射和无效区,也可以直接用直通滤波在X方向卡一个正前方区间。
实现上,PCL的代码就几行:
#include <pcl/filters/passthrough.h> pcl::PassThrough<pcl::PointXYZ> pass; pass.setInputCloud(cloud_in); pass.setFilterFieldName("z"); // 沿Z轴裁剪 pass.setFilterLimits(-2.0, 30.0); // 保留范围 // 注意:默认保留范围内,setFilterLimitsNegative(true)则是取反 pass.filter(*cloud_out);有个细节容易踩坑:setFilterLimitsNegative这个参数,默认是false,表示保留区间内的点。如果你搞反了,会把有用的点全部删光,只剩无效点。我在调试时犯过这个错误,检查半天才发现是取反逻辑写反了。
直通滤波虽然简单,但它解决了一个很实际的问题:大幅缩减后续算法的计算量。点云处理里,每多一个点,KD-Tree构建、邻域搜索、特征计算都会增加耗时。先用直通把空间范围压缩到感兴趣区域,等于帮后面的算法划定了一个“工作小区”。
2.2 体素滤波:用网格“压缩”点云密度
体素滤波(VoxelGrid Filter)是处理重叠冗余点的首选工具。它的思路是把整个点云空间划分成一个个固定边长的小立方体(也就是体素),每个立方体里的所有点合并成一个代表点。代表点可以是网格内所有点的重心(centroid),也可以是离网格中心最近的那个点。
体素边长的选择非常关键。边长太大,点云会失去细节,墙面轮廓变得粗糙;边长太小,降采样效果不明显,计算量仍然很大。我常用的经验值是:室外场景10厘米左右,室内精细建模2到5厘米,机械雷达原始帧可以放宽到20厘米。
#include <pcl/filters/voxel_grid.h> pcl::VoxelGrid<pcl::PointXYZ> voxel; voxel.setInputCloud(cloud_in); voxel.setLeafSize(0.1f, 0.1f, 0.1f); // 体素边长:10cm voxel.filter(*cloud_out);用体素滤波的时候要注意头顶的“蜂窝效应”。当体素网格和实际物体表面成一定角度时,降采样后的点云表面会呈现出规则的网格纹理,看起来像蜂窝一样。对视觉美观影响不大,但对要求高精度的配准任务,这种纹理可能会引入系统性偏差。解决办法是把体素边长选得比预期精度低一档,或者在体素后接一个轻量的平滑滤波(比如移动最小二乘)。
2.3 统计滤波:用数学方法识别离群点
统计滤波(StatisticalOutlierRemoval)是去稀疏离群点的主力。它的原理有点意思:对每一个点,计算它到所有k个最近邻的平均距离。假设全局点云密度分布均匀,那么每个点到邻居的平均距离应该符合高斯分布。离群点的平均距离会远超正常点的平均距离,落在高斯分布的尾巴上,把它们识别出来删掉就行。
这么说可能有点抽象,我换个生活化的类比。想象一个广场上站着的人群,大部分人三五成群,间距差不多。忽然有个醉汉独自躺在广场边缘30米外,周围没有人。统计滤波做的就是:绕每个人走一圈,测量他和身边几个人的平均距离。大部分人的平均距离是两三米,这个醉汉的平均距离是30米,远远偏离平均水平,立刻就会被标记为可疑对象。
代码写法:
#include <pcl/filters/statistical_outlier_removal.h> pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor; sor.setInputCloud(cloud_in); sor.setMeanK(30); // 近邻点数 sor.setStddevMulThresh(1.0); // 标准差倍数阈值 sor.filter(*cloud_out);参数上,setMeanK一般取20到50,取决于点云密度。密度大就取大一点,密度小就取小一点。setStddevMulThresh取1.0到2.0,值越小删得越狠。我通常在第一次处理时用1.0,看效果再微调。如果发现墙面上的有效点被误删,就往大了调,比如1.5或2.0。
统计滤波有个天生弱点:对“密度不均匀”的场景不友好。一个点云里既有密集的近场墙面(距离近,点间距小),又有稀疏的远场目标(距离远,点间距大),全局平均距离会被拉偏,远场点很容易被整体误删。这种情况我有两个办法,一是先裁剪出感兴趣区域再滤波,二是用半径滤波替代。
2.4 半径滤波:只看局部密度
半径滤波(RadiusOutlierRemoval)和统计滤波目标类似,但判断逻辑更直接:以每个点为中心画一个半径为r的圆(三维里是球),数一数这个圆内有多少个邻居点,如果少于阈值min_pts,就判定为离群点删除。
这个滤波器跟统计滤波比,优势在于它只关心局部密度,不受全局分布影响。适合处理点云密度差异大的场景。比如地形点云里,近处地面点密度高,远处山体点密度低,用统计滤波容易把远山的点整体误删,但半径滤波只要把半径调大一点、阈值调低一点,就能保留远处的有效地形。
#include <pcl/filters/radius_outlier_removal.h> pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror; ror.setInputCloud(cloud_in); ror.setRadiusSearch(0.1); // 搜索半径:10cm ror.setMinNeighbors(5); // 少于5个邻居则删除 ror.filter(*cloud_out);半径和邻居数目的搭配要动点脑筋。一般先用点云的平均点间距来估算半径,相邻点平均间距的2到3倍是一个比较合理的起始值。邻居数目阈值设低一点,比如3到5个,宁可多保留,也不能把边缘特征点删掉。
2.5 条件滤波与特殊场景方法
条件滤波(ConditionalRemoval)是一个多条件的组合过滤器。你可以对点云的每个字段设置条件,比如同时限制X在5到50米之间、Z在-1到2米之间、intensity大于50,把这些条件全部满足的点保留下来。它适合对点云属性有明确要求、需要精细筛选的场景。
不过条件滤波有个大坑:每增加一个条件,代码量就翻倍,而且组合条件多了以后,可读性很差。我的建议是,能用直通滤波完成的简单裁剪就别用条件滤波,它真正发光的场景是筛选带反射强度的点云——比如提取路面上高反射的斑马线、车道线。
双边滤波(BilateralFilter)则是另一种思路,它不是删点,而是“平滑”点的位置,让点的法向量方向更加一致。对存在随机抖动的扫描点云有奇效,但计算量比较大,不建议在全场景乱用。
我把这几种方法的核心特性整理成了一张表,方便对比:
| 滤波器 | 核心思路 | 主要用途 | 关键参数 | 适用场景 |
|---|---|---|---|---|
| 直通滤波 | 坐标轴范围截断 | 快速裁剪、限制ROI | 轴、范围 | 任意通用场景 |
| 体素滤波 | 栅格化降采样 | 降点数、控密度 | 叶子尺寸 | 点密度过高 |
| 统计滤波 | 邻域距离统计分布 | 稀疏离群点 | 邻域数、标准差倍数 | 全局密度均匀 |
| 半径滤波 | 局部邻域数量 | 孤立点、飞点 | 搜索半径、最小邻域数 | 密度差异大 |
| 条件滤波 | 多字段组合条件 | 精细筛选 | 各字段条件 | 带强度/色彩信息 |
| 双边滤波 | 位置平滑 | 减小法线抖动 | 邻域、sigma | 高质量重建 |
3. 实操:一帧64线点云的完整滤波流程
光讲原理不落地就是耍流氓。下面我用一个真实场景演示完整的滤波流程——一帧64线机械式激光雷达采集的户外道路点云,原始点数约12万个,主要目标是为后续的点云配准准备干净数据。
3.1 第一步:直通滤波先划定“工作区”
拿到原始点云的第一件事,永远是用直通滤波把空间范围卡到感兴趣区域。因为体素滤波和统计滤波都需要构建空间索引,点云越大,构建索引越慢。先在空间上砍掉一大半无效点,后面所有操作都会快很多。
这帧数据的雷达装在车顶,离地约1.8米,朝前扫。场景里有用的区域是前方0到50米、左右各20米、高度从地面到5米。直接写三个方向的裁剪:
#include <pcl/filters/passthrough.h> pcl::PassThrough<pcl::PointXYZ> pass_x, pass_y, pass_z; pass_x.setInputCloud(cloud_raw); pass_x.setFilterFieldName("x"); pass_x.setFilterLimits(0.0, 50.0); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_x(new pcl::PointCloud<pcl::PointXYZ>()); pass_x.filter(*cloud_x); pass_y.setInputCloud(cloud_x); pass_y.setFilterFieldName("y"); pass_y.setFilterLimits(-20.0, 20.0); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xy(new pcl::PointCloud<pcl::PointXYZ>()); pass_y.filter(*cloud_xy); pass_z.setInputCloud(cloud_xy); pass_z.setFilterFieldName("z"); pass_z.setFilterLimits(-2.0, 5.0); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_roi(new pcl::PointCloud<pcl::PointXYZ>()); pass_z.filter(*cloud_roi);三步走完,12万个点大概会砍掉一半左右,剩下6万多个点。这一步几乎不耗时,属于性价比极高的操作。
3.2 第二步:体素降采样压缩数据量
接下来处理重叠冗余点。我选了一个10厘米的体素边长。选这个值的逻辑是:配准算法需要的几何特征在10厘米尺度上已经保留得很完整,再往上比如20厘米,墙面拐角和小物体的轮廓就会被抹得太厉害;再往下比如5厘米,点数虽然又翻了一倍,但配准精度提升非常有限,不划算。
#include <pcl/filters/voxel_grid.h> pcl::VoxelGrid<pcl::PointXYZ> voxel; voxel.setInputCloud(cloud_roi); voxel.setLeafSize(0.1f, 0.1f, 0.1f); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_down(new pcl::PointCloud<pcl::PointXYZ>()); voxel.filter(*cloud_down);降采样之后,点云从6万多个点变成大约1.5万个点,压缩到四分之一。肉眼看去,墙面轮廓依然清晰,只是点变稀了。体素滤波最大的优势就在这儿——用很小的几何信息损失,换来了计算量的巨大下降。
3.3 第三步:统计滤波清除飞点
降采样之后的点云里还有一些残留的飞点,主要来自雷达打到远处树木边缘、雨雾等产生的稀疏离群点。这时候用统计滤波来处理。考虑到此时点云密度已经因为体素降采样变得比较均匀,用统计滤波是比较合适的。
参数我选的是:邻域点30、标准差倍数1.0。这是我处理室外路试点云的一个稳定组合,能把绝大多数飞点干掉,同时保留墙面、树干、路沿这些有明确几何结构的点。
#include <pcl/filters/statistical_outlier_removal.h> pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor; sor.setInputCloud(cloud_down); sor.setMeanK(30); sor.setStddevMulThresh(1.0); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_clean(new pcl::PointCloud<pcl::PointXYZ>()); sor.filter(*cloud_clean);这一步之后,点云从1.5万个点变成约1.3万个点,删掉了约2000个离群点。你可能会觉得删得不够多,但滤波的原则是:能少删就少删,只删确定有问题的。删多了,真正有价值的信息也被带走了。
3.4 第四步:用CloudCompare快速验证效果
命令行的代码跑完,一定要用可视化工具确认输出结果。我习惯用CloudCompare打开原始点云和滤波后的点云,左右分屏对比。
打开滤波后的点云,重点检查三件事:墙面轮廓是否平滑、远处地面是否还有大片飞点、近处小物体(比如路障锥桶)是不是被误删了。如果飞点残留多,说明统计滤波的阈值太宽松,可以把setStddevMulThresh降到0.8;如果小物体被误删,说明阈值太严格,把它升到1.5。
记住,CloudCompare本身也内置了滤波工具——Tools菜单下的“Filter by Value”可以做直通滤波,“Subsample”可以做体素降采样,“Noise filter”可以去除离群点。平时做快速验证,我经常直接拿CloudCompare调参,确定了合适参数再回代码里写死,这样效率非常高。
4. 滤波参数到底怎么定:一组能直接抄的经验值
很多新手最头疼的不是不会写代码,而是不知道参数怎么填。这节我直接给出几组覆盖典型场景的经验参数,大家先拿去用,再根据实际效果微调。
4.1 体素边长的选择逻辑
体素边长是体素滤波唯一的核心参数,其它都是辅助。选它的核心逻辑是:让体素边长约等于你这个任务需要保留的最小特征尺寸的一半。
打比方说,如果后续要做建筑物轮廓提取,墙体拐角、窗框边缘这种特征,尺寸大概在10到20厘米级别,那么体素边长取5到10厘米是安全的。如果只做车辆级别的目标检测,车辆长宽以米计,体素取20到30厘米都不心疼。
不同传感器和场景,我也整理了一个参考表:
| 应用场景 | 体素边长 | 说明 |
|---|---|---|
| 室内精细建模(扫描仪) | 0.02~0.05m | 保留家具、墙壁细节 |
| 室外地形扫描(无人机) | 0.1~0.2m | 平衡精度与计算量 |
| 车载雷达(机械式64线) | 0.1~0.2m | 道路场景特征尺度较大 |
| 车载雷达(固态式) | 0.05~0.1m | 点云密度更高,可细一点 |
| 大规模城市建模 | 0.2~0.5m | 重点关注整体结构 |
4.2 统计滤波与半径滤波参数联动调法
统计滤波的邻域数k和标准差倍数,是两个联动参数。k决定“我一个点参考多少个邻居来算平均距离”,标准差倍数决定“距离均值超过多少倍标准差才算离群”。
我调试的套路是这样的:先把标准差倍数固定为1.0,从k=20开始试,观察滤波后点云里还有没有飞点残留。有残留就加大k,比如30、50,让距离统计更稳定。k太大比如超过100,会把近处密集点之间的正常距离波动也识别为离群,导致误删。
半径滤波的调试思路类似,但更直观。先估算点云的平均点间距,然后让搜索半径等于2到3倍的点间距,邻居数阈值设置为5。在这个基础上,如果飞点没清干净,就把半径加大;如果边缘被啃掉,就把邻居数阈值调低。
这里有一个重要提醒:参数必须跟着场景走,没有“一劳永逸”的万能参数。同一个滤波参数,在空旷道路上跑得好好的,进了地下车库可能就崩盘——因为地下车库墙面平整、点云密度分布完全不一样。我的做法是:给每个典型场景配一套参数,写进配置文件里,切换场景时自动加载。
5. 滤波顺序有讲究:先粗后细还是先降后滤
滤波的先后顺序,看着是个小问题,实际影响很大。顺序不对,出来的结果可能截然不同。
5.1 推荐流程:先直通、再降采样、最后去离群
我在大多数项目里采用的流程是:直通裁剪 → 体素降采样 → 离群点去除。这个顺序背后的逻辑是逐步“瘦身”:先砍掉无效空间,再压缩冗余点,最后才处理稀疏离群点。
为什么要先直通?因为空间裁剪之后,KD-Tree的构建范围变小,后续的邻域搜索更快,连体素滤波的栅格划分都更快。为什么要先体素再去离群?因为体素降采样自带“平均”效果——如果一个飞点恰好和几个正常点落在同一个体素里,重心计算会把这个飞点的影响分摊掉,相当于做了次轻量去噪。把降采样放前面,统计滤波处理的数据量也小得多。
但这里有个例外情况需要单独说。如果原始点云里飞点特别多,比如雨雾天或者场景里有大量玻璃幕墙,我建议把统计滤波放到体素滤波前面。因为体素会把飞点和正常点“揉”在一起,产生一些处于半空中的假点,再去统计滤波就比较难识别了。先统计滤波把飞点干掉,再体素降采样,质量更高,代价是统计滤波的计算量会大不少。
5.2 不同下游任务对滤波顺序的敏感度
不同下游任务对滤波顺序的敏感度差别很大。
做点云配准,我强烈建议先直通、再统计、最后轻量体素(或者干脆不做体素)。因为配准算法需要足够多的几何特征点来寻找对应关系,体素太大等于把特征点全抹掉了。我做过对比:同一帧点云,体素边长10厘米时配准能收敛,边长30厘米时直接发散。
做点云分割(比如地面分割、障碍物聚类),顺序可以随意一点,但建议保留统计滤波环节。因为分割算法对离群点特别敏感,几个孤立的飞点可能会被误判成一个小聚类,产生大量假目标。
做三维重建(比如墙面建模),要求保留尽可能多的原始几何信息,所以体素滤波的边长要小,甚至可以只做离群点去除,不做降采样。重建质量优先于计算效率。
5.3 滤波“用完即停”,别画蛇添足
还有一个常见的毛病:滤波链太长了。见过一些同学把直通、体素、统计、半径、条件五六个滤波器全串一遍,每个都是默认参数,跑完发现点云都快被删没了,还觉得“很干净”。
滤波的原则是“够用就好”。点云处理是信息提取的过程,删掉的每个点都是不可恢复的信息损失。我的经验是,大多数场景三步滤波就够:直通 + 体素 + 统计,再加一个半径滤波处理特殊情况就行。多不等于好,能不滤波就不滤波,非要滤波就从最简单的组合开始。
6. 常见问题与排查实录
最后的最后,分享一些我在实际调试滤波时遇到过的坑。这些都是文档里不常写、只有亲手跑过才会懂的细节。
6.1 滤波后点云出现“空洞”怎么办
体素滤波之后,点云表面常常会出现一些不规则的“空洞”——某些区域本来应该有密集的点,却被抽稀成了空荡荡的缺口。这不是算错了,而是体素网格和点云表面相交时,网格内点数太少,重心偏移导致的。
排查思路分两步。第一步,确认是不是体素边长设置过大。点云平均点间距是3厘米,你把体素边长设成30厘米,那一个体素可能覆盖了本该有100个点的区域,最后只保留1个点,空洞明显是正常的。第二步,确认是不是统计滤波的参数过激。标准差倍数调到1.0甚至0.8,点云边缘密度低的区域很容易被整体误删成空洞。
处理方法:把体素边长和平均点间距的比例控制在3到5倍以内,标准差倍数不低于1.0。
6.2 离群点死活去不掉
有一类离群点特别顽固:它们密度不低,还成片出现。比如激光雷达扫描到水面,会形成一片连续的杂散点,形状不规则,但密度和正常点云差不多。统计滤波和半径滤波对这类点基本束手无策——它们按“局部密度是否异常”来判断,而这帮家伙密度完全正常。
对付这类场景,得回到源头想办法。一个是调整直通滤波的范围,直接从空间上排除水面区域。另一个是用基于反射强度(intensity)的条件滤波——水面和正常路面的反射强度有明显差异,设定一个强度阈值就能过滤掉。实在不行,就得靠分割算法把“长得很奇怪”的聚类单独分出来删掉,那就超出滤波的范畴了。
6.3 滤波后点云“太干净”,反而更难配准
这是新手最容易困惑的反直觉问题:滤波太狠,点云是干净了,配准却失败了。
原因在于配准算法依赖几何特征的丰富程度。墙面上一片平坦的区域,点云再密,特征也很单一,配准算法很难从中找到准确的对应关系。真正帮助配准收敛的是墙角、柱子、路沿石这些有明确几何突变的位置。体素边长太大会把这些特征磨平,统计滤波太狠会把稀疏的特征点误删。
我的经验是:给配准任务准备数据时,体素边长宁小勿大,统计滤波的标准差倍数宁大勿小。配准质量下降时,优先怀疑滤波参数太激进。
6.4 关于RVIZ可视化点云滤波结果
如果你在做ROS相关的点云处理,RVIZ可能是你最高频的可视化工具。我见过不少人直接在RVIZ里看滤波后的点云,觉得效果不错就完事了,但实际上RVIZ默认的点云显示对点的大小、颜色范围都会做自动适配,有些细节问题在RVIZ里看不出来。
我的习惯是:RVIZ只做粗看,确认大体范围、大致结构没问题;精细检查(比如局部空洞、边缘锯齿)一定要导出到CloudCompare或MeshLab里放大看。另外,RVIZ里调节点云大小(Size)和衰减时间(Decay Time)能帮你更清楚地观察滤波效果,这在调试滤波参数时是个实用的小技巧。
6.5 典型问题速查表
| 现象 | 可能原因 | 解决方向 |
|---|---|---|
| 点云整体稀疏,细节丢失 | 体素边长过大 | 缩小体素边长 |
| 点云密度不均匀,近密远疏 | 未做空间裁剪 | 先直通裁剪ROI |
| 墙面上出现斑点状空洞 | 统计滤波阈值过严 | 增大标准差倍数 |
| 飞点残留多 | 统计滤波邻域数不足 | 增大MeanK或降低倍数 |
| 远处有效点被整体删除 | 全局统计偏差 | 换半径滤波 |
| 配准不收敛 | 特征点被滤波抹掉 | 减小体素,放宽统计阈值 |
| 分割出现大量假聚类 | 离群点未清理干净 | 增加统计或半径滤波 |
这套问题排查思路,基本能覆盖日常工作中遇到的大部分滤波异常。真碰上搞不定的,建议把原始点云和滤波后的点云对比着看,一层层排查到底哪一步出了问题。
做点云滤波关键还是要多看多调。每个场景点云的特性都不一样,参数没有绝对的对错,只有适不适合。我早期做滤波,也经历过参数调到怀疑人生的阶段,后来慢慢摸索出了“先定流程、再调参数、最后验证”的套路,才把这件小事做得相对顺手。希望这篇文章能让你少走几步弯路,把更多时间留给真正有意思的点云算法。