ACT 详解:Action Chunking with Transformers

这里的 ACT 指机器人模仿学习算法 Action Chunking with Transformers。它最早用于 ALOHA 双臂机器人,核心目标是:

根据当前视觉和机器人状态,一次预测未来一段动作,而不是只预测下一步动作。

ACT 本身严格来说不是完整的 VLA:原始版本没有语言输入,主要是一个 Vision + Proprioception → Action 的单任务低层控制策略;但很多后续 VLA 系统会采用类似的 Action Chunking 思路,或者直接把 ACT 作为动作解码器。


1. ACT 要解决什么问题

假设示范轨迹为:

\[ \tau=\{(o_0,a_0),(o_1,a_1),\ldots,(o_T,a_T)\} \]

其中:

\[ o_t=(I_t,q_t) \]
  • \(I_t\):一个或多个相机图像
  • \(q_t\):机器人关节位置、夹爪状态等本体状态
  • \(a_t\):机器人动作

普通 Behavior Cloning 通常学习:

\[ \hat a_t=\pi(o_t) \]

也就是每次只预测一个动作。

这种方法主要有三个问题。

1.1 累积误差

训练数据中的状态来自人类专家,但部署时的状态由机器人之前预测的动作产生。

一旦某一步产生小误差:

\[ \hat a_t \neq a_t \]

机器人就可能进入训练集中没有出现过的状态。之后的预测会更加不准确,误差不断放大。

ACT 主要针对精细操作中的这一问题,例如插电池、打开杯盖、穿扎带等。这些任务对毫米级的位置偏差非常敏感。

1.2 单步动作缺少时间一致性

人类动作通常是一段连续运动,例如:

  1. 移动到物体附近;
  2. 调整姿态;
  3. 闭合夹爪;
  4. 抬起物体。

单步 Behavior Cloning 分别预测每个控制周期的动作,模型没有被直接要求这些动作组成一条连贯轨迹,因此容易产生:

  • 抖动;
  • 突然停顿;
  • 左右摆动;
  • 夹爪反复开合。

1.3 人类示范具有多模态性

相同场景可能存在多种正确动作:

  • 从物体左边绕过去;
  • 从物体右边绕过去;
  • 先移动左臂;
  • 先移动右臂。

若直接使用 MSE 回归,模型可能输出多个模式的平均值,而这个“平均动作”可能根本不可执行。


2. ACT 的核心:Action Chunking

ACT 不学习单步映射:

\[ o_t \rightarrow a_t \]

而是学习:

\[ o_t \rightarrow A_t=[a_t,a_{t+1},\ldots,a_{t+K-1}] \]

其中 \(K\)Chunk Size,即动作块长度。

例如控制频率为 \(50\ \mathrm{Hz}\)\(K=100\),模型一次预测未来约 \(2\) 秒的动作。

输出张量通常为:

\[ \hat A_t\in\mathbb{R}^{K\times d_a} \]

其中:

  • \(K\):预测的动作步数;
  • \(d_a\):动作维度。

原始 ALOHA 系统控制两个 6-DoF 机械臂及两个夹爪,动作是双臂的绝对关节目标,因此:

\[ d_a=14 \]

模型输出形状近似为:

\[ [B,K,14] \]

3. 为什么 Action Chunking 有效

3.1 降低有效决策时域

假设任务共有 \(H\) 个控制步。

单步 Behavior Cloning 需要完成大约 \(H\) 次连续决策:

\[ o_0\rightarrow a_0,\quad o_1\rightarrow a_1,\quad \ldots,\quad o_H\rightarrow a_H \]

动作分块后,一段高层运动由一个 Chunk 表示,因此有效决策时域可近似理解为:

\[ H_{\mathrm{eff}}\approx\frac{H}{K} \]

这并不意味着部署时必须完全开环执行 \(K\) 步。ACT 还可以通过重叠预测和 Temporal Ensembling 重新引入高频视觉反馈。

3.2 强制学习轨迹结构

模型不是只判断“下一步去哪”,而是必须预测:

\[ [\hat a_t,\hat a_{t+1},\ldots,\hat a_{t+K-1}] \]

因此 Transformer 会学习:

  • 动作的速度趋势;
  • 抓取前后的时序关系;
  • 双臂之间的配合;
  • 夹爪和机械臂的同步;
  • 接近、接触、插入等运动阶段。

3.3 缓解暂停造成的时间混淆

在人类示范中,操作者可能会短暂停顿。对于单步 Markov 策略,同样的视觉状态可能对应:

  • 继续移动;
  • 保持不动。

模型很难仅凭瞬时观察判断示范者正处于哪个动作阶段。

动作 Chunk 包含局部时间上下文,因此能够把“停顿”理解为一段轨迹的一部分,而不是孤立动作。


4. ACT 的整体模型结构

ACT 本质上是:

\[ \text{CVAE}+\text{Transformer}+\text{Action Chunking} \]

模型有两个主要部分:

  1. CVAE Encoder:仅在训练时使用;
  2. CVAE Decoder / ACT Policy:训练和部署时都会使用。

整体数据流如下:

训练阶段:

示范动作块 A_t ─────┐
当前关节状态 q_t ───┼─> CVAE Encoder ─> μ, logσ² ─> z
                    │
多相机图像 I_t ─────┐
当前关节状态 q_t ───┼─> ACT Decoder ─> 未来 K 步动作
潜变量 z ───────────┘


部署阶段:

多相机图像 I_t ─────┐
当前关节状态 q_t ───┼─> ACT Decoder ─> 未来 K 步动作
z = 0 ───────────────┘

原始 ACT 将策略训练为 CVAE 的 Decoder;CVAE Encoder 在部署时会被丢弃。


5. CVAE Encoder:动作风格编码器

5.1 Encoder 的输入

训练时,随机选择轨迹中的时间点 \(t\),获得:

  • 当前关节状态 \(q_t\)
  • 未来专家动作块 \(A_t\)

动作块定义为:

\[ A_t=[a_t,\ldots,a_{t+K-1}] \]

CVAE Encoder 建模:

\[ q_\phi(z\mid q_t,A_t) \]

原始实现为了加快训练,CVAE Encoder 不读取相机图像,只读取:

  • 当前关节状态;
  • 专家动作序列。

5.2 Token 构造

Encoder 输入可近似表示为:

\[ [\mathrm{CLS},q_t,a_t,a_{t+1},\ldots,a_{t+K-1}] \]

每个动作经过线性层投影到 Transformer 的 Hidden Dimension,并加入位置编码。

经过 Transformer Encoder 后,取 [CLS] Token 的输出:

\[ h_{\mathrm{CLS}} \]

然后预测潜变量分布参数:

\[ \mu=W_\mu h_{\mathrm{CLS}} \]
\[ \log\sigma^2=W_\sigma h_{\mathrm{CLS}} \]

从而得到:

\[ q_\phi(z\mid q_t,A_t) = \mathcal N\left( \mu, \operatorname{diag}(\sigma^2) \right) \]

ACT 将 \(z\) 称为动作序列的 Style Variable。它可以编码:

  • 从哪一侧接近;
  • 动作快慢;
  • 双臂先后顺序;
  • 局部轨迹差异;
  • 示范者的操作风格。

5.3 重参数化

训练时通过重参数化采样:

\[ \epsilon\sim\mathcal N(0,I) \]
\[ z=\mu+\sigma\odot\epsilon \]

这样梯度可以从 Decoder 反向传播到 Encoder。


6. ACT Decoder:真正部署的机器人策略

ACT Decoder 建模:

\[ p_\theta(A_t\mid I_t,q_t,z) \]

输入包括:

  • 多相机 RGB 图像;
  • 当前关节状态;
  • Style Latent \(z\)

输出未来 \(K\) 个动作。

6.1 图像编码

每个相机图像分别经过 CNN,一般使用 ResNet Backbone:

\[ F_t^c=\operatorname{ResNet}(I_t^c) \]

得到空间特征:

\[ F_t^c\in\mathbb{R}^{C\times H'\times W'} \]

随后展平为空间 Token:

\[ X_t^c\in\mathbb{R}^{H'W'\times d} \]

并加入二维位置编码,使模型知道每个视觉 Token 来自图像中的哪个位置。

多相机特征随后被拼接:

\[ X_{\mathrm{vision}} = [X_t^1;X_t^2;\ldots;X_t^C] \]

6.2 本体状态和潜变量编码

当前关节状态通过线性层:

\[ e_q=W_q q_t \]

潜变量通过另一线性层:

\[ e_z=W_z z \]

然后与视觉 Token 一起输入 Transformer Encoder:

\[ M= \operatorname{TransformerEncoder} \left( [X_{\mathrm{vision}},e_q,e_z] \right) \]

Transformer Encoder 负责融合:

  • 不同相机视角;
  • 物体空间位置;
  • 当前机器人姿态;
  • 动作风格信息。

6.3 Action Query

ACT 的实现借鉴了 DETR。

模型准备 \(K\) 个可学习的 Action Query:

\[ Q= [q_1^{\mathrm{act}}, q_2^{\mathrm{act}}, \ldots, q_K^{\mathrm{act}}] \]

每个 Query 代表未来动作块中的一个时间位置:

  • Query 1:预测 \(a_t\)
  • Query 2:预测 \(a_{t+1}\)
  • \(\ldots\)
  • Query \(K\):预测 \(a_{t+K-1}\)

Transformer Decoder 执行:

\[ H= \operatorname{TransformerDecoder}(Q,M) \]

然后通过动作输出头:

\[ \hat a_{t+i}=W_aH_i+b_a \]

最终得到:

\[ \hat A_t= [\hat a_t,\ldots,\hat a_{t+K-1}] \]

7. ACT 的损失函数

ACT 的损失包含两部分:

\[ \mathcal L_{\mathrm{ACT}} = \mathcal L_{\mathrm{recon}} + \beta\mathcal L_{\mathrm{KL}} \]

7.1 动作重建损失

官方实现使用 L1 Loss:

\[ \mathcal L_{\mathrm{recon}} = \frac{1}{K} \sum_{i=0}^{K-1} \left\| a_{t+i}-\hat a_{t+i} \right\|_1 \]

对于轨迹末尾不足 \(K\) 步的部分,会使用 Padding Mask,使补齐的虚假动作不参与有效损失。

代码形式可写为:

all_l1 = torch.nn.functional.l1_loss(
    actions,
    pred_actions,
    reduction="none",
)

valid_mask = (~is_pad).unsqueeze(-1)
l1_loss = (all_l1 * valid_mask).mean()

L1 Loss 相对 MSE 对少量大误差不那么敏感,通常也不容易过度平均尖锐的动作变化。

7.2 KL 散度

CVAE 要求潜变量分布接近标准正态分布:

\[ p(z)=\mathcal N(0,I) \]

因此:

\[ \mathcal L_{\mathrm{KL}} = D_{\mathrm{KL}} \left( q_\phi(z\mid q_t,A_t) \parallel \mathcal N(0,I) \right) \]

对于对角高斯:

\[ D_{\mathrm{KL}} = -\frac{1}{2} \sum_j \left( 1+\log\sigma_j^2-\mu_j^2-\sigma_j^2 \right) \]

总损失为:

\[ \mathcal L = \mathcal L_1+ \beta D_{\mathrm{KL}} \]

7.3 \(\beta\) 的意义

\(\beta\) 控制动作重建与潜变量正则化之间的平衡。

  • \(\beta\) 太小:Encoder 可能把整条动作轨迹全部编码进 \(z\),Decoder 过度依赖 \(z\);部署时令 \(z=0\) 会产生较大的训练—测试差异。
  • \(\beta\) 太大:潜变量可能发生 Posterior Collapse,\(z\) 几乎不包含动作模式信息。
  • 合适的 \(\beta\):让 \(z\) 只表达必要的示范风格,同时让观察条件承担主要预测责任。

8. 推理阶段为什么设置 \(z=0\)

部署时没有未来专家动作 \(A_t\),因此无法计算:

\[ q_\phi(z\mid q_t,A_t) \]

ACT 直接丢弃 CVAE Encoder,并令:

\[ z=\mathbb E_{p(z)}[z]=0 \]

于是推理变为:

\[ \hat A_t = \pi_\theta(I_t,q_t,z=0) \]

这样推理是确定性的。

这也意味着原始 ACT 虽然在训练时使用 CVAE 处理示范的多样性,但默认部署不会不断随机采样多个 \(z\),而是输出学习到的中心风格或典型模式。


9. 两种动作块执行方式

9.1 直接执行整个 Chunk

在时间 \(t\) 查询一次策略:

\[ \hat A_t= [\hat a_t,\ldots,\hat a_{t+K-1}] \]

然后连续执行这 \(K\) 个动作,直到下一次查询。

优点:

  • 推理负载低;
  • 动作连贯;
  • 能显著降低策略查询次数。

缺点:

  • \(K\) 步内缺少新的视觉反馈;
  • 物体被碰动后不能及时修正;
  • Chunk 太长时接近开环控制。

9.2 Temporal Ensembling

ACT 更重要的部署方式是:

每一个控制周期都重新预测一个未来动作块。

例如:

t=0 预测:â0, â1, â2, â3
t=1 预测:    â1, â2, â3, â4
t=2 预测:        â2, â3, â4, â5

在时刻 \(t=2\),会有多个模型预测对应同一个真实时间点:

\[ \hat a_2^{(0)},\quad \hat a_2^{(1)},\quad \hat a_2^{(2)} \]

ACT 对这些针对同一时刻的预测进行加权平均:

\[ a_t = \sum_i w_i\hat a_t^{(i)} \]

其中:

\[ w_i= \frac{\exp(-mi)} {\sum_j\exp(-mj)} \]

这和普通低通滤波不同。

普通滤波平均的是不同时间点的动作,例如 \(a_{t-1}\)\(a_t\)\(a_{t+1}\);ACT 平均的是多个模型对同一个时间点 \(t\) 的预测,因此不会直接把不同轨迹时间位置混在一起。

9.3 Temporal Ensembling 的直觉

较早生成的预测具有:

  • 更强的长期轨迹一致性;
  • 不容易因为单帧视觉噪声突然改变。

较新的预测具有:

  • 更新的视觉信息;
  • 更强的环境扰动响应能力。

时间集成在两者之间取得平衡:

\[ \text{轨迹稳定性} \quad\leftrightarrow\quad \text{闭环反应速度} \]

10. ACT 的训练数据构造

给定长度为 \(T\) 的示范轨迹,训练时通常执行:

  1. 随机选择轨迹;
  2. 随机选择时间点 \(t\)
  3. 读取当前图像 \(I_t\)
  4. 读取当前关节状态 \(q_t\)
  5. 截取未来动作:
\[ A_t=[a_t,\ldots,a_{t+K-1}] \]
  1. 不足 \(K\) 步时进行 Padding;
  2. 构造 is_pad Mask;
  3. 对关节状态和动作进行数据集统计量标准化;
  4. 前向计算 L1 Loss 和 KL Loss;
  5. 反向传播并更新参数。

部署时必须使用与训练阶段完全一致的均值和标准差,对当前关节状态进行标准化,并将模型输出反标准化回真实关节目标。


11. ACT 训练伪代码

for images, qpos, actions, is_pad in dataloader:
    # images:  [B, num_cameras, C, H, W]
    # qpos:    [B, action_dim]
    # actions: [B, chunk_size, action_dim]
    # is_pad:  [B, chunk_size]

    # CVAE Encoder
    mu, logvar = encoder(qpos, actions, is_pad)

    std = torch.exp(0.5 * logvar)
    eps = torch.randn_like(std)
    z = mu + std * eps

    # ACT Decoder
    pred_actions = policy(images, qpos, z)

    valid_mask = (~is_pad).unsqueeze(-1)

    l1_loss = (
        torch.abs(pred_actions - actions) * valid_mask
    ).mean()

    kl_loss = -0.5 * (
        1 + logvar - mu.pow(2) - logvar.exp()
    ).sum(dim=-1).mean()

    loss = l1_loss + beta * kl_loss

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

12. ACT 部署伪代码

action_buffer = {}

for t in range(episode_length):
    images = read_cameras()
    qpos = read_joint_positions()

    qpos_norm = normalize_qpos(qpos)

    # 原始 ACT 部署使用 z = 0
    z = torch.zeros(1, latent_dim, device=device)

    # 输出形状:[1, K, action_dim]
    action_chunk = policy(images, qpos_norm, z)

    # 将第 i 个预测放到对应的绝对时间 t+i
    for i in range(chunk_size):
        target_time = t + i
        action_buffer.setdefault(target_time, [])
        action_buffer[target_time].append(action_chunk[0, i])

    predictions = action_buffer[t]

    weights = exponential_weights(len(predictions))
    action_norm = weighted_average(predictions, weights)

    action = denormalize_action(action_norm)
    action = apply_safety_limits(action)

    send_joint_target(action)

真实系统还应在发送动作前增加:

  • 关节上下限裁剪;
  • 速度限制;
  • 加速度限制;
  • 碰撞检测;
  • 急停逻辑;
  • 相机与机器人时间戳同步检查。

13. Chunk Size 怎么选

Chunk Size 是 ACT 最重要的超参数之一。

13.1 Chunk 太小

\(K\approx1\) 时,ACT 接近普通单步 Behavior Cloning:

  • 累积误差严重;
  • 动作时间结构弱;
  • 更容易抖动;
  • Transformer 的序列建模优势有限。

13.2 Chunk 太大

\(K\) 接近完整 Episode 长度时:

  • 模型要一次预测很长轨迹;
  • 未来不确定性增大;
  • 对视觉扰动反应变慢;
  • Temporal Ensemble 的缓存和计算成本增大;
  • 容易产生按初始观察执行的开环行为。

13.3 工程选择原则

假设控制频率为 \(f\),Chunk 对应的时间长度为:

\[ T_{\mathrm{chunk}}=\frac{K}{f} \]

通常应让一个 Chunk 覆盖一个有意义的局部动作阶段,例如:

  • 接近物体;
  • 完成抓取;
  • 完成一次插入;
  • 抬起并转移一小段。

可使用以下范围作为初始实验配置:

控制频率 Chunk Size 覆盖时间
\(10\ \mathrm{Hz}\) 10–30 1–3 秒
\(20\ \mathrm{Hz}\) 20–60 1–3 秒
\(50\ \mathrm{Hz}\) 50–100 1–2 秒

14. ACT 常见失败模式

14.1 机器人动作中途暂停

可能原因:

  • 数据中存在较多停顿;
  • 训练时间不足;
  • 多种示范节奏相互冲突;
  • Chunk 太短;
  • Temporal Aggregation 权重过度偏向旧预测。

14.2 动作抖动

常见原因:

  • 没有启用 Temporal Ensemble;
  • 图像或关节状态不同步;
  • 动作标准差太小,反标准化时放大噪声;
  • 底层控制器跟踪性能不足;
  • 示范动作本身不平滑;
  • 模型尚未充分收敛。

14.3 预测出平均动作

假设数据中一半示范从左侧绕行,一半从右侧绕行。

虽然 CVAE 可以在训练阶段把两种模式编码到不同的 \(z\),但部署时固定 \(z=0\) 仍可能落在两个模式之间。

常见解决方式:

  • 统一示范策略;
  • 按动作模式拆分任务;
  • 加入语言条件或任务 ID;
  • 部署时采样多个 \(z\) 并选择轨迹;
  • 使用表达能力更强的生成策略,如 Diffusion Policy。

14.4 开始动作正确,后期逐渐漂移

可能原因:

  • 数据没有覆盖失败恢复状态;
  • 相机视角下目标逐渐被遮挡;
  • Episode 太长;
  • 没有使用历史信息;
  • 策略只依赖瞬时观察,存在感知歧义。

ACT 能缓解累积误差,但不能彻底解决训练分布之外的状态恢复问题。

14.5 训练 Loss 低但真实成功率低

这通常意味着:

\[ \text{离线动作误差} \neq \text{闭环任务成功率} \]

需要重点检查:

  • 相机标定和安装位置是否变化;
  • 图像预处理是否与训练一致;
  • qpos 和 Action 标准化是否正确;
  • 动作定义是 Absolute、Delta 还是 Velocity;
  • 控制频率是否匹配;
  • 夹爪量程是否一致;
  • 是否存在网络或相机延迟;
  • 训练集是否覆盖物体位置变化。

15. ACT 与 Diffusion Policy 对比

两者的共同点是:

\[ \text{都预测动作序列,而不只是单步动作} \]

但生成动作序列的方法不同。

特性 ACT Diffusion Policy
生成模型 CVAE 条件扩散模型
推理方式 一次 Transformer 前向 多步迭代去噪
输出 动作 Chunk 动作轨迹
多模态能力 中等 通常更强
推理延迟 较低 通常较高
训练复杂度 较低 较高
时间平滑 Temporal Ensemble 轨迹生成与 Receding Horizon
部署确定性 默认 \(z=0\) 可确定或随机采样
适用场景 实时性强、数据较统一 行为高度多模态、轨迹复杂

15.1 适合选择 ACT 的情况

  • 控制频率高;
  • GPU 算力有限;
  • 需要较低推理延迟;
  • 单任务或少量任务;
  • 示范行为比较一致;
  • 希望快速建立机器人模仿学习基线。

15.2 适合选择 Diffusion Policy 的情况

  • 同一场景存在明显的多种合法轨迹;
  • 动作空间较复杂;
  • 推理预算充足;
  • 数据规模较大;
  • 需要更强的动作分布建模能力。

16. ACT 最本质的理解

不要把 ACT 简单理解为:

用 Transformer 输出多个动作。

ACT 真正有效的是以下设计共同作用:

\[ \boxed{ \text{Action Chunking} + \text{CVAE} + \text{Transformer} + \text{Temporal Ensembling} } \]
  • Action Chunking:学习局部运动原语,减轻长时程累积误差;
  • CVAE:处理人类示范的随机性和多种动作风格;
  • Transformer:融合多相机视觉、本体状态和动作序列结构;
  • Temporal Ensembling:在每步重新观察环境的同时保持动作平滑。

一句话概括:

ACT 是一个以当前视觉和机器人状态为条件,利用 CVAE-Transformer 一次生成未来动作块,并通过重叠动作块的时间集成实现平滑闭环控制的模仿学习策略。


17. 参考资料

results matching ""

    No results matching ""