You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AiLearning-Theory-Applying/EmbodiedAI/第一章——具身智能到底在做什么.md

14 KiB

第一章——具身智能到底在做什么

这一章先不训练大模型。我们从一个能手算、能写成 Python 的推物例子开始,理解具身智能最基本的工作方式:

观察环境,根据目标决定动作,执行动作,再根据新的观察调整下一步动作。

这就是“观察 → 决策 → 动作 → 反馈”的闭环。后续章节中的行为克隆、ACT、Diffusion Policy 和 VLA,虽然生成动作的方式不同,但最终都要进入这个闭环。

1. 具体问题:机器人怎样把方块推到目标位置

假设桌上有一个方块,我们希望机器人把它推到指定位置。机器人在每个时刻都需要回答三个问题:

  1. 现在是什么情况? 读取相机、关节或位置传感器,得到观察。
  2. 下一步应该做什么? 策略根据观察和任务目标选择动作。
  3. 动作执行后发生了什么? 重新观察环境,再决定下一步动作。

例如,机器人原本打算把方块向右推 2 厘米,但由于桌面摩擦较小,方块实际移动了 3 厘米。机器人如果重新观察,就能减小下一步的推动量;如果不看执行结果而继续照原计划行动,误差可能越来越大。

2. 直观解释:闭环为什么重要

可以把机器人想象成正在蒙眼走向一个目标的人:

  • 每走一步睁眼看一次,再修正方向,是闭环控制(closed-loop control);
  • 开始前记住一串步伐,之后闭眼全部执行,是开环控制(open-loop control)。

这个类比只说明“是否利用执行后的反馈”。真实机器人还要处理传感器噪声、动作延迟、接触、摩擦和动力学等问题。

闭环并不保证一定成功。如果观察错误、策略能力不足或动作超出机器人能力,闭环仍然可能失败。它的意义是:策略有机会利用最新信息修正后续动作。

3. 五个核心概念

下标 t 表示第 t 个时间步(timestep)。

概念 符号 含义 推物例子
状态(State) s_t 足以描述环境当前情况及其后续演化的信息 推杆位置、方块位置、方块角度、速度等
观察(Observation) o_t 机器人通过传感器实际获得的信息 相机图像、关节角度、测得的位置
动作(Action) a_t 策略发送给环境或控制器的指令 推杆的目标坐标、机械臂关节目标
策略(Policy) \pi 根据观察和任务目标选择动作的规则或模型 根据方块和目标的位置决定向哪里推
状态转移(Transition) f 或 P 环境执行动作后如何变化 接触和摩擦使方块移动或旋转

3.1 状态不等于观察

状态包含环境演化所需的信息,观察则是机器人能够测到的信息,两者不能直接画等号。

以 PushT 推物任务为例,一个简化状态可以写成:


s_t=
\begin{bmatrix}
x_t^{\text{agent}} & y_t^{\text{agent}} &
x_t^{\text{block}} & y_t^{\text{block}} &
\theta_t^{\text{block}}
\end{bmatrix}
\in\mathbb{R}^{5}

其中:

  • x_t^{\text{agent}},y_t^{\text{agent}}:推杆在平面中的位置;
  • x_t^{\text{block}},y_t^{\text{block}}:方块中心的位置;
  • \theta_t^{\text{block}}:方块的旋转角度。

一个状态向量的形状是 (5,)。如果一次处理 B 个状态,组成的批量张量形状就是 (B, 5)。

真实机器人可能只能看到相机图像,无法直接知道精确位置、速度或摩擦系数。因此观察可能不完整,也可能带有噪声。当一次观察不足以判断当前状态时,策略可能需要使用连续多帧观察或历史信息。

3.2 策略不一定是神经网络

策略 \pi 是从输入到动作的映射。它可以是:

  • 人工编写的规则;
  • 传统控制算法;
  • 通过示范训练的神经网络;
  • Diffusion Policy 或 VLA 等模型。

所以,“使用策略”不等于“已经进行了机器学习”。本章先用人工规则理解闭环,第三章再让模型从示范数据中学习策略。

4. 用公式表示完整闭环

先采用无噪声、确定性的简化模型:


\begin{aligned}
o_t &= h(s_t),\\
a_t &= \pi(o_t,g),\\
s_{t+1} &= f(s_t,a_t),\\
o_{t+1} &= h(s_{t+1}).
\end{aligned}

公式中的符号分别表示:

  • s_t:执行动作前的环境状态;
  • o_t:策略实际获得的观察;
  • g:任务目标,例如方块的目标位置;
  • a_t:策略选择的动作;
  • s_{t+1}:执行动作后的新状态;
  • h:从真实状态产生传感器观察的过程;
  • \pi:根据观察和目标选择动作的策略;
  • f:环境的状态转移函数。

这里把目标 g 明确写进策略,因为同一个观察面对不同目标时,正确动作可能不同。另一种常见写法是把目标作为观察的一部分:


\tilde{o}_t=[o_t,g],\qquad a_t=\pi(\tilde{o}_t).

真实环境存在不确定性,相同状态下执行相同动作,结果也可能略有不同。这时状态转移可以写成概率形式:


s_{t+1}\sim P(\cdot\mid s_t,a_t).

P 表示:已知当前状态和动作时,下一个状态可能服从的概率分布。本章只需要知道不确定性存在,暂时不展开概率推导。

5. 反馈、奖励和成功信号不是一回事

闭环中的**反馈(feedback)**通常指执行动作后获得的新观察 o_{t+1}。**奖励(reward)**则是用数值评价动作结果的信号:


r_t=R(s_t,a_t,s_{t+1}).

例如:

  • 新观察:方块现在位于坐标 (3.1,2.0);
  • 奖励:方块更接近目标,得到 r_t=0.1;
  • 成功信号:方块进入目标区域,success=True。

模仿学习主要从专家的“观察—动作”示范中学习,不一定需要奖励;强化学习通常利用奖励改进策略。两者都会在环境中执行策略并获得新的观察。

6. 数学与手算例子:一维位置控制

为了手算,我们先把推物简化成一维位置控制:

  • 当前位置 x_0=2,目标位置 g=5;
  • 观察就是当前位置: o_t=x_t;
  • 动作是本步移动的距离: a_t\in[-1,1];
  • 暂不考虑接触、摩擦、惯性和传感器噪声。

人工策略定义为:


a_t=\mathrm{clip}\bigl(0.5(g-o_t),-1,1\bigr),

状态转移为:


x_{t+1}=x_t+a_t.

clip(value, -1, 1) 表示把数值限制在 [-1,1] 内。这里的 x_t、g、o_t 和 a_t 都是标量;如果统一写成一维张量,形状均为 (1,)。

第一步可以手算为:


\begin{aligned}
o_0 &= x_0=2,\\
a_0 &= \mathrm{clip}(0.5\times(5-2),-1,1)=1,\\
x_1 &= x_0+a_0=2+1=3.
\end{aligned}

第二步为:


\begin{aligned}
o_1 &= x_1=3,\\
a_1 &= \mathrm{clip}(0.5\times(5-3),-1,1)=1,\\
x_2 &= x_1+a_1=3+1=4.
\end{aligned}

7. 代码 Demo:观察、动作和反馈的循环

Demo 类型:原理演示。 这段程序不是物理仿真,也没有训练神经网络,只用来展示闭环交互和人工策略。真实 PushT 的动作表示和物理过程会在第七章介绍。

将下面的代码保存为 chapter01_closed_loop.py,使用 Python 3 运行即可,不需要安装第三方库。

def policy(observation, goal, gain=0.5):
    """根据当前位置和目标位置计算动作。"""
    raw_action = gain * (goal - observation)
    return max(-1.0, min(1.0, raw_action))


def run_episode(add_disturbance=False):
    x = 2.0
    goal = 5.0

    for t in range(6):
        # 1. 观察
        observation = x

        # 2. 决策
        action = policy(observation, goal)

        # 3. 执行动作,环境状态发生变化
        next_x = x + action

        # 在第 2 步执行后加入外部扰动
        if add_disturbance and t == 1:
            next_x -= 0.8

        # 4. 记录结果。下一轮会重新观察 next_x
        print(
            f"t={t}, observation={observation:.4f}, "
            f"action={action:.4f}, next_x={next_x:.4f}"
        )
        x = next_x


print("无扰动:")
run_episode(add_disturbance=False)

print("\n加入扰动:")
run_episode(add_disturbance=True)

实际运行结果如下:

无扰动:
t=0, observation=2.0000, action=1.0000, next_x=3.0000
t=1, observation=3.0000, action=1.0000, next_x=4.0000
t=2, observation=4.0000, action=0.5000, next_x=4.5000
t=3, observation=4.5000, action=0.2500, next_x=4.7500
t=4, observation=4.7500, action=0.1250, next_x=4.8750
t=5, observation=4.8750, action=0.0625, next_x=4.9375

加入扰动:
t=0, observation=2.0000, action=1.0000, next_x=3.0000
t=1, observation=3.0000, action=1.0000, next_x=3.2000
t=2, observation=3.2000, action=0.9000, next_x=4.1000
t=3, observation=4.1000, action=0.4500, next_x=4.5500
t=4, observation=4.5500, action=0.2250, next_x=4.7750
t=5, observation=4.7750, action=0.1125, next_x=4.8875

8. 结果解读

前两步计算出的原始动作大于或等于 1,因此动作被限制在 1。这表示执行器每一步最多只能移动一个单位。

当位置进入目标附近后,动作逐步变小。定义位置误差:


e_t=g-x_t.

当动作没有触发上下限时:


\begin{aligned}
a_t &= 0.5e_t,\\
e_{t+1} &= g-x_{t+1}\\
&=g-(x_t+0.5e_t)\\
&=0.5e_t.
\end{aligned}

也就是说,误差每一步缩小一半。它会逐渐接近零,但有限步内通常不会恰好等于零。因此工程中通常使用容差判断成功,例如:


|g-x_t|<\varepsilon,

其中 \varepsilon 是允许误差。如果 \varepsilon=0.1,无扰动实验执行 6 步后的误差为 5-4.9375=0.0625,可以判定成功。

扰动实验中,第 2 步执行后的位置由 4.0 变成了 3.2。下一轮策略重新观察到 3.2,于是把动作从原本可能采用的 0.5 调整为 0.9。这正是反馈在闭环中的作用。

gain=0.5 是策略中的比例系数:数值较小通常移动得更慢;数值过大时可能反复越过目标甚至振荡。这个例子省略了惯性和时间单位,因此只能解释闭环原理,不能直接作为真实机器人的控制器。

9. 从原理 Demo 连接到机器人仿真

Gymnasium 是常见的仿真环境接口。一个完整回合(episode)通常写成:

observation, info = env.reset(seed=0)

while True:
    action = policy(observation)
    next_observation, reward, terminated, truncated, info = env.step(action)
    observation = next_observation

    if terminated or truncated:
        break

其中:

  • reset():开始一个新回合并返回初始观察;
  • step(action):执行一个动作并将环境推进一个时间步;
  • reward:当前动作得到的奖励;
  • terminated:任务因成功或失败等内部条件结束;
  • truncated:任务因时间上限等外部条件结束;
  • info:用于调试或评测的额外信息。

本章代码中的 observation → policy → action → next_x,与这个接口中的交互结构相同。区别在于,真实推物仿真会用物理引擎计算碰撞、摩擦和方块旋转。

10. 小练习

请先自己计算或修改代码,再核对运行结果。

  1. 已知 x_1=3,手算 a_1 和 x_2。代码输出是否一致?
  2. 如果目标从 g=5 改为 g=-2,为什么只写 a_t=\pi(o_t) 不足以完整描述这里的策略?
  3. 将扰动从 next_x -= 0.8 改为 next_x += 0.8。下一步动作会变大还是变小?为什么?
  4. 分别把 gain 改为 0.2 和 1.2,比较 6 步后的位置和误差。哪个更快?是否出现越过目标的情况?
  5. 用自己的话解释:状态、观察、动作和策略分别是什么?反馈与奖励有什么区别?

11. 参考资料与许可

本章文字和一维控制代码为本教程原创。示例仅使用 Python 标准库,没有复制上游项目代码。

参考答案(做完再对照)

  1. a_1=\mathrm{clip}(0.5(5-3),-1,1)=1,x_2=3+1=4;与代码输出一致。
  2. 同样观察到 o_t=3 时,目标 g=5 要向右,目标 g=-2 要向左。策略需要同时知道观察和目标,即 a_t=\pi(o_t,g);也可以把目标并入观察。
  3. 动作会变小。原本下一步从 x=4 出发,动作是 0.5;加入 +0.8 扰动后从 x=4.8 出发,动作变成 0.1,因为它已经更接近目标。
  4. 无扰动、运行 6 步:gain=0.2 到达 x=4.213568,误差为 0.786432;gain=1.2 到达 x=5,误差为 0,因此本例中后者更快。无扰动时没有越过目标;如果按前一题加入 +0.8 扰动,gain=1.2 会短暂越过目标(到达 x=5.04)再反向修正。结论只针对本章简化模型。
  5. 状态是环境实际情况;观察是机器人能测到的信息,可能不完整;策略根据观察和目标选择动作;动作是交给控制器的指令。反馈通常是动作后的新观察。奖励是评价结果好坏的数值,例如 r=-\lvert g-x\rvert;目标 g 是想去的位置,本身不是奖励。

完成本章后,你应该能够:

  • 用自己的话解释“观察 → 决策 → 动作 → 反馈”的闭环;
  • 区分状态和观察、反馈和奖励;
  • 根据公式手算至少两个时间步;
  • 运行 Demo,并解释扰动后动作为什么发生变化;
  • 说明本章 Demo 是原理演示,不是真实机器人策略实验。