1. 道路边界约束概述

各位同学,大家好。今天我们来聊聊路径规划里一个最基础、也最容易被忽视的问题——道路边界。

说实话,我刚开始做自动驾驶那会儿,觉得路径规划嘛,不就是让车从A点走到B点?后来在测试场里跑了一次,车差点骑上路沿,我才意识到:没有边界约束的路径规划,就像蒙着眼睛开车。

为什么需要道路边界?

你想想看,车辆在道路上行驶,不是自由粒子。它必须待在「可行驶区域」内。这个区域,就是由道路边界定义的。

具体来说,道路边界的作用有三点:

  • 安全第一:防止车辆驶出路面,避免碰撞路沿、护栏或掉下路基。我在项目中遇到过,有一次车辆在弯道中因为边界约束太松,直接压上了路肩,幸好速度不快。
  • 规划约束:给路径搜索算法提供硬性限制。说白了,就是告诉算法:「你只能在红线里面找路」。
  • 舒适性保障:合理的边界约束能让车辆平滑行驶,不会突然猛打方向。

核心观点:道路边界不是「可选」的,而是路径规划的硬约束。没有它,规划出的路径就是一张废纸。

边界类型:路沿、护栏、车道线

实际道路上的边界,大致分三类。我按工程中遇到的频率来排个序:

类型 典型特征 数学表达方式 我的经验
路沿(Curb) 高度差明显,物理不可穿越 分段线性函数 / 样条曲线 激光雷达最容易检测到,但雨天容易误判
护栏(Barrier) 连续刚性结构,高度较高 多边形 / 线段集合 高速路上常见,但弯道处容易漏检
车道线(Lane Marking) 地面标记,视觉特征明显 多项式曲线(如三次多项式) 视觉方案的主力,但磨损或遮挡时很头疼

一个小技巧:实际工程中,我习惯把路沿和护栏作为硬边界(车辆绝对不能压),车道线作为软边界(变道时可以压,但正常行驶要保持在内部)。这样分层处理,规划起来更灵活。

数学表达:怎么把边界变成算法能懂的语言?

嗯,这里要上点干货了。边界在现实世界里是物理实体,但在算法里,它必须是一串数字。

我个人最常用的三种数学表达方式:

1. 分段线性函数

说白了,就是用一堆线段连起来。适合路沿这种形状比较规则的边界。

// 伪代码示例:路沿边界表达
struct CurbBoundary {
    vector<Point2D> points;  // 离散点序列
    double height;           // 路沿高度
    bool is_continuous;      // 是否连续
};

// 判断点是否在边界内
bool IsInsideBoundary(Point2D vehicle_pos, CurbBoundary boundary) {
    // 用射线法判断点在多边形内
    // 我踩过的坑:这里一定要考虑车辆轮廓,不能只用车后轴中心点
}

2. 多项式曲线

车道线常用三次多项式拟合。为什么是三次?因为曲率连续,车跑起来舒服。

// 三次多项式车道线模型
// y = a0 + a1*x + a2*x^2 + a3*x^3
struct LaneBoundary {
    double a0, a1, a2, a3;  // 多项式系数
    double confidence;       // 置信度,0~1
};

// 计算某x处的边界y值
double GetBoundaryY(double x, LaneBoundary lb) {
    return lb.a0 + lb.a1*x + lb.a2*x*x + lb.a3*x*x*x;
}

注意:多项式拟合有个坑——外推不可靠。我曾经在高速上遇到一个急弯,多项式在远距离处直接飞出去了。后来我加了距离截断,超过30米就改用直线外推。

3. 栅格化表达

这个适合复杂场景。把地图切成小格子,每个格子标记「可通行」或「不可通行」。

// 栅格地图边界表达
struct GridMap {
    int width, height;           // 栅格尺寸
    double resolution;           // 分辨率,比如0.1m
    vector<vector<bool>> grid;  // true=可通行, false=障碍/边界外
};

// 检查位置是否合法
bool IsValidPosition(double x, double y, GridMap map) {
    int ix = (int)(x / map.resolution);
    int iy = (int)(y / map.resolution);
    if (ix < 0 || ix >= map.width || iy < 0 || iy >= map.height)
        return false;
    return map.grid[iy][ix];  // 注意:y对应行索引
}

知识体系总览

下面这张图,是我自己总结的道路边界约束在路径规划中的位置。你看一眼就能明白整个逻辑链条:

道路边界约束知识体系 传感器输入 激光雷达 / 摄像头 / 高精地图 边界提取与分类 路沿检测 | 护栏分割 | 车道线拟合 数学表达方式 分段线性 | 多项式曲线 | 栅格化 | 样条曲线 路径规划中的应用 搜索空间剪枝 | 代价函数设计 | 碰撞检测 | 平滑约束 数据 流 方 向 ↓

这张图里,从上到下是数据流的方向。传感器拿到原始数据后,先提取边界,再转成数学表达,最后喂给规划算法。每一步都有坑,后面我们会一个一个填。

避坑指南:我踩过的三个坑

  1. 边界不连续:有一次在隧道里,GPS信号丢失,高精地图的边界直接断了一截。车差点往墙上开。后来我加了边界连续性检查,发现断点就降速并切换到视觉边界。
  2. 边界抖动:视觉车道线检测在颠簸路段会上下跳。如果直接把抖动边界喂给规划器,车就会像喝醉了一样左右晃。我的解决办法是加一个低通滤波器,时间常数0.3秒左右。
  3. 边界类型混淆:有一次系统把路沿误识别为车道线,结果规划出的路径贴着路沿走,非常危险。后来我强制要求:路沿和护栏的优先级永远高于车道线。

我的个人习惯:在工程代码里,我会用一个 BoundaryManager 类来统一管理所有边界数据。它负责融合多传感器信息、处理异常、输出统一的边界表达。这样规划模块只需要调用一个接口,不用关心底层是激光还是视觉。

好了,这一章我们搞清楚了道路边界是什么、为什么需要它、以及怎么用数学表达。下一章我们会深入讲如何从传感器数据中提取这些边界——那才是真正考验工程能力的地方。