做planning这行的人,见面聊不了三句就会绕到同一个话题:你还在用Frenet坐标系吗?这句半开玩笑的问法,其实抓住了这十年规划技术最大的变化。从十年前几乎清一色的状态机加采样规划,到如今端到端、占用网格、生成式规划陆续登场,规划(planning)已经从一个躲在感知和定位后面的小模块,变成整个系统里最核心的决策中枢。
这篇文章,我想站在一个多年做规划开发、也踩过不少坑的人的角度,把这十年的演进掰开揉碎讲一讲。适合三类人看:刚入行想建立技术全景的工程师、正在做量产项目跑规划模块遇到瓶颈的人、以及对这个方向感兴趣的学生。我不打算写成教科书,只想讲那些真正影响工程结果的变化,以及一些“当时不理解,事后才明白”的设计选择。
1. 十年演进的三条主线:规则编码、学习模型与端到端雏形
1.1 经典方法没有退场,而是下沉成“安全底座”
十年前我刚开始碰规划时,最常见的配置是:A*做全局路径,RRT或者DWA做局部绕障,前面再挂一个有限状态机,负责判断直行、跟车、停车、避让。那时候能把一套流程稳定跑完就已经很了不起,大家追求的不是“聪明”,而是“别出幺蛾子”。
这些年经典方法并没有被淘汰。我自己参与的项目里,A*仍然用来生成全局拓扑路径,DWA会在局部规划器失效时作为应急兜底,碰撞检测、速度约束、安全校验这些底层逻辑,几乎全是老的经典算法。原因很好理解:可解释、计算边界清晰、容易做安全论证。就像汽车的刹车踏板,无论智能驾驶发展到哪一步,物理结构依然要放在那里。
那演进究竟发生在哪里?答案是:上层决策从“人写规则”变成了“数据驱动的策略”,系统开始学着处理那些写不完规则的长尾场景。经典方法没有退场,只是从舞台中央退到了台侧,承担起“安全底座”的角色。
1.2 学习型规划如何从“能用”变成“好用”
学习型规划其实很早就有人尝试。早期模仿学习,本质就是拿大量人类驾驶数据训练一个策略,输入格子状的占位栅格,输出方向盘角度或者轨迹。问题也很典型:数据分布严重不均衡,直线驾驶的样本多到爆,复杂交互样本少得可怜,学出来的策略在简单场景像模像样,一遇到真正需要博弈的路口就显得畏手畏脚。
强化学习试图解决探索问题,但奖励函数设计是另一座大山。给得稀疏,训练半天不收敛;给得密集,策略又会钻奖励漏洞,比如贴着墙边蹭过去,为了绕路宁可停在原地。很长一段时间,强化学习规划只能被困在仿真器里自娱自乐。
真正的拐点是这几年出现的三件事:占用网格网络把异形障碍物和复杂交通参与者编码进同一张网格,Transformer让网络可以从高维感知直接映射到连续轨迹,再加上大规模仿真数据回灌,长尾场景终于有了训练样本。学习型方法从“能跑”变成了“好开”,行为连续性明显提升。但“好用”不等于“万能”,它对分布之外的新场景依然脆弱。所以成熟的工程做法是:学习型方法出主意,经典方法做校验,两层互相兜底。
1.3 端到端为什么在这几年成为行业共识的前夜
端到端这个概念并不是新东西,但这几年才开始被当成系统性方案来推动。原因很现实:大算力、海量数据、以及以Transformer为代表的架构成熟,缺一个都推不动。端到端的核心是把感知、预测、规划揉进一个大网络,中间不显式维护目标、地图、轨迹这些中间表达。
在端到端架构里,规划不再是一个独立模块,而是变成网络里的轨迹头、损失函数的一部分,或者一个下层约束。好处很明显:信息无损传递,预测和规划共享同一份特征,交互行为更统一,不会再出现“感知识别出了行人,但规划层因为目标没跟上来而无动于衷”这种断链问题。
但端到端也有代价:安全边界怎么验证、缺陷怎么定位、可解释性怎么做,这些年都没有完全解决。所以我的判断是,今后很长一段时间不会是非此即彼,而是分层端到端加安全校验的混合体。传统规划工程师的位置不会消失,只是工作重心会从“写规则”变成“定义约束、设计评测、诊断数据”。
2. 关键技术节点:Frenet坐标系、时空联合与约束处理的进化
2.1 从SL/ST分离到时空联合优化
讲规划绕不开Frenet坐标系。简单说,就是把道路中心线展开成一条直线,用车沿这条线的纵向距离s和横向偏移l来描述位置。这样规划就天然贴合车道结构,算起来也方便。
早期主流是把问题拆成两段。纵向规划基于ST图,s是纵轴、t是横轴,静态障碍物对应一片禁行区域,动态障碍物对应一条随时间移动的禁行曲线,然后求出速度曲线;横向规划单独求一条l曲线,决定要不要变道、偏移多少。这个拆法本质上是把一个二维问题降成两个一维问题,算得快,但没法表达“一边变道一边避让”这类时空耦合行为。
后来的突破是时空联合优化。不再分开求纵向和横向,而是一次性在一个时间段内采样多条完整轨迹,每条轨迹同时描述横向和纵向轮廓,然后在整条轨迹上检查安全、舒适性、可行性,再挑代价最低的。这样处理复杂交互时行为自然很多,尤其是“借道绕行”“压线避让”这类场景,效果提升肉眼可见。
代价是计算量变大。工程上怎么兜?多假设并行搜索、GPU向量化加速、轨迹池化复用,基本是标配。我的观点很明确:时空联合不是银弹,它只是把规划问题的思考维度从“路径点”换成了“短时间内的行为集合”,这是十年里最本质的一步跨越。
2.2 决策层与轨迹层从串行到耦合
十年前,决策和轨迹是严格分开的两层。有限状态机判断出“跟车”,轨迹层就生成一条跟着前车的速度曲线;判断出“停车”,就生成一条减速到零的曲线。这套结构简单、可预测,但有个致命弱点:决策一旦出错,轨迹层无法补救。前车旁边明明有空档可以绕过去,状态机却死死钉在“跟车”状态;或者决策已经输出“变道”,但旁边车道正好有车,轨迹层只能等决策模块反应过来。
中间有一段流行过行为树和并行决策。保持车道、变道、绕障、靠边停车这些意图各自独立运行,每个意图先生成自己的候选轨迹,再交给仲裁模块挑一个最优。这样好处是“变道意图”和“保持车道意图”会同时存在,切换不至于那么生硬,但决策的离散感还在。
现在主流已经走向意图生成加多假设轨迹加统一评估:决策层不再输出单一状态,而是输出一组带置信度的意图假设;轨迹层为每个假设都生成候选轨迹;评估层按风险、效率、舒适度统一打分,行为由分数高低决定。这套结构的最大价值,是终于能处理交互不确定——旁车“让不让”不靠一个布尔判断,而是让多条候选轨迹从代价上各凭本事。我用下来最大的感受是,它把驾驶直觉模糊化、概率化了,系统也因此能在不确定里做决策。
2.3 约束处理:硬约束、软约束到安全屏障
约束处理方式的进化,很能体现工程哲学的转变。早期做法是采样完轨迹就做碰撞检测,碰到障碍物就丢弃,剩下无碰撞的再挑。这种硬约束简单直接,但轨迹选择空间被压得很扁,经常出现“两条路都贴着障碍物边缘,哪条更安全根本分不出来”的窘境。
后来大多数团队转向QP或MPC做轨迹优化。碰撞躲避被建模成软约束,离障碍物越近罚得越狠;速度、加速度、曲率上限则作为硬约束。软约束的好处是轨迹平滑度大幅提升,系统对传感器的轻微抖动有容错,不会因为一帧检测波动就猛打方向。
再往后,安全屏障理论开始进入工程视野,比如控制障碍函数和可达集分析。不是说规划层“尽量别撞”,而是从底层动态上保证某些状态物理上不可达。相当于从“规划的时候小心点”升级成“车辆本身就进不了那片区域”。我的实操经验是,量产项目里三层缺一不可:硬约束管路缘和护栏这种绝对边界,软约束管行人和骑行者的安全距离,最后再叠一层主动刹车兜底保护。顺序不能乱,责任也要分清楚。
3. 工程落地很难避开的三个问题:频率、坐标系和调参
3.1 重规划频率与轨迹追踪稳定性的抉择
规划频率怎么选,是第一个玄学问题。高速场景下,主流选择在20到50赫兹;低速室内机器人十年如一日用10赫兹左右。决定因素不是“快就好”,而是传感器帧率、执行器延迟和风险等级共同作用的结果。
高频规划的典型坑是轨迹抖动。规划层每秒输出几十条轨迹,如果不做时间对齐,控制层即使只跟踪前段,依然会感受到频繁的修正,体现在车上就是方向盘小幅高频摆动。我的对策是:规划输出带时间戳,控制层按时间戳做渐进替代,不是收到新轨迹就整体覆盖。
另一个容易被忽略的点是轨迹长度。当前帧规划的虽然是未来8到10秒轨迹,但真正被执行的只有前0.5到1.5秒,后半段每帧都在被刷新。如果控制层把整条轨迹都当真,就会出现频繁改变意图的假象。正确做法是只在轨迹末端保留松弛变量,让二次规划在末端有退路,整条轨迹才不容易崩。
3.2 坐标系与时间戳:规划bug的最大来源
我调试过大量规划异常,最后排查到坐标系的至少占三成。最典型的是笛卡尔坐标与Frenet坐标互算,在曲率变化大的地方,s和l会跳变,规划轨迹就会突然啃到路缘。后来改成中点多采样加三次样条重拟合,问题少了很多。
另一大来源是时间戳不同步。相机、雷达、预测模块各自的时间基准不一样,规划层拿到的目标列表可能是半秒前的。如果不做时间补偿,车就会明显慢半拍。我在规划入口做了统一处理:所有目标数据先带时间戳入库,再按当前时刻外推一次。外推超过100到200毫秒就不再信任,主动降级为保守策略。
调试建议只有一条:第一步永远是把所有坐标系下的量,包括自车预测轨迹、目标历史轨迹、规划输出,全部画在同一张图上。坐标系错了,图上一眼就能看出来;图上看不出来,你才往参数方向找问题。这套顺序不要颠倒。
3.3 调参前先建立离线回放,再谈权重
规划调参最忌讳在线试。正确流程是先离线回放历史包:固定输入,让规划器重算,先保证相同输入一定有相同输出;如果回放结果都不稳定,先修非确定性,再谈调参数。
调权重也讲顺序。很多人一上来就调效率权重和安全权重,其实应该先做量级对齐。效率代价的单位是m/s,舒适代价的单位是m/s³,量级差几十倍,直接调权重等于瞎调。我习惯先把各代价项归一化到0到1区间,再调相对权重,顺便用代码把思路固定下来:
# 单条轨迹代价计算示例:先归一化再调权重 def trajectory_cost(traj): eff = normalized_speed_deviation(traj) # 归一化到[0,1] com = normalized_jerk(traj) # 归一化到[0,1] saf = normalized_clearance(traj) # 归一化到[0,1] return w_eff * eff + w_com * com + w_saf * saf这样每一步调整都有意义,也方便后续向别人解释。最后是指标设置。规划器的好坏不能只看碰撞率,安全指标用TTC和最小距离,舒适指标用jerk和纵向加速度变化率,效率指标用平均速度和行程时间。目标不是让单一指标极致,而是让组合指标在真实路测里可解释、可复盘。
4. 评测这件事:从统计指标到场景库再到数据闭环
4.1 从规则统计到场景化评测
早期评测规划器,基本是跑固定路线,统计碰撞率、任务完成时间、最大偏离距离。这套办法在开发早期有用,但问题也很明显:一个规划器可能平均指标很漂亮,却在某个特定场景烂到离谱,而这些场景在统计里被稀释了。
场景化评测于是成为主流。先把真实路况提炼成几百万个参数化场景,比如前车急刹、侧方切入、行人横穿,每个场景还能设置相对速度、距离、道路曲率。评测不再问“你稳不稳”,而是问“遇到鬼探头,你从感知到规划输出的反应时间是多少毫秒”。
对抗式评测是最近几年的新玩法。用对抗agent主动逼抢、压线、误导,逼规划器在极限交互下暴露缺陷。这一层评测的核心价值,从“验证功能”变成了“挖掘缺陷”,对安全验证尤其重要。我的经验是,场景库要分成验收集和探索集两套,分开维护,否则会被“能跑指标”的用例带偏。
4.2 数据闭环:让规划越用越好的关键
评测做好了,只是一个起点。真正让规划器持续变好的,是数据闭环。最经典的是影子模式:路测车辆正常跑,后台并行运行一版新规划器,不接管、只记录“如果它来开会怎么开”。新版旧版一对比,改动的副作用立刻能显现。
第二层是仿真回灌。真实路测中遇到的所有奇怪情况,连同传感器数据和人工标注的意图、轨迹,全部注入仿真器,变成回归测试用例。每修一个问题,就新增一个用例,以后再遇到相似场景,系统会自动提醒你是不是复发了。
第三层是自动难例挖掘。利用“规划输出与人类驾驶行为差异大”“碰撞风险指标超过阈值”等信号,自动筛选数据,再让人工标注边界,反哺训练集。对学习型规划来说,这一步特别关键,因为训练集的分布直接决定了系统能学会的行为边界。数据闭环不停,系统才会越用越熟。
4.3 不同评测方法的优劣对比
先说明一点:不同阶段要用不同评测,不要一上来就堆场景库。
| 评测方法 | 核心优点 | 主要局限 | 适用阶段 |
|---|---|---|---|
| 规则统计指标 | 逻辑闭环、客观可比 | 无法反映交互质量 | 早期开发 |
| 场景化评测 | 真实、有针对性 | 场景库构建成本高 | 中后期回归 |
| 对抗式评测 | 能挖掘极限缺陷 | 过度设计可能让规划变得保守 | 安全验证 |
| 数据闭环评测 | 可持续迭代 | 依赖数据规模和标注能力 | 量产持续优化 |
在实际项目里,我会在拿到第一版规划器时先用规则统计跑通流程,然后把重点放在场景化评测上;进入量产准备期后,三套并行,数据闭环常年开着。评测不是验收工具,它本身就是规划系统的一部分。
5. 高频故障排查与三条独家经验
5.1 高频问题速查表
以下表格来自我和同行在实际调试中反复遇到的情况,希望能帮你少走弯路。
| 问题现象 | 可能原因 | 排查思路 | 解决建议 |
|---|---|---|---|
| 规划轨迹抖动 | 规划频率不足或控制延迟未补偿 | 画时间戳轨迹图,看前段是否稳定 | 时间对齐加渐进替代 |
| 换道反复横跳 | 决策层多假设交替胜出 | 看意图置信度的历史曲线 | 加置信度滞回和低频意图锁定 |
| 二次规划失败 | 约束过紧、末端松弛不够 | 查看求解器返回码和违反的约束列表 | 末端加松弛变量,放宽无关约束 |
| 轨迹切路缘 | Frenet坐标跳变 | 打印s/l与笛卡尔互算结果 | 三次样条重拟合加中点多采样 |
| 速度曲线震荡 | 目标速度预测抖动 | 画目标速度时间序列 | 加一阶低通滤波和加速度限幅 |
| 行人附近不敢走 | 软约束权重过大 | 查看势场对轨迹代价的影响曲线 | 分等级安全距离,避免全局权重一刀切 |
表格里列的每一条,我都亲测过或者陪别人排查过。最容易被忽视的是“换道反复横跳”,它往往不是参数问题,而是决策层置信度没有滞回,策略胜负一帧一变。解决之后,路测体验提升非常明显。
5.2 三条独家经验,写代码和写文档里都翻不到
第一条,轨迹平滑不是越光滑越好。过度平滑会让车在紧急避障时显得犹豫,因为所有轨迹都绕开了激进选项。我最后采用的是三项目标一起平衡:最小jerk、最小曲率变化、与推荐路径保持合理偏移。光滑只是手段,保留机动性才是目的。
第二条,代价函数调不出来时,先检查归一化,再检查场景概率偏差。很多参数在仿真里行、路测失灵,问题出在训练场景分布跟真实场景不一致,模型学会了走极端。这时候加约束不如调数据分布,方向要对。
第三条,永远不要信任单帧规划结果。我加了一道二义性检查:如果当前帧最优轨迹的行为类别和上一帧不同,比如上一帧是跟车、这一帧是变道,强制要求连续确认才允许执行。这道检查帮我拦住无数次莫名其妙的抖动切换,也是我做过性价比最高的改动。
这十年如果只让我留一个体会,那就是:规划不是把路算出来,而是学会在不确定中做选择。十年前我们相信规则可以覆盖一切,五年前我们相信数据可以学会一切,现在大家慢慢明白,真正可靠的系统是规则、数据、安全验证三者共生。
对刚入行的人,我的建议始终是从Frenet坐标系和状态机开始学起,再去看学习型方法。地基稳了,上面长什么树都行。规划这行变化快,但底层的那份谨慎与敬畏,十年我也没变过。