模型预测控制还是强化学习?两条足式控制路线的分水岭
- 搞懂模型预测控制的核心机制:预测、优化、只执行第一步、下周期重来
- 分清两条路线的计算负担、知识载体、约束能力各自落在哪里
- 掌握一组可以自问的选型问题,以及两种路线各自典型的失败方式
真到了要给一个足式项目定技术路线的时候,问题会变得非常具体:我是去建模、推公式、写优化器,还是去搭仿真环境、设计奖励、训一个网络?
这个选择题在网上通常得不到有用的答案。你会看到一堆"RL 是未来"或者"RL 不可靠"的表态,但很少有人把两条路线各自假设了什么、各自把成本花在哪、各自坏掉的时候什么样摆清楚。而选型真正需要的恰恰是这三件事。
这篇不站队。两条路线具体怎么实现,本卷已经各有一篇——unitree_guide 那套经典控制骨架讲状态机、步态、力分配的代码结构,unitree_rl_gym 拆解讲 Train 到 Sim2Real 的完整流水线。这篇站在两者之上,只做一件事:把决策所需的判据整理出来。
按惯例交代边界:本文基于公开仓库的文档、代码结构以及控制领域的通用方法来写,没有真机验证,不会出现任何实测描述或性能数字。
先把 MPC 讲清楚
很多人听过这三个字母,但说不出它到底在算什么。它其实只有四步,而且这四步每个控制周期都重来一遍。
第一步,你手上得有一个模型。 一个能回答"我现在处于状态 x,施加控制量 u,下一时刻会变成什么状态"的数学描述。
第二步,向前预测一段时间。 假设未来若干个时间步都由你决定控制量,那么用模型可以推演出整条状态轨迹。这段被推演的时间叫预测时域。
第三步,把它写成一个优化问题。 目标是让预测出来的轨迹尽量贴近你想要的样子——身体高度别掉、速度跟上指令、姿态别歪——同时控制量本身别太剧烈。这些愿望被写成一个代价函数,求解器去找让代价最小的那一串控制量。
第四步,也是最反直觉的一步:算出来一整串未来的控制量,你只执行第一个。 下一个周期,读回最新的传感器状态,把整个问题重新求解一遍。
这就是滚动时域(receding horizon)。为什么要这么浪费?
因为模型一定是错的。你的预测时域越往后,推演出来的状态越不可信——地面比你以为的软一点、负载比标称重一点、脚打滑了一下,几步之后模型和现实就分家了。如果你把整串控制量都执行完再重新规划,中间这段时间系统是在开环运行,靠一个已经过时的推演在硬撑。
只执行第一步,等于把优化结果当成一个反馈律在用:每个周期都拿最新的真实状态当起点重来。模型的偏差不会累积,因为它每周期都被真实测量拉回来一次。滚动时域是 MPC 能容忍模型不准的根本原因,也是它和"离线规划一条轨迹然后照着跟"的本质区别。
还有一层:既然只有第一步真的会被执行,那后面那些预测步的意义是什么?它们的意义是让当前这一步为未来负责。不做预测的控制器只会按当下的偏差出力,它不知道两步之后身体的重心会甩到支撑区域外面。有了预测时域,控制器现在就会为了那个未来提前调整。这是 MPC 相对 PD 一类瞬时反馈最实在的增量——如果你还没建立起对反馈控制的直觉,PID 那篇是绕不过去的前置。
简化模型:足式 MPC 的关键妥协
前面说的每一步都要在一个控制周期里跑完。控制周期一旦被占满,控制器就断拍了,这在足式机器人上意味着摔倒。
一台足式机器人的完整刚体动力学,自由度和接触约束都不少,把它原样塞进优化问题里,求解时间很难压进控制周期。所以工程上普遍的做法是用简化模型换求解速度:比如把整个身体近似成一个单刚体,腿的质量忽略掉或者做等效处理,足端对身体的作用只保留力。
这个近似成立的前提是腿相对身体足够轻——腿摆动带来的动量对身体的影响可以忽略。这也是为什么关节电机的设计会往"电机往上放、腿部质量往下减"的方向走:它不只是为了减小转动惯量,也顺带让这类简化模型更站得住脚。
值得注意的是,unitree_guide 的 README 在结尾专门说明,这个仓库提供的是给初学者的基础四足控制器,要获得更好的表现可能需要额外的参数调优或者更进阶的方法,并把 MPC 举为进阶方法的例子。换句话说,仓库里并不包含 MPC 实现——"经典控制"和"MPC"不是一回事,前者是一大类,后者是其中比较重的一种。
分水岭:知识存在哪里,算力花在哪里
两条路线最根本的差异可以压缩成一句话:
MPC 把知识写进模型和约束里,运行时在线求解;强化学习把知识压进网络权重里,离线训练、在线只做推理。
顺着这句话往下推,很多表面差异都能被解释。
计算负担的位置完全不同。 MPC 的重活在运行时——机器人身上那台计算单元每个控制周期都要解一次优化问题,而且必须解完。强化学习的重活在训练时——unitree_rl_gym 的训练脚本带 --num_envs 参数,靠在 GPU 上并行跑大量环境实例把交互量堆出来;但训练完导出的产物只是一个策略网络文件,机器人上跑的就是一次前向传播,计算量相比在线优化轻得多。
知识的来源不同。 MPC 的知识是人写的:动力学方程、摩擦系数、力矩上限、你希望机器人优先保住什么。RL 的知识是从交互里统计出来的:奖励函数只规定了"什么样算好",具体怎么做到,是训练过程自己搜出来的。
因此,改一个行为的方式也不同。 MPC 里想让机器人更爱保姿态、更不在乎速度跟踪,就调代价函数的权重,改完立刻生效。RL 里想改行为,要调奖励项然后重新训练,反馈周期以小时甚至更长计。
逐条摆开
下面六条,每条都尽量说清楚"为什么会这样",而不只是"事实如此"。
可解释性:能不能追溯到某一项
MPC 的每一次输出,原则上都能拆开归因。速度跟不上,去看速度跟踪那一项的权重是不是太小、或者是不是被某个约束顶住了;脚打滑,去看摩擦锥约束的摩擦系数是不是设得过于乐观;姿态发飘,去看姿态项和位置项的相对权重。因为代价函数和约束是你自己写的,输出的每一个特征都能对应回某一行。
RL 策略给不了这个。网络输出一组关节指令,你没有一个"中间变量"可以去查——没有落脚点,没有分配到每条腿的力,没有相位。你能做的是改奖励、改训练环境、改随机化范围,然后再训一遍看看变没变。这个差别不是精度问题,是可追溯性问题。
约束处理:硬的还是软的
这一条是两条路线里差得最实在的地方。
力矩上限、摩擦锥、关节角度范围、法向力不能为负——这些在 MPC 里是约束,写进优化问题的可行域。求解器要么给你一个满足全部约束的解,要么告诉你无解。它不会给你一个"稍微超一点点"的答案。这也是为什么带约束的力分配天然对应二次规划这类形式:约束是一等公民。
RL 里没有这个位置。你唯一的手段是在奖励里加惩罚项——力矩超限就扣分,脚滑了就扣分。这是软约束:训练收敛之后策略大概率会避开这些区域,因为那里得分低;但"大概率避开"和"保证不违反"是两个不同强度的承诺。遇到训练时没见过的状态,没有任何机制拦住它输出一个越界的动作。
所以如果你的项目里有一条真正意义上不能破的红线,纯 RL 方案通常需要在外面再套一层限幅或安全监督,而不是指望策略自己守规矩。
对未建模情况的适应:RL 的主场
反过来,MPC 的全部能力都建立在模型上。模型没有描述的东西,优化问题里就不存在。碎石地、软泥、突然被推一把、负载偏心——这些要么难以建模,要么建了模也会让求解变慢到无法实时。
RL 在这里的优势是结构性的:它不需要把这些情况写成方程,只需要在训练时见过。训练环境里铺上随机地形、给随机外力、随机化摩擦系数和质量参数,策略就会在这些扰动上被反复筛选。unitree_rl_gym 的工具目录里单独有一个 terrain.py 负责地形生成,位置很说明问题——地形不是可选装饰,是训练配置的一部分。
这条优势也划出了它的边界:策略只对训练分布内的情况可靠。随机化范围之外的世界,它没有任何理由表现得好。
调试成本:推理还是试错
MPC 调参有物理意义可以攀。权重之间的比例关系、预测时域长短、约束松紧,每一个都能讲出一句"调大它会怎样"的道理。你可以画曲线、可以看求解器返回的状态、可以把某一项权重归零单独看效果。调试是可推理的。
RL 调奖励更接近试错。奖励项之间会互相牵制——你加大存活奖励,策略可能学会原地不动;你加大速度奖励,可能换来剧烈抖动。哪个权重该动、动多少,很难从原理推出来,只能试,而每一次试的反馈周期是一整轮训练。这部分经验难以形式化,也是这条路线上门槛最高、最依赖积累的一块。
计算资源:运行时的实时性 vs 训练期的算力
MPC 要求机器人身上有能力在每个控制周期跑完一次优化,而且是硬实时——偶尔超时一次,控制就断拍。这对板载计算单元的选型和整个软件栈的实时性都提了要求。
RL 的部署侧很轻:加载一个网络文件,读状态、拼向量、前向一次、发指令。unitree_rl_gym 的部署目录里同时给了 Python 和 C++ 两套实现,C++ 那套的价值主要在实时性和确定性,而不是算力不够。
但训练侧要付另一笔账:GPU、仿真器、以及等待。这笔钱花在项目前期,而且不是一次性的——每次改奖励、加新地形,都要再花一遍。
开发周期:前期重在哪
MPC 路线的前期是推导和建模:写动力学、选简化方式、把代价和约束整理成求解器能吃的形式、验证求解时间。这段工作枯燥但确定,做完了就是做完了。
RL 路线的前期是搭环境:机器人模型(URDF 和网格资产在仓库里往往占了文件数量的大头)、观测量怎么拼、终止条件怎么定、奖励怎么设计、随机化怎么配。搭完还要等训练。这段工作的不确定性更高,因为你不知道第一版奖励能不能收敛出可用的行为。
失败模式:这一节可能最有用
选型的时候大家都盯着"哪个效果好",但真正决定项目生死的常常是"坏掉的时候什么样"。
MPC 的失败通常是可理解的。 常见的几种:
- 模型偏差过大。真实系统和模型对不上,优化出来的力施加下去产生的效果不是预期的那个,表现为持续的稳态偏差或者振荡。你能看出来它在偏,也能顺着偏的方向去查模型的哪一项不准。
- 求解超时。约束变多、问题变难,某个周期没解完。这种失败有明确的现场——求解耗时曲线上会有尖峰。
- 约束冲突无解。要求的加速度在当前摩擦条件和力矩上限下根本做不到,求解器直接报不可行。这时候需要有兜底策略(放松某些约束、降级到保守模式),但至少你知道它为什么放弃。
这三类失败的共同点是:有征兆、可归因、可复现。你能拿到日志坐下来分析。
RL 的失败经常是没有前兆的。 策略在训练分布内表现稳定,一旦遇到分布外的状态——一个没随机化到的地面材质、一个超出训练范围的扰动、一次传感器异常导致的畸形观测——输出可能直接变成一组毫无道理的关节指令。
这种失败的麻烦在三点:没有预警(前一秒还好好的)、没有中间量可查(网络内部不对应任何物理概念)、难以复现(你未必能重建出触发它的那个具体状态)。
这也解释了为什么 RL 部署链路里那些看似琐碎的安全设施是必需品而不是可选项:unitree_rl_gym 的部署代码里单独有遥控器接入模块,官方文档也强调真机部署前要确保机器人处于调试模式。手上必须有一个能立刻切断策略输出的开关——因为你无法从策略内部预知它什么时候会失手。
顺带说一句,这也是为什么 Sim2Sim 那一步值得单独存在:换一个物理引擎再验一遍,能低成本筛掉一批"只在特定仿真器里成立"的策略。这条链路上的坑,sim2real 那篇里展开更细。
现实中不是二选一
把它写成一道单选题,本身就是这个话题最常见的误解。领域里被反复讨论的组合方式至少有几类:
用 MPC 兜安全边界,RL 管主策略。 策略输出的指令先过一层基于模型的检查或投影,把明显违反物理约束的部分修正掉。这样保留了 RL 的适应性,同时把"硬约束"这个能力补回来。
用 MPC 或经典控制生成数据,引导 RL 训练。 让一个已经能工作的控制器提供参考轨迹或参考动作,训练时给"贴近参考"的行为额外奖励。好处是能大幅缩短前期那段"策略什么都不会、奖励曲线纹丝不动"的时间。
分层:RL 在上,模型方法在下。 上层策略输出的不是关节力矩,而是更抽象的量——期望速度、落脚点、身体姿态目标;下层用模型方法把它变成关节指令。这样黑箱的范围被限制在决策层,执行层仍然可解释。
共享的基础设施不分路线。 两条路都要状态估计——RL 策略的观测向量里同样需要身体姿态和速度,这些量照样得靠融合估计出来;两条路都要一个外层状态机来管"什么时候允许控制器接管、什么时候必须回到安全状态"。经典控制器里那个 Passive 状态存在的理由,在 RL 部署里一点没变。
这些是领域内被广泛讨论的思路,不是某一个具体仓库的做法——前面引用的两个仓库各自走的都是单一路线,别把这一节的内容安到它们头上。
六个问题,自己回答
把选型压缩成一组可以自问的问题:
一、我的场景可建模吗? 地面性质稳定、负载已知、扰动有界——模型方法的假设成立,MPC 的收益就能兑现。反过来,如果核心难点恰恰是那些你写不出方程的东西,模型方法从一开始就在打逆风。
二、我需要能解释吗? 要向别人论证"这个系统在什么条件下不会失控",或者出了事故必须能归因,那可解释性就不是加分项而是准入条件。
三、我有仿真环境和算力吗? 机器人模型、仿真器、GPU、以及愿意花在训练上的时间。这四样缺一样,RL 路线的启动成本就会陡增。
四、我的约束是硬的还是软的? 有真正不能破的红线,就得考虑谁来保证它——是写进优化问题,还是在策略外面加一层。
五、谁来维护? 后续改需求的人,是更擅长推公式还是更擅长调训练?这条很少被摆上台面,但它决定了三个月后这套系统还能不能演进。
六、我在学习还是在交付? 如果目标是搞懂足式控制,模型方法几乎是必经之路——它会逼你把落脚点、力分配、约束这些子问题一个个想明白。带着这些认识再去看策略网络,你才知道它替你省掉了什么、又把什么变成了黑箱。
最后
MPC 的核心不是"用了模型",而是"滚动时域":每周期重新求解,用最新状态把模型偏差摁回去。理解了这一点,你就理解了它为什么能容忍不准的模型,以及为什么它对实时性那么敏感。
两条路线的分水岭是知识的存放位置。 写在模型和约束里,你就得到可解释性和硬约束,代价是运行时的计算负担和对建模的依赖;压进网络权重里,你就得到对未建模情况的适应,代价是黑箱、软约束和训练期的成本。
失败模式比性能更值得纳入选型。 一个坏得可归因的系统,和一个坏得莫名其妙的系统,在工程上是两种完全不同的东西。