上一篇整理:02:用四足机器人理解 MDP
写在前面:Observation 是 policy 的“输入接口”
上一篇把四足 locomotion 任务先放进了 MDP 框架里:状态、动作、转移、奖励、折扣因子。
这一篇继续往下拆一个最容易在代码里遇到、但也最容易被忽略的东西:observation。
我现在对 observation 的理解是:它不是“机器人真实拥有的所有信息”,而是我选择让 policy 看到的那一串数字。这串数字决定了神经网络凭什么做判断,也决定了后面真机部署时我能不能把同样的输入喂给它。
如果 action 是 policy 的输出接口,那么 observation 就是 policy 的输入接口。
一句话结论
Observation 是 policy 真正能看到的信息。它通常由 IMU、编码器、命令、上一帧 action、地形信息等拼成一个固定顺序的一维向量。四足机器人强化学习里,obs 设计的核心不是“给得越多越好”,而是“训练时能稳定学,部署时也能真实拿到”。
可以先记住这个形式:
其中:
- :当前 observation。
- :policy 网络。
- :policy 输出的 action。
也就是说,policy 并不是直接根据完整世界状态 做决策,而是根据我喂给它的 做决策。
State 和 Observation 不完全一样
理论上 MDP 里常说 state 。但在四足机器人代码里,更常见的是 obs、observations、obs_buf。
我现在先把两者区分成:
state:环境真实完整状态 |
比如仿真器内部可能知道:
- 机器人 base 在世界坐标系下的精确位置。
- 机器人 base 的精确线速度。
- 每个足端的接触力。
- 地面真实摩擦系数。
- 机器人质量、惯量、关节摩擦。
- 地形完整高度图。
- 所有外部扰动。
但真机上 policy 不一定能直接知道这些。
所以真正喂给 policy 的 observation 往往会更克制:
IMU 可以测到的 |
这也是为什么很多四足 locomotion 任务严格说更像 POMDP,而不是完全可观测的 MDP。
一个常见的四足 locomotion observation 结构
以速度跟踪任务为例,常见 observation 可以写成:
对应成更工程一点的列表就是:
base_ang_vel |
如果机器人有 12 个关节,一个典型维度可能是:
| 项目 | 维度 | 含义 |
|---|---|---|
| base angular velocity | 3 | 机身角速度 |
| projected gravity | 3 | 重力方向投影到机身坐标系 |
| commands | 3 | 期望 |
| joint position error | 12 | 关节角相对默认站姿偏差 |
| joint velocity | 12 | 关节速度 |
| previous action | 12 | 上一帧 policy 输出 |
| 合计 | 45 | policy 输入维度 |
所以一个很常见的 obs 维度就是 45 维。当然不同项目会不一样,加入地形高度采样以后可能一下变成一百多维、两百多维。
注意:表格只是理解用,具体维度一定要以代码里的
num_obs或 observation 拼接逻辑为准。
1. base angular velocity:机身角速度
base angular velocity 通常是机身角速度,可以理解成 IMU 陀螺仪能给出的量。
它一般是 3 维:
这个量告诉 policy:
身体现在是不是正在翻滚? |
对四足机器人来说,这很重要。因为 locomotion 不只是腿在动,身体姿态的稳定也很关键。
比如机器人快要向前栽倒时,pitch 方向角速度会变大;policy 如果能看到这个量,就有机会通过腿部动作把身体拉回来。
2. projected gravity:比 roll/pitch 更常见的姿态表达
很多四足 RL 代码不会直接把 roll、pitch、yaw 喂给 policy,而是喂 projected gravity。
直觉上,projected gravity 表示:
重力方向在机器人机身坐标系下看起来指向哪里 |
如果机器人是正着站的,重力大致沿着机身坐标系的负 z 方向;如果机器人身体歪了,重力在机身坐标系下的投影也会跟着变。
它通常也是 3 维:
我现在把它理解成 policy 的“身体倾斜感”。它告诉策略:
- 机身是不是向前倾了。
- 机身是不是向侧面倒了。
- 机身是不是接近翻车了。
为什么不直接给欧拉角?
主要有几个原因:
- 欧拉角有奇异性和跳变问题。
- yaw 对很多速度跟踪任务不一定重要。
- projected gravity 对姿态倾斜表达更直接。
- 它和 IMU 姿态估计结果更容易对应。
比如四足机器人在原地朝不同方向转头,只要身体没有倾倒,重力在机身坐标系下的投影不会因为世界系 yaw 改变而无意义地变化。这对学习稳定步态是有好处的。
3. commands:上层希望机器人怎么走
commands 是速度命令,通常来自上层控制器、键盘、遥控器或者课程采样器。
常见是 3 维:
分别表示:
- :希望机器人向前 / 向后走多快。
- :希望机器人横向走多快。
- :希望机器人绕 z 轴转多快。
为什么 commands 要放进 observation?
因为同一个 policy 通常不是只学一种速度,而是要学一个“速度跟踪器”。
如果不给 command,policy 不知道当前到底应该:
站着不动 |
给了 command 之后,policy 学到的是一个条件策略:
也就是:在当前身体状态下,结合目标速度,输出合适的腿部动作。
4. joint position:通常给相对默认姿态的偏差
关节角是四足机器人最基础的本体感知信息之一。
但常见做法不是直接给 ,而是给:
其中:
- :当前关节角。
- :默认站姿关节角。
这样做的好处是让输入更居中。默认站姿附近的关节角偏差接近 0,神经网络更容易处理。
如果机器人有 12 个主动关节,这一项就是 12 维。
FL_hip, FL_thigh, FL_calf, |
具体顺序非常关键。训练和部署必须完全一致。
5. joint velocity:关节速度
关节速度 也是常见 observation。
它告诉 policy:
每个关节现在正在往哪个方向动?动得快不快? |
只知道关节位置还不够,因为同一个关节角下,关节可能正在快速向前摆,也可能正在快速向后收。速度信息能帮助 policy 判断运动趋势。
如果有 12 个关节,这一项也是 12 维。
不过真机上关节速度可能噪声比较大,所以工程里经常会做滤波,或者在训练中加噪声,让 policy 不要过度依赖过于干净的速度值。
6. previous action:上一帧动作为什么要放进去
上一帧 action 在四足 RL 里很常见。
它看起来有点奇怪:上一帧动作不是 policy 自己输出的吗?为什么还要再喂回来?
我的理解是,它至少有三个作用:
- 让 policy 知道自己上一刻刚刚命令了什么。
- 帮助输出更连续,不要每一帧剧烈跳变。
- 在没有显式历史网络的时候,给 policy 一点点“短期记忆”。
很多 reward 里还会惩罚 action rate:
这会鼓励 action 不要抖得太厉害。
把 previous action 放到 observation 里,也让 policy 更容易根据上一帧动作调整当前动作。
复杂地形时会加入 height samples
如果任务不是平地速度跟踪,而是走楼梯、斜坡、石头路等复杂地形,policy 往往还需要看到地形信息。
一种常见做法是在机器人周围采样一圈或一片高度点:
height samples around base |
比如在机身前方、侧方采样若干个点,得到相对机身高度。
这相当于告诉 policy:
前面有没有台阶? |
如果没有地形 observation,机器人只能靠接触之后的反馈来反应;如果提前看到高度采样,就能更早调整摆腿高度和落脚位置。
但 height samples 也有部署问题:
- 真机上需要深度相机、激光雷达或其他地形估计模块。
- 感知延迟会影响 policy。
- 高度图噪声会影响策略稳定性。
- 仿真里的完美高度信息不一定能在现实中复现。
所以是否加入地形观测,要看任务目标。如果只是先学平地 locomotion,可以不急着加。
privileged observation:训练时能看,部署时不能看
四足 RL 里还经常出现一个概念:privileged observation。
它指的是:训练时 critic 或 teacher 可以看到的额外信息,但部署时 actor / policy 不直接看到。
比如:
真实摩擦系数 |
这些信息在仿真里很好拿,但真机上不一定能拿到。
一个常见结构是:
actor observation:部署时 policy 能看到的信息 |
也就是说:
而 critic 可能用:
这样做的好处是训练时 value 估计更准,但最终部署的 actor 仍然只依赖真机可获得的信息。
我后面看代码时要注意区分:
num_observations |
它们不一定是同一个东西。
为什么 observation 不是越多越好
直觉上可能会觉得:信息越多,policy 越聪明。
但四足机器人里不一定。
因为多给的信息可能带来几个问题:
-
真机拿不到
仿真里能直接读 base linear velocity,真机上可能只能估计。 -
噪声太大
传感器信号不干净,policy 如果过度依赖,部署会变脆。 -
维度变高,训练更难
不重要的信息太多,网络要自己学会忽略。 -
造成仿真作弊
policy 在仿真里利用了现实不存在的信息,sim2real 时直接失效。 -
不同频率和延迟
IMU、电机、视觉、高度图的更新频率可能不同,把它们强行拼一起会引入时序问题。
所以 observation 设计的原则不是“全塞进去”,而是:
足够完成任务 |
observation 的尺度也很重要
神经网络不喜欢不同输入量级差异太大。
比如:
角速度可能是 0~几 rad/s |
如果不做 scale,有些量会在网络输入里显得特别大,有些量几乎被淹没。
所以代码里经常有类似:
obs = torch.cat(( |
这些 scale 不是随便写的,它们会影响训练稳定性。
我看代码时应该特别关注:
obs_scales.ang_vel |
因为最后喂给 policy 的不是原始物理量,而是缩放、裁剪、拼接后的向量。
observation noise:训练时故意加噪声
真机传感器一定有噪声,所以训练时经常会给 observation 加噪声。
例如:
角速度加一点噪声 |
这样 policy 不会只适应仿真里的完美观测。
可以把它理解成一种 domain randomization:
不是只随机物理参数,也随机机器人看到的东西 |
但噪声也不是越大越好。噪声太大会让任务变得过难,policy 学不到稳定规律。
observation history:为什么有时要堆叠历史帧
如果 policy 只看当前一帧 observation,它对过去发生了什么知道得很少。
有些项目会把多帧 observation 拼起来:
这样可以给 MLP policy 一点历史信息。
比如:
当前帧看起来一样,但过去几帧趋势不同 |
堆叠历史后,policy 更容易估计速度、延迟、接触状态等隐含信息。
不过代价是输入维度会变大。例如单帧 45 维,堆 4 帧就是 180 维。
另一种路线是用 RNN / GRU,让网络自己维护隐藏状态。但工程上 MLP + frame stack 更直接。
obs 顺序是部署时最容易踩的坑
这一点非常重要。
训练时 obs 拼接顺序如果是:
角速度 → 重力方向 → 命令 → 关节角 → 关节速度 → 上一帧动作 |
部署时就必须完全一样。
如果部署时写成:
命令 → 角速度 → 关节角 → 重力方向 → 关节速度 → 上一帧动作 |
从维度上看可能完全没报错,但 policy 会直接乱掉。
因为神经网络不会知道“第 0~2 维现在不是角速度了”。它只会按训练时学到的权重解释每一维。
这类 bug 很隐蔽:
程序能跑 |
所以部署前最好做一个 obs 对照表,把训练端和部署端逐项核对。
一个简单的 obs 拼接示例
假设我现在有一个 12 自由度四足机器人,采用平地速度跟踪任务,可以先这样理解:
obs = concat([ |
维度就是:
3 + 3 + 3 + 12 + 12 + 12 = 45 |
这 45 个数每一步都会输入 policy,policy 输出下一步 action。
如果加上 187 个 height samples,那可能变成:
45 + 187 = 232 |
这就是为什么看代码时 num_obs 很重要。它必须和模型训练时的输入维度一致。
真机部署时要核对哪些东西
如果以后要把训练好的 policy 放到真机上,observation 这一块我觉得至少要核对:
-
维度是否一致
训练时num_obs=45,部署时也必须是 45。 -
顺序是否一致
每一段的排列顺序必须一样。 -
关节顺序是否一致
仿真里的 12 个关节顺序和真机驱动里的顺序可能不同。 -
单位是否一致
rad 还是 degree?m/s 还是 mm/s? -
坐标系是否一致
角速度是在 body frame 还是 world frame? -
符号是否一致
某个关节正方向在仿真和真机是否相反? -
scale 是否一致
训练时乘了obs_scales,部署时也要乘同样的 scale。 -
clip 是否一致
训练时 observation clip 到某个范围,部署时也要一致。 -
上一帧 action 是否一致
是网络原始输出?还是 scale 后的 action?这个要看训练代码。 -
频率是否一致
policy 是 50 Hz、100 Hz 还是其他频率?不同频率下 obs 动态分布会变。
这些问题里,最容易出错的是:关节顺序、坐标系、符号、scale。
我现在的理解小结
这篇先把 observation 理成四足 RL 里的“输入接口”。
我现在会这样记:
obs 不是完整 state |
最重要的一句话是:
训练时 policy 看到什么,部署时就必须以同样顺序、同样单位、同样尺度再给它一次。
否则模型本身没坏,机器人也可能跑得很离谱。
下一篇整理:04:Action 为什么常用关节位置目标