论文: AutoVLA: A Vision-Language-Action Model for End-to-End Autonomous Driving with Adaptive Reasoning and Reinforcement Fine-Tuning

github

模型总体架构:
在这里插入图片描述
训练步骤:

1. 将ground truth trajectory 进行tokenization

trajectory是模型的预测目标. 我们把trajectory分成一小段一线段的segments, 每个segments有6个采样点, 分别是在地图坐标系标出的6个时刻的点:

{
  "gt_trajectory": [
    [0.0, 0.0, 0.0],
    [1.0, 0.1, 0.02],
    [2.1, 0.2, 0.03],
    [3.2, 0.5, 0.10],
    [4.0, 1.0, 0.20]
  ]
}

含义:

[x, y, heading] -> 位置, 车头朝向

位置和朝向是世界坐标, 是以地图中心为原点.

1.1. transform_to_local

转换到 ego local frame, 也就是每个位置现在的状态都是以前一个时刻的状态为(0, 0, 0)的, 然后去

因为原始输入的(x, y, heading), 位置和朝向是世界坐标, 是以地图中心为原点.

现在转为local之后, 是以ego为center, 也就是车辆t=0时刻为原点, heading为0
然后后面每个时刻都以前一个时刻为(0, 0, 0)

也就是说transform_to_local的输入,是以地图某个位置和方向为(0, 0, 0),
输出是以t-1时刻的车辆位置和方向为(0, 0, 0), 其实就是一个
(delta_x, delta_y, delta_heading)。

1.2. 计算每段轨迹末尾车的状态

有了step 1的车辆每个时刻相对前一时刻ego的车辆位置和朝向之后, 就可以得到每个轨迹(6个点)的最后时刻的车辆位置和朝向. (0, 0, 0)为t=0时刻车状态。例如
例如:

t x y heading
0 0.0 0.0 0.00
1 1.0 0.1 0.02
2 2.1 0.2 0.03
3 3.2 0.5 0.10
4 4.0 1.0 0.20

变成

step Δx Δy Δheading
1 1.0 0.1 0.02
2 1.1 0.1 0.01
3 1.1 0.3 0.07
4 0.8 0.5 0.10

根据以下信息得到车辆最后一个时刻车辆的4个端点 cal_polygon_contour():

  • 位置和朝向(x, y, heading)
  • 车辆大小计算最后一个时刻
[
 [5.55, 0.92],
 [5.74,-1.02],
 [0.82,-1.50],
 [0.63, 0.44]
]

车辆的4个端点位置(8, ), 用这4个端点位置代表轨迹.

1.3. 聚类生成codebook

对n个轨迹(n, 8)做聚类, 聚成2048个类, 这2048个聚类中心构成codebook. 距离用L2 norm

聚类方法: 对(n, 8)个点做类似k-means的操作, 得到2048个聚类中心之后, 以后对每条轨迹, 看跟这2048个中心哪个距离近就用哪个token来表示

1.5. 对每个新的轨迹, 计算最后时刻车辆的4个端点位置, 然后看属于哪一类

通过以上步骤, 把车辆的trajectory进行了token化, 方便后面统一进入VLA模型来训练。每个token的意义类似于:

token id trajectory pattern
0 直行
1 轻微左转
2 轻微右转
3 急左转
4 急刹

为什么需要2048个cluster? 轨迹空间非常大, 你不能只有几个 token,否则无法表达复杂动作. 2048 是经验值:

token 数量 意义
128 太少,只能表示几种方向,精度差
512 可以表示弯道/直行/刹车等组合
2048 足够覆盖绝大多数驾驶轨迹模式,同时模型仍可训练
8192+ 更精细,但训练难度增大,显存/计算量大

简单理解:每个 token 是 一类车辆未来动作的模板。token 越多 → 描述越精细 → 模型预测越精确

2. 模型训练

首先模型的输入batch为:

{
  'input_ids': torch.tensor([
      [101, 3001, 3055, 151644, 77091, 2003, ...],  # 第1条样本 tokenized 文本
      [101, 4010, 4022, 151644, 77091, 2050, ...],  # 第2条样本 tokenized 文本
  ]),  # shape [2, seq_len]

  'attention_mask': torch.tensor([
      [1,1,1,1,...],  # 第1条
      [1,1,1,1,...],  # 第2条
  ]),

  'labels': torch.tensor([
      [-100, -100, -100, 151644, 77091, 2003, ...],  # 只计算 assistant 消息 loss
      [-100, -100, -100, 151644, 77091, 2050, ...],
  ]),

  'image_inputs': torch.stack([...]),  # shape [2, n_images, C, H, W] 或者 pixel values
  'video_inputs': torch.stack([...]),  # shape [2, n_videos, frames, C, H, W]

  'gt_trajectory': torch.stack([
      [[0.0,0.0,0.0],[0.5,0,0.01], ... ],  # 第1条
      [[1.0,0.0,0.0],[1.5,0,0.01], ... ]   # 第2条
  ]),  # shape [batch, 6, 3]

  'gt_action': torch.tensor([ [42], [128] ]),  # shape [batch, 1]
  'has_cot': torch.tensor([False, False])
}

2.1. SFT训练

将真实场景的数据送到VLM模型进行next token prediction的训练, 输入信息有:

  • 车载摄像头的图像数据
  • vehicle_velocity: 车辆的速度, 会放到prompt里
  • vehicle_velocity: 车辆的加速度, 会放到prompt里
  • driving_command: 当前驾驶指令 string,例如 “turn left”, 来自于车载导航软件

一个batch数据类似:

{
  'text': "<system> ... </system> <user> ... </user> <assistant> ... </assistant>",
  'image_inputs': [
      'front_camera_1.png', 'front_camera_2.png', 'front_camera_3.png', 'front_camera_4.png',
      'front_left_camera_1.png', ...
  ],
  'video_inputs': [
      ['front_camera_1.mp4', 'front_camera_2.mp4', 'front_camera_3.mp4', 'front_camera_4.mp4'],
      ['front_left_camera_1.mp4', ...],
      ['front_right_camera_1.mp4', ...]
  ],
  'gt_trajectory': torch.tensor([
      [0.0, 0.0, 0.0],
      [0.5, 0.0, 0.01],
      [1.0, 0.0, 0.02],
      [1.5, 0.0, 0.03],
      [2.0, 0.0, 0.03],
      [2.5, 0.0, 0.04],
  ]),  # shape [6, 3]
  'gt_action': torch.tensor([42]),  # action token index
  'has_cot': False,
  'data_path': "scene001.json"
}

模型生成action序列, 然后做next token prediction, 计算CE loss.

shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
loss = F.cross_entropy(shift_logits.view(-1, V), shift_labels.view(-1))

动作 token 单独 loss

action_mask = (labels_flat >= self.autovla.action_start_id)
ce_loss_all = F.cross_entropy(logits_flat, labels_flat, reduction='none')
action_loss = ce_loss_all[action_mask].mean()

找出 动作token(>= action_start_id), 对这些 token 的 CE loss 单独计算并平均, 可以用 加权 合并到总 loss 中(尤其是 CoT 样本):

if hascot[0] == True:
    loss = loss * 40 + action_loss

总结:

  • 普通 token → cross-entropy
  • 动作 token → 单独 CE loss + 可加权
  • CoT 样本 → 总 loss 放大

总结 Loss 流程图:

batch → get_prompt → input_ids, attention_mask, pixel_values_videos
    ↓
vlm.forward(inputs) → logits (B, L, V)
    ↓
cross-entropy(logits, labels) → LM loss
    ↓
选出动作 token → action_loss
    ↓
根据 CoT flag → 放大 LM loss + 加入 action_loss
    ↓
log("train_loss", loss)

输入:多路视频 + 车辆状态 + 指令文本 → tokenized prompt
输出:每个 token 的预测 logits + loss

Loss:

  • 基础:所有 token 的 cross-entropy
  • 加强:动作 token CE loss
  • CoT 样本:loss 放大(例如 ×40)

prompt:

instruction = input_features["driving_command"].lower()
...
user_content = [
    ...
    {
        "type": "text",
        "text": (
            f"The current velocity of the vehicle is {velocity:.3f} m/s, and the current acceleration is {acceleration:.3f} m/s². "
            f"The driving instruction is: {instruction}. Based on this information, plan the action trajectory for the autonomous vehicle over the next five seconds."
        )
    },
]

来源:input_features[“driving_command”], 这是数据集 JSON 文件里提供的高层驾驶指令,例如 “turn left”、“keep lane”、“stop” 等。

格式化:把指令 + 当前速度 + 加速度组合成一段文本。

文本示例:The current velocity of the vehicle is 5.20 m/s, and the current acceleration is 0.30 m/s². The driving instruction is: turn left. Based on this information, plan the action trajectory for the autonomous vehicle over the next five seconds.

作用:这个文本作为 user content 的一部分,会在 tokenized prompt 中被编码成 input_ids,供模型理解“当前车辆状态 + 高层指令”,然后生成动作 token。

注意这里的CoT是每次只预测1个token来计算loss的.

2.2 RFT训练

reinforcement finetuning训练的输入数据:

{
  input_features: {
       images,
       velocity,
       acceleration
  },

  target_trajectory: {
       gt_pos_raw,
       gt_idx
  },

  token: "scene_03492"
}

这里面的scent是类似

- Front: A construction worker is in the center lane, holding a SLOW
paddle, and a construction vehicle is blocking the right lane …
- Left: The left lane is clear, with a few vehicles ahead .…
- Back: Light traAic is present at a distance behind the ego vehicle …

整体思路:

环境 (video + state + instruction)
        ↓
模型生成 trajectory
        ↓
reward function 评分
        ↓
policy gradient 更新模型

训练流程: LLM + 自动驾驶 reward → RL 优化

训练流程:

prompt (视频+状态)
      ↓
model.generate()
      ↓
生成 action tokens
      ↓
decode → trajectory
      ↓
reward_function()
      ↓
policy gradient 更新模型

2.2.1 Step1:生成 trajectory

sample = self.generate_sample(...), 里面做的是:prompt → model.generate(), 生成:completion tokens 然后提取:action tokens

action token处理: actions_tokens = completion_ids[0][completion_ids[0] >= self.action_start_id], 只保留:trajectory tokens

然后 decode:token → trajectory, trajectory = decode_token_ids_to_trajectory(...), 得到:(x,y) trajectory

2.2.2. Step2:计算 reward

reward = self.reward_function(sample), 核心:reward = PDM score, PDM 是 nuPlan 的 driving metric。评价指标:

  • collision
  • lane keeping
  • progress
  • comfort
    也就是常见的安全性+舒适性

2.2.3 CoT penalty

如果生成文本包含:“complex scenario”, 就会有 penalty:reasoning太长 → reward减少, 代码:

sigmoid((len(text)-center)*coef)

目的:限制CoT长度

generate_sample 细节, 这里其实是整个 RL pipeline 的核心。流程:

feature → prompt
       ↓
generate text
       ↓
extract action tokens
       ↓
decode trajectory

trajectory token处理

如果 token 数量不对:

  • > num_poses → truncate
  • < num_poses → padding

保证:固定 trajectory 长度

完整 pipeline:

Dataset
   ↓
input_features
   ↓
prompt
   ↓
LLM generate
   ↓
action tokens
   ↓
decode trajectory
   ↓
reward function
   ↓
advantage
   ↓
policy gradient
   ↓
update AutoVLA

2.2.4, reward计算

class PDM_Reward, 作用:

输入

  • trajectory:模型生成的轨迹(AutoVLA 输出)
  • token:当前场景 ID

输出

  • reward:一个数值,表示这条轨迹的驾驶质量

本质是:

生成轨迹
     ↓
仿真器模拟执行
     ↓
计算驾驶指标
     ↓
综合评分
     ↓
作为 RL reward

也就是:

trajectory → simulator → metrics → score → reward

PDMSimulator
self.simulator = PDMSimulator(self.future_sampling)

作用:仿真 ego 车沿着 trajectory 行驶, 比如:模型输出:ego trajectory, 仿真器会模拟:

  • ego 行驶
  • 其他车辆反应
  • 交通灯
  • lane
  • 碰撞

得到:

simulation result
4 PDMScorer
self.scorer = PDMScorer(self.future_sampling)

作用:计算 自动驾驶评价指标, 例如:

  • metric 含义
  • collision 是否撞车
  • offroad 是否出车道
  • progress 是否向目标前进
  • lane adherence 是否在车道
  • jerk 是否舒适

最后综合成:PDM score

2.2.5 rl_pdm_score函数

核心函数:def rl_pdm_score(self, trajectory, token):
输入:

  • trajectory -> 模型生成
  • token -> 场景ID

2.2.6 加载场景缓存

metric_cache_path = self.metric_cache_loader.metric_cache_paths[token]

根据 token 找到场景缓存文件。例如:

token = 8f34ab...
path = metric_cache/8f34ab.xz

# 读取缓存
with lzma.open(metric_cache_path, "rb") as f:
    metric_cache = pickle.load(f)

说明缓存是:

  • lzma压缩
  • pickle序列化

加载后得到metric_cache, 里面包含:

  • map
  • agents
  • ego history
  • route
  • traffic lights
  • lane graph
    然后调用 pdm_score, 核心:
result = pdm_score(
    metric_cache=metric_cache,
    model_trajectory=trajectory,
    future_sampling=self.future_sampling,
    simulator=self.simulator,
    scorer=self.scorer,
)

这里做了完整流程:

  • Step1 轨迹仿真

    • simulator 执行:
    • ego 按 trajectory 行驶
    • 同时模拟:
      • 其他车辆
      • 交通灯
      • lane
  • Step2 计算 metrics, scorer 计算, 例如:

    • collision
    • offroad
    • progress
    • speed limit
    • comfort
  • Step3 汇总 score, 得到result.score, 范围一般:0 ~ 1, 例如:

score 含义
0.95 很好
0.7 一般
0.2 很差
0 crash

reward返回

final_reward = result.score
return final_reward

所以reward = PDM score

整个reward pipeline

完整流程:

model
   ↓
生成 trajectory
   ↓
PDM_Reward.rl_pdm_score
   ↓
加载场景 metric_cache
   ↓
simulator 仿真 ego 行驶
   ↓
scorer 计算驾驶指标
   ↓
汇总 PDM score
   ↓
return reward

2.2.7 场景问题

  1. 为什么要用 PDM score 做 reward

因为自动驾驶评价标准不能只看, L2 trajectory error, 而要看:

  • 安全性
  • 交通规则
  • 舒适度
  • 导航进度

所以 RL reward 用Driving metrics而不是trajectory regression loss

  1. 为什么需要 token

token -> scene id, 用来找:metric_cache[token], 例如:token: 98af23, cache:

  • map
  • agents
  • traffic lights
  • route

否则 无法仿真这个场景。

  1. 这个 RL 实际优化的是什么

强化学习优化:maximize PDM score, 也就是让模型学会生成:

  • 安全
  • 合法
  • 舒适
  • 进度合理

注意: reward 完全不依赖 GT trajectory, 因为:RL 不需要 GT。只要:trajectory → simulator → score即可。总结一句话

PDM_Reward 的本质:把自动驾驶仿真评估指标变成 RL reward, 流程:

trajectory + scene token
        ↓
load scene
        ↓
simulate ego
        ↓
compute metrics
        ↓
PDM score
        ↓
reward
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐