VLA模型通常可以拆成两个逻辑模块视觉语言模型和动作生成模型前者用于生成视觉语言语义特征后者用于生成连续动作序列。在OpenPi中VLM并不是先完整运行一遍再把最终特征交给动作模型。PaliGemma和Action Expert会在Transformer的每一层共同计算注意力。换句话说OpenPi虽然也有两个模块但在计算图上已经被编织成了一个整体。GR00T则保留了明显的模块边界Qwen3-VL先生成视觉语言Token随后DiT把这些Token当成条件通过Cross-Attention预测动作。两者最核心的差异可以概括为对比项OpenPi π0/π0.5GR00T N1.7VLMSigLIP PaliGemmaCosmos-Reason2-2B即Qwen3-VL结构动作模型Gemma Action Expert独立的Diffusion Transformer连接位置每一层Transformer内部VLM最终特征与DiT之间连接机制联合注意力、不同专家参数Cross-AttentionVLM信息形态逐层更新的Prefix K/V固定的Backbone Features状态输入π0放在Action Expertπ0.5放入离散Prefix单独的State Encoder默认微调方式支持全量或LoRA默认冻结VLM训练动作头模块替换难度较高相对较低OpenPi让VLM和Action Expert逐层交互OpenPi中π0的主要实现位于src/openpi/models/pi0.py src/openpi/models/gemma.py模型初始化时并不是创建一个PaliGemma再在其后增加普通MLP动作头而是同时创建两个Gemma配置paligemma_config get_config(gemma_2b) action_expert_config get_config(gemma_300m) llm GemmaModule( configs[ paligemma_config, action_expert_config, ] )第一个专家负责图像和语言第二个专家负责状态、噪声动作以及扩散时间。两边使用不同的模型参数和隐藏层宽度。默认配置中PaliGemma的宽度为2048Action Expert的宽度为1024但两者具有相同的层数、注意力头数量和Head DimensionPaliGemma18层width2048 Action Expert18层width1024 共同配置num_heads8head_dim256num_kv_heads1隐藏层宽度虽然不同但两边都可以投影到相同的注意力空间。因此OpenPi不需要先把VLM最终特征压缩成固定长度向量也没有一个独立的VLM到动作模型投影层。输入首先被拆成Prefix和Suffix。Prefix是视觉语言部分image_tokens siglip(images) language_tokens gemma_embed(prompt) prefix_tokens concat( image_tokens, language_tokens, )Suffix是动作部分。以π0为例state_token state_proj(robot_state) action_token action_in_proj(noisy_action) time_token time_embedding(t) suffix_tokens concat( state_token, mix(action_token, time_token), )关键代码位于Gemma Attention中。两个专家先分别使用自己的参数计算Q、K、Vvlm_qkv vlm_attention_proj(prefix_tokens) action_qkv action_attention_proj(suffix_tokens)然后沿Token序列维度拼接q concat(vlm_q, action_q) k concat(vlm_k, action_k) v concat(vlm_v, action_v) attention_output attention(q, k, v, mask)注意力计算完成后再把结果按照Prefix和Suffix的长度切开分别送入两个专家自己的输出投影和MLPvlm_output vlm_out_proj( attention_output[:, :prefix_len] ) action_output action_out_proj( attention_output[:, prefix_len:] )需要注意的是按Prefix和Suffix切分得到的vlm_output与action_output并不是模型的最终结果而是当前Transformer层中两条分支各自的注意力更新量。 两路结果会先分别经过各自的输出投影再与进入当前层之前的隐藏状态做残差连接。随后VLM Token和Action Token分别进入各自参数独立的MLP得到下一层的输入。经过最后一层后最终的Prefix Output不直接参与动作预测。模型只截取Suffix Output中对应Action Horizon的部分并通过action_out_proj预测Flow Matching的速度场。Prefix分支作用在于训练时Action Query在每一层读取VLM的Key/Value推理时这些逐层生成的Prefix Key/Value会被保存在KV Cache中供每个去噪步骤中的Action Expert重复读取。训练时Prefix和Suffix会放在一次完整的前向计算中(prefix_out, suffix_out) model( [prefix_tokens, suffix_tokens], maskasymmetric_attention_mask, ) pred_velocity action_out_proj( suffix_out[:, -action_horizon:] )Flow Matching只监督动作部分x_t t * noise (1 - t) * action target_velocity noise - action loss mse( pred_velocity, target_velocity, )代码中没有为VLM单独增加文本生成损失。VLM是否更新取决于训练配置既可以全量训练也可以通过LoRA只更新部分注意力层和FFN。即使损失只计算在动作输出上只要VLM参数没有被冻结动作损失仍然可以通过联合注意力传回PaliGemma。推理时OpenPi会先运行一次图像和语言Prefix并保存其KV Cacheprefix_output, kv_cache vlm(prefix_tokens)之后每一步去噪只重新计算Action Expertfor t in denoise_steps: suffix_tokens embed_action(x_t, state, t) velocity action_expert( suffix_tokens, past_key_valueskv_cache, ) x_t x_t dt * velocity说明OpenPi的VLM和动作专家虽然连接很深但推理时不需要在每个Flow Matching Step中重复运行视觉编码器和完整PaliGemma。π0.5在连接方式上又做了两个调整第一π0中机器人状态是Action Expert的连续State Tokenπ0.5会把状态离散化并放入语言侧的Prefix。此时动作专家读取到的Prefix已经同时包含图像、指令和机器人状态。第二π0.5不再把时间编码直接与动作特征拼接而是使用时间编码调制Action Expert中的AdaRMSNormπ0 Action Embedding Time Embedding → MLP π0.5 Action Embedding → Action Expert Time Embedding → AdaRMSNorm具体来看在π0.5中动作嵌入和时间嵌入不会直接拼接或相加。动作嵌入作为Action Expert的Token输入时间嵌入则作为条件调制Action Expert每个Transformer Block中的归一化和残差连接。GR00T将VLM特征作为DiT的条件输入源码实现如下gr00t/model/modules/qwen3_backbone.py gr00t/model/gr00t_n1d7/gr00t_n1d7.py gr00t/model/modules/dit.pyGR00T的连接方式更加接近传统的Encoder—Decoder。首先图像和语言指令进入Cosmos-Reason2-2B。该模型采用Qwen3-VL架构outputs qwen3_vl( input_idsinput_ids, pixel_valuespixel_values, image_grid_thwimage_grid_thw, output_hidden_statesTrue, ) vl_features outputs.hidden_states[-1]返回结果仍然是一组Token既包含文本Token也包含图像Token。代码还通过image_token_id生成image_mask用于区分两类信息image_mask input_ids image_token_id这些特征会先经过LayerNorm以及可选的视觉语言Self-Attentionvl_features vlln(vl_features) vl_features vl_self_attention(vl_features)另一侧机器人状态和噪声动作不进入VLM而是由动作头独立编码state_features state_encoder( state, embodiment_id, ) action_features action_encoder( noisy_action, timestep, embodiment_id, ) sa_features concat( state_features, action_features, )sa_features是DiT的查询序列vl_features是Cross-Attention的条件序列model_output dit( hidden_statessa_features, encoder_hidden_statesvl_features, timesteptimestep, )两模块不要求隐藏层宽度一致。Cross-Attention内部会分别对DiT Query和VLM Key/Value做线性投影因此更换VLM时只要重新适配cross_attention_dim不需要像OpenPi那样保证两个专家具有相同的层数、Head数量和Head Dimension。GR00T N1.7默认使用AlternateVLDiT其Transformer Block交替执行两种操作Cross-Attention动作Token读取VLM特征 Self-Attention 状态和动作Token彼此交互在Cross-Attention层中又会根据image_mask交替读取文本和图像第一个Cross-Attention Block主要读取非图像Token 下一个Cross-Attention Block主要读取图像Token 之后继续交替对应的代码逻辑可以简化为if current_block_attends_text: condition_mask non_image_mask else: condition_mask image_mask action_features cross_attention( queryaction_features, keyvl_features, valuevl_features, maskcondition_mask, )避免了动作Token在每一层都无差别地读取全部视觉语言序列让语言约束和图像细节以不同节奏进入动作生成过程。DiT最终输出会经过具身相关的Action Decoderprediction action_decoder( model_output, embodiment_id, )embodiment_id会同时进入State Encoder、Action Encoder和Action Decoder把不同机器人之间的差异放在动作模型侧处理而不是要求VLM理解每种机器人的具体关节定义。训练阶段同样使用Flow Matchingnoisy_action (1 - t) * noise t * action target_velocity action - noise prediction action_head( vl_features, state, noisy_action, t, ) loss mse(prediction, target_velocity)GR00T默认配置为tune_llm False tune_visual False tune_projector True tune_diffusion_model True tune_vlln True默认冻结Qwen3-VL的语言和视觉部分只训练状态编码器、动作编码器、DiT、输出解码器以及VLM特征适配层。需要时也可以解冻顶部若干LLM Layer。推理时Qwen3-VL只运行一次vl_features backbone(images, instruction) state_features state_encoder(state)随后多个去噪步骤反复调用DiTactions random_noise() for t in range(num_inference_timesteps): action_features action_encoder(actions, t) velocity dit( hidden_statesconcat( state_features, action_features, ), encoder_hidden_statesvl_features, ) actions actions dt * velocity两种连接方式反映了不同的设计取向OpenPi的关键并不是PaliGemma后面接了一个动作头而是让VLM Expert和Action Expert在每一层共同参与注意力计算。动作专家读取的不是VLM最后一层压缩后的结果而是逐层演化的视觉语言表示。可以将其概括为如下OpenPi VLM Layer 1 ←联合注意力→ Action Expert Layer 1 VLM Layer 2 ←联合注意力→ Action Expert Layer 2 VLM Layer 3 ←联合注意力→ Action Expert Layer 3 ...GR00T则先完成VLM编码再由动作模型读取其最终TokenGR00T 图像、语言 - Qwen3-VL Backbone - 固定的VL Token序列 - Cross-Attention - - DiT Action Head - 动作序列OpenPi的优势是语义理解与动作生成结合得更紧动作专家在浅层就可以读取视觉和语言信息并在后续每一层继续加工适合联合预训练大规模VLA。代价是结构约束较多。两个专家需要保持相同层数并且注意力头配置必须兼容。替换VLM、改变层数或接入完全不同的动作网络时需要修改联合Transformer的内部实现。GR00T的优势是模块边界清晰VLM只需要输出Token序列DiT通过Cross-Attention读取条件。更换视觉语言骨干、冻结VLM、独立导出动作头以及进行多具身适配都更加直接。代价是VLM和动作模型主要在Backbone出处连接。与OpenPi的逐层交互相比动作模型无法参与VLM内部表征形成过程只能对已经生成的视觉语言特征进行二次读取。对于希望保持预训练VLM稳定、方便替换模型或适配多种机器人的项目GR00T的Backbone-DiT结构更容易扩展对于希望通过大规模联合训练让语义特征从底层开始服务于动作生成的模型OpenPi的双专家联合注意力结构更加紧密。
OpenPi、GR00T的视觉语言模型与动作模型连接方式差异分析总结
VLA模型通常可以拆成两个逻辑模块视觉语言模型和动作生成模型前者用于生成视觉语言语义特征后者用于生成连续动作序列。在OpenPi中VLM并不是先完整运行一遍再把最终特征交给动作模型。PaliGemma和Action Expert会在Transformer的每一层共同计算注意力。换句话说OpenPi虽然也有两个模块但在计算图上已经被编织成了一个整体。GR00T则保留了明显的模块边界Qwen3-VL先生成视觉语言Token随后DiT把这些Token当成条件通过Cross-Attention预测动作。两者最核心的差异可以概括为对比项OpenPi π0/π0.5GR00T N1.7VLMSigLIP PaliGemmaCosmos-Reason2-2B即Qwen3-VL结构动作模型Gemma Action Expert独立的Diffusion Transformer连接位置每一层Transformer内部VLM最终特征与DiT之间连接机制联合注意力、不同专家参数Cross-AttentionVLM信息形态逐层更新的Prefix K/V固定的Backbone Features状态输入π0放在Action Expertπ0.5放入离散Prefix单独的State Encoder默认微调方式支持全量或LoRA默认冻结VLM训练动作头模块替换难度较高相对较低OpenPi让VLM和Action Expert逐层交互OpenPi中π0的主要实现位于src/openpi/models/pi0.py src/openpi/models/gemma.py模型初始化时并不是创建一个PaliGemma再在其后增加普通MLP动作头而是同时创建两个Gemma配置paligemma_config get_config(gemma_2b) action_expert_config get_config(gemma_300m) llm GemmaModule( configs[ paligemma_config, action_expert_config, ] )第一个专家负责图像和语言第二个专家负责状态、噪声动作以及扩散时间。两边使用不同的模型参数和隐藏层宽度。默认配置中PaliGemma的宽度为2048Action Expert的宽度为1024但两者具有相同的层数、注意力头数量和Head DimensionPaliGemma18层width2048 Action Expert18层width1024 共同配置num_heads8head_dim256num_kv_heads1隐藏层宽度虽然不同但两边都可以投影到相同的注意力空间。因此OpenPi不需要先把VLM最终特征压缩成固定长度向量也没有一个独立的VLM到动作模型投影层。输入首先被拆成Prefix和Suffix。Prefix是视觉语言部分image_tokens siglip(images) language_tokens gemma_embed(prompt) prefix_tokens concat( image_tokens, language_tokens, )Suffix是动作部分。以π0为例state_token state_proj(robot_state) action_token action_in_proj(noisy_action) time_token time_embedding(t) suffix_tokens concat( state_token, mix(action_token, time_token), )关键代码位于Gemma Attention中。两个专家先分别使用自己的参数计算Q、K、Vvlm_qkv vlm_attention_proj(prefix_tokens) action_qkv action_attention_proj(suffix_tokens)然后沿Token序列维度拼接q concat(vlm_q, action_q) k concat(vlm_k, action_k) v concat(vlm_v, action_v) attention_output attention(q, k, v, mask)注意力计算完成后再把结果按照Prefix和Suffix的长度切开分别送入两个专家自己的输出投影和MLPvlm_output vlm_out_proj( attention_output[:, :prefix_len] ) action_output action_out_proj( attention_output[:, prefix_len:] )需要注意的是按Prefix和Suffix切分得到的vlm_output与action_output并不是模型的最终结果而是当前Transformer层中两条分支各自的注意力更新量。 两路结果会先分别经过各自的输出投影再与进入当前层之前的隐藏状态做残差连接。随后VLM Token和Action Token分别进入各自参数独立的MLP得到下一层的输入。经过最后一层后最终的Prefix Output不直接参与动作预测。模型只截取Suffix Output中对应Action Horizon的部分并通过action_out_proj预测Flow Matching的速度场。Prefix分支作用在于训练时Action Query在每一层读取VLM的Key/Value推理时这些逐层生成的Prefix Key/Value会被保存在KV Cache中供每个去噪步骤中的Action Expert重复读取。训练时Prefix和Suffix会放在一次完整的前向计算中(prefix_out, suffix_out) model( [prefix_tokens, suffix_tokens], maskasymmetric_attention_mask, ) pred_velocity action_out_proj( suffix_out[:, -action_horizon:] )Flow Matching只监督动作部分x_t t * noise (1 - t) * action target_velocity noise - action loss mse( pred_velocity, target_velocity, )代码中没有为VLM单独增加文本生成损失。VLM是否更新取决于训练配置既可以全量训练也可以通过LoRA只更新部分注意力层和FFN。即使损失只计算在动作输出上只要VLM参数没有被冻结动作损失仍然可以通过联合注意力传回PaliGemma。推理时OpenPi会先运行一次图像和语言Prefix并保存其KV Cacheprefix_output, kv_cache vlm(prefix_tokens)之后每一步去噪只重新计算Action Expertfor t in denoise_steps: suffix_tokens embed_action(x_t, state, t) velocity action_expert( suffix_tokens, past_key_valueskv_cache, ) x_t x_t dt * velocity说明OpenPi的VLM和动作专家虽然连接很深但推理时不需要在每个Flow Matching Step中重复运行视觉编码器和完整PaliGemma。π0.5在连接方式上又做了两个调整第一π0中机器人状态是Action Expert的连续State Tokenπ0.5会把状态离散化并放入语言侧的Prefix。此时动作专家读取到的Prefix已经同时包含图像、指令和机器人状态。第二π0.5不再把时间编码直接与动作特征拼接而是使用时间编码调制Action Expert中的AdaRMSNormπ0 Action Embedding Time Embedding → MLP π0.5 Action Embedding → Action Expert Time Embedding → AdaRMSNorm具体来看在π0.5中动作嵌入和时间嵌入不会直接拼接或相加。动作嵌入作为Action Expert的Token输入时间嵌入则作为条件调制Action Expert每个Transformer Block中的归一化和残差连接。GR00T将VLM特征作为DiT的条件输入源码实现如下gr00t/model/modules/qwen3_backbone.py gr00t/model/gr00t_n1d7/gr00t_n1d7.py gr00t/model/modules/dit.pyGR00T的连接方式更加接近传统的Encoder—Decoder。首先图像和语言指令进入Cosmos-Reason2-2B。该模型采用Qwen3-VL架构outputs qwen3_vl( input_idsinput_ids, pixel_valuespixel_values, image_grid_thwimage_grid_thw, output_hidden_statesTrue, ) vl_features outputs.hidden_states[-1]返回结果仍然是一组Token既包含文本Token也包含图像Token。代码还通过image_token_id生成image_mask用于区分两类信息image_mask input_ids image_token_id这些特征会先经过LayerNorm以及可选的视觉语言Self-Attentionvl_features vlln(vl_features) vl_features vl_self_attention(vl_features)另一侧机器人状态和噪声动作不进入VLM而是由动作头独立编码state_features state_encoder( state, embodiment_id, ) action_features action_encoder( noisy_action, timestep, embodiment_id, ) sa_features concat( state_features, action_features, )sa_features是DiT的查询序列vl_features是Cross-Attention的条件序列model_output dit( hidden_statessa_features, encoder_hidden_statesvl_features, timesteptimestep, )两模块不要求隐藏层宽度一致。Cross-Attention内部会分别对DiT Query和VLM Key/Value做线性投影因此更换VLM时只要重新适配cross_attention_dim不需要像OpenPi那样保证两个专家具有相同的层数、Head数量和Head Dimension。GR00T N1.7默认使用AlternateVLDiT其Transformer Block交替执行两种操作Cross-Attention动作Token读取VLM特征 Self-Attention 状态和动作Token彼此交互在Cross-Attention层中又会根据image_mask交替读取文本和图像第一个Cross-Attention Block主要读取非图像Token 下一个Cross-Attention Block主要读取图像Token 之后继续交替对应的代码逻辑可以简化为if current_block_attends_text: condition_mask non_image_mask else: condition_mask image_mask action_features cross_attention( queryaction_features, keyvl_features, valuevl_features, maskcondition_mask, )避免了动作Token在每一层都无差别地读取全部视觉语言序列让语言约束和图像细节以不同节奏进入动作生成过程。DiT最终输出会经过具身相关的Action Decoderprediction action_decoder( model_output, embodiment_id, )embodiment_id会同时进入State Encoder、Action Encoder和Action Decoder把不同机器人之间的差异放在动作模型侧处理而不是要求VLM理解每种机器人的具体关节定义。训练阶段同样使用Flow Matchingnoisy_action (1 - t) * noise t * action target_velocity action - noise prediction action_head( vl_features, state, noisy_action, t, ) loss mse(prediction, target_velocity)GR00T默认配置为tune_llm False tune_visual False tune_projector True tune_diffusion_model True tune_vlln True默认冻结Qwen3-VL的语言和视觉部分只训练状态编码器、动作编码器、DiT、输出解码器以及VLM特征适配层。需要时也可以解冻顶部若干LLM Layer。推理时Qwen3-VL只运行一次vl_features backbone(images, instruction) state_features state_encoder(state)随后多个去噪步骤反复调用DiTactions random_noise() for t in range(num_inference_timesteps): action_features action_encoder(actions, t) velocity dit( hidden_statesconcat( state_features, action_features, ), encoder_hidden_statesvl_features, ) actions actions dt * velocity两种连接方式反映了不同的设计取向OpenPi的关键并不是PaliGemma后面接了一个动作头而是让VLM Expert和Action Expert在每一层共同参与注意力计算。动作专家读取的不是VLM最后一层压缩后的结果而是逐层演化的视觉语言表示。可以将其概括为如下OpenPi VLM Layer 1 ←联合注意力→ Action Expert Layer 1 VLM Layer 2 ←联合注意力→ Action Expert Layer 2 VLM Layer 3 ←联合注意力→ Action Expert Layer 3 ...GR00T则先完成VLM编码再由动作模型读取其最终TokenGR00T 图像、语言 - Qwen3-VL Backbone - 固定的VL Token序列 - Cross-Attention - - DiT Action Head - 动作序列OpenPi的优势是语义理解与动作生成结合得更紧动作专家在浅层就可以读取视觉和语言信息并在后续每一层继续加工适合联合预训练大规模VLA。代价是结构约束较多。两个专家需要保持相同层数并且注意力头配置必须兼容。替换VLM、改变层数或接入完全不同的动作网络时需要修改联合Transformer的内部实现。GR00T的优势是模块边界清晰VLM只需要输出Token序列DiT通过Cross-Attention读取条件。更换视觉语言骨干、冻结VLM、独立导出动作头以及进行多具身适配都更加直接。代价是VLM和动作模型主要在Backbone出处连接。与OpenPi的逐层交互相比动作模型无法参与VLM内部表征形成过程只能对已经生成的视觉语言特征进行二次读取。对于希望保持预训练VLM稳定、方便替换模型或适配多种机器人的项目GR00T的Backbone-DiT结构更容易扩展对于希望通过大规模联合训练让语义特征从底层开始服务于动作生成的模型OpenPi的双专家联合注意力结构更加紧密。