☰
VLA 系统学习第 12 课:为什么机器人不能只看一帧?——时间序列、Observation History 与 Action Chunk
2026/10/9 5:41:10 网站建设 项目流程

第十一课标准答案

上一课真正要建立的是一个判断习惯:

看到 Robot Learning / VLA 的测试结果,先不要急着看 Success Rate,先看它到底怎么切数据、测试了什么“没见过的东西”。


1. 为什么 Robot Dataset 随机按 Frame 划分特别容易造成 Data Leakage?

因为同一个 Episode 中相邻 Frame 高度相关。

例如:

Episode 7 Frame 100 → Train Frame 101 → Test Frame 102 → Train

假设控制频率很高,那么 Frame 100 和 Frame 101 可能只相隔几十毫秒。

它们的:

  • 图像;
  • Robot State;
  • Action;

都可能几乎一样。

于是虽然:

\[ Frame_{101} \]

名义上属于 Test,但模型训练时已经看过几乎完全相同的:

\[ Frame_{100},Frame_{102} \]

这样得到的 Test Performance 会被高估。

这就是一种典型的:

\[ \boxed{\text{Data Leakage}} \]


2. Episode-level split 解决了什么?

Episode-level split 会把一整次任务轨迹作为最小划分单位。

例如:

\[ Episode_{0\sim79}\rightarrow Train \]\[ Episode_{80\sim89}\rightarrow Validation \]\[ Episode_{90\sim99}\rightarrow Test \]

这样:

一个 Episode 中的相邻 Frame 不会一部分进入 Train、一部分进入 Test。

因此至少解决了:

\[ \boxed{\text{同一条轨迹内部的时间泄漏}} \]

问题。


3. 为什么按 Episode 划分后仍不能自动说“泛化很好”?

因为不同 Episode 虽然是不同的轨迹,但实验条件可能几乎完全一样。

例如所有 Episode 都是:

同一个物体 同一个背景 同一个任务 同一个相机 相近的初始位置

那么 Test 只是:

在相似条件下重新做一遍任务。

它并没有证明模型能够处理:

  • 新物体;
  • 新场景;
  • 新任务;
  • 新语言;
  • 新位置分布。

所以必须继续问:

\[ \boxed{\text{Generalize to what?}} \]


4. Seen Object 和 Unseen Object 有什么区别?

最直接理解:

如果某个物体在训练数据中出现过,那么测试时再次出现,可以称为:

Seen Object

如果训练阶段完全没有出现,而只在测试阶段第一次出现,则可以称为:

Unseen Object

但以后看到论文写unseen,不能自动理解成“整个任务都没见过”。

必须看作者具体定义的是:

  • unseen object;
  • unseen task;
  • unseen scene;
  • unseen instruction;
  • 还是其他东西。

5. IID Test 和 OOD Test 怎么直观理解?

IID 可以先理解成:

考试题是新题,但仍然来自和平时练习差不多的出题规律。

例如训练:

\[ x\sim[-0.1,0.1] \]

测试也从差不多的范围随机采样。

而 OOD 可以理解成:

考试时出现了训练数据没有覆盖好的情况。

例如训练:

\[ x\in[-0.1,0.1] \]

测试突然变成:

\[ x\in[0.3,0.4] \]

数据条件发生明显变化。


6. Train 中

\[ x\in[-0.1,0.1] \]

Test 中:

\[ x\in[0.35,0.45] \]

更接近什么?

更接近:

\[ \boxed{\text{OOD}} \]

因为测试位置明显超出了训练主要覆盖范围。

模型需要对训练分布之外的位置进行泛化。


7. 为什么 95% Success Rate 不一定强于 85%?

因为两篇论文可能根本不是在考同一种考试。

例如论文 A:

Seen Object Seen Scene Seen Task

成功率:

\[ 95\% \]

论文 B:

Unseen Object Unseen Scene

成功率:

\[ 85\% \]

B 的测试可能明显更困难。

所以模型比较不能只看:

\[ Success\ Rate \]

还必须一起看:

\[ \boxed{ Dataset + Split + Task + Evaluation\ Protocol + Metric } \]


8. 阅读 Robot Learning Repository,除了 Dataset Class 还必须检查什么?

至少继续找:

\[ \boxed{\text{Dataset Split Protocol}} \]

也就是:

Train / Validation / Test 到底按照什么单位划分?

重点看:

  • Frame-level?
  • Episode-level?
  • Object-level?
  • Task-level?
  • Scene-level?

以及 Test 中哪些条件属于:

  • Seen;
  • Unseen;
  • IID;
  • OOD。

这直接决定最终评估结果到底在证明什么。


VLA 系统学习第 12 课:为什么机器人不能只看一帧?——时间序列、Observation History 与 Action Chunk

到目前为止,我们为了把 Behavior Cloning 讲清楚,一直把 Policy 写成:

\[ a_t=\pi(o_t) \]

意思是:

给我当前时刻 \(t\) 的 Observation,我预测当前时刻应该执行的一个 Action。

这个形式很重要,但它把真实机器人中的两个时间问题隐藏掉了。

第一个问题:

只看当前这一帧,真的足够判断机器人现在在干什么吗?

第二个问题:

Policy 为什么一定要每次只预测一个 Action?能不能一次预测未来一串动作?

这两个问题分别会引出:

\[ \boxed{\text{Observation History}} \]

以及:

\[ \boxed{\text{Action Chunk}} \]

而 Action Chunk 正是后面 ACT 的核心入口之一。


一、先理解:一张静态图片经常缺少“运动信息”

假设你只看到一张照片:

一个机械臂夹爪在物体左侧

你知道夹爪现在在哪里。

但你未必知道:

它正在往右靠近物体?

还是:

它刚刚从右边往左退回来?

单独看:

\[ I_t \]

有时候两种情况看起来几乎一样。

但如果同时看到:

\[ I_{t-2},I_{t-1},I_t \]

你就可能发现:

前两帧夹爪更靠左 现在逐渐向右

于是知道:

它正在向右运动。

这就是时间序列信息的价值。


二、Robot State 也有同样的问题

假设当前关节位置:

\[ q_t=0.5 \]

只看:

\[ q_t \]

你只知道:

当前关节在 0.5。

但不知道:

它正在向正方向运动,还是负方向运动。

如果前一时刻:

\[ q_{t-1}=0.4 \]

那么:

\[ 0.4\rightarrow0.5 \]

说明它最近在增大。

如果:

\[ q_{t-1}=0.6 \]

那么:

\[ 0.6\rightarrow0.5 \]

说明它最近在减小。

所以:

\[ q_t \]

只是位置,

而:

\[ (q_{t-1},q_t) \]

还隐含了运动趋势。


三、这就是 Observation History

以前我们使用:

\[ o_t \]

现在把过去若干个 Observation 一起给模型:

\[ [o_{t-k+1},\ldots,o_{t-1},o_t] \]

这就是:

Observation History

也可以理解成:

当前时刻之前的一小段历史窗口。

例如 History Length:

\[ T_o=3 \]

那么一个输入可能是:

\[ [o_{t-2},o_{t-1},o_t] \]

Policy 就变成:

\[ a_t = \pi( o_{t-2}, o_{t-1}, o_t ) \]

而不是:

\[ a_t=\pi(o_t) \]


四、这时候 Tensor 为什么突然多了一个维度?

以前一个 Robot State:

\[ s_t:[D_s] \]

例如:

\[ D_s=10 \]

那么:

\[ s_t:[10] \]

如果现在给过去 4 个时刻:

\[ s_{t-3},s_{t-2},s_{t-1},s_t \]

把它们堆在一起:

\[ [4,10] \]

第一个维度:

\[ 4 \]

就是:

Time / Sequence Length

通常用:

\[ T \]

表示。

于是:

\[ [T,D_s] \]

加入 Batch:

\[ \boxed{ [B,T,D_s] } \]

例如:

\[ [32,4,10] \]

表示:

  • 32 个 Sample;
  • 每个 Sample 有连续 4 个时间点;
  • 每个时间点的 Robot State 是 10 维。

五、一定要区分 \(B\)、\(T\)、\(D\)

以后 Transformer、ACT 中会频繁看到:

\[ [B,T,D] \]

这三个维度必须形成条件反射。

\[ B \]

是:

Batch Size

表示:

一次训练多少个样本。


\[ T \]

是:

Sequence Length / Time Length

表示:

每个样本内部有多少个时间位置。


\[ D \]

是:

Feature Dimension

表示:

每一个时间位置用多少个数字表示。

所以:

\[ [32,4,10] \]

绝对不能读成:

32×4×10 的三维数组。

虽然数学上没错,但这没有理解数据语义。

你应该读成:

32 个训练样本,每个样本包含 4 个连续时间步,每个时间步有 10 维特征。

这就是后面读 Transformer Tensor 的基本方式。


六、图像有时间维以后 Shape 怎么变化?

原来一张 RGB 图:

\[ [3,H,W] \]

一个 Batch:

\[ [B,3,H,W] \]

如果每个 Sample 现在包含 \(T\) 张连续图片:

\[ \boxed{ [B,T,3,H,W] } \]

例如:

\[ [32,4,3,224,224] \]

解释:

32个Sample ↓ 每个Sample 4个时间点 ↓ 每个时间点1张RGB图片 ↓ 3个颜色通道 ↓ 224×224

这就是 Observation History 的图像版本。


七、但“历史帧”不是越多越好吗?

不是。

假设使用:

\[ T=2 \]

模型只看很短历史。

优点:

  • 数据量小;
  • 计算量低;
  • 延迟小。

但可能缺少更长时间信息。

如果:

\[ T=100 \]

模型看到的历史很多,

但:

  • Tensor 更大;
  • 显存更高;
  • 计算更慢;
  • 很久以前的信息可能没有价值。

所以:

\[ T \]

是一个设计参数。

不是:

越大越先进。


八、到这里我们解决的是“输入看多久”

也就是:

\[ \boxed{\text{Observation Horizon}} \]

可以粗略理解:

Policy 决策时考虑多少历史 Observation。

例如:

\[ T_o=4 \]

表示使用:

\[ o_{t-3:t} \]

也就是最近 4 个 Observation。

但是这里又出现第二个时间问题:

输出为什么只能是一个 Action?


九、以前我们一次只预测一个 Action

最基础 BC:

\[ o_t \rightarrow a_t \]

模型输入当前 Observation:

\[ o_t \]

输出:

\[ a_t \]

然后下一时刻:

\[ o_{t+1} \rightarrow a_{t+1} \]

所以运行:

Observe ↓ Predict one Action ↓ Execute ↓ Observe ↓ Predict one Action ↓ Execute ...

这叫:

single-step action prediction

单步动作预测。


十、单步动作预测有什么问题?

假设一个抓取动作需要连续:

向前 向前 向下 向下 关闭夹爪 向上

如果模型每一步都完全重新预测:

第1帧 → Action1 第2帧 → Action2 第3帧 → Action3 ...

那么相邻 Action 之间可能不够一致。

例如:

Action t: 向右 1 cm Action t+1: 突然向左 0.8 cm Action t+2: 又向右 1.2 cm

就可能表现为:

动作抖动、不连贯。

尤其机器人控制本身具有明显的时间连续性。

因此一个自然想法是:

能不能一次预测未来一小段连续动作?

答案是可以。


十一、于是从一个 Action 变成一串 Action

原来:

\[ a_t \]

现在预测:

\[ A_t= [ a_t, a_{t+1}, a_{t+2}, \ldots, a_{t+K-1} ] \]

这里:

\[ K \]

表示一次预测多少个 Action。

这串 Action 就可以称为:

Action Chunk

动作块。

如果:

\[ K=4 \]

那么模型一次预测:

\[ [a_t,a_{t+1},a_{t+2},a_{t+3}] \]

而不是只预测:

\[ a_t \]


十二、Action Chunk 的 Shape 怎么来?

假设单个 Action:

\[ a_t:[D_a] \]

例如:

\[ D_a=7 \]

以前一个 Sample 输出:

\[ [7] \]

现在预测未来:

\[ K=16 \]

个 Action。

那么:

\[ A_t:[16,7] \]

加入 Batch:

\[ \boxed{ A:[B,K,D_a] } \]

例如:

\[ [32,16,7] \]

应该读成:

32 个 Sample,每个 Sample 预测未来 16 个 Action,每个 Action 有 7 个维度。


十三、这里的 \(T\) 和 \(K\) 不一定一样

这是一个很重要的点。

输入可能看:

\[ T_o=2 \]

个 Observation。

输出却可能预测:

\[ K=16 \]

个 Action。

于是:

\[ Observation: [B,2,D_o] \]

而:

\[ Action: [B,16,D_a] \]

所以:

输入历史长度和输出动作长度是两个独立概念。

不要看到两个时间维就默认它们一样。


十四、为什么 Action Chunk 可能让动作更连贯?

因为模型不是把每一步当成完全孤立的问题。

它直接学习一段:

\[ [a_t,a_{t+1},\ldots,a_{t+K-1}] \]

这意味着:

模型可以同时考虑这一小段动作之间的整体关系。

例如一次抓取动作中:

靠近 ↓ 下降 ↓ 闭合 ↓ 抬起

这些动作在 Demonstration 中本来就是连续发生的。

如果模型一起预测,它有机会学习:

这一小段动作应该作为一个整体保持协调。

这就是 Action Chunking 非常重要的直觉。


十五、但预测 16 个 Action,是不是一定全部执行?

不一定。

这里必须区分两个概念:

\[ \boxed{\text{Prediction Horizon}} \]

和:

\[ \boxed{\text{Execution Horizon}} \]

假设模型一次预测:

\[ 16 \]

步:

\[ [a_t,\ldots,a_{t+15}] \]

并不意味着机器人必须一口气把 16 步全部执行完。

它可以只执行前:

\[ 4 \]

步。

然后重新获取 Observation,再预测新的 Action Chunk。

于是:

Observation at t ↓ Predict 16 actions ↓ Execute first 4 ↓ New Observation ↓ Predict another 16 ↓ Execute first 4 ↓ ...

这个思想以后在:

  • Diffusion Policy;
  • Receding Horizon Control;

中都会非常重要。


十六、为什么不直接把整个 Chunk 全执行完?

假设:

\[ K=100 \]

模型预测未来 100 个 Action。

如果全部盲目执行:

Action 1 Action 2 ... Action 100

中间环境稍微变化,

后面的动作可能已经不适合当前状态。

所以一个自然策略是:

预测得长一点,但只执行其中一部分,然后重新观察。

这样兼顾:

  • 动作连续性;
  • 闭环反馈。

十七、现在出现三个非常容易混淆的 Horizon

以后尤其学 Diffusion Policy 时必须分清。

Observation Horizon

\[ T_o \]

表示:

输入看多少历史 Observation。


Prediction Horizon

\[ T_p \]

表示:

模型一次预测多长的未来 Action Sequence。


Action / Execution Horizon

\[ T_a \]

表示:

预测出来以后,真正执行其中多少步再重新规划。

例如:

\[ T_o=2 \]\[ T_p=16 \]\[ T_a=8 \]

表示:

看过去2个Observation ↓ 预测未来16个Action ↓ 实际执行前8个 ↓ 重新观察 ↓ 再次预测

不过不同论文对命名可能略有不同。

所以以后不能只看到horizon就自己默认含义,必须检查作者定义和代码。


十八、ACT 的 Action Chunking 为什么叫“Chunking”?

现在终于可以第一次真正理解 ACT 这个名字中的一个核心部分。

ACT:

Action Chunking with Transformers

先只看:

Action Chunking

它强调的就是:

Policy 不再只预测单个 Action,而是一次预测一段未来动作。

形式上:

\[ o_t \rightarrow [a_t,a_{t+1},\ldots,a_{t+K-1}] \]

所以 ACT 并不是突然发明一个玄学概念。

它来自一个非常自然的问题:

单步预测机器人动作容易受到时序不稳定和高频决策等问题影响,那么能不能一次学习一段动作?

答案就是:

Action Chunking


十九、但是这里有一个很重要的问题:训练数据怎么得到未来 Action Chunk?

假设 Dataset 中原本存的是:

\[ a_0,a_1,a_2,\ldots,a_T \]

现在训练样本在:

\[ t=20 \]

我们希望预测:

\[ K=4 \]

个 Action。

那么 Target 就可以从 Dataset 中切:

\[ [a_{20},a_{21},a_{22},a_{23}] \]

所以原本:

\[ (o_t,a_t) \]

这个 Sample 定义变成:

\[ \boxed{ (o_t, [a_t,a_{t+1},\ldots,a_{t+K-1}]) } \]

这就是为什么我们前面一直强调:

Dataset 中的时间结构非常重要。

Action Chunk 不是模型凭空创造未来监督信号。

这些 Future Actions 原本就在 Demonstration Trajectory 里。


二十、这时__getitem__()的职责也变了

最简单 BC Dataset:

def __getitem__(self, index): obs = ... action = ... return obs, action

Target 只有:

\[ a_t \]

但 Action Chunk Dataset 的逻辑可能更接近:

def __getitem__(self, index): obs = ... action_chunk = ... return obs, action_chunk

其中:

\[ action\_chunk = [a_t,\ldots,a_{t+K-1}] \]

所以现在你应该重新理解:

__getitem__()不只是“读取第几个文件”。

它还可能决定:

一个训练 Sample 的时间窗口到底怎么切。

这就是代码阅读为什么必须看 Dataset 实现。


二十一、Episode 结尾会发生什么?

假设 Episode 总共只有:

\[ 100 \]

步。

现在:

\[ t=98 \]

而我们要求:

\[ K=8 \]

未来需要:

\[ a_{98},a_{99},a_{100},\ldots \]

但 Episode 已经结束。

数据不够。

于是这里才自然出现我们之前暂时没展开的:

Padding

例如有效 Action 只有:

\[ a_{98},a_{99} \]

剩下位置需要补齐到固定长度:

\[ K=8 \]

可能得到:

\[ [a_{98},a_{99},PAD,PAD,PAD,PAD,PAD,PAD] \]


二十二、为什么 Padding 后还需要 Mask?

模型必须知道:

哪些是真实 Action?

哪些只是为了凑 Shape 补出来的?

于是可能有:

\[ mask= [1,1,0,0,0,0,0,0] \]

其中可以约定:

\[ 1 \]

表示有效,

\[ 0 \]

表示 Padding。

那么计算 Loss 时:

Padding 的那些位置不应该当成真实 Action 参与训练。

否则模型会被迫学习:

“Episode 结束以后应该预测 PAD 伪数据。”

这是错误的。

所以:

\[ \boxed{ Padding \rightarrow 通常需要配套Mask } \]

这里 Padding 和 Mask 出现就很自然了——因为现在确实遇到了变长时间序列问题。


二十三、现在看一次完整 Tensor

假设:

\[ B=32 \]

Observation History:

\[ T_o=3 \]

Robot State Dimension:

\[ D_s=10 \]

那么:

\[ state:[32,3,10] \]

如果图像也保留时间:

\[ image:[32,3,3,H,W] \]

注意这里出现两个 3:

第一个:

\[ 3=T_o \]

表示 3 个时间点。

第二个:

\[ 3=C \]

表示 RGB 三个 Channel。

所以必须根据位置和语义区分。

Action Chunk:

\[ K=16 \]

Action Dimension:

\[ D_a=7 \]

于是:

\[ action:[32,16,7] \]

这就是一个典型时间序列 Robot Learning Sample 在 Batch 后可能出现的形态。


二十四、为什么这个[B,T,D]会直接把我们带向 Transformer?

现在出现了一个新问题。

以前:

\[ [B,D] \]

每个 Sample 只是一个向量。

现在:

\[ [B,T,D] \]

每个 Sample 内部变成了一串:

\[ x_1,x_2,\ldots,x_T \]

也就是:

Sequence

序列。

例如:

\[ [x_{t-2},x_{t-1},x_t] \]

或者 Action Query:

\[ [q_1,q_2,\ldots,q_K] \]

那么模型需要理解:

序列中不同位置之间是什么关系?

这正是 Attention / Transformer 擅长处理的问题。

所以 Transformer 并不是突然切换到另一个主题。

它是从:

\[ \boxed{ Robot\ Data开始具有Sequence结构 } \]

自然出现的。


二十五、ACT 的问题已经逐渐显现出来了

现在我们已经能提出 ACT 要解决的一部分问题:

问题 1

Robot Manipulation 是时间连续的。

问题 2

单步 Action Prediction 可能缺少一整段动作协调性。

问题 3

Demonstration 本身就是:

\[ a_0,a_1,\ldots,a_T \]

这样的 Sequence。

于是可以考虑:

\[ Observation \rightarrow Action\ Sequence \]

也就是:

\[ o_t \rightarrow A_t \]

其中:

\[ A_t:[K,D_a] \]

然后用能够处理 Sequence 的 Transformer 建模。

现在“Action Chunking with Transformers”这几个词已经第一次真正连起来了:

\[ \boxed{ Action\ Chunk + Sequence\ Modeling \rightarrow Transformer } \]

但现在还不能直接进入 ACT 源码。

因为我们还缺一个关键基础:

Transformer 到底如何理解 Sequence 中不同位置之间的关系?

这就是 Attention。


第二十六、整章链路回看

这一课从最简单 BC:

\[ o_t\rightarrow a_t \]

进行了两个方向的扩展。

输入端:

\[ o_t \]

扩展为:

\[ [o_{t-k+1},\ldots,o_t] \]

也就是:

\[ \boxed{\text{Observation History}} \]

因此 Tensor 出现:

\[ [B,T,D] \]

输出端:

\[ a_t \]

扩展为:

\[ [a_t,a_{t+1},\ldots,a_{t+K-1}] \]

也就是:

\[ \boxed{\text{Action Chunk}} \]

因此 Action Tensor 变成:

\[ [B,K,D_a] \]

最终:

\[ \boxed{ Sequence\ Input \rightarrow Sequence\ Modeling \rightarrow Sequence\ Output } \]

这就是我们接下来进入 Attention / Transformer 的直接原因。


第十二课自测

  1. 为什么只看当前一帧 \(o_t\) 有时候无法判断机器人当前的运动趋势?

  2. 什么叫 Observation History?

  3. Tensor:

\[ [32,4,10] \]

中的 32、4、10 分别表示什么?

  1. 图像 Tensor:

\[ [16,3,3,224,224] \]

如果第一个 3 表示时间长度,那么第二个 3 表示什么?

  1. 什么叫 Single-step Action Prediction?

  2. 什么叫 Action Chunk?

  3. 如果:

\[ B=32,\quad K=20,\quad D_a=7 \]

那么 Action Chunk Batch 的 Shape 是什么?

  1. Observation Horizon 和 Prediction Horizon 为什么不是同一个东西?

  2. 为什么“预测 16 个 Action”不一定意味着“一口气执行完 16 个 Action”?

  3. 如果 Episode 只剩 3 个 Action,但模型要求固定预测 10 个 Action,为什么可能需要 Padding?

  4. Padding 后为什么通常还需要 Mask?

  5. 为什么 Robot Data 一旦变成:

\[ [B,T,D] \]

这种 Sequence 结构,就会自然把我们带向 Attention / Transformer?


下一课:第 13 课——Transformer 前最后一块真正必要的基础:Sequence、Token 和 Embedding

下一课我们暂时还不会直接写 Q/K/V。

因为如果现在直接写:

\[ Q=XW_Q,\qquad K=XW_K,\qquad V=XW_V \]

你很可能会知道怎么算,却不知道这里的:

\[ X \]

到底是什么。

所以第 13 课只解决这一件事:

Transformer 看到的到底不是“图片”“语言”“关节状态”,而是一串 Token 表示。

我们会把:

\[ [B,T,D] \]

进一步解释成:

\[ \boxed{ Batch \times Token/Sequence \times Embedding\ Dimension } \]

并讲清:

  • Sequence 和 Token 到底是什么关系;
  • 一个数字为什么不能直接代表 Token;
  • Embedding 到底是什么;
  • Language Token 怎么变成向量;
  • Robot State 怎么变成 Token;
  • Image 为什么也能变成 Visual Token;
  • 为什么 Transformer 最终可以统一处理语言、视觉、机器人状态;
  • Position Information 为什么必须补进去。

这一步完成以后,第 14 课就可以正式进入:

\[ \boxed{ Q,\ K,\ V,\ Attention } \]

然后再进入完整 Transformer Block。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询