4、点云分割(上):RANSAC算法原理、平面分割、圆柱体分割
各位同学,欢迎来到点云分割的实战环节。
点云分割,说白了就是把一堆乱七八糟的点,按照它们的几何形状归类。比如地面归为一类,墙面归为一类,路上的车归为一类。今天咱们先聊最经典的两个:平面分割和圆柱体分割。而实现它们的核心算法,就是大名鼎鼎的 RANSAC。
4.1 RANSAC 算法原理:为什么它能「抗噪声」?
RANSAC,全称 Random Sample Consensus,随机采样一致性。名字挺长,但思想很简单。
想象一下,你面前有一堆点,大部分都落在一个平面上,但有几个点飞得很远(噪声)。如果用最小二乘法去拟合,那几个噪声点会把整个平面拉歪。RANSAC 的做法是:我不跟你讲道理,我直接随机抽三个点,算一个平面,然后看看有多少点支持这个平面。支持的点多,我就认为这个平面是对的。
核心思想: 通过反复随机采样,找到能容纳最多「内点」的模型参数。
具体步骤我总结为四步:
- 随机采样:从点云中随机选 n 个点(平面选3个,圆柱体选5个)。
- 计算模型:用这 n 个点算出模型参数(平面方程、圆柱体轴线和半径)。
- 统计内点:遍历所有点,计算它们到模型的距离。距离小于阈值的,算内点。
- 迭代更新:重复1-3步,保留内点最多的那个模型。
嗯,这里要注意:迭代次数不是越多越好。我刚开始做项目时,傻乎乎设了10万次迭代,结果跑了一分多钟。后来发现,根据内点比例和置信度,用公式算一下最优次数就够了。
我的经验: 迭代次数公式:k = log(1-p) / log(1-w^n)。其中 p 是期望成功率(一般0.99),w 是内点比例,n 是采样点数。实际项目中,我通常设 100-500 次,效果就很好了。
4.2 平面分割:地面检测的「基本功」
平面分割是点云处理里最基础的操作。你想想看,自动驾驶场景中,地面、墙面、路面标识牌,基本都是平面。尤其是地面分割,几乎是每个感知系统的第一步。
RANSAC 做平面分割,流程是这样的:
- 随机选3个点,计算平面方程
ax + by + cz + d = 0。 - 计算每个点到该平面的距离
dist = |ax0 + by0 + cz0 + d| / sqrt(a² + b² + c²)。 - 距离小于阈值(比如0.1米)的点,标记为内点。
- 重复迭代,保留内点最多的平面。
我给大家看一段伪代码,实际项目中我经常这么写:
// 伪代码:RANSAC 平面分割
int best_inliers = 0;
Plane best_plane;
for (int iter = 0; iter < max_iterations; iter++) {
// 1. 随机选3个点
Point p1 = points[rand() % N];
Point p2 = points[rand() % N];
Point p3 = points[rand() % N];
// 2. 计算平面参数
Plane plane = computePlane(p1, p2, p3);
// 3. 统计内点
int inlier_count = 0;
for (auto &p : points) {
float dist = pointToPlaneDist(p, plane);
if (dist < distance_threshold) inlier_count++;
}
// 4. 更新最优
if (inlier_count > best_inliers) {
best_inliers = inlier_count;
best_plane = plane;
}
}
避坑指南: 我曾经在高速场景下,把路边的护栏误检成了地面。为什么?因为护栏也是一个大平面,而且距离阈值设得太大了。后来我加了两个约束:法向量必须朝上(z轴分量大于0.8),平面高度必须在合理范围(比如-0.5米到0.5米之间)。
4.3 圆柱体分割:从「杆状物」说起
圆柱体分割,在自动驾驶里主要用来检测路灯杆、交通标志杆、树干等。这些目标虽然看起来简单,但用 RANSAC 做起来比平面复杂不少。
为什么?因为圆柱体有5个自由度:轴线的方向(2个)、轴线上的一个点(3个)、半径(1个)。所以每次随机采样需要至少5个点。
具体做法是这样的:
- 随机选5个点。
- 用其中2个点确定轴线方向,再用另外3个点确定半径和位置。
- 计算每个点到圆柱体表面的距离(点到轴线的距离减去半径)。
- 统计内点,迭代优化。
我个人习惯在圆柱体分割时,先做一次地面滤波。因为地面上的点太多,会干扰圆柱体的拟合。我一般先跑一遍平面分割,把地面去掉,再对剩下的点做圆柱体分割。这样效率能提升30%以上。
关键参数:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 距离阈值 | 0.05 - 0.1 米 | 点到圆柱体表面的允许误差 |
| 半径范围 | 0.05 - 0.5 米 | 路灯杆一般0.1-0.3米,树干可能更粗 |
| 最小内点数 | 50 - 200 | 太少了可能是噪声,太多了可能漏检 |
我记得有一次做园区物流车项目,路两边全是细长的路灯杆。用 RANSAC 做圆柱体分割,一开始总是把两个相邻的杆子拟合成一个。后来我把最小内点数调高,并且加了轴线方向约束(必须垂直于地面),问题就解决了。
4.4 本章知识体系
为了让大家更直观地理解,我画了一张流程图,把 RANSAC 分割的核心逻辑串起来:
这张图把 RANSAC 的迭代循环画得很清楚。你想想看,每次迭代就是一次「猜模型、验证、保留最优」的过程。说白了,RANSAC 就是一个暴力枚举的优化版本,只不过它用随机采样代替了全量搜索。
我的建议: 在实际工程中,不要只跑一次 RANSAC。我通常的做法是:先分割出地面(平面),把地面点去掉;再分割墙面(平面),去掉;最后剩下的点,做圆柱体分割。这样层层剥离,效果比一次全量分割好得多。
好了,这一章的内容就到这里。RANSAC 的原理和两种分割方法,大家一定要动手写代码跑一跑。下一章我们会聊点云分割的下半部分——欧式聚类和区域生长,到时候咱们再接着聊。
公众号:蓝海资料掘金营,微信deep3321