3. 点云分割与聚类:基于欧几偶里得聚类的目标分割、DBSCAN算法在点云中的应用

点云分割与聚类,说白了就是把一堆乱七八糟的点,按照物体分堆。你想想看,激光雷达扫回来几万个点,里面可能有车、有行人、有树、有路灯。如果不把它们分开,后续的跟踪就没法做。

我个人习惯把聚类看作是感知系统的「第一道筛子」。筛得好不好,直接决定了后面目标识别的上限。今天我们就聊聊两种最常用的方法:欧几里得聚类和DBSCAN。

点云聚类 欧几里得聚类 距离阈值 r 最小点数 minPts DBSCAN eps 邻域半径 核心点/边界点/噪声 核心区别 欧几里得:基于全局固定距离阈值 DBSCAN:基于密度连通性,自动处理噪声 应用场景:自动驾驶目标分割 | 障碍物检测 | 点云预处理

3.1 欧几里得聚类:最直观的分割方式

欧几里得聚类,名字听着挺唬人,其实原理特别简单。就是算两个点之间的直线距离,如果距离小于某个阈值,就认为它们属于同一个物体。

我在项目中第一次用这个方法时,觉得它太「笨」了。但后来发现,对于稀疏的远距离点云,它反而比那些花哨的算法更稳定。嗯,这里要注意一个关键点:阈值的选择。

核心思想:在三维空间中,同一个物体表面的点,彼此之间的距离通常很近。不同物体之间,存在明显的空隙。

算法流程其实就三步:

  1. 选一个种子点:从点云中随便挑一个没处理过的点
  2. 找邻居:用KD-Tree快速搜索半径 r 内的所有点
  3. 生长:把这些邻居点加入当前簇,再以它们为种子继续找

重复以上步骤,直到所有点都被标记。说白了就是个「滚雪球」的过程。

我的经验:阈值 r 怎么设?我一般先看激光雷达的水平角分辨率。比如32线雷达,10Hz频率下,100米处相邻两线的间距大概在0.3米左右。所以 r 设在0.5-1.0米比较合理。太小了会把一辆车切成好几块,太大了会把车和旁边的树粘在一起。

代码实现其实不复杂,PCL库里有现成的:

// PCL 欧几里得聚类示例
pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
ec.setClusterTolerance(0.5);      // 距离阈值 0.5米
ec.setMinClusterSize(100);        // 最少100个点才算一个目标
ec.setMaxClusterSize(25000);      // 最多25000个点
ec.setSearchMethod(tree);         // KD-Tree加速
ec.setInputCloud(cloud);
ec.extract(cluster_indices);      // 输出聚类结果

你看,核心参数就三个:距离阈值、最小点数、最大点数。最小点数这个参数,我吃过亏。有一次把路边的几个噪点也聚类成一个目标,结果跟踪模块一直报假警。后来我设了最小点数100,这种误检就少了很多。

3.2 DBSCAN:能处理噪声的密度聚类

欧几里得聚类有个硬伤:它假设所有区域的点云密度是一样的。但实际场景中,近处的点密密麻麻,远处的点稀稀拉拉。用同一个阈值,近处容易过分割,远处容易欠分割。

DBSCAN 就是来解决这个问题的。它不看绝对距离,而是看密度。一个点周围如果足够「热闹」,它就是核心点;如果周围冷冷清清,它就是噪声。

DBSCAN 的三个关键概念:

  • 核心点:在半径 eps 内至少有 minPts 个点
  • 边界点:在核心点的邻域内,但自身不满足核心点条件
  • 噪声点:既不是核心点也不是边界点

算法怎么工作的?我简单说说:

  • 先找一个核心点,把它邻域内的所有点都拉进来
  • 对这些新拉进来的点,如果它们也是核心点,继续扩张
  • 直到没有新的核心点可以加入,一个簇就形成了
  • 剩下的点,要么是边界点(归入最近的簇),要么是噪声(直接扔掉)

我曾经在一个项目中,用欧几里得聚类处理高速公路场景。结果远处的车因为点太稀疏,被当成了噪声。换成 DBSCAN 后,把 eps 设大一些,远处的车也能被正确聚类。这就是密度聚类的优势——它不要求全局密度一致。

避坑指南:DBSCAN 的参数 eps 和 minPts 是联动的。我建议先固定 minPts=10,然后根据点云密度调整 eps。如果场景中既有近处密集点又有远处稀疏点,可以考虑用自适应 eps,或者分区域设置不同参数。

PCL 里也有 DBSCAN 的实现,不过需要自己封装一下:

// 伪代码:DBSCAN 核心逻辑
for each point p in cloud:
    if p is visited: continue
    neighbors = regionQuery(p, eps)   // 找邻域点
    if neighbors.size() < minPts:
        mark p as noise
    else:
        expandCluster(p, neighbors)   // 扩张簇

3.3 两种方法怎么选?

说实话,没有绝对的好坏。我个人的经验是:

场景 推荐方法 理由
近距离城市道路(0-50米) 欧几里得聚类 点云密集,阈值好调,速度快
高速公路(50-150米) DBSCAN 密度变化大,DBSCAN 更鲁棒
有大量噪声的场景 DBSCAN 天然支持噪声过滤
实时性要求极高 欧几里得聚类 计算量小,容易并行化

你想想看,如果场景简单、点云均匀,欧几里得聚类又快又好。但如果场景复杂、噪声多,DBSCAN 的密度思想就派上用场了。

我的一个小技巧:在实际工程中,我经常把两种方法结合起来用。先用 DBSCAN 做粗聚类,把明显的噪声去掉。再用欧几里得聚类做细分割,把粘连的目标分开。这样既保证了鲁棒性,又控制了计算量。

好了,关于点云分割与聚类,今天就聊这么多。这两种方法都是基础中的基础,但用好它们,需要你对场景有深刻的理解。多调参、多试错,慢慢就有感觉了。

本章要点回顾:

  • 欧几里得聚类:基于距离阈值,简单高效,适合均匀密度场景
  • DBSCAN:基于密度连通,能处理噪声和密度变化
  • 参数调优是关键:阈值、最小点数、eps 需要根据场景反复调试
  • 实际工程中可组合使用,取长补短
可可精品资料库