4、地面与非地面分割:三种主流方法深度解析

地面分割这件事,说实话,是激光雷达感知里最基础但也最容易被忽视的一环。我刚开始做自动驾驶时,总觉得地面分割嘛,随便找个平面拟合就完事了。结果呢?车在坡道上直接漏检了一根车道线,差点出事。从那以后,我再也不敢小看这个模块了。

今天咱们就聊聊三种主流的地面分割方法。每种方法都有自己的脾气,你得根据场景来选。

4.1 基于RANSAC的平面拟合

RANSAC,全称是Random Sample Consensus。说白了,就是在一堆点里,反复随机挑几个点去拟合一个平面,然后看看有多少点支持这个平面。支持最多的那个,就是地面。

我个人习惯用RANSAC做快速验证。代码写起来简单,效果也还行。

// 伪代码示例
int maxIterations = 100;
double distanceThreshold = 0.1; // 单位:米

for (int i = 0; i < maxIterations; i++) {
    // 随机选3个点
    Point p1 = points[rand() % N];
    Point p2 = points[rand() % N];
    Point p3 = points[rand() % N];
    
    // 计算平面方程 ax + by + cz + d = 0
    Plane plane = fitPlane(p1, p2, p3);
    
    // 统计内点数量
    int inliers = 0;
    for (auto& p : points) {
        double dist = distanceToPlane(p, plane);
        if (dist < distanceThreshold) inliers++;
    }
    
    // 记录最优平面
    if (inliers > bestInliers) {
        bestPlane = plane;
        bestInliers = inliers;
    }
}

注意:RANSAC有个致命问题——它假设地面是平的。你想想看,真实道路哪有那么平?遇到坡道、减速带,RANSAC直接就懵了。我曾经在高速匝道上吃过这个亏,地面分割后车道线全断了。

我的建议:RANSAC适合做预处理,比如快速剔除大部分地面点。但别指望它搞定复杂场景。迭代次数设到100-200次就够了,再多就是浪费算力。

4.2 基于射线坡度的地面分割

这个方法就聪明多了。它把激光雷达的扫描线想象成一条条射线,从中心往外发射。每条射线上,相邻点之间的坡度变化,就是判断地面的关键。

为什么会这样?因为地面上的点,坡度变化是连续的、缓慢的。而障碍物上的点,坡度会突然跳变。

具体做法是这样的:

  1. 把点云按角度分到不同的射线(bin)里
  2. 对每条射线,按距离排序
  3. 计算相邻点的坡度(高度差 / 水平距离)
  4. 坡度小于阈值的,认为是地面点
// 射线坡度分割核心逻辑
double slopeThreshold = 0.1; // 坡度阈值,约5.7度

for (auto& ray : rays) {
    sortByDistance(ray.points);
    
    for (int i = 1; i < ray.points.size(); i++) {
        double dz = ray.points[i].z - ray.points[i-1].z;
        double dr = hypot(
            ray.points[i].x - ray.points[i-1].x,
            ray.points[i].y - ray.points[i-1].y
        );
        
        double slope = dz / dr;
        
        if (abs(slope) < slopeThreshold) {
            markAsGround(ray.points[i]);
        } else {
            markAsObstacle(ray.points[i]);
        }
    }
}

核心优势:射线坡度法天然适应坡道。因为它是局部比较,不是全局拟合。上坡、下坡都能处理。我在城市道路场景里,基本都用这个方法。

不过,它也有弱点。如果地面有坑洼或者小石子,坡度会剧烈变化,容易误判。嗯,这里要注意,阈值不能设得太死。

4.3 基于Patchwork的地面分割

Patchwork是近两年比较火的方法。它把点云分成一个个小网格(patch),每个网格独立判断是不是地面。最后再把这些小网格拼起来,形成完整的地面。

我个人觉得,Patchwork是这三种方法里最鲁棒的。它结合了局部信息和全局信息。

具体流程:

  1. 把点云投影到二维网格(比如50x50的网格)
  2. 每个网格内,计算最低点、最高点、平均高度
  3. 用这些特征判断网格是不是地面
  4. 相邻地面网格合并,形成完整地面
方法 优点 缺点 适用场景
RANSAC 简单、快速 假设地面平坦 停车场、高速公路
射线坡度 适应坡道 对噪声敏感 城市道路、乡村路
Patchwork 鲁棒性高 计算量大 复杂场景、越野

避坑指南:我曾经在雨雪天用Patchwork,发现地面分割效果变差。后来排查发现,是雨滴和雪花被当成了地面点。解决办法是加一个高度滤波,把离群点先滤掉。

4.4 三种方法的对比与选择

你可能会问,到底该用哪个?我的经验是这样的:

  • 如果只是做Demo,RANSAC就够了,5分钟搞定
  • 如果是量产项目,建议用射线坡度法,效率高、效果好
  • 如果场景特别复杂(比如越野、矿区),上Patchwork

其实,很多量产方案是混合使用的。先用RANSAC粗分割,再用射线坡度法精调。这样既快又准。

一句话总结:地面分割没有银弹。你得根据传感器安装高度、道路类型、计算资源来选。我建议你把这三种方法都实现一遍,跑跑自己的数据,自然就知道哪个适合你了。

地面分割方法对比 原始点云 RANSAC平面拟合 假设地面平坦 射线坡度分割 局部坡度比较 Patchwork分割 网格化+特征判断 地面点 + 非地面点 地面点 + 非地面点 地面点 + 非地面点 车道线检测(仅非地面点)

这张图很直观。原始点云进来,三条路都能走。但最终目的都一样——把地面和非地面分开,然后只对非地面点做车道线检测。你想想看,如果地面点不滤掉,车道线检测算法会疯掉的,到处都是干扰。

我的小技巧:实际部署时,我习惯把三种方法的结果做个投票。两个方法都认为是地面的点,才标记为地面。这样误检率能降低不少。当然,计算量会大一些,但现在的硬件完全扛得住。

好了,地面分割就聊到这儿。这三种方法你都可以试试,跑跑自己的数据,看看哪个效果最好。记住,没有最好的方法,只有最适合你场景的方法。

可可精品资料库