一句话理解 MemER
MemER 的核心思想是:

不要把机器人从开头到现在看到的所有画面都塞给模型,而是让一个高层视觉语言模型不断判断“现在该做哪个子任务”和“最近看到的哪些帧以后可能还要用”,再把这些被挑出来的关键帧作为长期视觉记忆喂回模型。

它解决的是长时程机器人任务里的“我刚才看见过,但现在画面里没有”的问题。例如:

机器人之前看过某个物体在右边箱子里,现在要找它时不应该重新翻左边箱子。
机器人已经舀了两勺花生,当前画面只看到勺子,不一定能知道已经舀了几勺。
机器人把架子上的物体拿走、擦完架子以后,需要记住物体原本放在哪一层。
普通策略如果只看当前帧,会缺记忆;如果直接看很长视频,会慢、贵、冗余,而且容易把无关历史当捷径。MemER 的做法是用“最近窗口 + 少量关键帧”代替完整历史。

  1. 方法整体架构
    MemER 是一个分层策略:

高层策略 pi_h:
输入: 总任务指令 + 长期关键帧记忆 K_t + 最近 N 帧 R_t
输出: 当前应执行的语言子任务 l’_t + 最近 N 帧中应保存的候选关键帧 J_t

低层策略 pi_l:
输入: 当前图像 I_t + 机器人状态 q_t + 高层给出的子任务 l’_t
输出: 未来一小段机器人动作 A_t
论文把整体策略写成:

pi(A_t | o_{0:t})
= pi_l(A_t | I_t, q_t, l’_t)
* pi_h(l’t, J_t | I{t-N+1:t}, K_t)
其中:

A_t 是未来动作 chunk。
I_t 是当前多相机图像。
q_t 是关节角、夹爪等 proprioception。
l_t 是用户给的总任务指令。
l’t 是当前要交给低层策略执行的子任务。
R_t = I
{t-N+1:t} 是最近 N 帧。
K_t 是过去被选中的长期关键帧。
J_t 是高层模型从最近窗口里新提名的候选关键帧。
当前公开代码主要覆盖高层策略:数据导出、Qwen3-VL 微调格式、结构化推理、记忆更新、离线 rollout 评估和在线部署封装。低层 pi0.5 的训练代码不在这个仓库里。

  1. 为什么需要“高层记忆策略”
    直接把历史全喂给策略有三个问题:

太贵:视频帧是高维输入,长历史会让 VLM/VLA 的 token 和显存成本快速上涨。
太慢:机器人闭环控制需要低延迟。论文里 Long History baseline 用 32 帧大约 16 秒历史,推理延迟已经接近闭环可忍受上限。
太脆:长历史里包含大量无关动作,模型可能学到示范轨迹中的顺序捷径。一旦部署时低层策略卡顿、重试、失败恢复,历史分布就变了,模型更容易误判当前进度。
MemER 的设计取舍是:

最近窗口保留局部动作上下文。
关键帧记忆保留长期必要信息。
关键帧由高层模型自己根据任务语义提名,不额外训练一个图像检索模型。
最后用一个很简单的时间聚类器去掉重复提名,让记忆稳定。
4. 数据构建:从一段视频到很多道训练题
MemER 的高层数据构建,最简单的理解是:

把一整段机器人演示视频,切成很多道给 VLM 做的训练题。每道题都问模型两件事:现在应该执行哪个子任务?最近看到的几帧里,有没有以后要记住的关键画面?

完整流程是:

原始机器人演示轨迹
-> 每帧都有图像、总任务、当前子任务标签、机器人状态、动作
-> 根据子任务标签和规则,找出整条轨迹里哪些帧值得记住
-> 对每个时间步 t 单独构造一条高层训练样本
-> 导出成 Qwen SFT 的 train.json + media/*.jpg
先把几个名字分清楚,后面就不会乱:

名字 它是什么 在训练样本里扮演什么角色
GT keyframes 整条演示轨迹里,规则认为应该记住的绝对帧号 离线生成监督信号
memory_indices 当前时间步以前、已经离开最近窗口的 GT keyframes 作为输入图片给模型看
context_indices 当前时间步附近的最近 N 个下采样帧 作为最近视频输入给模型看
keyframe_positions context_indices 里哪些位置应该被提名为新 keyframe 作为模型输出答案
一句话说:

memory_indices 是“过去已经该记住的帧”,放进输入;
keyframe_positions 是“最近刚出现、以后应该记住的帧”,放进输出。
这个区别是数据构建里最关键的点。MemER 不是让模型直接输出一个历史帧号,而是训练模型在“最近窗口”里指出哪些帧值得保存;等这些帧未来离开最近窗口后,它们才会成为长期 memory。

4.1 原始演示数据
论文里的每条机器人轨迹可以理解成一串时间步:

(I_t, q_t, l_t, l’_t, a_t)
含义是:

I_t:多相机图像,论文实验使用 wrist camera 和 third-person/base camera。
q_t:机器人自身状态。
l_t:总任务指令,例如“把架子上的物品拿下来,擦架子,再放回原位”。
l’_t:当前子任务标签,例如“dust bottom shelf”。
a_t:机器人动作。
子任务标签来自数据采集过程:操作者执行预先规定的子任务列表,完成一个子任务后按键切换到下一个子任务。因此每个时间步天然有当前子任务标签。

4.2 高层策略训练样本
高层策略不直接预测机器人动作,只预测:

{
“current_subtask”: “…”,
“keyframe_positions”: [1, 5]
}
其中:

current_subtask 是当前应该让低层策略执行的语言子任务。
keyframe_positions 是最近窗口中哪些帧值得保存,位置是 1-indexed,即 1 表示最近窗口第一帧,8 表示第八帧。
公开代码导出的 Qwen SFT 样本大致长这样:

{
“id”: “episode_000000_t000120”,
“image”: [
“media/episode_000000/frame_000015.jpg”,
“media/episode_000000/frame_000050.jpg”,
“media/episode_000000/frame_000085.jpg”,
“media/episode_000000/frame_000090.jpg”,
“… recent frames …”
],
“conversations”: [
{“from”: “system”, “value”: “…输出 JSON …”},
{“from”: “human”, “value”: “Task: … … …”},
{“from”: “gpt”, “value”: “{“current_subtask”: “…”, “keyframe_positions”: [7]}”}
],
“metadata”: {
“memory_indices”: [15, 50],
“context_indices”: [85, 90, 95, 100, 105, 110, 115, 120],
“answer”: {
“current_subtask”: “…”,
“keyframe_positions”: [7]
}
}
}
注意这里的图片顺序是:

先放 memory keyframes,再放 recent context frames
prompt 文本会告诉模型前几张是“从完整历史中选出的重要帧”,后几张是“最近动作视频”。

4.3 多相机图像怎样处理
公开代码支持单相机或多相机。多相机时,每个时间步的多个相机视角会被竖直拼成一张图:

stacked_frame_t =
[wrist image at t
exterior image at t]
默认 Panda/Franka 配置是:

{
“camera_keys”: [
“observation.images.wrist_left”,
“observation.images.exterior_1_left”
],
“view_width”: 320,
“view_height”: 180
}
因此最终单帧输入宽度是 320,高度是 180 * 相机数。这个拼接方式保证训练、评估、部署看到的视角顺序一致。

  1. 关键帧标签怎样得到
    MemER 并不是让人工逐帧标所有关键帧。论文和代码采用的是半自动规则:

先有每一帧的子任务标签。
把连续相同子任务看成一个 segment。
人工为每类 subtask 设定规则:取 segment 第一帧、最后一帧、两者都取,或不取。
规则固定后自动应用到所有演示轨迹。
比如:

Object Search:look inside the bin 取最后一帧,因为最后通常能看清箱子里有什么。

Logo

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

更多推荐