一、这篇论文在讲什么?
传统系统的痛点:刻板的流水线工厂
想象一下,传统的自动驾驶系统就像一个刻板的流水线工厂:
- 感知部门(看路)把报告交给预测部门(猜别人怎么走)
- 预测部门再交给规划部门(决定自己怎么开)
- 一旦感知部门看走了眼,整个工厂就会**“一步错,步步错”**(误差累积)
- 部门之间缺乏沟通,效率极低
- 他们还非要扛着一张无比沉重的"全局鸟瞰图"(Dense BEV)来工作,把电脑累得够呛
三大痛点剖析
作者在引言部分一针见血地指出了目前端到端自动驾驶(E2E-AD)的三大痛点:
| 痛点 | 传统方案的问题 | 影响 |
|---|---|---|
| 顺序执行的魔咒 | UniAD 等明星模型依然玩"感知-预测-规划"的串行游戏 | 误差累积 + 训练不稳定 |
| 缺乏协同效应 | 强行排了先后顺序 | 无法学会"为规划而感知"或"博弈论式互动" |
| 沉重的 BEV 包袱 | 使用密集的鸟瞰图(Dense BEV)特征 | 想看远、想记历史 → 计算量爆炸 |
DriveTransformer 的破局宣言
彻底砸碎流水线,把所有部门拉到同一张圆桌上开会!
DriveTransformer 抛弃了沉重的 BEV 特征,完全用纯 Transformer 架构搭建了一个 “并行、稀疏、流式” 的新世界:
- 大大降低系统复杂度
- 模型极其容易扩大规模(Scale up)
- 用最优雅的 Transformer 语言统一检测、地图构建、运动预测和路径规划
二、核心方法:三大神功 + 三套连招
这是全篇最硬核、最精彩的部分!DriveTransformer 的成功秘诀在于它的架构设计。
🥇 神功一:任务并行 (Task Parallelism) —— 圆桌会议
再也没有上下级之分! 系统里有三种 Token 探针(Query),它们在每一个 Transformer 模块里直接互相交流,信息瞬间互通有无:
| 探针类型 | 数量 | 负责任务 |
|---|---|---|
| Agent Queries(智能体探针) | 900个 | 盯住动态的车辆、行人 |
| Map Queries(地图探针) | 100个 | 识别静态的车道线、红绿灯等 |
| Planning Queries(规划探针) | 专属 | 决定自己的车怎么开 |
这彻底消除了层级带来的卡顿和误差传递!
🥈 神功二:稀疏表示 (Sparse Representation) —— 直饮源头水
丢掉庞大笨重的全局 BEV 鸟瞰图!
这些轻量级的探针直接一头扎进**原始的多视角传感器画面(Raw Sensor Features)**里去提取它们关心的信息。
生动比喻:这就像你用谷歌搜索精准直达答案,而不是把整本大英百科全书背下来。
🥉 神功三:流式处理 (Streaming Processing) —— 时光记忆背包
怎么记住过去的事情?传统方法是保存过去好几帧的沉重 BEV 图像。
DriveTransformer 借用了先进的 FIFO(先进先出)队列:
- 只把上一帧最有价值的 Top-K 个探针(K=50) 塞进队列里
- 当作历史先验信息传递给下一帧
- 既省内存,又保留了高级的语义信息
🔄 三套连招:统一注意力机制
在每一次"施法"(每个 Transformer Layer)时,所有任务只用这三种统一的操作:
| 连招 | 操作名称 | 作用 | 生动比喻 |
|---|---|---|---|
| 第一招 | Task Self-Attention(任务自注意力) | 探针与探针之间的情报共享 | 探针们的"群聊" |
| 第二招 | Sensor Cross-Attention(传感器交叉注意力) | 探针向原始传感器画面索取视觉线索 | 去"对图" |
| 第三招 | Temporal Cross-Attention(时序交叉注意力) | 探针向历史探针取经,融合时间信息 | “翻旧账” |
细节彩蛋:因为探针们可以直接从传感器和历史中获取信息,DriveTransformer 根本不需要像老模型那样做分阶段的预训练,从头到尾一体化训练,一气呵成!
三、时序魔法详解:运动补偿的精妙设计
核心问题:时间穿越时的"刻舟求剑"
你开车时不仅要记住 1 秒前有一辆车在你左边(历史记忆),还得猜到这 1 秒内它往前开了一段距离。传统模型经常犯"刻舟求剑"的错误。
DriveTransformer 通过两步走策略,给历史探针打上了完美的"记忆补丁":
第一步:自车坐标系对齐 (Ego Transform)
解决"我"动了的问题
在把过去的 Top-K 探针拿来用之前,首先得意识到:我们自己的车(Ego Vehicle)在这段时间里也往前开了!
系统会根据自车在这段时间内的位移和旋转(通过里程计或定位信息得到),把历史探针携带的位置信息(PE),统一转换到当前时刻的自车坐标系下。
这步搞定后,像红绿灯、车道线这种静态的地图探针就完全对齐了。
第二步:智能体运动补偿 (Agent Motion Compensation)
解决"他"动了的问题
对于动态的智能体探针,光做第一步是不够的——他们在动!
DriveTransformer 施展了一个数学法术:
-
算出"他跑了多远"
- 历史帧里的智能体探针预测了物体的速度 $v^t_{agent}$
- $速度 \times 时间 = 位移$
- 计算时间差 $\Delta t = (t - t_0)$
-
炼制"记忆补丁"
- 把位移向量喂给 MLP 当"翻译官"
- MLP 输出两个参数:缩放因子 $\gamma$ 和 偏移因子 $\beta$
-
施放"自适应层归一化"法术
$$ \hat{PE}^t_{agent} = \text{LayerNorm}(\text{原始历史 } \hat{PE}^t_{agent}, \; \text{受 } [\gamma, \beta] \text{ 控制}) $$
这样做的绝妙之处
给模型戴上了一副 “基于物理运动规律的预判眼镜”
模型在对历史探针说:“嘿哥们,我知道你 1 秒前在那里,但根据你当时的速度,你现在应该跑到这儿了。我就在这个新位置去匹配当前摄像头的画面!”
优势对比:
| 方案 | 传统 Dense BEV | DriveTransformer |
|---|---|---|
| 做法方法 | 把沉重 BEV 像素用空间变换网络扭曲平移 | 只对几十个探针的 PE 做乘法加法 |
| 计算开销 | 巨大 | 丝滑无比,瞬间完成 |
| 跟踪匹配 | 需要复杂的追踪算法 | 免去 Tracking-free,自然学习就能"相认" |
四、探针设计的核心秘密:“干湿分离”
一个非常专业的问题
如果位置编码(PE)已经通过"加法"融化在特征(Feature)里了,那后来怎么可能再把它单独捞出来做坐标系转换呢?
DriveTransformer(以及它借鉴的 StreamPETR 等现代架构)采用的是 “干湿分离"设计!
探针的"双轨制”:公文包设计
在历史队列(FIFO Queue)里保存上一帧的探针时,存下来的不是一个融合向量,而是一个 “公文包”:
| 文件 | 内容 | 说明 |
|---|---|---|
| 文件 A:语义特征 | 高维向量(如 256维) | 记录"这是什么"(如:红色轿车,车灯亮着)—— 不需要坐标转换 |
| 文件 B:显式 3D 锚点 | 纯物理坐标 $(x, y, z)$ | 记录"它在哪"—— 轻量、可直接做几何运算 |
运动补偿的实际流程
-
直接对"物理坐标"动手脚
- 因为位置信息是作为显式坐标单独存放的
- 直接拿出 $(x, y, z)$,用位移和旋转矩阵做几何运算
- 嗖的一下!坐标就完美对齐到当前坐标系
-
位置编码的"现做现卖"
- 有一个专门的"编码加工厂"(MLP 网络)
- 接收新坐标 $(x', y', z')$,动态生成当前时刻的高维 PE
-
注入"他动"魔法
- 在 PE 准备和语义特征相加之前,运动补偿闪亮登场
- 用 MLP 算出 $\gamma$ 和 $\beta$
- 通过 Adaptive LayerNorm 作用在新鲜生成的 PE 上
-
终极合体
- 所有空间转换和运动补偿都做完了
- “终极进化版 PE"才与"语义特征"相加,投入注意力机制计算
核心设计哲学
我不提前泼水!
通过将语义特征与显式 3D 坐标拆分保存:
- 只对纯物理坐标做刚体变换
- 然后动态生成 PE 并进行运动补偿调制
- 最后才与特征合并
- 保证特征本身的纯洁性
五、实例级表达:DETR 范式的灵魂
核心问题
历史信息中为什么会有实例级别的速度信息?网络对周围障碍物和地图做了实例化输出吗?
是的!网络不仅做了彻底的实例化输出,历史信息里的速度是模型在上一帧"自己亲口预测出来"的!
抛弃"像素填色游戏”,拥抱"实例锁定"
以前基于 Dense BEV 的模型像是在玩"填色游戏":把周围环境划分成无数小格子,然后告诉系统"这个格子里有车"。如果要提取"一辆车",还需要额外的后处理(NMS、聚类)。
DriveTransformer 掀翻了这个桌子! 使用 DETR 的理念:
| 探针 | 比喻 | 作用 |
|---|---|---|
| Agent Queries(900个) | 900枚"自导鱼雷" | 每一枚死死咬住马路上的一个具体实例 |
| Map Queries(100个) | 100个测绘员 | 每个去认领一条具体的车道线、斑马线或红绿灯 |
模型不需要繁琐的后处理,输出天生就是高度结构化的"实例列表"!
速度信息从哪来?探针的"述职报告"
每个智能体探针锁定一个物体后,在每一帧 Transformer 处理完毕后,都要提交一份 “述职报告”:
- 物体的 3D 边界框($x, y, z$, 长, 宽, 高, 旋转角)
- 物体的类别(车、人、自行车)
- 当前绝对速度 $(v_x, v_y)$ 和未来运动轨迹!
时序闭环:“昨天的预测"变成"今天的先验”
最绝妙的逻辑闭环来了!历史信息里的速度是模型自己前一秒算出来的。
完整的时间流:
| 时间 | 动作 |
|---|---|
| T-1 时刻 | 1号智能体探针锁定右前方车辆,输出报告:“卡车,位置 $(x, y, z)$,速度 10 m/s” |
| 存入背包 | 系统把探针打包存入历史队列(语义特征 + 位置 + 预测的速度) |
| T 时刻 | 系统从背包掏出探针:“你上一帧说速度 10 m/s,过去 0.1s,应该往前开了 1m” |
| 运动补偿 | 用速度计算位移,通过 $\gamma$ 和 $\beta$ 魔法,把历史探针平移到预期位置 |
| 锁定当前帧 | 在交叉注意力中,直接去"新位置"附近寻找,降低搜索难度 |
完美流畅的端到端流式预测!
六、稀疏 vs 稠密:为什么"丢掉背景"反而更强?
一个"顶会审稿人级别"的质疑
前后帧的信息传递中损失了很多信息,有点像 1.5 段式端到端,相比 1 段式存在性能瓶颈
这个担忧是学术界争论最激烈的话题之一。但为什么 DriveTransformer 依然能打破"瓶颈"?
担忧一:是不是"1.5段式"?
如果模型只把位置和速度传递给下一帧,确实退化成 1.5 段式。
但别忘了"公文包"!除了显式的物理坐标,探针里还带着一个高维度的语义特征。
这个高维特征是不被人类定义的黑盒,可能包含:
- “这辆车有打转向灯的意图”
- “那个行人看起来喝醉了”
- 无法用简单位置和速度描述的深层隐含信息
表面上戴着 1.5 段式(实例结构化)的面具,背地里依然在用纯正 1 段式的高维张量进行端到端的暗流涌动!
担忧二:丢掉背景信息造成损失?
如果上一帧的 Top-K 探针没有捕捉到路边草丛里准备窜出来的小狗,下一帧是不是就完全不知道了?
关键认知误区!
虽然模型在传递历史时是稀疏的(只传 Top-K),但在看当前世界时,它是绝对稠密且完整的!
每一帧的 Sensor Cross-Attention 中,所有探针都会一头扎进当前帧完整的 2D 多视角摄像头图像中疯狂提取信息:
- 如果历史探针漏掉了草丛里的小狗 → 没关系!
- 当前帧新初始化的 900 个探针会在扫描高清画面时,直接把它"揪"出来
历史记忆是稀疏的(只记重点),但当下感知是稠密的(不漏死角)
终极反转:为什么丢掉背景反而是破局关键?
在真实的 AI 工业界,计算力和显存才是真正的瓶颈!
传统 Dense 方案(把历史好几帧的巨大 BEV 堆叠):
| 问题 | 影响 |
|---|---|
| 算力爆炸 | 历史 10 帧 BEV 特征加在一起,显存直接撑爆 |
| Scaling 灾难 | BEV 占用太大,只能配很小的"眼睛"(如 ResNet50),看低分辨率照片 |
DriveTransformer 通过果断丢掉历史中无意义的背景(如"10 秒钟以前马路牙子上的一块砖"),把系统精简到极致。
省下来的海量算力和显存,拿去干嘛了?
换上了一双 “神之眼”!主干网络扩展到参数量极为庞大的视觉大模型 EVA02-CLIP-L,输入图像分辨率也拉到了超高清。
实验证明:用一个超大模型去处理极其精准的稀疏探针,其性能(Scaling Law)远远超过用一个小模型去死磕庞大的稠密特征!
潜在风险:OOD / Long Tail 场景
在一种极端情况下,你的顾虑依然是致命的:开放世界中的异形障碍物。
如果路上出现了一个既不像车、也不像人的东西(如侧翻半挂车掉下来的巨大钢卷),而 Agent Queries 一直被训练去寻找"标准的车辆和行人",稀疏的实例表达可能因"不认识"而忽略它。
前沿研究方向:引入 Occupancy Network(占用网络) 作为稀疏探针的兜底方案 —— 既有稀疏探针去抓重点,又有粗糙但全覆盖的稠密网格去防止"漏网之鱼"。
七、任务头输出:DETR 风格的终极归宿
核心机制
在经过了充分交互后,探针通过 DETR 风格的任务头直接输出自动驾驶指令。
最大魅力:没有中间商赚差价!没有繁琐的锚框、没有让人头疼的 NMS 后处理,网络直接吐出结构化的实例结果。
🚗 智能体头 (Agent Head)
处理那 900 个 Agent Queries,同时输出两份作业:
| 作业 | 输出内容 |
|---|---|
| 检测作业 | 3D 边界框($x, y, z$, 长, 宽, 高, 旋转角)+ 当前速度 $(v_x, v_y)$ + 语义类别 |
| 预言作业 | 未来几秒钟的运动轨迹 |
隐藏细节:预测其他车辆轨迹时,**以这辆车自己当前位置为原点(局部坐标系)**来预测未来位移。巧妙地把"检测"和"预测"解耦,大大降低学习难度。
🗺️ 地图头 (Map Head)
处理那 100 个 Map Queries,在线构建局部矢量地图:
- 输出不是像素,而是折线(Polylines)
- 一系列三维坐标点,连起来就是车道线、路面边界或斑马线
隐藏细节:同一条折线上的不同点,在 Sensor Cross Attention 时会带有不同的位置编码(PE)。即使是同一条车道线,它的头和尾也能分别精准去画面里寻找属于自己的视觉线索!
🏎️ 规划头 (Planning Head)
最特殊,只处理专属的规划探针:
- 输出极其干净利落:自车在未来 $T$ 秒内应该行驶的未来路点轨迹
- 底层控制器(PID)可以直接拿去踩油门、打方向盘
🌟 极大招:动态 PE 更新与深度监督
如果你以为这些头只是挂在网络最后面,那就大错特错了!
DriveTransformer 内部有好几个串联的 Transformer Block(如 6 层),每一个 Block 后面,都挂着这三个任务头!
深层监督
在训练时,每一层的任务头都要输出结果,并与真实标签(GT)做 DETR 招牌式的 “二分匹配” 来计算误差。这逼迫网络从第一层开始就得努力干活。
探针的自我进化
在第 $N$ 层,网络预测出物体的位置、类别或自车意图。在进入第 $N+1$ 层之前,模型会把预测结果用 MLP 重新编码成新的位置编码!
- Agent 和 Map 的新 PE:融合了刚刚猜出的空间位置和语义类别
- 规划的 Ego 新 PE:融合了刚刚生成的自车未来意图
最美丽的闭环
探针进去 → 看图交流 → 用任务头输出预测 → 把预测结果变成新的起点(PE) → 进入下一层继续看图交流...
每一层都在修正上一层的误差,探针找得越来越准,预测得越来越远。当它走到最后一层输出最终结果时,整个场景已经在探针的"群聊"中被解构得明明白白了!
八、Loss 设计:多任务协同的"评分标准"
DriveTransformer 采用多任务联合训练的策略,所有任务共享同一个损失函数,通过权重平衡各任务的贡献。这种设计让模型能够同时学习感知、预测和规划,真正实现端到端的协同优化。
总体损失函数架构
$$\mathcal{L}_{\text{total}} = \mathcal{L}_{\text{agent}} + \lambda_{\text{map}} \mathcal{L}_{\text{map}} + \lambda_{\text{motion}} \mathcal{L}_{\text{motion}} + \lambda_{\text{plan}} \mathcal{L}_{\text{plan}}$$其中 $\lambda$ 权重系数用于平衡各任务的贡献。
🚗 Agent Detection Loss(智能体检测损失)
Agent Head 需要完成三个子任务:分类(识别物体类别)、检测(输出 3D 边界框)、速度预测。
$$\mathcal{L}_{\text{agent}} = \lambda_{\text{cls}} \mathcal{L}_{\text{cls}} + \lambda_{\text{box}} \mathcal{L}_{\text{box}} + \lambda_{\text{vel}} \mathcal{L}_{\text{vel}}$$分类损失:Focal Loss
$$\mathcal{L}_{\text{cls}} = -\sum_{i} \alpha_{c} (1 - p_i)^{\gamma} \log(p_i)$$- $\alpha_c$:类别平衡权重,缓解类别不平衡问题
- $\gamma$:聚焦参数(通常取 2),降低易分类样本的权重
- $p_i$:预测为正确类别的概率
边界框回归:L1 Loss
$$\mathcal{L}_{\text{box}} = \sum_{i \in \mathcal{M}} \left( \| \hat{b}_i - b_i \|_1 \right)$$其中 $\hat{b}_i$ 是预测的 3D 边界框参数 $(x, y, z, l, w, h, \theta)$,$b_i$ 是真实值,$\mathcal{M}$ 是匹配成功的探针集合。
速度回归:L1 Loss
$$\mathcal{L}_{\text{vel}} = \sum_{i \in \mathcal{M}} \left( \| \hat{v}_i - v_i \|_1 \right)$$其中 $\hat{v}_i = (\hat{v}_x, \hat{v}_y)$ 是预测速度,$v_i$ 是真实速度。
关键细节:速度损失非常关键!因为预测出的速度会被存入 FIFO 队列,作为下一帧运动补偿的依据。如果速度预测不准,整个时序推理链条都会受影响。
🗺️ Map Detection Loss(地图构建损失)
Map Head 负责在线构建矢量地图,输出折线形式的地图元素。
$$\mathcal{L}_{\text{map}} = \lambda_{\text{cls}} \mathcal{L}_{\text{cls}} + \lambda_{\text{pline}} \mathcal{L}_{\text{pline}}$$分类损失:Focal Loss
$$\mathcal{L}_{\text{cls}} = -\sum_{i} \alpha_{c} (1 - p_i)^{\gamma} \log(p_i)$$区分车道线、路面边界、斑马线、红绿灯等地图元素类型。
折线回归损失
$$\mathcal{L}_{\text{pline}} = \sum_{i \in \mathcal{M}} \sum_{j=1}^{N_{pts}} \| \hat{p}_{i,j} - p_{i,j} \|_1$$其中:
- $N_{pts}$:每条折线的点数
- $\hat{p}_{i,j} = (x, y, z)$:预测的第 $i$ 条折线上第 $j$ 个点的坐标
- $p_{i,j}$:真实的坐标点
设计要点:折线回归采用的是点级别的 L1 损失,每个折线点都需要精准定位。这保证了地图元素的几何精度,对后续规划至关重要。同一条折线上的不同点在训练时独立计算损失,实现了细粒度的监督。
🔮 Motion Prediction Loss(运动预测损失)
Agent Head 还需要预测周围物体的未来运动轨迹。
$$\mathcal{L}_{\text{motion}} = \sum_{i \in \mathcal{M}} \sum_{t=1}^{T} \| \hat{traj}_{i,t} - traj_{i,t} \|_1$$其中:
- $T$:预测的未来时间步数(如 12 步,对应 3 秒 @ 4Hz)
- $\hat{traj}_{i,t} = (\Delta x_t, \Delta y_t)$:第 $i$ 个物体在第 $t$ 步的相对位移(局部坐标系)
- $traj_{i,t}$:真实的相对位移
隐藏细节:轨迹预测采用局部坐标系——以被预测物体当前位置为原点。这样做的好处是:
- 把"检测"(物体在哪)和"预测"(物体往哪走)解耦
- 降低学习难度,提高训练稳定性
- 物体的绝对位置变化不会影响轨迹预测的学习
🏎️ Planning Loss(规划损失)
Planning Head 输出自车未来应该行驶的轨迹路点。
$$\mathcal{L}_{\text{plan}} = \sum_{t=1}^{T} \| \hat{wp}_t - wp_t \|_1$$其中:
- $T$:规划的未来时间步数
- $\hat{wp}_t = (x_t, y_t)$:第 $t$ 步的预测路点(自车坐标系)
- $wp_t$:真实的路点位置
设计哲学:规划损失直接监督最终输出,让整个网络为"开得好"而学习。所有感知和预测任务都通过梯度反向传播,间接服务于规划目标——这就是端到端的核心魅力!
坐标系选择:规划路点通常在自车坐标系下定义,以当前自车位置为原点。这保证了规划的连续性——无论自车在世界坐标系中走到哪里,规划输出始终是"前方 X 米、左/右 Y 米"的相对描述。
🔄 Hungarian Matching(匈牙利匹配)
在 DETR 范式中,每个探针(Query)需要和一个真实的 Ground Truth 物体进行匹配。DriveTransformer 使用 匈牙利算法(Hungarian Matching) 完成这一任务。
匹配代价函数
$$\mathcal{C}_{\text{match}}(\hat{y}, y) = \lambda_{\text{cls}} \mathcal{C}_{\text{cls}} + \lambda_{\text{box}} \mathcal{C}_{\text{box}} + \lambda_{\text{vel}} \mathcal{C}_{\text{vel}} + \lambda_{\text{motion}} \mathcal{C}_{\text{motion}}$$其中各代价项定义:
| 代价项 | 公式 | 说明 |
|---|---|---|
| $\mathcal{C}_{\text{cls}}$ | $-\log(p_{\text{gt\_class}})$ | 分类代价,预测为 GT 类别的概率越高,代价越低 |
| $\mathcal{C}_{\text{box}}$ | $\|\hat{b} - b\|_1$ | 边界框代价,预测框与 GT 框的距离 |
| $\mathcal{C}_{\text{vel}}$ | $\|\hat{v} - v\|_1$ | 速度代价,预测速度与真实速度的差异 |
| $\mathcal{C}_{\text{motion}}$ | $\|\hat{traj} - traj\|_1$ | 轨迹代价,预测轨迹与真实轨迹的差异 |
最优匹配求解
$$\hat{\sigma} = \arg\min_{\sigma \in \mathfrak{S}_N} \sum_{i=1}^{N} \mathcal{C}_{\text{match}}(\hat{y}_i, y_{\sigma(i)})$$其中:
- $\mathfrak{S}_N$ 是所有可能匹配方案的集合
- $\sigma$ 是匹配函数,将第 $i$ 个探针映射到第 $\sigma(i)$ 个 GT 物体
- $\hat{y}_i$ 是第 $i$ 个探针的预测输出
- $y_{\sigma(i)}$ 是匹配到的 GT 物体
生动比喻:匈牙利匹配就像是"相亲大会",每个探针(单身男士)都要找到一个最合适的 GT 物体(单身女士)。匹配代价就是"相处成本",算法的目标是找到让总成本最低的配对方案——这就是经典的"二分图最优匹配"问题。
📊 Deep Supervision(深层监督)
DriveTransformer 在每一层 Transformer Block 后都挂了任务头,实现了 深层监督:
- 每一层都输出预测:第 1 层、第 2 层…第 6 层都要输出检测结果
- 每一层都计算损失:所有层的预测都与 GT 匹配并计算损失
- 总损失汇总:
优势:深层监督逼迫网络从第一层就开始"干活",而不是等到最后一层才输出有意义的结果。这大大加速了训练收敛,提高了模型的优化效率。
⚖️ Loss 权重配置
论文中各损失的权重配置(参考 DETR 和 UniAD 的惯例):
| 损失类型 | 权重符号 | 典型数值 | 作用 |
|---|---|---|---|
| Agent Classification | $\lambda_{\text{cls}}$ | 2.0 | 分类是基础任务 |
| Agent Box | $\lambda_{\text{box}}$ | 5.0 / 0.1 | 边界框定位精度要求高 |
| Agent Velocity | $\lambda_{\text{vel}}$ | 1.0 | 速度影响时序推理 |
| Map Classification | $\lambda_{\text{map\_cls}}$ | 2.0 | 地图元素分类 |
| Map Polyline | $\lambda_{\text{pline}}$ | 1.0 | 折线点位精度 |
| Motion Trajectory | $\lambda_{\text{motion}}$ | 1.0 | 预测轨迹精度 |
| Planning | $\lambda_{\text{plan}}$ | 1.0 | 最终驾驶目标 |
权重设计原则:
- 分类权重较高(Focal Loss 已内置类别平衡)
- 边界框权重分层配置(位置 5.0,尺寸/角度 0.1)
- 规划权重适中,避免过度约束感知任务
训练策略:所有任务从头开始联合训练,不需要分阶段预训练感知模块。这证明了 DriveTransformer 架构设计的优雅性——任务并行、统一优化。
九、完整流程的 Python 伪代码
class DriveTransformer:
def __init__(self, num_layers=6, history_k=50):
self.backbone = VisionBackbone("EVA02-CLIP-L") # 强大的眼睛
self.layers = ModuleList([TransformerLayer() for _ in range(num_layers)])
self.fifo_queue = FIFOQueue(max_size=history_k) # 历史记忆背包
# 定义三种初始探针(Queries)
self.agent_queries = Embedding(900, 256) # 智能体:看别人
self.map_queries = Embedding(100, 256) # 地图:看路
self.ego_query = Embedding(1, 256) # 规划:看自己
def forward(self, multi_view_images, ego_motion):
"""
multi_view_images: 当前时刻 6 路摄像头画面
ego_motion: 自车从上一帧到这一帧的位移和旋转矩阵
"""
# 1. 提取视觉特征(Raw Sensor Features)
image_features = self.backbone(multi_view_images)
# 2. 时序魔法:从 FIFO 队列提取并补偿历史探针
history_queries, history_pe, history_vel = self.fifo_queue.get()
# A. 自车运动补偿 (Ego Transform)
aligned_history_pe = apply_ego_transform(history_pe, ego_motion)
# B. 智能体运动补偿 (Agent Motion Compensation)
dt = 1/10.0 # 假设 10Hz
displacement = history_vel * dt
compensated_history_pe = self.motion_mlp(aligned_history_pe, displacement)
# 3. 准备当前帧的初始探针
curr_queries = concat([self.agent_queries, self.map_queries, self.ego_query])
curr_pe = self.initialize_pe()
# 4. 核心:Transformer 循环层 (任务并行 + 统一注意力)
for layer in self.layers:
# (1) Task Self-Attention: 探针之间"圆桌会议"
curr_queries = layer.self_attn(curr_queries)
# (2) Sensor Cross-Attention: 探针去原始画面里"对图"
curr_queries = layer.sensor_cross_attn(curr_queries, image_features)
# (3) Temporal Cross-Attention: 向历史探针"取经"
curr_queries = layer.temp_cross_attn(curr_queries, compensated_history_pe, history_queries)
# (4) 动态位置更新:DETR 头的深度监督与反馈
temp_preds = layer.task_heads(curr_queries)
curr_pe = layer.update_pe(temp_preds)
# 5. 任务头输出最终结果 (DETR Style)
final_results = {
"detection": self.agent_head.predict_box(curr_queries[:900]),
"prediction": self.agent_head.predict_trajectory(curr_queries[:900]),
"map": self.map_head.predict_lines(curr_queries[900:1000]),
"planning": self.ego_head.predict_waypoints(curr_queries[-1])
}
# 6. 更新记忆背包 (FIFO Update)
top_k_queries = select_top_k(curr_queries, final_results["detection"].scores)
self.fifo_queue.put(top_k_queries, curr_pe_of_top_k, final_results["detection"].velocity)
return final_results
时序交叉注意力的详细实现
class TemporalCrossAttention(nn.Module):
def __init__(self, embed_dims=256, num_heads=8):
super().__init__()
self.mha = MultiheadAttention(embed_dims, num_heads)
self.norm = LayerNorm(embed_dims)
self.dropout = Dropout(0.1)
def forward(self, curr_queries, curr_pe, hist_queries, compensated_hist_pe):
"""
curr_queries: 当前帧正在进化的探针特征 [N, C]
curr_pe: 当前帧探针的位置编码 [N, C]
hist_queries: 从 FIFO 队列取出的历史探针特征 [K, C] (Top-K)
compensated_hist_pe: 经过运动补偿后的历史位置编码 [K, C]
"""
# 1. 准备 Query (这里用了临时变量q,所以curr_queries没有融合pe)
q = curr_queries + curr_pe
# 2. 准备 Key & Value (来自历史)
k = hist_queries + compensated_hist_pe
v = hist_queries # Value 使用原始历史特征,保留纯粹的语义信息
# 3. 执行多头交叉注意力计算
temporal_feat, attn_weights = self.mha(
query=q.unsqueeze(1),
key=k.unsqueeze(1),
value=v.unsqueeze(1)
)
# 4. 残差连接与层归一化
out = curr_queries + self.dropout(temporal_feat.squeeze(1))
out = self.norm(out)
return out
十、实验结果
封闭环路虚拟仿真:Bench2Drive (CARLA v0.9.15.1)
| 配置 | 详情 |
|---|---|
| 训练数据 | 基于官方 1000 个片段 |
| 测试路线 | 220 条路线(含 Dev10 子集) |
| 历史队列长度 | $T=10$(10Hz 下的 1 秒钟) |
战绩:
- Driving Score 最高可达 63.46
- Success Rate 35.01%
- 大大降低了碰撞率
- 在传感器失效或长尾场景时,鲁棒性远超对手
开放环路真实世界:nuScenes 数据集
| 配置 | 详情 |
|---|---|
| 数据类型 | 真实世界的高清摄像头数据 |
| 历史队列长度 | $T=4$(2Hz 下的 2 秒钟) |
战绩:
- 展现极高竞争力的 L2 规划误差
- 没有使用任何"刷榜作弊"手段(不依赖 Ego-status 偷鸡)
效率与扩展性
| Backbone | 性能影响 |
|---|---|
| ResNet50 → ResNet101 → EVA02-CLIP-L | 模型越大,规划能力越强 |
| 输入分辨率最高 (384, 1056) | 超高清视觉输入 |
推理速度:
- DriveTransformer-Large 延迟低至 211.7 毫秒
- 轻松满足自动驾驶实时性需求
- 高 FPS,丝滑流畅
十一、总结与未来展望
DriveTransformer 就像是用"奥卡姆剃刀"剔除了自动驾驶中所有不必要的赘肉:
任务并行 + 稀疏表示 + 流式时序处理
用最优雅的 Transformer 语言统一了检测、地图构建、运动预测和路径规划。
核心优势总结
| 特性 | 说明 |
|---|---|
| 不依赖"专家提点" | 纯正端到端框架 |
| 不依赖"前置任务预训练" | 一体化训练,一气呵成 |
| 训练稳定性大幅飙升 | 消除层级带来的误差累积 |
| 深不可测的 Scaling Law 潜力 | 模型越大,性能越强 |
未来方向
可以预见,未来的自动驾驶将越来越像 DriveTransformer 这样——聪明、敏捷、一气呵成!
可能的演进方向:
- 引入 Occupancy Network 作为稀疏探针的兜底方案
- 更强大的视觉 Backbone
- 更长的时序历史处理能力
- 与世界模型的结合
这就是 DriveTransformer 的全部奥秘!它用一支训练有素、协同作战的"赛车突击队",展示了端到端自动驾驶的新范式。🏎️💨