Diffusion Policy 学习笔记——VLA动作范式,将Diffusion用于动作空间

19 阅读11分钟

时间:2023-5-7

原文链接:arxiv.org/abs/2303.04…

读论文前:

对智驾 VLA 算法而言,这篇文章提出了核心的动作生成范式:“VLM 提取特征 + Diffusion Head 生成动作”。因此,理解这篇文章是如何将 Diffusion 用于动作空间的,是学习后续内容的前提。

对智驾 VLA 算法的核心贡献:

  1. 解决了“多模态分布崩塌”问题
    1. 在连续控制任务中,优雅且稳定地解决“多解问题”的方案
    2. 证明了可以用去噪过程来表达复杂的动作分布,而不是简单的回归均值
  2. 确立了“时序轨迹预测”的主流范式
    1. 摒弃了强化学习中常见的单步决策(stats_t \rightarrow a_t),转而采用预测未来一段动作序列(stat:t+ks_t \rightarrow a_{t:t+k}),并采用“预测多步、执行首步”的重规划策略
    2. 对智驾而言,必须保证方向盘和油门的连续性,不能抖动。一次生成未来 3-5 秒的轨迹,天然保证了时序的一致性和平滑性
    3. 即使传感器或网络有延迟,模型手里依然有未来几步的“存货”动作可以执行,这对于高速行驶的自动驾驶至关重要
  3. 验证了 Transformer + Diffusion 在控制领域的扩展性
    1. 目前的 VLA(如 RT-2, OpenVLA)本质上就是:用 VLM 提取语义特征,通过 Transformer 架构,引导 Diffusion Head 生成动作。这篇文章提供了这个架构最底层的 Action Decoder 设计蓝图

训练流程

训练目标:预测噪声

对应图中箭头从左往右指的过程:

  1. 数据构造(Ground Truth):从人类专家的演示数据中,提取出一段完美的动作序列 A0\mathbf{A}_0(比如一段完美的泊车轨迹)。
  2. 前向加噪:系统随机采样一个噪声 ϵ\epsilon 和一个时间步 kk,把噪声加到完美轨迹上,得到模糊的轨迹 Ak\mathbf{A}_k
  3. 网络预测:把模糊轨迹 Ak\mathbf{A}_k 和观测图像(Visual Condition)一起喂给神经网络。网络的任务是:“把刚才加进去的噪声 ϵ\epsilon 给我找出来”。
  4. 计算 Loss

条件逆向去噪过程:

Atk1=α(Atkγϵθ(Ot,Atk,k)+N(0,σ2I))A_{t}^{k-1} = \alpha (A_{t}^{k} - \gamma \epsilon_\theta(O_t, A_{t}^{k}, k) + \mathcal{N}(0, \sigma^2 I))

  • AtkA_{t}^{k}:当前手中的“半成品”动作序列(含有噪声)。
  • OtO_t:条件约束,这是 tt 时刻的视觉观测(Visual Observation),比如摄像头拍到的图像特征。
  • ϵθ(Ot,Atk,k)\epsilon_\theta(O_t, A_{t}^{k}, k):神经网络。不仅看噪声动作 AtkA_t^k,还要看着图像 OtO_t 来预测噪声。
  • Atk1A_{t}^{k-1}:去噪后的下一步动作序列(更清晰一点)

关键:机器人不是在瞎猜动作,而是在观测OtO_t的约束下进行去噪

  • 如果没有 OtO_t:模型可能会随机生成任何合理的动作(比如随机乱挥手)。
  • 有了 OtO_t:模型会生成符合当前路况的动作(比如前面有墙,生成的动作轨迹就会避开墙)

损失函数

L=MSE(ϵk,ϵθ(Ot,Atk,k))L = \text{MSE}(\epsilon^k, \epsilon_\theta(O_t, A_{t}^{k}, k))

  • ϵk\epsilon^k:真值噪声(Ground Truth Noise)。
  • ϵθ()\epsilon_\theta(\dots):神经网络预测的噪声。
  • OtO_t:视觉观测,作为条件。

注意:模型只针对动作AtkA_{t}^{k}进行去噪和生成,并不生成OtO_tOtO_t是直接观察得来的,以此保证生成速度。

什么是动作序列AtkA_{t}^{k}

动作序列由一系列动作组成,在智驾或机器人领域,一个动作通常是一个**低维向量,**对于车,可能是 [油门, 刹车, 方向盘转角],维度 D=3。

那么动作序列就是把未来n个“动作向量”排在一起,形成一个二维矩阵,形状 (Shape): (Tp,Daction)(T_p, D_{action}),比如 (16,3)(16, 3)

# 动作序列 A_t (Ground Truth / Clean Data)
[
  [0.5, 0.0, -0.2],  # 第 0.0s: 加速,微左转
  [0.6, 0.0, -0.1],  # 第 0.1s: 继续加速,方向回正一点
  [0.6, 0.0,  0.0],  # 第 0.2s: 直行
  ...
  [0.0, 0.8,  0.0]   # 第 1.6s: 刹车
]

Diffusion Policy Generation Formulation

现在来看看“时序轨迹预测”范式是如何工作的(图中右半部分)

从上到下分成三个时间轴:Tobs,Tpred,TexecT_{obs}, T_{pred}, T_{exec}

  • 输入TobsT_{obs}:不仅仅看当下
    • 模型不仅输入当前这一帧图像,而是输入过去 ToT_o 步的历史观测序列。
    • 智驾意义: 比如判断前车是否在减速,光看一张图是不够的,必须看过去几帧的连续变化。这就赋予了模型感知速度和加速度的能力
  • 输出TpredT_{pred}:一次预测未来一段路
    • 模型输出的不是一个动作,而是未来 TpT_p 步的完整动作序列(Action Sequence)。
    • 智驾意义: 这就像老司机开车,眼睛看着前面,脑子里规划的是未来 3-5 秒的轨迹
  • 执行TexecT_{exec}:只执行开头一小段
    • 虽然预测了 TpT_p 步,但实际只执行前 TaT_a 步(通常 Ta<TpT_a < T_p)。执行完后,抛弃剩下的计划,利用最新的观测重新规划。
    • 保证平滑性的同时,随时应对突发状况

模型架构

原文用了两套架构:CNN-based & Transformer-based

  • CNN-based是基于U-Net的传统Diffusion架构,训练稳定、推理速度快,但并不是 VLA 的基础
  • Transformer-based 全局相关性强,是 VLA 的基础,这里重点关注

  1. Input Tokens
    1. 输入张量形状:(B,Tp,Daction)(B, T_p, D_{action})
    2. 将每一个时间步 (Timestep) 的动作向量,视为一个 Token
    3. 线性映射 (Linear Embedding): 先通过一个 MLP 把动作维度(比如 2维)映射到 Transformer 的隐藏层维度(比如 256维)
    4. 此时,有 16 个 Token,每个 Token 代表轨迹上的一个点
  2. 加入两类位置编码
    1. 序列位置编码 (Positional Embedding):动作的先后顺序
    2. 扩散步数编码 (Diffusion Step Embedding):现在是第几步扩散,“现在的噪声强度是 k=99 (全是噪点),还是 k=5 (快修好了)”
  3. Self-Attention 处理“轨迹内部关系”
    1. 输入: 16 个动作 Token。
    2. 作用: 让第 1 步的动作“看见”第 16 步的动作。
    3. 拉通整个时间轴的信息,保证生成的轨迹是平滑的,符合物理规律的,而不是抖动的
  4. Cross-Attention 处理“视觉感知”
    1. 视觉信息在这里被“注入”到了动作序列中
    2. Query(Q): 来自动作 Token(当前的轨迹猜想)。
    3. Key(K) & Value(V) : 来自视觉特征(Visual Encoder 提取的图像 Patch 特征,或者是 CLIP 的输出)
    4. 过程:
      1. 动作 Token 问:“我要往右拐了,右边有路吗?” (Q)
      2. 视觉特征回答:“右边是墙(特征 K),别撞上去(特征 V)。”
  5. Transformer 输出一个形状和输入一模一样的序列 ϵpred\epsilon_{pred},即Transformer 认为的“这张图里叠加的噪声”
  6. Diffusion 算法用 AnoisyϵpredA_{noisy} - \epsilon_{pred} (配合系数),减去噪声
  7. 得到稍微干净点的动作,回到 Step 2,再次喂给 Transformer

Q:动作序列是如何映射为Token的?与LLM中的离散Token有区别吗?是否会损失精度?

A:通过线性投影。由 2维 -> 256维,不会损失精度,甚至会精度增强

工程化技巧

预测“位置 (Position)”而非“速度 (Velocity)”

  • 传统控制理论喜欢控制速度(比如输出油门力度、转向角速度)
  • 如果预测速度:误差会累积。第一步偏了 1 度,第 10 步可能就偏到沟里去了
  • 如果预测位置(在智驾里就是 Trajectory/Waypoints,轨迹点):模型每次都直接输出“车应该在 (x,y) 坐标”。即使上一帧偏了,这一帧直接预测正确的位置,误差瞬间修正
  • 在智驾中,目前的 SOTA 方案(如 UniAD, VAD)都是预测未来几秒的轨迹点坐标 (x, y),然后通过一个简单的 PID 控制器去追踪这些点。这能极大提升稳定性。

推理加速:DDIM 采样 (Inference Speedup)

只有训练时用 100 步,推理时允许跳步,往往只用 10步 ~ 15步 就能生成非常高质量的轨迹

DDPM 的推理

  • 公式: xt1=Mean(xt)+σzx_{t-1} = \text{Mean}(x_t) + \sigma \cdot \mathbf{z}
  • 机制: 每一步去噪时,DDPM 都会主动加入随机噪声 z\mathbf{z}

DDIM 的推理

  • 机制: DDIM 把那个随机噪声项 σ\sigma 设为了 0
  • 转化: 它把随机的马尔可夫链(Markov Chain)转化为了确定性的常微分方程(ODE)
  • 直观理解: 一旦给定了初始的噪声 xKx_K,生成的轨迹就被锁死了。整个去噪过程变成了一条光滑的、确定性的曲线。
  • 优势: 因为曲线是光滑的(不再左右摇晃),我们在数学上就可以使用数值积分的近似法(比如欧拉法)。这意味着我们可以跨大步(比如从 t=100t=100 直接跳到 t=90t=90),在这个跨度内,曲线的变化很小,可以用直线来近似。

DDIM 之所以能跳步,核心在于它有一个“看透终点”的能力。

在每一步推理(比如 t=50)时,神经网络实际上做了一件事:它通过当前的 x50x_{50} 预测出了最终的 x0x_0(干净的动作)大概长什么样。每一步都利用对终点的“预判”来修正方向,所以不需要频繁的小步迭代。

动作数据的归一化(Data Normalization)

Diffusion 对数据分布极度敏感,不归一化根本练不出来

  • 陷阱: 你的轨迹坐标可能是米(0100m),方向盘是度数(-500500),油门是(0~1)。这三个维度量级差太多。
  • 强制操作: 必须把所有动作数据(Actions)标准化到 [-1, 1] 区间。
    • 通常使用 min-max normalization。
    • 推理完输出后,再反归一化回物理单位。

旋转的表示:6D Rotation Representation

如果智驾任务涉及从 3D 空间规划(比如机械臂,或者复杂的 6自由度车辆姿态),不要用欧拉角,也不要用四元数

  • 问题:
    • 欧拉角(Euler Angles):有万向节死锁问题,且不连续(179度变-179度突变)。
    • 四元数(Quaternion):有“双倍覆盖”问题(qqq-q 代表同一个旋转),神经网络很难拟合这种不连续性。
  • 解决方案: 论文推荐使用 Rotation 6D 表示法(用两个正交向量表示旋转)。
  • VLA 智驾启示:
    • 如果是纯平面驾驶(只管 x,y,yawx, y, yaw),用 [cos(yaw), sin(yaw)] 来代替直接预测角度 θ\theta 也是一种平滑的技巧,能避免角度的周期性突变问题。

附录

为什么DDPM在每一步去噪过程都要引入额外的随机噪声?

核心的数学原因:为了“采样分布”,而不是“预测均值”

去噪过程的数学定义:我们要拟合的是一个条件概率分布 p(xt1xt)p(x_{t-1} | x_t)

  • 事实: 这个分布本身就是一个高斯分布(Gaussian Distribution),它有均值(Mean),也有方差(Variance)。
  • 操作: 神经网络 ϵθ\epsilon_\theta 帮我们预测出了这个分布的中心(均值)。
  • 问题: 如果我们只取均值(不加噪声),那我们实际上是在取这个分布的“平均数”。
  • 后果:对于图像:所有可能性的“平均”就是模糊的一团灰色。
    • 对于动作:所有可能动作的“平均”就是中间那条死路(多模态的例子中,左转和右转的平均是直撞)。
    • 加噪声的目的: 是为了从这个高斯分布里随机“抽”一个点出来,而不是死板地取中心点。只有加上这个随机项 σz\sigma zxt1x_{t-1} 才真正成为了从分布 p(xt1xt)p(x_{t-1} | x_t) 中采样出的一个样本。

形象的物理原因:为了“纠错”,防止采样过程陷入局部最优解,确保最终能收敛到正确的数据分布上

我们可以把去噪过程想象成“在雾中下山”

  • 目标: 山脚下的村庄(真实数据分布 x0x_0)。
  • 现状: 我们在半山腰的雾里(xtx_t)。
  • 神经网络: 是指南针,它告诉你“大概往下走是这个方向”(预测的梯度)

如果不加噪声(只减去预测的噪声):你完全听指南针的,沿着直线往下冲。

  • 风险: 万一指南针在某一步稍微指偏了一点点(神经网络有误差),或者你陷入了一个局部的坑洼(Local Minimum),你就会一路偏下去,最后走不到村庄,而是走到了旁边的悬崖或者死胡同里。而且一旦走错,后面的步骤很难修正回来,因为你是一条道走到黑

如果加噪声(DDPM 的做法):你往下走一步,然后由于脚底打滑(随机噪声 zz),你稍微往左或往右踉跄了一下。

  • 作用: 这个“踉跄”实际上给了你一种“动态修正”**的机会。如果你不小心走偏陷入了死胡同,这个随机的扰动可能会把你“踢”出来,让你有机会重新回到通往山脚的主路上。

为了“生成多样性”

  • 场景: 假设你要机器人“拿桌上的杯子”。
  • 事实: 拿杯子的动作不是唯一的。可以稍微快一点拿,可以慢一点拿,手可以稍微高一点,也可以低一点。这些都是正确的。
  • 加噪声: 正是因为每一步都加了随机噪声,哪怕初始状态 xKx_K 一模一样,每次运行去噪过程,中间的路径都会发生微妙的偏转。
  • 结果: 最终生成的 x0x_0 也就是多样的。这让机器人的动作看起来更自然、更像人类(Human-like),而不是像机械臂编程那样死板。