自动驾驶大概是深度学习工程化程度最高的领域:模型要跑在车规级芯片上,延迟按毫秒算,长尾场景按亿公里计。过去几年,这套技术栈经历了一次剧烈重构——从几十个模块拼装的传统流水线,到 BEV 统一表征,再到如今特斯拉 FSD V12 和各家新势力押注的端到端大模型。本文按"感知-预测-规划-架构"的顺序,把这条演进主线拆开讲清楚。
传统模块化栈:精确但脆弱
经典的自动驾驶栈是一条长流水线:传感器标定与预处理 → 目标检测与跟踪 → 高精地图定位 → 行为预测 → 决策规划 → 控制。每个模块独立开发、独立评测,用接口契约串起来。
这种架构的优点是工程上可控:出问题能定位到模块,规则系统兜底安全,符合车规开发流程。Mobileye 和早期 Waymo 都是这个路线的代表。
问题在规模化之后暴露出来:模块间传递的是结构化中间结果(比如 3D 框列表),信息被不可逆地压缩——检测器丢掉的一个模糊目标,规划器永远不知道它存在过;误差逐级累积放大;最要命的是每个长尾 corner case 都要人工写规则,代码库越滚越大,系统却越来越难收敛。行业里"解决 99% 的问题只花了 1% 的时间"的调侃,说的就是这条路线的天花板。
BEV 感知:给多传感器一个统一坐标系
BEV(Bird's Eye View,鸟瞰图)是这几年感知侧最重要的范式升级。思路是:把车周多个相机(加雷达)的特征统一变换到以自车为中心的俯视平面,在 BEV 空间里做检测、分割和跟踪。
为什么非要 BEV?因为规划和控制天然在俯视平面工作,而单个相机的透视图像里,近大远小、遮挡关系、跨相机的同一个目标,处理起来都很别扭。统一到 BEV 后,多相机融合变成特征拼接,时序融合变成对齐历史帧,下游直接消费。
技术路线上分两大流派:
- 自底向上(LSS 系):BEVDepth、BEVFusion 走这条路。对每个像素预测深度分布,按深度把图像特征"抬"到 3D 再压扁到 BEV。显式用深度,几何解释性好;
- 自顶向下(Transformer 系):BEVFormer 用一组 BEV 网格 query 通过可变形注意力去各相机特征里"采样",隐式学习投影关系,不依赖显式深度,端到端可训练。
工程实践里的共识是:深度监督(哪怕来自稀疏激光雷达)对收敛帮助巨大;时序历史帧基本是白送的精度;BEV 分辨率要在显存和精度间做权衡,量产方案常用 0.5m~1m 栅格。
Occupancy:从"识别目标"到"描述空间"
目标检测有一个先天缺陷:它只能输出训练时定义过的类别。路上掉一个异形货物、侧翻的锥桶,类别体系里没有,检测器就当背景——这在安全场景里是不可接受的。
Occupancy Network(占用网络)换了个问法:不问"这是什么",只问"这个体素被占据吗、在往哪动"。把 BEV 平面再拉高成 3D 体素栅格,逐格预测占据状态和语义,相当于对周围空间做稠密的体素重建。任意形状的障碍物、施工区域、翻倒的车辆,都能被表达。
特斯拉在 2022 年 AI Day 重点推了这个方案,国内各家随后跟进。代价是算力开销明显上升,量产上常配合稀疏体素表示或者 TPV(三平面)这类压缩表征来省显存。
端到端大模型:把整条栈变成一个网络
BEV 解决了感知内部的统一,但感知和规划之间仍然是人工接口。2023 年 CVPR 最佳论文 UniAD 第一次把检测、跟踪、建图、预测、规划全部装进一个 Transformer 架构里,以规划为导向联合训练,证明了"可微分全栈"可行。
特斯拉 FSD V12 走得更彻底:从光子到控制信号,一个网络直接输出转向和加减速。马斯克的说法是删掉了 30 多万行 C++ 规则代码。国内华为 ADS 3.0、小鹏、理想也都在转向端到端架构,通常保留感知中间输出做安全冗余和可解释性兜底。
端到端的优势是系统以最终驾驶目标优化,不再有信息瓶颈,行为更像人;挑战是数据需求爆炸(需要海量高质量人类驾驶片段)、可解释性差、安全验证体系要从模块测试改成场景统计。目前行业的普遍判断是:端到端负责"能力上限",规则冗余负责"安全下限",两者会长期共存。
动手试试:BEVFormer 推理与 LSS 核心逻辑
理解 BEV 最好的方式是看 LSS(Lift-Splat-Shoot)的核心变换:
import torch # Lift: 为每个像素预测深度分布, 把图像特征抬升到3D # x: (B, N相机, C, fH, fW), depth_logits: (B, N, D, fH, fW) x = torch.randn(1, 6, 64, 32, 88) depth_logits = torch.randn(1, 6, 41, 32, 88) depth = depth_logits.softmax(dim=2) # 深度概率 lifted = x.unsqueeze(2) * depth.unsqueeze(3) # 外积: (B,N,D,C,fH,fW) # Splat: 按相机内外参把特征点投到BEV栅格 (需预先算好索引) # bev = voxel_pooling(lifted, coords) # 工程上用CUDA加速的柱体池化 print(lifted.shape) # torch.Size([1, 6, 41, 64, 32, 88])完整实验可以直接跑 OpenDriveLab 的 BEVFormer 或 mmcv 生态的 bevfusion 配置,nuScenes mini 数据集在单张 3090 上就能训出一个可用的 demo。
三代架构横向对比
| 维度 | 模块化流水线 | BEV+多任务架构 | 端到端大模型 | | --- | --- | --- | --- | | 信息传递 | 结构化中间结果 | BEV统一特征 | 端到端隐式特征 | | 优化目标 | 各模块独立指标 | 联合损失 | 直接以驾驶行为为损失 | | 长尾处理 | 人工规则堆叠 | 数据驱动+占用网络 | 数据驱动+世界模型 | | 可解释性 | 强 | 中等 | 弱(靠中间监督补偿) | | 数据需求 | 标注为主