0. 简介

FlowWAM 面向机器人操作里的世界-动作模型(World Action Model, WAM),要解决的是一个很具体的矛盾:预训练视频生成器里存着大量关于「像素怎么随时间移动」的运动先验,但机器人的动作要么是各家不通用的数值关节向量,要么是抽象的隐动作码,这些表示都塞不进视频模型习惯的 RGB 输入格式,先验就用不上。它没有再给视频模型外挂一套动作 token 或动作头,而是把动作本身编码成光流视频——用 HSV 色轮把每像素位移画成一张和 RGB 同格式的图,再和 RGB 一起送进同一个预训练视频生成器双流建模。从实验看,最值得关注的是 RoboTwin 2.0 上 Clean 成功率做到 92.94%Random 做到 92.14%,同时因为光流能从无动作标签的视频里直接抽取,模型还能吃 EgoDex 这类纯人类第一视角视频做预训练。对应的部分代码已经放在Github上了。

1. 为什么又造了一个新名词

1.1 视频先验很强,但动作表示卡在门口

先看一个具体冲突。预训练视频生成器(Wan、CogVideoX 这类)已经在海量视频上学会了「物体怎么运动、机械臂怎么连续移动」这种帧间动态,这正是操作任务最需要的先验,因为操作的成败不只取决于把指令对应到物体,更取决于逐帧捕捉机器人和环境的连续运动。可问题在于,机器人要执行的动作是一串关节角或末端位姿数值,这种符号化的东西和视频模型习惯的像素输入根本不是一个空间。要用上视频先验,动作就得换一种既贴合视频输入格式、又保留跨帧运动线索的表示方式。这里的关键是,过去的表示都在这两个要求上顾此失彼,FlowWAM 想给出一个同时满足两者的答案。

1.2 三类现有路线各自的缺口

进一步看,现有 WAM 的动作表示大致分三类。第一类是数值动作 token(DreamZero、Cosmos Policy、UWM 这些),把动作向量拼到视频隐变量旁边协同生成,精确但不同机器人动作空间不同,跨本体迁移困难,而且动作始终是独立于视频先验的符号流。第二类是隐动作码(Motus、LAPA 一系),从帧间转移里学一个与具体机器人无关的隐表示,摆脱了本体依赖,却过于抽象,丢掉了控制需要的稠密、空间对齐的运动线索。第三类是图像空间动作信号(ray map、embodiment mask、多视角动作图),把控制信号渲染成视觉形态直接放进视频模型输入域,方向对了,但这些大多是「指出动作发生在哪」的静态空间提示,并没有编码「每个可见部件如何跨帧移动」。核心问题在于,前两类接不进视频先验,第三类接进去了却是帧级静态条件,不是随预测视频一起演化的时间稠密动作表示。FlowWAM 的判断是,光流恰好能同时补上这三个缺口:它和 RGB 同格式、稠密、且天然带跨帧运动方向与幅度。

在这里插入图片描述

2. 整体框架

2.1 输入输出接口与两种运行模式

FlowWAM 的输入是一张参考图 I 0 I_0 I0 和一句语言指令 τ \tau τ,输出则取决于运行在哪种模式。这里要厘清的是,整个框架用一个双流扩散 Transformer 同时建模 RGB 视频和光流视频两条流,而策略模式与世界模型模式的差别,仅仅在于光流这条流是要被生成,还是被当作条件给定。当光流由模型生成时,FlowWAM 就是一个策略,生成的运动计划被动作专家解码成可执行动作;当光流被外部提供时,FlowWAM 就是一个世界模型,渲染出与指定运动一致的未来 RGB 视频。这种「一套权重、两种用法」的设计,把动作预测和世界建模统一成了同一个视频到视频的生成任务。两条流的联合建模可以写成:

z = E ( V ) , z f = E ( F ) , [ z t + 1 , z t + 1 f ] = DiT θ ( [ z t , z t f ] , t , τ ) \mathbf{z} = \mathcal{E}(\mathbf{V}), \qquad \mathbf{z}^{f} = \mathcal{E}(\mathbf{F}), \qquad [\mathbf{z}_{t+1}, \mathbf{z}^{f}_{t+1}] = \text{DiT}_\theta([\mathbf{z}_t, \mathbf{z}^f_t], t, \tau) z=E(V),zf=E(F),[zt+1,zt+1f]=DiTθ([zt,ztf],t,τ)

其中 V \mathbf{V} V F \mathbf{F} F 分别是 RGB 与光流视频序列, E \mathcal{E} E 是被冻结的共享 VAE 编码器, z \mathbf{z} z z f \mathbf{z}^f zf 是两条流的隐变量,DiT 在共享的 Transformer 块里对两条流做联合去噪。这里要厘清的是,两条流走的是完全相同的编码器与 Transformer,差别只在极少量的流独立适配器(patch 嵌入与输出头),因此模型几乎原封不动地继承了预训练视频生成器的全部生成能力,而不是从头拼一个新架构。

在这里插入图片描述

2.2 训练与推理的关键不对称

这里有一个值得注意的不对称设计,涉及动作专家读取的隐变量分布。直观理解是,训练时动作专家可以读到「干净」的 VAE 隐变量(直接编码真值观测得到),但推理时它只能读到双流模型迭代去噪生成出来的隐变量,后者难免带残余去噪误差。如果不处理这个分布差异,动作专家在推理时就会因为输入分布漂移而变脆。FlowWAM 的做法是,在训练时以概率 p = 0.5 p{=}0.5 p=0.5 往喂给动作专家的隐变量里混入噪声,并把采样到的噪声水平作为额外嵌入告诉动作专家,让它学会在带误差的隐变量上也能稳定解码。

3. 第一条核心机制:把光流编码成 RGB 同格式的图

3.1 HSV 色轮编码为什么是关键

第一条核心机制是光流的 RGB 化编码。这里的关键是,只有让光流和场景帧格式完全一致,同一个 VAE 编码器和视频生成器才能不加任何动作 tokenizer 地直接处理它。给定相邻帧,光流场 f t ∈ R H × W × 2 \mathbf{f}_t \in \mathbb{R}^{H\times W\times 2} ftRH×W×2 记录每像素位移 ( u , v ) (u,v) (u,v),既捕捉运动发生在哪,也捕捉可见点怎么移动。FlowWAM 用一个 HSV 色轮编码 ϕ \phi ϕ 把它转成 RGB 图:

F t = ϕ ( f t ) : H = atan2 ⁡ ( v , u ) + π 2 π , S = ∥ f t ∥ m , V = 1 F_{t}=\phi(\mathbf{f}_{t}):\quad \text{H}=\frac{\operatorname{atan2}(v,u)+\pi}{2\pi},\quad \text{S}=\frac{\|\mathbf{f}_{t}\|}{m},\quad \text{V}=1 Ft=ϕ(ft):H=2πatan2(v,u)+π,S=mft,V=1

其中 m m m 是光流幅度的归一化常数,H、S、V 分别是色相、饱和度、明度通道。这里色相编码方向、饱和度编码幅度,且在选定归一化下这个编码是可逆的, ϕ − 1 ( F t ) \phi^{-1}(F_t) ϕ1(Ft) 能恢复出数值光流场。换句话说,动作和视频在像素空间里被彻底统一了:因为光流图 F t F_t Ft 和场景帧 I t I_t It 格式完全一致,动作相关的运动可以被同一个 VAE 编码器和视频生成器直接处理,不再需要一个单独的动作 tokenizer,这正是整套统一表示能成立的技术前提。

难点提示(为什么用 HSV 而不是直接堆 (u,v) 两通道):想象你要把「往右上方向、走了 3 格」这条运动告诉一个只认识彩色照片的画家。直接给他两个数字 (u,v) 他没概念,因为他一辈子只见过 RGB 图;但你把方向画成颜色(右上=某种青色)、把快慢画成颜色深浅,他立刻就能像看普通照片一样理解。HSV 编码就是这个「翻译成画家母语」的动作,论文消融里把它换回裸 ( u , v ) (u,v) (u,v) 张量,成功率直接崩掉,就是因为破坏了预训练 VAE 期望的输入格式。

在这里插入图片描述

3.2 工程价值:可逆编码撑起两种模式

这个编码的工程价值在于可逆性带来的双向可用。因为 ϕ \phi ϕ 可逆,同一张光流图在策略模式里是生成目标、在世界模型模式里是条件输入,两种角色靠一套编解码就能来回切换。论文的世界模型侧消融给了很硬的数字:把条件从文本、数值动作、裸 ( u , v ) (u,v) (u,v) 张量、图像空间 mask 一路换到完整的 RGB 光流,EWMScore 从 49.31 提升到 65.23。这里要厘清的是,收益不是来自「加了视觉条件」这么泛的东西,而是来自 RGB 光流同时具备「每像素运动」和「视频原生格式」两个属性,缺一不可。

3.3 代码透视:HSV 编码与逆解码

下面这段直接摘自官方仓库 training/reversible_flow_codec.pyFlowCodec.encode/decode,它把每像素位移先转成极坐标,再把方向和幅度分别塞进色相与饱和度两个通道,展示极坐标分解、幅度归一化,以及编码与逆解码为什么必须严格互逆——只有严格互逆,世界模型模式里给定的光流条件才能被还原成物理位移:

# training/reversible_flow_codec.py  ——  FlowCodec.encode / FlowCodec.decode
def encode(self, flow, max_magnitude=None, sigma=0.15):
    dx, dy = flow[..., 0], flow[..., 1]
    magnitude = np.sqrt(dx ** 2 + dy ** 2)
    angle = np.arctan2(dy, dx)                          # [-pi, pi]
    if max_magnitude is not None and max_magnitude == -1:
        diag = np.sqrt(float(H ** 2 + W ** 2))          # VideoJAM 对角线归一化
        max_magnitude = sigma * diag
    elif max_magnitude is None:
        max_magnitude = float(np.percentile(magnitude, 99.5)) + 1e-6  # 自适应上限
    mag_norm = np.clip(magnitude / max_magnitude, 0, 1)
    angle_norm = np.clip((angle + np.pi) / (2 * np.pi), 0, 1)         # 方向 -> [0,1]
    hue = (angle_norm * 179).astype(np.uint8)           # OpenCV: H in [0,179]
    sat = (mag_norm * 255).astype(np.uint8)             # 幅度 -> 饱和度
    val = np.full_like(hue, 255, dtype=np.uint8)        # V 恒为 255
    hsv = np.stack([hue, sat, val], axis=-1)
    return cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB), max_magnitude

def decode(self, rgb_image, max_magnitude):
    hsv = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2HSV)
    angle = hsv[..., 0].astype(np.float64) / 179.0 * 2 * np.pi - np.pi  # 逆映射方向
    magnitude = hsv[..., 1].astype(np.float64) / 255.0 * max_magnitude  # 逆映射幅度
    dx, dy = magnitude * np.cos(angle), magnitude * np.sin(angle)       # 极坐标 -> 直角
    return np.stack([dx, dy], axis=-1).astype(np.float32)

这段代码做的事很直接:编码时把位移方向经 arctan2 归一化成色相、把幅度归一化后写成饱和度、明度恒为 255。为什么这样设计,是因为光流图必须落进 RGB 的合法值域,才能被冻结的 VAE 无损吃下去。这里要厘清一个和论文正文的差异:仓库里 max_magnitude 的默认策略不是固定 25px,而是取当前帧幅度的 99.5 百分位自适应上限(max_magnitude=None 时),另外还提供了一个按对角线 σ ⋅ H 2 + W 2 \sigma\cdot\sqrt{H^2+W^2} σH2+W2 归一化的 VideoJAM 模式;论文附录提到的 25px 上限是数据预处理阶段的一个具体配置,代码则把归一化常数做成了可切换项。工程细节上,decode 严格是 encode 的逆运算,max_magnitude 通过 .meta sidecar 文件或图像右下角像素回传,这保证了世界模型模式里给定的光流条件能还原成物理位移,正是 3.2 节说的可逆性在代码层面的落地。

4. 第二条核心机制:双流共享同一个视频先验

4.1 设计动机:为什么不能开两个模型

第二条核心机制是双流架构。这里的关键是,如果给 RGB 和光流各开一个独立视频模型,不仅参数翻倍,两条流之间还无法做深度时空交互,光流也就退化成了外挂的辅助信号——这恰恰是过去把光流当辅助监督的做法的通病。FlowWAM 的做法是让两条流共享同一个冻结 VAE、同一批 Transformer 块,只有 patch embedding 层和输出头是流独立的。在每个自注意力层里,RGB token 和光流 token 拼接起来做联合注意力,然后再拆回各自的流,RoPE 位置编码对两条流独立施加。这意味着两条流能做深度时空交互,同时共享的 Transformer 块保证模型完整保留了预训练视频生成器的生成能力。

4.2 代码透视:联合注意力与流身份嵌入

仓库把这套双流拆成两块:FlowStreamModulediffsynth/models/wan_video_dit_dual_stream.py)持有光流独有的 patch 嵌入、输出头和流身份参数,而联合注意力的具体拼接逻辑在 diffsynth/pipelines/wan_video_dual_stream.py 里,两者配合起来才构成完整的双流通路。先看流独立适配器怎么从 RGB 层深拷贝初始化,这是让新加的光流流一上来就继承视频先验的关键一步:

# diffsynth/models/wan_video_dit_dual_stream.py  ——  FlowStreamModule
class FlowStreamModule(nn.Module):
    def __init__(self, dit):
        super().__init__()
        self.patch_size = tuple(dit.patch_size)
        self.flow_patch_embedding = copy.deepcopy(dit.patch_embedding)  # 从 RGB 深拷贝
        self.flow_head = copy.deepcopy(dit.head)                        # 输出头也深拷贝
        self.stream_embed = nn.Parameter(torch.zeros(1, 1, dit.dim))    # 可学习流身份

再看联合自注意力那一段,它是整个双流交互真正发生的地方:位置编码对两条流各自独立施加,避免把彼此的时空坐标搅在一起;随后两流的查询、键、值沿序列维拼接,共享同一套注意力权重做一次全局注意力;算完之后再按各自的 token 长度切回两条流,各走各的输出头。可以看到,深度交互和保留先验这两个目标就是靠「独立位置编码 + 共享注意力权重」这一组合同时兑现的:

# diffsynth/pipelines/wan_video_dual_stream.py  ——  dual_stream_block (节选)
flow_tokens = flow_tokens + flow_stream.stream_embed   # 注入流身份
rgb_q = rope_apply(rgb_q, rgb_freqs, sa.num_heads)     # RGB 用自己的位置频率
flow_q = rope_apply(flow_q, flow_freqs, sa.num_heads)  # 光流用自己的位置频率
q = torch.cat([rgb_q, flow_q], dim=1)                  # 沿序列维拼接
k = torch.cat([rgb_k, flow_k], dim=1)
v = torch.cat([rgb_v, flow_v], dim=1)
attn_out = sa.o(flash_attention(q, k, v, sa.num_heads))  # 一套权重做联合注意力

这两段代码做了什么:FlowStreamModulecopy.deepcopy 把预训练 DiT 的 patch 嵌入和输出头各拷一份给光流通路,stream_embed 是一个加到光流 token 上的可学习流身份信号,让模型分得清哪条是光流、哪条是 RGB。为什么这样设计,是因为光流帧用同一个 VAE 编码,所以用 RGB 层权重初始化光流通路能提供兼容的图像-隐变量先验,比随机初始化收敛快得多。工程细节上,rgb_freqsflow_freqs 是两组独立的 RoPE 频率,位置编码各自施加不互相干扰,但 torch.cat 之后走的是同一套 self_attn 权重,注意力矩阵跨流打通——这就是「深度交互 + 保留先验」两个目标的平衡点。

在这里插入图片描述

4.3 工程细节亮点

进一步看两个亮点。其一,基座是 Wan2.2-TI2V-5B,一个 50 亿参数的图生视频扩散 Transformer,配 UMT5-XXL 文本编码器和 Wan2.2 因果 VAE,训练时 VAE 和文本编码器全程冻结,只更新 DiT。其二,光流通路的 patch embedding 和输出头都是从对应 RGB 层「深拷贝」来的,而不是新建随机层——这是一个很省事但很关键的初始化技巧,让新加的光流流一上来就站在预训练视频先验的肩膀上,而不是从零学起。

5. 训练时的监督模块

5.1 动作专家与运动感知重加权

…详情请参照古月居

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐