为什么双足行走如此困难
在所有人形机器人的演示里,最抓人的往往不是抓取也不是对话,而是稳稳地走起来。让两条腿交替支撑一个一米多高的身体,看上去是人类三岁就会的事,工程上却是控制领域最难的问题之一。难点不在机械加工,而在动力学:双足机器人在任何瞬间只有一只脚或两只脚着地,支撑面小得可怜,身体重心一旦越出支撑区域,重力会迅速把它拉倒。
和轮式、四足平台相比,双足系统的困难集中在三点。第一是欠驱动:腿部的关节数远多于受控自由度,机器人无法像机械臂那样直接指定末端位置。第二是接触的不连续:脚每落地一次,动力学方程就切换一次,冲击与摩擦都发生在这个瞬间。第三是强耦合:摆动手臂会改变躯干姿态,躯干姿态又反过来影响落脚点,任何一处调整都会牵动全身。
因此,双足行走从来不是「管好一条腿」这么简单,而是一套分层的系统工程:先用简化模型弄清楚身体该往哪走,再规划脚该落在哪里,最后用全身控制把几十个关节的力矩算出来并按时下发。
把双足机器人想成一根被手指顶着的长杆:手指必须不停微调位置,长杆才能保持直立。行走,就是这根长杆主动把「手指」挪到前方再落地。
- 欠驱动双足机器人靠脚底接触获得支撑,本质上是不稳定、欠驱动的系统。
- 线性倒立摆把全身质量简化为一个质心,得到可解析、可控的水平动力学。
- ZMP零矩点必须落在支撑多边形内部,机器人才不会翻倒。
- 全身控制用带约束的二次规划同时求解平衡、姿态与手臂任务。
倒立摆模型:把身体简化为质心
要控制一个几十自由度的系统,第一步是降维。工程上最常用的简化是线性倒立摆模型,也就是把全身质量集中到一个质心点上,把双腿看成一根没有质量、长度可伸缩的杆:杆的一端是质心,另一端踩在地面上。这个模型丢掉了关节细节,却抓住了行走最关键的部分——质心的水平运动。
在该模型中,质心高度固定为 z,水平方向的动力学可以写成一个二阶微分方程,系数由自然频率 ω 决定,而 ω 等于重力加速度除以质心高度后的平方根。ω 越大,系统越「急」,身体被拉回支撑点的速度越快。以身高约 1.7 米、质心高度约 0.85 米的机器人为例,ω 大致落在 3.4 弧度每秒附近。
下面这段代码用最朴素的前向欧拉法推进倒立摆模型,观察不加控制时质心位移会怎样发散:
import numpy as np
# 线性倒立摆:质心高度 zc,重力加速度 g
g, zc, dt = 9.81, 0.85, 0.01
w = np.sqrt(g / zc) # 自然频率,约 3.4 rad/s
def step_lipm(state, ux):
# state = [x, vx];ux 为质心水平加速度指令
x, vx = state
ax = w * w * (x - ux)
return np.array([x + vx * dt, vx + ax * dt])
state = np.array([0.0, 0.30]) # 初值:前向 0.30 m/s
traj = []
for k in range(200):
state = step_lipm(state, 0.0) # 不给控制,观察发散
traj.append(state[0])
print("第 2 秒质心位移:", round(traj[-1], 3))
跑一遍就会看到,只要质心稍微偏离支撑点,位移会在两秒内迅速放大——这正是「倒立摆不稳定」的数值证据,也是每一步都必须主动规划落脚点的根本原因。反过来说,只要能把落脚点选在合适的位置,让质心在两次落脚之间自然摆动,就能用很小的控制力维持行走。
ZMP 与步态规划
落脚点定在哪里,需要一个稳定的判据。零矩点给出的答案最经典:它是地面上使机器人整体力矩水平分量为零的那个点。只要零矩点始终落在支撑多边形内部,机器人在数学上就不会绕支撑边界翻倒。把零矩点当作「必须被守在脚下的点」,是理解整个步态规划流程的钥匙。
规划时,工程师先由期望行走速度反解出质心轨迹,再由质心轨迹推出对应的零矩点参考,然后用预观控制让实际零矩点跟踪参考值:控制器提前若干个控制周期读取未来的零矩点参考,据此算出当前的质心修正量。这也是经典的平板小车模型解法,长期被工业界直接用于双足平台的行走控制。
| 步态参数 | 含义 | 典型取值 |
|---|---|---|
| 质心高度 | 决定自然频率与行走节奏 | 0.8–1.0 米 |
| 步长 | 单步前进的距离 | 0.4–0.8 米 |
| 步频 | 每秒迈步的次数 | 1.2–1.8 次 |
| 双支撑期占比 | 双脚同时着地的时间比例 | 15%–30% |
| 落脚点容差 | 允许的落点偏差 | 正负 2–5 厘米 |
步态规划的另一半是时间规划:每一步的抬脚高度、摆动时长、落地时刻都要与实际关节的能力匹配。迈得太快,关节跟不上摆动轨迹,直接变成绊脚;迈得太慢,单腿支撑期过长,平衡控制的余量被一点点吃掉。经验做法是先把步频定在每秒 1.5 步左右,再逐步放宽到机器人能稳定承受的极限。
平衡控制:从状态估计到全身控制
规划给出的是「理想身体」,真实机器人还要面对摩擦、延迟和外部扰动。平衡控制的第一环是状态估计:把惯性测量单元的角速度与加速度、关节编码器的位置,以及足底力传感器的读数融合起来,估计出躯干姿态与质心速度。互补滤波与卡尔曼滤波是两种最常见的做法,前者实现简单,后者在噪声建模准确时更优。
第二环是分层控制。上层任务层把「以 0.5 米每秒前进」翻译成质心参考与落脚点序列;中层用模型预测控制滚动优化未来一小段时间的输入;底层则是全身控制:把所有任务写成带优先级或带权重的约束,用一个二次规划同时求解,既满足关节力矩上限与摩擦锥约束,又尽量完成平衡、姿态与手部任务。
def control_loop(robot, planner, wbc, dt=0.001):
while robot.running:
t = robot.time()
# 1. 状态估计:融合 IMU 与关节编码器
est = robot.estimate_state()
# 2. 任务层:由速度指令生成质心与落脚点参考
ref = planner.update(est, robot.command)
# 3. 全身控制:求解带约束的二次规划
tau = wbc.solve(est, ref)
# 4. 下发关节力矩,等待下一个控制周期
robot.send_torque(tau)
robot.sleep_until(t + dt)
这套分层结构的价值在于可解释:平衡变差时,可以依次检查状态估计是否漂移、模型预测的时域是否过短、二次规划的约束是否过紧,而不是面对一个端到端网络无从下手。工程上常用的调参顺序也是从下往上:先把状态估计调准,再放开控制层的约束。
仿真环境与训练流程
真实的双足机器人摔一次代价很高,所以大部分策略先在仿真里练。MuJoCo、Isaac Sim、PyBullet 是三种主流选择:第一种以接触求解精度见长,第二种胜在渲染质量与大规模并行,第三种则以轻量和易上手著称。无论选哪一个,域随机化都是缩小「仿真到真机」差距的关键手段——随机化摩擦系数、连杆质量、传感器噪声与执行延迟,让策略学会适应不确定的环境。
scene:
robot: unitree_g1
terrain: flat
gravity: 9.81
sim:
timestep: 0.001
solver_iterations: 100
render: false
train:
algorithm: ppo
num_envs: 4096
episode_len: 1000
randomize:
friction: [0.6, 1.2]
mass_scale: [0.9, 1.1]
latency_ms: [0, 20]
reward:
alive: 1.0
track_lin_vel: 1.5
feet_air_time: 0.4
torque_penalty: -0.0001
强化学习的训练目标由一个奖励函数决定,常用的几项包括:
- 存活奖励:每个控制周期都给一个小正数,鼓励机器人尽量不摔倒。
- 速度跟踪:奖励实际线速度与指令速度的接近程度,这是「会走」的核心。
- 步态周期:奖励两脚交替摆动的时间均衡,防止机器人拖着一只脚前进。
- 能耗与力矩惩罚:抑制大幅抖动,让动作更平滑也更省电。
训练得到的策略通常输出关节目标位置或力矩,再经过一层滤波后下发。值得注意的是,仿真里表现完美的策略,在真机上仍可能因为延迟与摩擦建模误差而失效,因此域随机化的区间往往要覆盖到比预期更极端的参数范围。
常见问题与调参经验
把机器人从「能走」调到「走得稳」,大部分时间花在反复排查几类典型问题上:
- 高频抖动:多来自控制频率不足或增益过大,先确认控制周期是否稳定在 1 毫秒,再逐级降低增益。
- 脚下打滑:摩擦锥约束写得过松,或地面摩擦系数估计偏高;把摩擦系数下界调低后重新训练。
- 步态不对称:左右腿零位标定有偏差,用关节编码器的静态读数逐关节对齐。
- 仿真到真机掉点:延迟没有建模,或电机响应被理想化;把延迟随机化区间扩大到实测范围。
- 能耗过高:轨迹过于激进,蹲得过低或抬脚过高;先优化质心高度,再谈步长与步频。
排查顺序建议自下而上:先看机械装配与标定,再看状态估计,最后才怀疑策略网络。很多被当成「算法问题」的现象,追到最后其实是传感器零点漂移或通信延迟造成的。
从实验室到落地的工程路径
一条稳妥的落地路径通常分五步:仿真中稳定行走,吊架保护下原地踏步,平地缓慢直行,转向与斜坡,最后才进入复杂地形与长时间运行。每一步都要求上一阶段能连续稳定运行足够长的时间再进入下一阶段,避免把多个尚未验证的因素叠在一起,否则一旦出问题很难定位。
硬件层面同样充满取舍:更高的控制频率意味着更好的抗扰能力,但也对总线带宽与算力提出更高要求;更轻的腿部降低能耗,却牺牲了抗扰动所需的惯量。这些取舍没有通解,只能结合目标场景——是服务、巡检还是科研演示——来定。
稳定行走不是某一项算法的胜利,而是建模、控制与工程细节共同收敛的结果:模型给出方向,实测给出修正,而可复现性来自把每一个环节都验证到位。
站稳只是第一步。当机器人能够稳定移动之后,下一步要解决的是「用手干活」——灵巧手的抓取能力与多模态交互,我们放在下一篇展开。