要点#
GO-1整体采用“理解-规划-执行”的分层结构。首先通过InternLM2构建的VLM将视觉和语言信息编码为多层KV cache作为环境语义表示;随后Latent Planner通过少量latent query token对这些KV进行cross-attention,提取与动作相关的规划信息并生成新的planner KV;最后Action Expert在planner KV条件下,通过Transformer与diffusion机制逐步生成连续动作序列。值得注意的是,论文中提出的LAM模块在当前代码中并未显式实现,其作用被隐式融合进VLM表示与latent planner的结构瓶颈中,从而实现跨模态动作语义的建模。
论文背景#
这个是:平台 + 数据 + 模型的系统工作。论文核心做了两件事:
构建 AgiBot World Colosseo:一个大规模真实机器人操作平台 (大规模构建数据)
提出 GO-1:一个分层的通用机器人策略模型,用于把视觉语言理解转成连续动作控制 (怎么统一异构数据)
论文想解决的问题是:
机器人通用策略之所以难,不只是模型问题,更是因为缺少大规模、高质量、标准化、真实世界的操作数据。
数据构成#
单纯的人视频数据,没有机器人action
各种不同型号机器人动作数据,有机器人action数据
文本,没有机器人数据
go1要做的本质就是:不同来源、不同形式、不同标注方式的“视觉序列 +(可选)动作信息”,最后被统一成“帧序列 + 对齐信号”(action token)的形式。
怎么解决异构数据#
提出Latent Action Model,一个高维度的特征空间统一表达。(这里是被隐式的收藏入Latent Planner结构中)
论文里面的故事#
LAM(统一动作表示)I_t, I_{t+H} → z
把所有序类的变化变为:“变化” → latent action
然后通过一个Latent Planner 做cross attention 得到规划后的4个action token(这个就非常像是多模态里面的桥接器Q-former)本质上就是特征压缩
代码真实操作#
文本与图像统一到序列里面
对于action state –》token
state是这样变化的#
self.state_adaptor = nn.Linear(self.state_dim, self.action_hidden_size)
到
state: [B, T, state_dim]
→ Linear
→ [B, T, hidden_size]
Action是这样变化#
action_trajs = self.action_adaptor(noisy_action)
到
action_trajs = [B, T, hidden_size]
时间信息#
[B, 1, hidden_size]
控制频率#
[B, 1, hidden_size]
state+action+timestep+freq#
分两步骤
1 state_action_trajs
[B, 1 + T + T, hidden_size]
2 state_action_trajs_w_tfps
[B, 1 + (1 + T + T), hidden_size]
最终维度
[ timestep ]
[ freq ]
[ state_1, state_2, …, state_T ]
[ action_1, action_2, …, action_T ]
在当前 GO-1 的代码实现中,VLM 只接收图像和文本输入,用于构建统一的语义 KV 表示,并不接收任何动作信息;动作(以及状态、时间步等控制变量)是在后续 Action Expert 阶段通过 adaptor 映射为 token 序列输入,并在 attention 中与 VLM 的 KV memory 交互,从而实现感知与控制的解耦。
本质上和pi0相似都是:VLM + 条件 Transformer + Diffusion 动作生成
但但 GO-1 多了一层“Latent Planner(结构性规划瓶颈)”,而 pi0 是直接从 VLM 到 action
go1#
VLM#
这里有自己的一个模型。go1/internvl/model/go1/modeling_internlm2_go1.py
里面还是经典的VLM模型
实例化#
输入#
图像
vit_embeds = self.extract_feature(pixel_values)
文本
input_embeds = self.language_model.get_input_embeddings()(input_ids).clone()
一起融合
vlm_outputs = self.language_model(…)
vlm_key_values = vlm_outputs.past_key_values
输出#
这里的输出还是KV对
抽取每一层的kv 这里可能用到了cv里面对多尺度的理解
大概长这个样子
vlm_key_values = [
(K1, V1),
(K2, V2),
...
(KL, VL)
]kv就是包含了上下文语义信息的(通过self-attention进行关联)
Latent Planner(隐式LAM latent action model操作)#
原理#
Latent Planner 的位置在 VLM 和 Action Expert 中间。它不直接输出动作,而是把 VLM 的语义记忆进一步加工成更适合动作生成的 planner KV。(个人觉得这里非常像Q-former,就是一个桥接器,做特征压缩)
输入#
显式输入只有两个:
vlm_key_values_downsampleattention_mask
里面的kv就是VLM里面的,经过投影层变换
for vlm_key_value, k_proj, v_proj in zip(vlm_key_values, self.k_proj_layers, self.v_proj_layers):
vlm_key_values_downsample.append((k_proj(vlm_key_value[0]), v_proj(vlm_key_value[1])))mask是做无关特征mask包括padding
核心#
核心就算构造4个造 4 个 latent query token
在 LatentPlannerModel.forward() 里:
latent_state = torch.ones((B, 1, 1), dtype=torch.bfloat16, device=attention_mask.device) * -1然后在 latent_action_process() 里:
H = self.latent_token_nums
action = torch.cat([state] * H, dim=1) # [B, 4, 1]
也就是先造出 4 个值为 -1 的占位 token。
然后将把这 4 个 token 映射到hidden空间
state_action_trajs = self.latent_state_action_adaptor(action)变成query,维度为[B, 4, hidden_size]
然后做crossattention
outputs = self.latent_action_expert(state_action_trajs, attention_mask, vlm_key_values_downsample)所以 planner 不是直接输出动作,而是先从 VLM 语义空间里抽取“动作相关信息”。
然后这个应该就是变成kv了给action expert使用
输出#
接上上面的就是输出
return (
latent_vlm_key_values_downsample,
outputs_latent,
)Latent Planner 不是直接生成动作,而是把 VLM 的通用语义记忆,压缩/提炼成更偏“动作规划”的 KV memory。
这个就是论文里面所谓的核心点,动作规划(讲故事)
Action expert#
输入#
state token + action token + 时间步 token + 控制频率 token 然后再结合上游的KV memory
然后代码里面有配置是否开启planner,也就是说维度对齐了,这个开不开都行,这也就是go air版本
state / action / timestep / freq 组成序列的
state_action_trajs = torch.cat([state_trajs, action_trajs], dim=1)
state_action_trajs_w_tfps = torch.cat(
[timestep_tokens, freq_tokens, state_action_trajs], dim=1
)
[ timestep ][ freq ][ state tokens ][ action tokens ]
融合KV,这里很粗暴,直接拼接在里面
key_states = torch.cat([vlm_key_values[0], key_states], dim=2)
value_states = torch.cat([vlm_key_values[1], value_states], dim=2)
把上游语义 memory 直接并入自己的 attention memory 里。
输出#
ActionExpertModel.forward() 最后输出的是:
BaseModelOutputWithPast(
last_hidden_state=hidden_states,
past_key_values=next_cache,
…
)
在主干里只取它的 hidden states:
state_action_output_tokens = outputs[0]
action_output_tokens = state_action_output_tokens[:, -self.action_chunk_size :, …]
action_logits = self.final_layer(action_output_tokens)
所以最终:
Action Expert 先输出 hidden sequence
再取最后那段 action token 对应的 hidden
再过
final_layer得到
action_logits。
概括#
VLM 负责把图像和文本编码成统一的多层 KV memory;Latent Planner 用 4 个 latent query token 从 VLM 的 KV 中提取动作规划信息,并生成新的 planner KV;Action Expert 再把 timestep、freq、state、noisy_action 组成 token 序列,在 planner KV 条件下通过 transformer + diffusion 生成连续动作。当前主干代码里,最终显式监督只落在动作端,即用 MSE 将 action_logits 回归到 action_gts,而 VLM 和 Latent Planner 没有在这条主路径中显式单独加 loss。