四足机器人强化学习系列 02:用四足机器人理解 MDP

上一篇整理:01:四足 RL 到底在学什么

写在前面:这一篇先把 MDP 当成“读代码的对照表”

上一篇主要是在整理一个直觉问题:四足机器人强化学习到底在学什么?

这一篇我想开始补 MDP。说实话,MDP 这个东西我现在也还不是那种“完全吃透”的状态。它在书上看起来很标准:状态、动作、转移、奖励、折扣因子。但一放到四足机器人代码里,就会变成 observation、action、reward terms、reset、domain randomization、sim step 这些工程细节。

所以这篇不是想写成教程,而是给自己搭一个对照表:

以后看 legged_gym / Isaac Lab / PPO 代码时,知道哪些代码大概对应 MDP 里的哪个部分。

MDP 的全称是 Markov Decision Process,中文常翻译为马尔可夫决策过程。现在我先把它理解成:用一套符号描述“机器人看到什么、做什么、环境怎么变、这一步得多少分”。

一句话结论

我现在对四足 locomotion MDP 的理解是:每个控制周期里,机器人根据当前观测输出一个动作,仿真器或真实物理世界把它推进到下一状态,同时给一个 reward;训练要做的,就是慢慢找到一个长期 reward 更高的策略。

最短可以写成:

stπatPst+1,rts_t \xrightarrow{\pi} a_t \xrightarrow{P} s_{t+1},\quad r_t

对应到四足机器人里就是:

当前机器人状态/观测
↓ policy
关节动作 / 关节目标
↓ physics
下一帧机器人状态 + reward

MDP 的标准定义:先记住五个符号

书上通常把 MDP 写成一个五元组。我先把它当作一个索引,不急着要求自己一步到位完全理解:

M=(S,A,P,R,γ)\mathcal{M}=(\mathcal{S},\mathcal{A},P,R,\gamma)

其中:

  • S\mathcal{S}:状态空间,state space。
  • A\mathcal{A}:动作空间,action space。
  • PP:状态转移概率,transition probability。
  • RR:奖励函数,reward function。
  • γ\gamma:折扣因子,discount factor。

有些资料也会把初始状态分布 ρ0\rho_0 和终止条件也写进去,例如:

s0ρ0(s)s_0\sim \rho_0(s)

我目前先记住上面这五个元素就够用了,后面看代码时再一点点对应。

1. 状态空间 S\mathcal{S}:先理解成“真实世界全量状态”

理论上的 state sts_t 表示环境在时刻 tt 的完整状态。

对于四足机器人,如果写得非常完整,状态可能包括:

st={qt,q˙t,pbase,Rbase,vbase,ωbase,ct,μ,m,}s_t=\{q_t,\dot q_t,p_{\mathrm{base}},R_{\mathrm{base}},v_{\mathrm{base}},\omega_{\mathrm{base}},c_t,\mu,m,\cdots\}

这个式子其实我也不会一眼就完全记住,先拆开看:

  • qtq_t:关节角。
  • q˙t\dot q_t:关节速度。
  • pbasep_{\mathrm{base}}:机身位置。
  • RbaseR_{\mathrm{base}}:机身姿态。
  • vbasev_{\mathrm{base}}:机身线速度。
  • ωbase\omega_{\mathrm{base}}:机身角速度。
  • ctc_t:足端接触状态。
  • μ\mu:地面摩擦系数。
  • mm:机器人质量/动力学参数。

这里我暂时不用机器人学里常见的“左上角坐标系标记”写法。网页显示时它很容易显得上下标错位。这篇先用更朴素的变量名,例如 pbasep_{\mathrm{base}}ωbase\omega_{\mathrm{base}},重点先理解 MDP 结构。

真实物理世界当然还包含更多信息,比如地形、接触力、电机温度、延迟、外部扰动等。

但在四足 RL 论文和代码里,policy 通常拿不到完整 state,而是拿到 observation。

2. 观测 oto_t:代码里更常见的是 observation

严格来说,很多四足机器人 locomotion 问题更接近 POMDP,也就是 partially observable MDP。因为 policy 看到的不是完整状态 sts_t,而是观测 oto_t

可以写成:

ot=Ω(st)o_t=\Omega(s_t)

其中 Ω\Omega 是从真实状态到观测的映射。

在四足速度跟踪任务里,常见 observation 可能包括:

ot=[ωbasegtqtqdefaultq˙tctcmdat1]o_t= \begin{bmatrix} \omega_{\mathrm{base}} \\ g_t \\ q_t-q_{\mathrm{default}} \\ \dot q_t \\ c_t^{\mathrm{cmd}} \\ a_{t-1} \end{bmatrix}

这里每一项大概表示:

  • ωbase\omega_{\mathrm{base}}:机身角速度,通常来自 IMU。
  • gtg_t:重力方向在机身坐标系下的投影,用来表示姿态。
  • qtqdefaultq_t-q_{\mathrm{default}}:当前关节角相对默认站姿的偏移。
  • q˙t\dot q_t:关节速度。
  • ctcmdc_t^{\mathrm{cmd}}:速度命令,比如 vxcmd,vycmd,ωzcmdv_x^{\mathrm{cmd}},v_y^{\mathrm{cmd}},\omega_z^{\mathrm{cmd}}
  • at1a_{t-1}:上一帧动作,用来帮助策略输出更平滑。

所以在工程里,经常不是写:

at=π(st)a_t=\pi(s_t)

而是写:

at=π(ot)a_t=\pi(o_t)

这点我觉得很关键:理论上讲 state,但代码里经常写 obs。真实机器人部署时,policy 也只能拿到传感器能提供的量,而不是仿真器内部完整状态。

3. 动作空间 A\mathcal{A}:先搞清楚 action 具体代表什么

动作 ata_t 是 policy 在每一步输出的控制量。

理论上动作可以有很多选择:

  • 直接输出关节力矩。
  • 输出关节位置目标。
  • 输出关节位置目标偏移。
  • 输出足端位置目标。
  • 输出 gait 参数或更高层命令。

在很多四足 locomotion RL 里,policy 输出的是关节位置目标偏移。假设机器人有 12 个主动关节,则:

atR12a_t\in\mathbb{R}^{12}

常见做法可以写成:

qttarget=qdefault+αatq_t^{\mathrm{target}}=q^{\mathrm{default}}+\alpha a_t

其中:

  • qdefaultq^{\mathrm{default}} 是默认站姿关节角。
  • ata_t 是神经网络输出的动作。
  • α\alpha 是 action scale,用来限制动作幅度。

然后底层 PD 控制器把目标关节角转成力矩:

τt=Kp(qttargetqt)Kdq˙t\tau_t=K_p(q_t^{\mathrm{target}}-q_t)-K_d\dot q_t

所以我看代码时要注意:policy 输出的 action 不一定就是最终电机力矩,中间可能还有 action scale、默认姿态、PD 控制器这些东西。

4. 转移概率 PP:这部分通常藏在仿真器里

MDP 中的转移概率写作:

P(st+1st,at)P(s_{t+1}\mid s_t,a_t)

意思是:在状态 sts_t 下执行动作 ata_t,下一状态是 st+1s_{t+1} 的概率。

对于四足机器人,这个转移由动力学决定:

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

如果在仿真器里,我可以先粗略理解为:

Isaac Gym / Isaac Lab / MuJoCo / RaiSim
根据当前状态和动作积分一步动力学
得到下一状态

如果忽略随机性,也可以写成确定性形式:

st+1=f(st,at)s_{t+1}=f(s_t,a_t)

但实际训练时,通常会加入 domain randomization,例如随机化:

  • 摩擦系数。
  • 机身质量。
  • 电机强度。
  • 地面高度。
  • 传感器噪声。
  • 控制延迟。
  • 外部推力扰动。

这样转移就更像随机过程:

st+1P(st+1st,at,ξ)s_{t+1}\sim P(s_{t+1}\mid s_t,a_t,\xi)

其中 ξ\xi 可以理解为被随机化的环境参数。

5. 奖励函数 RR:最像工程调参的部分

奖励函数是我觉得最工程、也最容易看晕的部分。它不像 MDP 定义那么干净,代码里经常是一堆 reward term 加权相加。

MDP 里通常写作:

rt=R(st,at,st+1)r_t=R(s_t,a_t,s_{t+1})

在四足速度跟踪里,reward 往往由很多项组成:

rt=wvrtvel+wωrtyaw+whrtheightwτrttorquewartactionwsrtslipr_t=w_v r_t^{\mathrm{vel}}+w_\omega r_t^{\mathrm{yaw}}+w_h r_t^{\mathrm{height}} -w_\tau r_t^{\mathrm{torque}}-w_a r_t^{\mathrm{action}}-w_s r_t^{\mathrm{slip}}

拆开理解:

  • rtvelr_t^{\mathrm{vel}}:线速度跟踪奖励。
  • rtyawr_t^{\mathrm{yaw}}:角速度跟踪奖励。
  • rtheightr_t^{\mathrm{height}}:机身高度稳定奖励。
  • rttorquer_t^{\mathrm{torque}}:力矩惩罚。
  • rtactionr_t^{\mathrm{action}}:动作变化惩罚。
  • rtslipr_t^{\mathrm{slip}}:足端打滑惩罚。
  • ww_*:各项权重。

速度跟踪奖励常见写法是指数形式:

rtvel=exp(vxy,tcmdvxy,t2σv)r_t^{\mathrm{vel}} =\exp\left(-\frac{\lVert v_{xy,t}^{\mathrm{cmd}}-v_{xy,t}\rVert^2}{\sigma_v}\right)

角速度跟踪也类似:

rtyaw=exp((ωz,tcmdωz,t)2σω)r_t^{\mathrm{yaw}} =\exp\left(-\frac{(\omega_{z,t}^{\mathrm{cmd}}-\omega_{z,t})^2}{\sigma_\omega}\right)

动作平滑可以惩罚相邻动作差:

rtaction=atat12r_t^{\mathrm{action}}=\lVert a_t-a_{t-1}\rVert^2

能耗或力矩惩罚可以写成:

rttorque=τt2r_t^{\mathrm{torque}}=\lVert \tau_t\rVert^2

这些公式不是标准答案,更像是我现在用来理解 reward 结构的模板:

想让它做的事 → 正奖励
不想让它做的事 → 惩罚项

6. 折扣因子 γ\gamma:先理解成“眼前和长期的权衡”

强化学习不是只看当前一步奖励,而是看一段时间里的累计表现。这个对四足很重要,因为“当前一步看起来不错”的动作,可能下一秒就让机器人摔倒。

从时刻 tt 开始的 return 写作:

Gt=k=0γkrt+kG_t=\sum_{k=0}^{\infty}\gamma^k r_{t+k}

其中:

0γ10\le\gamma\le1

如果 γ\gamma 接近 0,policy 更短视,只关心眼前奖励。

如果 γ\gamma 接近 1,policy 更关注长期表现。

四足机器人里,如果只看眼前一步,可能会学出奇怪动作,比如瞬间蹬一下获得速度,但马上摔倒。折扣累计奖励会鼓励 policy 在长期内保持稳定。

Policy:先记成 observation 到 action 的函数

策略 π\pi 表示“看到状态或观测后如何选动作”。

随机策略写作:

π(atot)\pi(a_t\mid o_t)

意思是:在观测 oto_t 下选择动作 ata_t 的概率。

确定性策略可以写作:

at=π(ot)a_t=\pi(o_t)

PPO 训练时常用随机策略,因为训练阶段需要探索。我现在先把它理解成:网络不是只输出一个动作,也可能输出一个动作分布,例如高斯分布:

atN(μθ(ot),Σθ)a_t\sim\mathcal{N}(\mu_\theta(o_t),\Sigma_\theta)

其中:

  • μθ(ot)\mu_\theta(o_t) 是网络输出的动作均值。
  • Σθ\Sigma_\theta 是动作方差或协方差。
  • θ\theta 是神经网络参数。

部署到真实机器人时,常常直接取均值动作:

at=μθ(ot)a_t=\mu_\theta(o_t)

这样动作更稳定。

Value function:我先理解成“这个状态后面有没有前途”

有了 return,就可以定义价值函数。

状态价值函数:

Vπ(s)=Eπ[Gtst=s]V^\pi(s)=\mathbb{E}_\pi\left[G_t\mid s_t=s\right]

意思是:如果当前在状态 ss,之后一直按照策略 π\pi 行动,期望能拿到多少累计奖励。

动作价值函数:

Qπ(s,a)=Eπ[Gtst=s,at=a]Q^\pi(s,a)=\mathbb{E}_\pi\left[G_t\mid s_t=s,a_t=a\right]

意思是:当前在状态 ss,先执行动作 aa,之后再按照策略 π\pi 行动,期望累计奖励是多少。

我目前的直觉是:

V:这个状态好不好
Q:这个状态下做这个动作好不好

放到四足机器人里:

  • 如果机器人身体正、速度接近命令、脚下接触稳定,那么 VV 应该较高。
  • 如果机器人快摔倒了、姿态倾斜很大,那么 VV 应该较低。
  • 在快摔倒时能把身体拉回来的动作,其 QQ 应该更高。

Bellman 关系:先记住“当前 + 未来”

价值函数最重要的一点是可以递推。

对于给定策略 π\pi,Bellman expectation equation 可以写作:

Vπ(s)=Eaπ,sP[R(s,a,s)+γVπ(s)]V^\pi(s)=\mathbb{E}_{a\sim\pi,\,s'\sim P} \left[R(s,a,s')+\gamma V^\pi(s')\right]

也就是:

当前状态的价值 = 当前一步奖励 + 下一状态价值的折扣期望

这句话我先记下来:很多强化学习算法,本质上都绕不开“当前 reward + 下一步价值”这个递推思想。后面看 PPO / critic 的时候应该还会反复遇到。

最优价值函数可以写作:

V(s)=maxaEsP[R(s,a,s)+γV(s)]V^*(s)=\max_a\mathbb{E}_{s'\sim P} \left[R(s,a,s')+\gamma V^*(s')\right]

最优策略则可以理解为:

π(s)=argmaxaEsP[R(s,a,s)+γV(s)]\pi^*(s)=\arg\max_a\mathbb{E}_{s'\sim P} \left[R(s,a,s')+\gamma V^*(s')\right]

四足机器人里当然不可能真的枚举所有状态和动作,维度太高。所以后面才会用神经网络去近似 policy 和 value。

把速度跟踪任务写成 MDP:我的当前理解

如果把一个最小速度跟踪任务写成 MDP,我现在会这样对照:

状态 / 观测

ot=[ωbasegtqtqdefaultq˙tvx,tcmdvy,tcmdωz,tcmdat1]o_t= \begin{bmatrix} \omega_{\mathrm{base}} \\ g_t \\ q_t-q_{\mathrm{default}} \\ \dot q_t \\ v_{x,t}^{\mathrm{cmd}} \\ v_{y,t}^{\mathrm{cmd}} \\ \omega_{z,t}^{\mathrm{cmd}} \\ a_{t-1} \end{bmatrix}

动作

atR12a_t\in\mathbb{R}^{12}

qttarget=qdefault+αatq_t^{\mathrm{target}}=q^{\mathrm{default}}+\alpha a_t

转移

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

在仿真里就是 physics step。

奖励

rt=rttracking+rtstabilityrtenergyrtsmoothr_t=r_t^{\mathrm{tracking}}+r_t^{\mathrm{stability}}-r_t^{\mathrm{energy}}-r_t^{\mathrm{smooth}}

其中速度跟踪可以写为:

rttracking=exp(vxy,tcmdvxy,t2σv)+exp((ωz,tcmdωz,t)2σω)r_t^{\mathrm{tracking}} =\exp\left(-\frac{\lVert v_{xy,t}^{\mathrm{cmd}}-v_{xy,t}\rVert^2}{\sigma_v}\right) +\exp\left(-\frac{(\omega_{z,t}^{\mathrm{cmd}}-\omega_{z,t})^2}{\sigma_\omega}\right)

终止条件

episode 结束条件可以写成:

dt=1[ϕt>ϕmax  θt>θmax  tT]d_t=\mathbf{1}\left[|\phi_t|>\phi_{\max}\ \lor\ |\theta_t|>\theta_{\max}\ \lor\ t\ge T\right]

其中 ϕt\phi_tθt\theta_t 可以理解为 roll / pitch。超过阈值说明机器人姿态太差,可能已经摔倒。

Markov 性:我现在还需要慢慢消化的一点

Markov 性的意思是:未来只依赖当前状态和当前动作,不依赖更早历史。

数学上写作:

P(st+1st,at,st1,at1,)=P(st+1st,at)P(s_{t+1}\mid s_t,a_t,s_{t-1},a_{t-1},\cdots) =P(s_{t+1}\mid s_t,a_t)

也就是说,如果 sts_t 已经包含足够信息,那么过去发生过什么理论上就不重要。这个说起来简单,但放到机器人上我觉得还挺容易混淆。

但四足机器人实际观测往往不完全 Markov。例如:

  • policy 不一定知道真实接触力。
  • policy 不一定知道地面摩擦系数。
  • policy 不一定知道电机延迟。
  • 只看单帧观测可能不知道速度趋势。

所以工程上常用一些办法补信息:

  • 把上一帧动作 at1a_{t-1} 放进 observation。
  • 堆叠多帧历史观测。
  • 使用 RNN / LSTM。
  • 加入 privileged information 训练 critic。
  • 做 domain randomization,让 policy 对未知参数更鲁棒。

所以 observation design 不是随便拼几个量,而是在尽量让 policy 看到“足够接近 Markov”的信息。这个点我后面看代码时要特别留意。

MDP 和代码之间怎么对应:以后读代码就按这个找

如果看 Isaac Gym / legged_gym / Isaac Lab 这一类代码,MDP 元素通常会散落在不同函数里。

我先记一个粗略对应关系:

compute_observations()  →  o_t
policy(obs) → a_t
pre_physics_step() → 把 action 转成控制目标
physics step → P(s_{t+1}|s_t,a_t)
compute_reward() → r_t
check_termination() → done / reset

也就是说,MDP 不是单独写在某个文件里的一个公式,而是分布在环境代码的各个部分。

我后面读代码时可以按这个顺序找:

  1. observation 由哪些量拼出来。
  2. action 代表什么控制量。
  3. reward 由哪些项组成。
  4. termination 条件是什么。
  5. reset 时随机化了哪些东西。

这五个问题搞清楚,基本就知道这个 locomotion task 的 MDP 是怎么定义的。

这一篇我自己的理解:先有框架,不假装全懂

写到这里,我感觉 MDP 对我现在最有用的地方,不是背定义,而是提供一个检查清单。

看一篇四足 RL 论文或一段环境代码时,我可以问:

  • 这里的 state / observation 是什么?
  • policy 输出的 action 是什么?
  • physics / simulator 怎么把状态推进到下一步?
  • reward 到底鼓励了什么,惩罚了什么?
  • episode 什么时候结束?
  • 它有没有满足 Markov 性?如果没有,靠什么补?

这些问题应该比单纯说“用了 PPO”更基础。

因为 PPO 只是优化算法,MDP 才是任务本身。如果 MDP 定义得不好,比如 observation 缺关键信息、action 过于难学、reward 有漏洞,那么换再高级的算法也可能学不出稳定步态。这个判断我现在还需要通过代码和实验继续验证。

小结

这一篇先把 MDP 放到四足机器人 locomotion 里做一个自学版理解:

  • S\mathcal{S}:机器人和环境真实状态。
  • oto_t:policy 实际看到的观测。
  • A\mathcal{A}:policy 输出的动作,常见是 12 维关节目标偏移。
  • PP:由仿真器或真实动力学决定的状态转移。
  • RR:速度跟踪、稳定性、能耗、动作平滑等 reward 组合。
  • γ\gamma:长期奖励折扣。
  • VV / QQ:状态或动作的长期价值。
  • Bellman 关系:当前价值等于当前奖励加下一状态价值。

下一步再看 PPO 时,很多符号应该就不会那么突兀了。PPO 不是凭空来的,它是在这个 MDP 上,用采样数据去更新 policy,让长期累计奖励变大。

这一篇我先接受自己只是“建立框架”,不是完全掌握。后面看 observation、reward、PPO 代码时,再不断把这些符号和实际实现对上。

algorithms axis-angle bode calibration chrome cmake cmakelists colcon conan control cpp d435i data_struct db dots eigen fcpx figure forge fov gazebo gdb git gnu ibus interest isaac gym isaaclab kdl latex launch legged locomotion life linux mac math matlab matrix memory mlp motor moveit ode operator optimal algorithm ppo python qos realsense robot robotics ros ros2 rtb shell simulation stl thread tools twist ubuntu uml unitree urdf valgrind vcxsrv velocity vim web work wsl 中文输入 交叉编译 依赖管理 分支管理 四足机器人 强化学习 机器人视觉 构建系统 深度相机 点云 版本控制 输入法 配置类
知识共享许可协议