地面分割与道路提取:RANSAC平面拟合、基于射线的地面分割、基于网格的地面分割、道路边界提取
地面分割这件事,说白了就是让自动驾驶的车知道「哪里能走,哪里不能走」。我刚开始做这个模块时,总觉得地面不就是个平面嘛,随便拟合一下就行。结果第一次上路测试,车在坡道上差点冲进绿化带——嗯,从那以后我再也不敢小看地面分割了。
今天咱们聊聊四种主流方法:RANSAC平面拟合、基于射线的地面分割、基于网格的地面分割,以及道路边界提取。每种方法我都踩过坑,咱们一个一个说。
RANSAC平面拟合:最经典,但别迷信
RANSAC(Random Sample Consensus)是地面分割的「老大哥」。它的核心思想很简单:从点云中随机选三个点,拟合一个平面,然后看有多少点落在这个平面上。重复这个过程,找到支持点最多的那个平面。
代码实现其实不复杂:
def ransac_plane_fitting(points, max_iter=100, distance_threshold=0.2):
best_plane = None
best_inliers = []
for _ in range(max_iter):
# 随机选三个点
idx = np.random.choice(len(points), 3, replace=False)
p1, p2, p3 = points[idx]
# 计算平面参数
normal = np.cross(p2 - p1, p3 - p1)
d = -np.dot(normal, p1)
# 计算所有点到平面的距离
distances = np.abs(np.dot(points, normal) + d) / np.linalg.norm(normal)
inliers = points[distances < distance_threshold]
if len(inliers) > len(best_inliers):
best_inliers = inliers
best_plane = (normal, d)
return best_plane, best_inliers
我个人习惯把距离阈值设成0.15到0.25米之间。太小了会把起伏路面误判为障碍物,太大了又会把低矮障碍物当成地面。
所以我的建议是:RANSAC适合结构化道路、停车场等平坦场景。遇到复杂地形,得换方法。
基于射线的地面分割:更适合非结构化道路
这个方法是我在越野场景项目中学会的。它的思路很直观:从激光雷达原点出发,沿每条射线方向看过去,地面点的高度应该是连续变化的。
具体做法是:
- 把点云按方位角分成若干条射线(比如每0.5度一条)
- 对每条射线上的点,按距离排序
- 计算相邻点的高度差和坡度角
- 如果坡度角小于阈值(比如5度),就认为是地面点
这里有个关键参数——坡度角阈值。我曾经在矿山项目上调试这个参数,发现不同地形需要的阈值差别很大:
| 场景类型 | 推荐坡度阈值 | 典型问题 |
|---|---|---|
| 高速公路 | 3° - 5° | 阈值太小会漏掉缓坡 |
| 城市道路 | 5° - 8° | 路沿容易被误判为地面 |
| 越野/矿山 | 10° - 15° | 大石块可能被当成地面 |
基于网格的地面分割:工程中最实用
说实话,我现在大部分项目都用网格法。为什么?因为它快、稳定、好调参。
核心思路是把点云投影到二维网格上,每个网格单元记录最低点、最高点、平均高度等信息。然后判断每个网格是地面还是障碍物。
我常用的网格尺寸是0.2米×0.2米。太大了会丢失细节,太小了计算量爆炸。
判断逻辑是这样的:
- 如果网格内点的高度方差很小 → 可能是地面
- 如果网格内点的高度差很大 → 可能是障碍物边缘
- 如果网格内点很少 → 可能是噪声或远处点
我曾经在雨夜测试中遇到一个问题:地面有水洼,激光雷达打到水面上会产生镜面反射,导致地面点云出现空洞。网格法会把空洞区域误判为障碍物。后来我加了一个「邻居投票」机制——如果一个网格被判定为障碍物,但周围8个邻居都是地面,那就把它也标为地面。
道路边界提取:从「能走」到「怎么走」
地面分割告诉我们哪里是路面,但车还需要知道路在哪里结束。这就是道路边界提取要做的事。
我常用的方法是基于高度跳变检测:
- 在地面分割的基础上,沿横向扫描线分析
- 检测地面点到非地面点的高度突变
- 连续多个扫描线都有突变的位置,就是道路边界
这里有个坑——路沿和绿化带怎么区分?路沿通常只有10-15厘米高,而绿化带可能半米高。我一般设两个阈值:
- 高度跳变在5-20厘米 → 路沿/路肩
- 高度跳变大于20厘米 → 障碍物/绿化带
另外,道路边界不一定是直线。弯道、匝道、交叉口,边界都是曲线。我建议用B样条曲线拟合边界点,而不是硬套直线模型。
下面是我画的一张流程图,把今天讲的四种方法串起来:
你看,实际工程中很少只用一种方法。我通常的做法是:先用场景分类器判断是结构化道路还是非结构化道路,然后选择主算法,最后用网格法做统一的后处理。这样既保证了精度,又兼顾了实时性。
- 结构化道路 → RANSAC平面拟合(快、准)
- 非结构化道路 → 射线法(适应性强)
- 任何场景 → 网格法做后处理(稳定、鲁棒)
- 道路边界 → 高度跳变 + 曲线拟合
最后说一句,地面分割没有银弹。我在不同项目上试过各种组合,最终发现最好的方案往往是「场景感知 + 多方法融合」。你想想看,高速公路和矿山小路怎么可能用同一套参数?
嗯,今天就聊到这儿。这些方法我都亲手调过,踩过的坑也都写在里面了。希望能帮你少走些弯路。
可可精品资料库