想象你是一场交响乐团的排练总监。老办法是让每个乐手回家单独录音,再把音轨混在一起——每个声部单独听都完美,合起来却总是乱糟糟:小提琴慢了半拍、长笛音准飘了、大鼓没跟上渐强。传统自动驾驶正是这么干的:感知、跟踪、预测、规划各自独立训练到最优再串联,结果每个模块都挺好,最终却在真实道路上做出危险决策。CVPR 2023 最佳论文 UniAD(Unified Autonomous Driving,统一自动驾驶)的破局之道,是把整个乐团请进同一个排练厅:共享一份总谱(BEV 鸟瞰特征)、由总指挥(规划模块)统一调度、全程录音(端到端可微训练)随时纠错。它首次把跟踪、建图、预测、规划四个模块放进同一个可微框架联合优化,让信息不再逐级衰减,而是像乐手之间的眼神交流一样,实时流动、互相成就。

岗位关联

对 AI 从业者而言,UniAD 标志着自动驾驶从“分模块拼装”走向“全局联合优化”,理解它等于拿到一份全栈智能驾驶架构的现代教科书,也可迁移到机器人、多模态 AI 等各类多任务场景。

目录
  1. 可视化演示
  2. 问题背景:分轨录音的困局
  3. 整体架构:同一张总谱
  4. 跟踪模块:锁住旋律线
  5. 建图模块:现场画五线谱
  6. 预测模块:预习下一乐章
  7. 规划模块:总指挥定调
  8. 联合训练:同步彩排,全程录音
  9. 意义与局限:新乐章的回响
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示端到端可微Pipeline、Query传递机制及联合训练对比,交互切换理解UniAD端到端规划核心技术。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+modeData.pipeline.caption+'

问题背景:分轨录音的困局

一个乐团要完成演出,常见做法是让每个乐手单独录好音轨,再拿进录音棚混音。每条音轨单独听都无可挑剔,可混在一起总差口气——小提琴慢了半拍、长笛音准飘了、鼓手没跟上渐强。传统自动驾驶正是这种“分轨录音”:感知、跟踪、预测、规划各模块先用标注数据独立训练到最优,再串联成完整系统。每个模块都追求自己的局部最佳,却忽略了下一个环节真正需要什么。感知模块输出的“看起来很好”的检测框,未必是预测模块最想要的表示;预测模块的微小偏差,到了规划阶段可能被放大成一次危险变道。

UniAD 的动机正是打破这种困局。它观察到,级联 pipeline 中信息在模块间逐级传递时不断丢失:跟踪只保留检测框、建图只保留车道线、预测只保留轨迹点,原始图像里丰富的上下文被层层压缩。同时,每个模块的误差也会随着链式传递不断累积,最终让自车在真实道路上做出错误的决策。论文提出了一个朴素却深刻的追问:能不能让所有模块像同一场排练的乐团一样,共享同一份总谱、听从同一个指挥,彼此聆听、联合优化?

技术上,UniAD 把传统级联流程重构成一个可微的整体。从多相机图像到最终自车轨迹,每个中间模块的输入输出都是结构化查询,误差不再是孤立的,而是通过可微连接逐级传导、统一回传。假设每个模块独立引入的误差分别为跟踪误差 ε_track、建图误差 ε_map、预测误差 ε_motion、规划误差 ε_plan,那么传统级联到最终路径的总误差近似为:

把多轨音轨混音时,每条音轨的微小偏差都会叠加成最终作品的“糊感”。UniAD 的决心,就是要结束这种各自为政的录音方式,把乐团请进同一个排练厅,让每一处瑕疵在合奏中被当场发现、当场修正。

εtotal=εtrack+εmap+εmotion+εplan\displaystyle \varepsilon_{\text{total}} = \varepsilon_{\text{track}} + \varepsilon_{\text{map}} + \varepsilon_{\text{motion}} + \varepsilon_{\text{plan}}
  1. 总误差 ε_total最终自车规划轨迹与理想轨迹的偏差,是衡量整个系统好坏的最终指标。εtotal\varepsilon_{\text{total}}
  2. 跟踪误差 ε_track目标位置与速度的估计偏差,是第一级噪声来源,相当于小提琴手起拍的微小迟疑。εtrack\varepsilon_{\text{track}}
  3. 建图误差 ε_map车道线、道路边界等要素的定位误差,让后续模块“看错谱子”。εmap\varepsilon_{\text{map}}
  4. 预测误差 ε_motion对其他交通参与者未来轨迹的估计偏差,会直接影响避障决策,像长笛预习错了下一小节的旋律。εmotion\varepsilon_{\text{motion}}
  5. 求和累加在级联系统中,各级误差同向叠加,最终误差往往大于任何单一模块的误差。εtotal=∑k=14εk\varepsilon_{\text{total}} = \sum_{k=1}^{4} \varepsilon_k

整体架构:同一张总谱

要合奏,先得有统一的总谱和舞台。UniAD 的“总谱”是一个鸟瞰视角的共享特征表示——BEV(Bird's Eye View,鸟瞰图)特征。所有模块,无论跟踪、建图还是预测、规划,都从这张总谱上读自己的声部,而不是各拿各的残缺草稿。这就像把五位乐手请到同一间排练厅,每个人的谱架上放着同一份完整的五线谱,只不过各自注入了不同颜色的标记。

技术上,UniAD 的整体架构由两部分构成:一个 BEV 特征编码器,加上四个任务模块。特征编码器基于 ResNet 和 BEVFormer 的设计思想,将环视相机采集的多张图像投射到统一的鸟瞰网格上,输出一个高分辨率的特征张量。随后,跟踪模块(TrackFormer)和建图模块(MapFormer)并行地从 BEV 特征中解码出目标轨迹查询与地图查询;预测模块(MotionFormer)融合前两者的查询信息,输出每个目标的未来轨迹;最后,规划模块(PlanFormer)综合全部信息,生成自车路径。整个过程从相机图像到规划轨迹全程可微,没有一处手工设计的不可导接口。

正是这张共享总谱,让四个任务模块不再各自为政。每个乐手面前都摆着同一张总谱,各看各的声部,却在同一个节奏框架里演奏——这正是 UniAD 后续所有联合优化能够成立的地基。

FBEV=Eθ(I1,I2,…,I6)\displaystyle F_{\text{BEV}} = E_{\theta}\left( I_1, I_2, \ldots, I_6 \right)
  1. 编码器 E_θ一个可学习的神经网络,参数为 θ,负责把多路图像合成鸟瞰表示,相当于把现场各角落的乐手位置翻译到同一张总谱上。EθE_{\theta}
  2. 输入图像 I_1 到 I_6车辆周围 6 个环视摄像头的原始画面,是乐手们看到的真实世界的碎片。I1,I2,…,I6I_1, I_2, \ldots, I_6
  3. 输出 F_BEV融合后的鸟瞰特征张量,每个网格位置都编码了“这里有什么”的信息,供所有任务读取。FBEVF_{\text{BEV}}

跟踪模块:锁住旋律线

首席小提琴的职责,是不让旋律线断掉——哪怕其他声部暂时盖过了它,哪怕乐手的手指在快速换把中短暂模糊,主旋律也必须被牢牢锁定。TrackFormer(跟踪模块)就是 UniAD 里的首席小提琴:它负责在连续的视频帧中锁定每一个交通参与者的身份,并输出稳定的目标轨迹。

TrackFormer 的核心机制是轨迹查询(Track Query)。你可以把每个 track query 想象成一张“目标身份证”:每个查询向量代表一个被跟踪的目标,通过交叉注意力从 BEV 特征中“拉取”目标的外观和位置信息,解码出目标框。关键的设计在于,这些查询会跨帧持续存在并不断更新——上一帧的查询带着目标的历史记忆进入下一帧,自然实现了目标关联,完全不需要传统方法里复杂的匈牙利匹配或卡尔曼滤波后处理。当目标被遮挡时,查询并不会消失,而是保持“待机”状态,等目标重新出现时直接接上,就像首席小提琴在旋律间隙也不会丢掉节拍。

注意力打分是这一机制的核心计算:

注意力权重就像首席小提琴在每个乐句开头“听一遍”各声部的位置,确定旋律该往哪儿走。而这条被锁住的旋律线——稳定的目标轨迹——会作为一份重要的“声部状态”,传递给后面负责预测和规划的其他乐手。

wij=softmax⁡j(qi⋅kjTd)\displaystyle w_{ij} = \operatorname{softmax}_j \left( \frac{ q_i \cdot k_j^{\mathsf{T}} }{ \sqrt{d} } \right)
  1. 查询向量 q_i第 i 个目标查询的向量表示,携带“我要找谁”的语义。qiq_i
  2. 键向量 k_jBEV 特征网格第 j 个位置的键向量,回答“我这里有什么”。kjTk_j^{\mathsf{T}}
  3. 点积打分查询与每个位置做点积,数值越大表示该位置与目标越相关,就像首席小提琴在每个乐句开头“听一遍”各声部的位置。qi⋅kjTq_i \cdot k_j^{\mathsf{T}}
  4. 缩放因子 √d除以特征维度的开方,防止点积过大导致 softmax 输出过于尖锐、梯度消失。d\sqrt{d}
  5. Softmax 归一化把相关性分数归一成权重之和为 1 的注意力分布,用于加权聚合特征。softmax⁡j(⋅)\operatorname{softmax}_j \left( \cdot \right)

建图模块:现场画五线谱

乐手演奏需要五线谱来定位音符,自动驾驶也需要知道车道线在哪里、路口是什么形状。传统方案依赖提前采集、离线制作的高精地图,就像每位乐手随身携带一张印好的旧乐谱——但现实世界不断在施工、改道,旧谱子经常会过时。MapFormer(建图模块)则是现场手绘五线谱的乐务总监:它不需要离线地图,直接从当前的 BEV 特征里实时画出眼前这条路的“线谱”。

MapFormer 使用一组可学习的地图查询(Map Query)解码出矢量化的车道元素——车道中心线、车道边界、人行横道等,每一项都表达为有序的点集。与输出稠密栅格的传统建图方法不同,矢量表达更加紧凑,天然适合后续规划模块直接读取,就像五线谱上清晰的一条条线,而不是一整片模糊的墨水印。在线建图的另一个优势是灵活:即便前方发生临时施工,只要相机能拍到锥桶和改道标线,建图模块就能实时更新“乐谱”,而离线高精地图做不到这一点。

地图查询与 BEV 特征融合并回归坐标的过程可以表示为:

这张“现场手绘的五线谱”虽然只覆盖自车当前视野,却足够让预测模块知道“哪些音符(轨迹)是顺着谱子在走”,也让规划模块明白“哪里可以演奏、哪里不能越界”。它解决的是实时性问题,也是端到端框架中不可缺失的一环。

Plane=MLP⁡map(Qmap⊕FBEV)\displaystyle P_{\text{lane}} = \operatorname{MLP}_{\text{map}} \left( Q_{\text{map}} \oplus F_{\text{BEV}} \right)
  1. 地图查询 Q_map一组可学习的查询向量,每个向量负责“画”某一条车道线或某一段道路元素。QmapQ_{\text{map}}
  2. 特征融合 ⊕地图查询与 BEV 特征按位置广播相加,让查询“看清”当前车道的实际布局。Qmap⊕FBEVQ_{\text{map}} \oplus F_{\text{BEV}}
  3. 回归头 MLP_map一个多层感知机,把融合后的特征回归成有序的坐标点集。MLP⁡map\operatorname{MLP}_{\text{map}}
  4. 输出 P_lane矢量车道点集,例如一条车道线由 20 个二维点依次连接而成。PlaneP_{\text{lane}}

预测模块:预习下一乐章

在合奏中,最优秀的乐手不会等到音符落在眼前才开始反应,而是会提前看好后面几小节,为接下来的旋律走向做好准备。MotionFormer(预测模块)就是 UniAD 里那个“预习下一乐章”的角色:它不仅要理解每个目标此刻在哪里,还要预测它们在接下来几秒可能去哪里。

MotionFormer 巧妙地把前面两个模块的产物——跟踪模块的目标框查询和建图模块的车道线查询——同时作为输入。有了“谁在哪里”(跟踪)和“路是什么样”(建图),它就能针对每个目标输出多模态的未来轨迹:论文中为每个目标预测 6 种可能的轨迹模式,每种模式附带一个置信度分数。为什么需要多模态?因为真实驾驶充满不确定性——前车可能直行、可能变道、也可能转弯,把多种可能性都摊在桌面上,规划模块才能做出更稳健的决策。与此同时,它对所有目标同时建模预测,目标与目标之间的交互(比如两车同时向同一车道靠近)也被隐式地编码进来。

训练时,预测模块的损失函数只要求至少一种模式接近真值轨迹,毕竟我们并不苛求模型精确猜到那一个未来,而是希望它把可能发生的情况都覆盖到:

预习下一乐章的长笛声部,并不会替指挥做决定,但它把即将到来的旋律走向清晰地呈现在所有人面前。MotionFormer 输出的多模态轨迹,正是规划模块做出最终决策时最重要的参考依据。

Lmotion=min⁡j∑t=1T∥x^t j−xtgt∥22\displaystyle L_{\text{motion}} = \min_{j} \sum_{t=1}^{T} \left\| \hat{x}_t^{\,j} - x_t^{\text{gt}} \right\|_2^2
  1. 模式编号 j遍历模型输出的所有轨迹模式(共 6 种),每一种代表一种可能的未来走向。jj
  2. 预测位置 x̂_t^j第 j 种模式在时刻 t 预测的目标位置,例如未来 3 秒内每隔 0.1 秒一个点,共 30 个点。x^t j\hat{x}_t^{\,j}
  3. 真值位置 x_t^gt数据集中标注的目标真实位置,是“标准答案”。xtgtx_t^{\text{gt}}
  4. L2 距离平方衡量预测与真值偏差的欧氏距离平方,偏差越大惩罚越大。∥⋅∥22\left\| \cdot \right\|_2^2
  5. 对时间求和把整条轨迹上所有时间点的偏差加起来,评价整条轨迹的好坏。∑t=1T\sum_{t=1}^{T}
  6. 最小化 min在所有模式中选出最接近真值的那一个来计算损失,鼓励“至少一种可能猜对”。min⁡j\min_{j}

规划模块:总指挥定调

总指挥不演奏任何乐器,但他决定整场演出的节奏、强弱与走向。PlanFormer(规划模块)就是 UniAD 的“总指挥”:它不直接输出检测框或轨迹预测,而是综合所有乐手的状态——跟踪模块报出的目标位置、建图模块画出的车道线、预测模块预判的他人走向——结合共享的 BEV 特征,最终拍板:自车接下来该怎么开。

PlanFormer 以规划查询(Planning Query)为入口,通过注意力机制从前序模块输出的结构化查询中聚合信息,解码出未来 3 秒内自车的轨迹,每 0.1 秒一个路径点,共 20 个点。与传统规划方法的一大不同是:它没有手工编写一大堆代价函数(如避障代价、车道偏离代价、乘坐舒适性代价),而是从海量人类驾驶数据中直接学习“什么是安全且舒适的轨迹”。数据教会它何时减速、何时变道、如何在车流中优雅地穿行。论文还设计了一个相当漂亮的实验——把某个中间模块的输出替换为真值,观察规划精度的变化,以此证明每一个前序模块都为规划贡献了独特的信息:

指挥的每一次挥手,都建立在对每个声部状态的清晰把握之上。PlanFormer 的决策之所以可靠,正是因为它看到的不是孤立的中间结果,而是整个乐团在同一时刻的完整画面——这也是端到端架构最核心的价值。

τ=PlanFormer⁡(FBEV, Qtrack, Qmap, Qmotion)\displaystyle \tau = \operatorname{PlanFormer}\left( F_{\text{BEV}},\ Q_{\text{track}},\ Q_{\text{map}},\ Q_{\text{motion}} \right)
  1. F_BEV共享的鸟瞰特征,是规划决策的“全景舞台”。FBEVF_{\text{BEV}}
  2. Q_track跟踪模块输出的目标查询,告诉规划模块“哪些障碍物在移动”。QtrackQ_{\text{track}}
  3. Q_map建图模块输出的地图查询,告诉规划模块“道路结构长什么样”。QmapQ_{\text{map}}
  4. Q_motion预测模块输出的轨迹查询,告诉规划模块“别人接下来可能去哪”。QmotionQ_{\text{motion}}
  5. 输出 τ自车未来 3 秒的规划轨迹,由 20 个二维路径点组成。τ\tau

联合训练:同步彩排,全程录音

乐团彩排时,指挥会随时喊停、统一调度,让所有声部同步修改——小提琴这里慢一点、长笛那里弱一点。UniAD 用联合训练实现了这种“同步彩排”:四个任务不再各自为政地训练,而是共享同一个优化目标,从同一个损失函数中吸收反馈。

具体来说,每个任务都有自己的监督损失:跟踪损失匹配目标框、建图损失回归车道点、预测损失对齐多模态轨迹、规划损失度量自车路径。联合训练时,总损失是四个损失的加权和,反向传播时梯度会同时更新共享编码器和所有模块的参数。这里的关键词是可微连接:因为整个 pipeline 从感知到规划都是可微的,规划损失的梯度可以一路回流到特征编码器,让最底层的感知特征学会“牺牲一点点检测指标的完美,换来最终规划结果的大幅提升”。这种跨模块的梯度共享,是分模块训练无论如何也做不到的。

总损失函数可以写作:

“同步彩排、全程录音”的威力在于:每个模块的进步都会通过可微连接传导给其他模块,任何一处偏差都能从最终结果中溯源并修正。这就好比指挥不仅能指出“第三小节长笛慢了”,还能精确告诉长笛手“是因为你起拍时没听定音鼓的提示”——全局的和谐,才是真正的和谐。

Ltotal=λ1Ltrack+λ2Lmap+λ3Lmotion+λ4Lplan\displaystyle L_{\text{total}} = \lambda_1 L_{\text{track}} + \lambda_2 L_{\text{map}} + \lambda_3 L_{\text{motion}} + \lambda_4 L_{\text{plan}}
  1. 权重系数 λ_k每个任务损失前的权重,控制各任务在联合优化中的重要程度,相当于指挥对不同声部的关注比例。λ1,λ2,λ3,λ4\lambda_1, \lambda_2, \lambda_3, \lambda_4
  2. 跟踪损失 L_track跟踪模块输出的目标框与真值框的回归误差。LtrackL_{\text{track}}
  3. 建图损失 L_map矢量车道点集与真值车道点的坐标误差。LmapL_{\text{map}}
  4. 预测损失 L_motion多模态预测轨迹与真值轨迹的误差(前文已展开)。LmotionL_{\text{motion}}
  5. 规划损失 L_plan自车规划轨迹与人类驾驶轨迹的误差,是最终“演出效果”的直接度量。LplanL_{\text{plan}}

意义与局限:新乐章的回响

这场“同台合奏”的首演,惊艳了整个学术界——UniAD 拿下 CVPR 2023 最佳论文(Best Paper),成为自动驾驶领域里程碑式的工作。论文在 nuScenes 数据集上对跟踪、建图、预测、规划四大类指标进行系统评测,绝大多数指标都超越了此前最强的分模块系统。更值得注意的是规划效果:UniAD 的规划误差相比级联基线大幅下降,充分证明了“全局联合优化”确实优于“局部最优的拼接”。

论文通过一组“真值替换”实验进一步剖析了各模块的贡献:依次把跟踪、建图、预测模块的输出替换成真值,观察规划误差能下降多少。实验发现,无论替换哪个模块,规划误差都会进一步降低——这说明每个模块都还有提升空间,也说明模块间的可微连接确实把信息有效传递到了规划端。如果定义级联基线的误差为 Err_base、UniAD 的误差为 Err_uniad,则相对提升率可以写作:

当然,这场演出也并非没有代价。UniAD 的模型体量庞大,推理耗时明显高于轻量级级联方案,对车端芯片的算力提出了更高要求;它高度依赖 BEV 编码器的质量,更换传感器配置往往需要重新训练;在线建图的覆盖范围也受限于当前视野。不过,作为 CVPR 2023 的最佳论文,UniAD 已经为端到端自动驾驶指明了新的航向——让感知与决策真正成为一个整体,而不是一堆零件。

Rimp=Errbase−ErruniadErrbase\displaystyle R_{\text{imp}} = \frac{ \mathrm{Err}_{\text{base}} - \mathrm{Err}_{\text{uniad}} }{ \mathrm{Err}_{\text{base}} }
  1. 级联基线误差 Err_base传统分模块训练后串联系统的规划误差,是“分轨录音”的基准表现。Errbase\mathrm{Err}_{\text{base}}
  2. UniAD 误差 Err_uniad端到端联合优化后的规划误差,是“同台合奏”的成绩。Erruniad\mathrm{Err}_{\text{uniad}}
  3. 分子差值两者之差表示联合优化带来的绝对误差减少量。Errbase−Erruniad\mathrm{Err}_{\text{base}} - \mathrm{Err}_{\text{uniad}}
  4. 相对提升 R_imp除以基线误差得到相对提升率,方便在不同数据集、不同指标间横向比较。RimpR_{\text{imp}}

AI 应用场景

自动驾驶公司的端到端算法研发

阶段:模型训练与部署阶段

解决的问题:传统分模块系统需要为感知、跟踪、预测、规划分别准备标注数据、分别调参,且模块间接口由人工设计,信息损耗严重。UniAD 的端到端联合训练让规划损失的梯度直接指导感知表征学习,减少中间表示的冗余设计。

具体案例:某 L4 自动驾驶公司希望降低城市道路的接管率。工程师把 UniAD 式框架部署到车端,用同一份 BEV 特征同时输出目标框、局部地图、动态目标预测和自车规划轨迹。相比此前感知与规划两组工程师各自调参,联合训练让雨天遮挡场景下的规划失误明显减少,接管率下降了近三成。

仿真场景重建与数据闭环

阶段:数据处理与闭环仿真阶段

解决的问题:真实路采数据中极端场景稀少,难以覆盖规划模块的边界情况。UniAD 的建图与预测输出是结构化的矢量地图和多模态轨迹,天然适合用来重建仿真场景,并支持对场景要素进行编辑和回放。

具体案例:数据闭环团队从城市路采视频中,利用建图模块提取矢量车道、利用跟踪与预测模块提取动态目标轨迹,自动生成“前车急刹 + 实线变道”等高难仿真场景。规划模块在仿真中批量测试新版本,迭代周期从两周缩短到三天。

多任务多模态模型的工业化迁移

阶段:模型架构设计与迁移阶段

解决的问题:机器人、车路协同等场景同样面临多任务联合优化需求,但各自独立部署多个模型会消耗大量算力。UniAD“共享编码器 + 查询传递”的范式可以迁移到通用多任务感知系统,实现一套网络、多项任务。

具体案例:某园区无人配送机器人团队借鉴 UniAD 架构,把目标检测、路径回归、障碍预测整合为单一端到端模型,机载算力开销降低 40%,决策延迟从 180ms 下降到 110ms,在窄道会车场景中的表现更加连贯自然。

Python 演示

python138 行
import torchimport torch.nn as nn  class FeatureEncoder(nn.Module):    """共享编码器: 从BEV特征提取统一表示"""     def __init__(self, in_dim=512, dim=256):        super().__init__()        self.fc = nn.Linear(in_dim, dim)        self.layer_norm = nn.LayerNorm(dim)     def forward(self, bev):        return self.layer_norm(self.fc(bev))  class TrackFormer(nn.Module):    """跟踪模块: 用track query关联跨帧目标"""     def __init__(self, dim=256, num_query=10):        super().__init__()        self.dim = dim        self.track_query = nn.Parameter(torch.randn(num_query, dim) * 0.02)        self.decode = nn.Linear(dim, 4)     def forward(self, feat):        scores = torch.matmul(feat, self.track_query.T) / (self.dim ** 0.5)        weights = torch.softmax(scores, dim=-1)        aggregated = weights.unsqueeze(-1) * feat.unsqueeze(1)        boxes = self.decode(aggregated)        return boxes  class MapFormer(nn.Module):    """建图模块: 预测矢量车道线点集"""     def __init__(self, dim=256, num_points=20):        super().__init__()        self.map_query = nn.Parameter(torch.randn(num_points, dim) * 0.02)        self.reg = nn.Linear(dim, 2)     def forward(self, feat):        query_feat = feat.unsqueeze(1) + self.map_query.unsqueeze(0)        points = self.reg(query_feat)        return points  class MotionFormer(nn.Module):    """预测模块: 输出多模态未来轨迹"""     def __init__(self, dim=256, num_modes=6, horizon=30):        super().__init__()        self.num_modes = num_modes        self.horizon = horizon        self.fc_box = nn.Linear(4, dim)        self.mode_query = nn.Parameter(torch.randn(num_modes, dim) * 0.02)        self.traj_head = nn.Linear(dim, horizon * 2)     def forward(self, feat, boxes):        box_feat = self.fc_box(boxes.mean(dim=1))        context = feat + box_feat        context = context.unsqueeze(1) + self.mode_query.unsqueeze(0)        trajs = self.traj_head(context)        return trajs.view(-1, self.num_modes, self.horizon, 2)  class PlanFormer(nn.Module):    """规划模块: 融合所有中间特征输出自车轨迹"""     def __init__(self, dim=256, horizon=20):        super().__init__()        self.horizon = horizon        self.proj_box = nn.Linear(4, dim)        self.proj_map = nn.Linear(2, dim)        self.proj_traj = nn.Linear(2, dim)        self.plan_head = nn.Linear(dim * 4, horizon * 2)     def forward(self, feat, boxes, map_points, trajs):        h_box = self.proj_box(boxes.mean(dim=1))        h_map = self.proj_map(map_points.mean(dim=1))        h_traj = self.proj_traj(trajs.mean(dim=(1, 2)))        fused = torch.cat([feat, h_box, h_map, h_traj], dim=-1)        return self.plan_head(fused).view(-1, self.horizon, 2)  class UniAD(nn.Module):    """UniAD简化框架: 共享编码器 + 四个任务头联合训练"""     def __init__(self):        super().__init__()        self.encoder = FeatureEncoder()        self.track = TrackFormer()        self.map = MapFormer()        self.motion = MotionFormer()        self.plan = PlanFormer()     def forward(self, bev, gt_boxes=None, gt_map=None, gt_traj=None, gt_plan=None):        feat = self.encoder(bev)        boxes = self.track(feat)        map_points = self.map(feat)        trajs = self.motion(feat, boxes)        plan = self.plan(feat, boxes, map_points, trajs)         losses = {}        if gt_boxes is not None:            losses["track"] = nn.functional.l1_loss(boxes, gt_boxes)        if gt_map is not None:            losses["map"] = nn.functional.l1_loss(map_points, gt_map)        if gt_traj is not None:            losses["motion"] = nn.functional.l1_loss(trajs, gt_traj)        if gt_plan is not None:            losses["plan"] = nn.functional.l1_loss(plan, gt_plan)        return plan, losses  def main():    torch.manual_seed(42)    model = UniAD()    optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)     bev_feat = torch.randn(4, 512)    gt_boxes = torch.randn(4, 10, 4)    gt_map = torch.randn(4, 20, 2)    gt_traj = torch.randn(4, 6, 30, 2)    gt_plan = torch.randn(4, 20, 2)     for epoch in range(3):        plan, losses = model(bev_feat, gt_boxes, gt_map, gt_traj, gt_plan)        total = (losses["track"] + 0.5 * losses["map"]                 + losses["motion"] + 2.0 * losses["plan"])        optimizer.zero_grad()        total.backward()        optimizer.step()        print(f"epoch {epoch}: total loss {total.item():.4f}")  if __name__ == "__main__":    main()
目的:用 PyTorch 实现一个简化版 UniAD,让四个任务共享一个编码器,通过加权损失联合训练,演示“端到端”的梯度统一回传。
  1. feat = self.encoder(bev)用共享编码器把原始 BEV 特征压缩成统一向量,所有任务共用一个特征源头,就像所有乐手使用同一份总谱。
  2. boxes = self.track(feat)跟踪头用 track query 从特征里“拉取”目标框,对应 TrackFormer 的目标关联。
  3. map_points = self.map(feat)建图头解码出车道线点集,对应 MapFormer 的在线画谱。
  4. trajs = self.motion(feat, boxes)预测头把跟踪框信息融入,输出多模态轨迹,对应 MotionFormer 的预习下一乐章。
  5. plan = self.plan(feat, boxes, map_points, trajs)规划头融合全部中间结果,输出自车轨迹,对应 PlanFormer 的总指挥定调。
  6. total = (losses["track"] + 0.5 * losses["map"] + losses["motion"] + 2.0 * losses["plan"])加权求和形成联合损失,权重 λ 表达对各任务的不同侧重。
  7. total.backward()梯度从规划损失一路回流到编码器,完成端到端学习,这是联合训练与本框架的灵魂。

常见误区

误区以为端到端就是拿传感器原始图像直接预测油门刹车,中间不用跟踪、建图、预测这些模块,纯黑箱,完全不理解中间发生了什么。

正解UniAD 恰恰保留了全部中间任务,只是把它们放进同一个可微网络里联合优化。端到端改变的是“训练方式”和“信息流”,不是去掉中间表示。它让每个模块仍输出可解释的结构化结果(目标框、车道、轨迹),只是这些结果不再由人手工设计连接,而是由梯度自动调节。就像乐团合奏依然需要每个声部,只是大家不再各录各的。

为何常见:早期端到端自动驾驶(如直接模仿学习)确实是输入到输出的黑箱,容易把“端到端”和“无中间模块”画等号。

误区认为 TrackFormer、MapFormer、MotionFormer 里都是注意力,换个名字而已,查询传递没啥特别。

正解区别在于 query 携带了跨任务的结构化信息:跟踪的 query 是“目标身份”,建图的 query 是“地图元素”,预测的 query 融合了前两者的语义。信息以 query 为单位在模块间传递,比单纯拼接特征更加紧凑,而且天然支持变长目标数量。正是这种“接力棒”式传递,才让模块间的联合优化有了共同的中间语言。

为何常见:只看架构图会以为是重复的 Transformer 堆叠,忽略了 query 语义从跟踪到规划的流动过程。

误区既然论文说联合训练好,那把四个模块直接拼起来训就一定更强,权重随便设置就行。

正解联合训练要成立的前提很多:共享特征规模足够大、损失权重得当、各任务监督信号充分。UniAD 论文中的真值替换实验表明,即使联合训练后,把某个中间模块替换成真值,规划误差还能继续下降——说明感知模块仍有瓶颈。数据不足或权重失衡时,联合训练可能比单模块训练更难收敛。联合训练是“更强的优化方式”,不是“无敌的免死金牌”。

为何常见:论文展示了大幅领先的指标,容易让人产生“只要联合训练就万事大吉”的简单化印象。

误区认为 MapFormer 既然能实时画车道线,离线高精地图就没有存在意义了,可以彻底扔掉。

正解UniAD 的在线建图输出的是感知范围内的局部矢量地图,范围受相机视野限制;而高精地图提供全局道路拓扑、限速、路口关系等强先验。两者是互补而非替代。去掉高精地图后,车辆在进入陌生区域或长距离导航时依然缺少全局信息支撑。

为何常见:看到“在线建图”的卖点,容易夸大其对高精地图的替代能力,忽略了两者用途的根本差异。

误区认为只要计算图可微,反向传播梯度就能顺畅流动,所有模块必然学到好的特征。

正解可微只是前置条件。梯度信号要真正有用,还需要逐任务损失设计合理、中间表示具备足够表达能力、训练过程数值稳定。UniAD 的大量实验正是在检验“梯度是否真的流到了该去的地方”——比如用真值替换观察规划变化。如果监督信号被某个模块“短路”吸收,后面的模块依然学不好。可微像打通了水管,但水压和水质同样重要。

为何常见:数学上“可微”与“优化成功”是两回事,但概念上容易被混为一谈。

面试题

Q1. UniAD 发表于哪一年的 CVPR 并获得 Best Paper?

单选题 · easy

  • A.2021 年 CVPR
  • B.2022 年 CVPR
  • C.2023 年 CVPR← 正确答案
  • D.2024 年 CVPR

答案:C。UniAD 是 CVPR 2023 的最佳论文,全称 Unified Autonomous Driving,中文常译为“面向规划的自动驾驶”。如同刚成立的乐团第一场演出就拿了奖,2023 是它的成名时刻,其他年份均为干扰项。

Q2. UniAD 全称"Unified Autonomous Driving",其核心思想是?

单选题 · easy

  • A.多任务分阶段独立完成
  • B.多任务端到端联合优化← 正确答案
  • C.仅优化最终规划输出
  • D.仅依赖高精度离线地图

答案:B。UniAD 的 Unified 体现在把跟踪、建图、预测、规划放进同一个可微框架联合训练,相当于让各声部同台排练而非分轨录音。A 是传统级联做法,C 忽略了中间任务的价值,D 与论文强调在线建图的方向相反。

Q3. UniAD 中依次传递信息的四个任务模块是?

单选题 · mid

  • A.跟踪、建图、检测、规划
  • B.检测、建图、预测、规划
  • C.跟踪、预测、检测、规划
  • D.跟踪、建图、预测、规划← 正确答案

答案:D。UniAD 结构中 TrackFormer 与 MapFormer 先从 BEV 特征解码,MotionFormer 融合前两者输出预测轨迹,PlanFormer 最后输出规划。选项 D 与此顺序一致。A、B 混入了“检测”——UniAD 没有独立检测头,目标由跟踪查询负责;C 顺序错误。

Q4. 传统分模块自动驾驶 pipeline 的主要缺陷是?

单选题 · easy

  • A.模块间误差逐级累积放大← 正确答案
  • B.单个模块训练速度太慢
  • C.传感器成本过高难以量产
  • D.规划模块不需要感知输入

答案:A。传统级联把每个模块拆开训练,像分轨录音中每条音轨单独完美但合在一起总有偏差,误差在模块间沿 pipeline 逐级放大。B、C 是工程问题而非架构缺陷,D 完全错误——规划恰恰需要感知输入。

Q5. TrackFormer 跨帧关联目标的核心机制是?

单选题 · mid

  • A.卡尔曼滤波状态预测
  • B.轨迹查询向量持续传递← 正确答案
  • C.特征图上非极大抑制
  • D.光流法逐像素匹配

答案:B。TrackFormer 的核心是 track query 跨帧持续存在,用注意力从 BEV 特征中更新状态,自然完成关联,像首席小提琴不管什么时刻都能接住旋律线。A 是传统多目标跟踪方法,C 是检测后处理,D 是光流法,都不是 UniAD 的做法。

Q6. 以下属于 UniAD 中四个任务模块的有?

多选题 · mid

  • A.TrackFormer 跟踪模块← 正确答案
  • B.MapFormer 建图模块← 正确答案
  • C.MotionFormer 预测模块← 正确答案
  • D.PlanFormer 规划模块← 正确答案

答案:ABCD。UniAD 的四个模块就是 TrackFormer、MapFormer、MotionFormer、PlanFormer,分别负责跟踪、建图、预测、规划,四个全选。每个模块都输出结构化查询,像接力棒一样依次传给下一模块,缺一不可。

Q7. UniAD 中 BEV 特征的作用是?

单选题 · hard

  • A.提供统一的鸟瞰图共享表示← 正确答案
  • B.仅用于存储离线三维地图
  • C.只服务于最终规划模块
  • D.替代所有传感器的原始数据

答案:A。BEV 特征把环视相机信息统一投射到鸟瞰网格,像乐团总谱把各声部统一在一张纸上,四个任务模块都从它读取信息。B 说存地图不对,C 说只服务规划不对,D 说替代原始数据不对——BEV 是融合后的中间表示,并非原始数据本身。

Q8. 联合训练相比分模块训练的主要优势有?

多选题 · hard

  • A.梯度可跨模块传播共享← 正确答案
  • B.规划损失指导感知调优← 正确答案
  • C.各模块指标必然全部最优
  • D.全局误差最小化而非局部← 正确答案

答案:ABD。联合训练让规划损失的梯度能回传到感知模块(A、B 正确),追求的是全系统误差最小(D 正确)。但 C“各模块指标必然全部最优”过于绝对——联合优化可能让感知指标略降而规划指标大涨,整体上更优,局部未必全部最优。

Q9. MapFormer 在线建图的输出形式是?

单选题 · mid

  • A.栅格化的稠密占据网格
  • B.矢量化的车道级元素← 正确答案
  • C.高精度三维点云地图
  • D.离散的语义分割标签

答案:B。MapFormer 输出的是矢量车道线、人行横道等有序点集,像五线谱上清晰的一条条线,紧凑且便于规划模块读取。A 是栅格表征,C 是激光雷达点云产物,D 是像素级分类,都与 MapFormer 的矢量输出形式不符。

Q10. 关于 UniAD 的局限性与未来方向,下列说法正确的有?

多选题 · extreme

  • A.计算开销大,实时性受挑战← 正确答案
  • B.依赖 BEV 编码器提取特征← 正确答案
  • C.在线建图范围有限需扩展← 正确答案
  • D.已完全取代高精地图方案

答案:ABC。UniAD 的局限包括模型体量大、推理耗时高(A),依赖 BEV 编码器质量(B),在线建图只覆盖当前视野(C)。D 过于绝对——论文只证明局部在线建图有效,并未宣称替代全局高精地图,两者是互补关系。