• 正文
  • 相关推荐
申请入驻 产业图谱

世界模型 | FlowWAM:把光流当成动作,让预训练视频模型直接开机器人

09/07 13:41
378
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

转载自公众号:敢敢AUTOHUB

0. 简介

FlowWAM 面向机器人操作里的世界-动作模型(World Action Model, WAM),要解决的是一个很具体的矛盾:预训练视频生成器里存着大量关于「像素怎么随时间移动」的运动先验,但机器人的动作要么是各家不通用的数值关节向量,要么是抽象的隐动作码,这些表示都塞不进视频模型习惯的 RGB 输入格式,先验就用不上。它没有再给视频模型外挂一套动作 token 或动作头,而是把动作本身编码成光流视频——用 HSV 色轮把每像素位移画成一张和 RGB 同格式的图,再和 RGB 一起送进同一个预训练视频生成器双流建模。

从实验看,最值得关注的是 RoboTwin 2.0 上 Clean 成功率做到 92.94%Random 做到 92.14%,同时因为光流能从无动作标签的视频里直接抽取,模型还能吃 EgoDex 这类纯人类第一视角视频做预训练。

论文地址:https://arxiv.org/html/2607.13017v1
GitHub地址:https://github.com/YixiangChen515/FlowWAM

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 的输入是一张参考图  和一句语言指令 ,输出则取决于运行在哪种模式。这里要厘清的是,整个框架用一个双流扩散 Transformer 同时建模 RGB 视频和光流视频两条流,而策略模式与世界模型模式的差别,仅仅在于光流这条流是要被生成,还是被当作条件给定

当光流由模型生成时,FlowWAM 就是一个策略,生成的运动计划被动作专家解码成可执行动作;当光流被外部提供时,FlowWAM 就是一个世界模型,渲染出与指定运动一致的未来 RGB 视频。这种「一套权重、两种用法」的设计,把动作预测和世界建模统一成了同一个视频到视频的生成任务。两条流的联合建模可以写成:

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

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

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

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

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

第一条核心机制是光流的 RGB 化编码。这里的关键是,只有让光流和场景帧格式完全一致,同一个 VAE 编码器和视频生成器才能不加任何动作 tokenizer 地直接处理它。给定相邻帧,光流场  记录每像素位移 ,既捕捉运动发生在哪,也捕捉可见点怎么移动。FlowWAM 用一个 HSV 色轮编码  把它转成 RGB 图:

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

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

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

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

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

下面这段直接摘自官方仓库 training/reversible_flow_codec.py 的 FlowCodec.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 时),另外还提供了一个按对角线  归一化的 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))  # 一套权重做联合注意力

这两段代码做了什么:FlowStreamModule 用 copy.deepcopy 把预训练 DiT 的 patch 嵌入和输出头各拷一份给光流通路,stream_embed 是一个加到光流 token 上的可学习流身份信号,让模型分得清哪条是光流、哪条是 RGB。为什么这样设计,是因为光流帧用同一个 VAE 编码,所以用 RGB 层权重初始化光流通路能提供兼容的图像-隐变量先验,比随机初始化收敛快得多。工程细节上,rgb_freqs 和 flow_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 动作专家与运动感知重加权

训练时的核心监督模块是动作专家,一个约 780M 参数的 AdaLN 扩散 Transformer,30 层、隐维 1024、16 头、FFN 4096,层数刻意对齐 Wan2.2 视频 DiT 的深度。它不重新 patch 化 VAE 隐变量,而是逐层交叉注意到双流视频 DiT 每一层读出的 RGB+光流隐状态,同时条件化于当前本体状态(14 维关节位置 qpos),在流匹配目标下预测  步动作块 。

这里有一个工程选择三件套:为什么条件化于逐层隐状态而不是最终输出,是因为逐层特征保留了从粗到细的运动信息;为什么把 qpos 投影后拼到 T5 指令上下文里,是因为动作既要跟着生成的运动计划走,也要锚定机器人初始状态;为什么用流匹配而不是直接回归,是为了和视频侧的生成目标保持一致的训练范式。

针对操作光流空间稀疏(运动集中在本体、被操作物、接触区)的问题,FlowWAM 对光流损失做运动感知重加权,避免静止背景主导损失。这里的关键是,操作场景里绝大多数像素是静止的桌面和背景,如果损失一视同仁,模型会把容量浪费在拟合静止区上。逐位置权重按光流隐变量相对参考帧的偏差计算,让会动的区域获得更高权重:

其中  沿隐变量通道做平均, 控制增强强度(Stage 2 取 2.0),把学习引向运动丰富的区域。这个权重项的分母是整帧偏差的最大值,所以它本质是把每个位置的运动强度归一化到  再线性放大,运动越强的像素权重越接近 ,几乎静止的背景权重则退回到  附近。

工程价值(运动感知重加权解决了什么):想象一张操作视频里,95% 的像素是一动不动的桌面和背景,只有机械臂末端那一小块在动。如果损失对所有像素一视同仁,模型会把精力全花在「把静止背景画得更准」上,而真正要学的手臂运动反而被稀释。这个权重项相当于给会动的区域加一盏聚光灯,让梯度优先照顾运动线索。消融里去掉它,静止背景就会主导光流损失,成功率随之下降。

5.2 代码透视:训练 step 主循环

先看运动感知重加权在仓库里的真实实现,它就在训练脚本的预处理阶段,用光流隐变量相对第一帧的偏差算出逐位置权重,让会动的区域在损失里获得更高权重,从而避免大片静止背景主导整个光流损失。这段代码是论文里那个运动权重公式的直接落地,可以对照着看它怎么把「通道平均偏差、整帧最大值归一化、线性放大」三步串起来:

# training/flow_action_train.py  ——  forward_preprocess (节选)
flow_fz = flow_input_latents[:, :, 0:1]                      # 光流首帧作参考
flow_motion_weight = None
if self.flow_motion_boost > 0:                              # 默认 2.0
    with torch.no_grad():
        deviation = (flow_input_latents - flow_fz).abs().mean(dim=1, keepdim=True)  # 通道平均偏差
        dev_max = deviation.amax(dim=(2, 3, 4), keepdim=True).clamp(min=1e-6)       # 整帧最大
        motion_w = 1.0 + self.flow_motion_boost * (deviation / dev_max)             # 归一化后放大
    flow_motion_weight = motion_w

再看动作分支里的随机噪声条件化,也就是训练与推理分布对齐那一步:训练时它以一定概率把喂给动作专家的视频隐变量加噪到某个随机档位,再把这个档位作为额外条件告诉动作专家,好让它在推理时面对带残余去噪误差的隐变量也不至于崩。这里的关键是,无论加不加噪,首帧条件帧都被单独保护起来始终保持干净,这样加噪只影响后续待预测帧,不破坏图生视频的条件语义:

# training/flow_action_train.py  ——  action branch (节选)
video_cond_timestep = torch.zeros(B, ...)                   # 默认给动作专家干净隐变量
rgb_cond, flow_cond = rgb_clean, flow_clean
if self.cond_noise_prob > 0.0:                              # 默认 0.5
    cond_mask = torch.rand(B, device=rgb_clean.device) < self.cond_noise_prob
    if bool(cond_mask.any()):
        cond_t = self.pipe.scheduler.timesteps[cond_tid]    # 随机采一个噪声档位
        rgb_noised  = self.pipe.scheduler.add_noise(rgb_clean,  torch.randn_like(rgb_clean),  cond_t)
        flow_noised = self.pipe.scheduler.add_noise(flow_clean, torch.randn_like(flow_clean), cond_t)
        rgb_cond  = torch.where(sel_rgb,  rgb_noised,  rgb_clean)
        flow_cond = torch.where(sel_flow, flow_noised, flow_clean)
        rgb_cond[:, :, :1]  = rgb_clean[:, :, :1]           # 首帧始终保持干净
        flow_cond[:, :, :1] = flow_clean[:, :, :1]
        video_cond_timestep = torch.where(cond_mask, cond_t, 0)  # 噪声档位告诉动作专家
# 后续:capture_video_layer_features(...) 抓逐层特征喂给动作专家
loss = loss_video + self.action_loss_weight * loss_action   # 总目标

这两段代码做了什么:运动权重用 flow_motion_boost(默认 2.0,对应论文里的 )把每个位置的运动强度归一化到  再线性放大,运动越强权重越接近 ,几乎静止的背景退回到  附近;动作分支则以 cond_noise_prob(默认 0.5,对应论文的 )随机决定这一步给动作专家干净还是加噪的隐变量,加噪时把噪声档位一并告诉它。为什么这样设计,是因为推理时动作专家读到的隐变量必然带残余去噪误差,训练时不模拟这种误差就会分布漂移

这里要厘清一个和论文正文的数字差异:论文写 ,仓库 argparse 默认 --action_loss_weight 也是 1.0,但 FlowActionTrainer 构造函数里的默认值写的是 5.0--flow_loss_weight 同理,argparse 默认 0.5 而论文写 0.1——实际取值以训练脚本传入的参数为准,读代码时不要被构造函数默认值误导。工程细节上,无论加不加噪,首帧永远保持干净([:, :, :1] 那两行),对齐图生视频的条件帧语义。

6. 推理时的执行模块

6.1 策略模式:生成光流再解码动作

推理时策略模式的流程是这样的:两条流的首帧固定为参考观测的干净 VAE 编码,其余帧都从高斯噪声初始化,然后联合迭代去噪。双流 DiT 同时合成未来 RGB rollout 和对应光流视频,动作专家从中读取中间特征解码出可执行动作。这里要厘清的是,光流在这个模式里是被生成出来的,它是一份稠密的图像空间运动计划,动作专家读的是这份计划而不是直接从像素回归动作,这也是 FlowWAM 相对直接动作预测策略的结构性差异。

这一段直接摘自官方推理入口的策略回滚函数,注意它是「先双流联合去噪、再在指定噪声档位抓逐层特征、最后交给动作专家解码动作」的三段式流程。三段严格顺序执行、不能颠倒,因为动作专家依赖的正是去噪完成后的那份光流运动计划,只有光流先被生成出来,逐层特征里才带着可解码的运动信息:

# inference/flow_action_server.py  ——  rollout_and_predict_actions (节选)
rgb_noise[:, :, :1] = rgb_prefix                    # 首帧锚定参考观测
flow_noise[:, :, :1] = flow_prefix
rgb_latents, flow_latents = rgb_noise.clone(), flow_noise.clone()

# Stage 1: 双流视频联合去噪
pipe.scheduler.set_timesteps(video_inference_steps, shift=sigma_shift)
for progress_id, timestep in enumerate(pipe.scheduler.timesteps):
    rgb_pred, flow_pred = model_fn_wan_video_dual_stream(
        dit=pipe.dit, flow_stream=flow_stream,
        latents=rgb_latents, flow_latents=flow_latents,
        timestep=timestep.unsqueeze(0), context=context)
    rgb_latents  = pipe.scheduler.step(rgb_pred,  pipe.scheduler.timesteps[progress_id], rgb_latents)
    flow_latents = pipe.scheduler.step(flow_pred, pipe.scheduler.timesteps[progress_id], flow_latents)
    rgb_latents[:, :, :1]  = rgb_prefix             # 每步都把首帧钉回条件帧
    flow_latents[:, :, :1] = flow_prefix

# Stage 2: 在 t≈0 抓双流 DiT 的逐层特征喂给动作专家
feats = capture_video_layer_features(
    dit=pipe.dit, flow_stream=flow_stream,
    latents=rgb_latents, flow_latents=flow_latents,
    timestep=torch.full((1,), float(action_cond_sigma)), context=context)
# Stage 3(后续):ActionExpertIDM 对 feats 做逐层交叉注意力,去噪出动作块

这段代码的解读段落如下:策略模式把两条流一起从噪声去噪,光流被真正合成出来,然后 capture_video_layer_features 在噪声档位 action_cond_sigma 上抓逐层 RGB+光流特征,动作专家再对这些特征做交叉注意力解码动作块。换句话说,动作不是从原始像素直接回归的,而是从一份显式的、稠密的运动计划里解码出来的。这里值得注意 rgb_latents[:, :, :1] = rgb_prefix 每一步都在执行,也就是首帧条件帧在整个去噪循环里被反复钉住,和训练时的语义完全一致。这也是可解码性分析(Pearson 相关 )背后的机制:光流预测越准,成功率越高,说明收益确实来自光流本身而非解码器的某种捷径。

6.2 世界模型模式:给定光流条件化视频

世界模型模式只改一处:光流隐变量  不再从噪声初始化,而是设成期望运动轨迹的干净 VAE 编码并在整个采样过程中保持固定,只有 RGB 隐变量从噪声出发去噪。模型据此渲染出与指定运动一致的未来视频,用于规划与评估。性能上,RoboTwin 评测用 25 步视频去噪 / 50 步动作去噪,执行-重规划窗口为 25,每块生成 9 个像素帧、解码 32 步动作(帧间时间步长 4)。这套配置的取舍是,视频去噪步数给得少(25 步)以控制延迟,而动作去噪步数给到 50 步保证控制精度

下面这段是在上面策略模式真实代码基础上,按论文 Sec.3.2 世界模型模式改写的示意版(官方仓库的 flow_action_server.py 走的是策略模式,两条流都去噪;世界模型模式的差别仅在于光流流被固定),只改两处:flow_latents 用目标光流编码后固定不动,去噪循环里不再更新它:

# 基于 rollout_and_predict_actions 改写的世界模型模式示意(论文 Sec.3.2)
flow_latents = encode_flow(target_flow)              # 光流条件全程固定,不参与去噪
rgb_latents  = rgb_noise.clone()
rgb_latents[:, :, :1] = rgb_prefix                   # RGB 首帧锚定观测
for progress_id, timestep in enumerate(pipe.scheduler.timesteps):
    rgb_pred, _ = model_fn_wan_video_dual_stream(     # 光流分支输出丢弃
        dit=pipe.dit, flow_stream=flow_stream,
        latents=rgb_latents, flow_latents=flow_latents,
        timestep=timestep.unsqueeze(0), context=context)
    rgb_latents = pipe.scheduler.step(rgb_pred, pipe.scheduler.timesteps[progress_id], rgb_latents)
    rgb_latents[:, :, :1] = rgb_prefix               # 只更新 RGB 流
return pipe.vae.decode(rgb_latents)                  # 渲染与指定运动一致的未来视频

这段代码的关键差异只在两处:flow_latents 用真实目标光流编码后固定不动、不放进 scheduler.step,去噪循环里也只更新 rgb_latents。为什么这样设计,是因为世界模型模式要回答的是「给定这段运动,未来长什么样」,光流是条件不是待预测量。工程细节上,把光流当稠密逐像素运动场去条件化,让生成器有一个可去噪的空间对齐目标,这也是 FlowWAM 在 WorldArena 的深度精度(Depth Accuracy)拿到最高分的原因——稠密光流顺带约束了几何。

直觉理解(策略模式和世界模型模式为什么能共享一套权重)

7. 无标签视频预训练:可扩展性从哪来

7.1 为什么光流能吃无标签视频

FlowWAM 第三个技术优势是可扩展性,它来自一个朴素事实:光流可以直接从原始第一视角视频里抽取,不需要任何动作标签。因为视频损失只依赖 RGB 帧和抽取的光流,双流视频生成器可以在任意视频语料上训练,包括没有机器人动作标签的人类操作视频。论文用 RAFT 抽光流:EgoDex 直接在相邻 RGB 帧间算,RoboTwin 则先在 SAPIEN 里用机器人 URDF 回放录制的关节动作、渲染出「只有本体运动」的帧再算 RAFT,这样能去掉背景、光照、物体运动的干扰,给机器人诱导的运动更干净的监督。

这个流匹配速度场目标对 RGB 和光流两条流完全一致,也不依赖任何动作标签,这就是为什么同一个目标既能训机器人数据也能训纯视频。这里的关键是,噪声隐变量由  线性插值构造,模型学的是从噪声指向干净数据的速度场,整个过程只用到 RGB 帧和抽取的光流,不碰任何动作标注。

直觉理解(无标签预训练的价值):机器人示教数据贵,一条要人遥操作采集;但 YouTube 式的人类第一视角操作视频几乎无限。光流的妙处在于,它把「人的手怎么动」和「机械臂怎么动」翻译成同一种像素位移语言。这意味着模型可以先看海量人类视频学会「抓、放、拧」这些运动长什么样,再用少量机器人数据学会「怎么把这份运动计划映射成关节指令」。

7.2 预训练放大器效应

进一步看实验证据。在 EgoDex 上做无标签预训练后,FlowWAM 在 Random 设定下的增益明显大于 Clean 设定,这符合「随机化场景最受益于视频生成器积累的视觉鲁棒性」的预期。同一张表还显示,即便不做人类视频预训练,FlowWAM 已经超过 VLA 基线,说明光流流本身贡献了增益的主体,预训练只是放大器。这里要厘清的是,收益来自「在动作解码前把预测运动暴露成稠密光流计划」这个结构,而不仅仅靠直接的低层动作预测。

8. 总结

FlowWAM 的核心贡献不是再造一个 WAM 名词,而是把光流确立为视频原生、可预测、可条件化、还能从无标签视频学习的统一动作表示。RoboTwin Clean 92.94%、WorldArena EWMScore 63.71、真机平均 75.7% 这三个数据点合起来,足以让「光流即动作」进入下一代世界-动作模型的候选表示清单。

但腕部光流缺失和 sim 回放光流的分布差还悬着——前者决定双臂精细操作能压到多低的失败率,后者决定这套管线能不能扩到富接触的真实场景。等 FlowWAM 的预训练数据规模真正推到互联网级、并把 RAFT 光流管线换成更贴近真实的方案之后,再回过头看这套范式能不能落到工业操作场景。

相关推荐