简介:足式机器人平衡算法的经典文献,源自Marc H. Raibert的《Legged Robots that Balance》,适合机器人控制、仿生运动与人工智能交叉领域的研究者及进阶学习者阅读。文档系统梳理了腿足式机器人从弹性腿模型到三维空间奔跑控制的发展脉络,并围绕主动平衡、运动控制、动力学建模、传感器融合与反馈控制等关键环节展开论述。压缩包内为1份PDF全文,大小7.13MB,适合离线阅读与批注。该资源已有394人学习下载,可作为足式机器人控制理论入门的参考资料。内容除正文外,还包含目录框架与作者的前言叙述,章节安排从研究动机、动力学与平衡性分析,到奔跑控制分解、三维空间运动及多腿系统均有系统性论述;读者亦可从中了解Leg实验室的研究历程、实验录像带索引以及动态腿部系统控制中尚未解决的开放问题,有助于把握平衡算法的核心思路与历史背景。
1. 足式机器人平衡算法:从经典文献到能跑的控制器
四足、双足机器人真正难住人的从来不是机构设计,而是让那条单腿在落地瞬间不“跪”下去的那几百毫秒。市面上大多数开源足式机器人项目,硬件装起来一周,但调平衡稳定至少要一个月——原因在于大家把“平衡”理解成了“静态站稳”,而真实场景里机器人每一步落地都是一次动态冲击。doc1里那份经典文献《Legged Robots that Balance》早在几十年前就把答案写清楚了:平衡不是力大出奇迹,而是把机身建模成一个倒立摆,用关节力矩去“追”质心的加速度轨迹。这篇笔记我会按照从原理到落地的顺序,把倒立摆建模、ZMP 判据、仿真到实机的迁移、增益整定这些环节逐个拆开,给出可以直接复用的代码与参数经验。
如果你正在做四足机器人或者双足项目的姿态控制,发现自己明明PID 调得很“稳”,但机器人一走起来就东倒西歪,那这篇文章就是给你写的。我会刻意绕开那些玄学调参套路,用一条从状态方程到代码实现的完整链路,让你知道每一步在干什么、为什么这么干,以及实机上最常见的翻车点在哪。
2. 平衡算法核心模型:倒立摆与 ZMP 的理论基础
2.1 为什么线性倒立摆模型能描述足式机器人的动态平衡
足式机器人平衡算法里最经典、也最容易被新手误用的模型,是线性倒立摆模型(Linear Inverted Pendulum Model,LIPM)。理解它的第一步是接受一个事实:我们不是要模拟机器人每条腿的完整动力学,而是把整个机身等价成一个集中质量点,这个点位于质心位置,腿则被抽象成一根无质量的可伸缩杆,杆底端就是支撑脚与地面的接触点。
之所以能做这种简化,是因为足式机器人行走过程中,支撑腿承受的力主要沿腿轴方向,而摆动腿的惯性力相对整机质量来说占比极小。把动力学方程在质心高度恒定的约束下做线性化之后,水平方向的运动方程会变成:
x_ddot = g / z_c * (x - p_x)这里x是质心水平位置,z_c是质心高度(假设恒定),p_x是 ZMP(零力矩点)位置,g是重力加速度。这个方程的价值在于:它把“平衡”这个问题从关节空间直接映射到了“质心- ZMP”这对变量上。控制目标因此变得极其清晰——你不需要关心每条腿的关节角是多少,只需要保证 ZMP 落在支撑多边形内部,机器人就不会翻倒。
实际工程中,我在做双足机器人时通常把目标 ZMP 轨迹规划成前后摆动的正弦曲线,然后反解出质心轨迹作为前馈;四足机器人对角小跑时则用同样的方程分别解出前后方向和左右方向的质心运动。这里有个容易犯的错误:LIPM 只在质心高度近似恒定时才成立,如果你把机器人做成一跳一跳的弹跳步态,那么就必须换成弹簧倒立摆模型(SLIP),否则模型误差会直接让控制器发散。
2.2 ZMP 判据的物理意义与工程简化
ZMP(Zero Moment Point,零力矩点)的定义是:地面对机器人反作用力的合力在地面上的作用点,且该点处合力矩为零。听起来很学术,但工程上的意义非常直白——如果 ZMP 落在支撑脚的凸包范围之外,机器人就会绕支撑多边形边缘翻转,也就是说,ZMP 就是“机器人会不会倒”的判据。
实际测量 ZMP 有两种方式:一种是依靠力传感器,通过六个力/力矩传感器直接计算地面反力的作用点;另一种是依靠动力学模型估计,用关节角度、角速度和力矩通过浮基动力学方程推算。我个人的经验是,如果硬件条件允许,优先用脚底力传感器测 ZMP,因为模型估计的方式依赖惯量参数标定,而惯量参数在实机上很难标准——尤其是在加了各种载荷之后。
ZMP_x = - (M_y + x_com * F_z + z_com * F_x) / F_z注意这个计算里最容易被忽略的项是z_com * F_x。当机器人快速前进或后退时,水平方向的惯性力会产生一个绕 y 轴的力矩,这个力矩直接改变了 ZMP 位置。很多同学在实机上看到的“机器人加速时前倾翻车”,本质上就是没有把质心高度的水平加速度产生的力矩算进 ZMP 里。
工程简化上,我一般把 ZMP 算出来之后做一个低通滤波,截止频率设在 5~10Hz 左右,因为力传感器的噪声很大,直接拿原始值做闭环控制会让关节抖动得厉害。滤波引入的延迟大约 20~50ms,对平衡控制来说是可以接受的,但对快速扰动(比如突然踢一脚)的响应会有明显延迟,所以控制频率至少要跑到 1kHz 以上才有足够的相位裕度。
2.3 捕食者-猎物模型:LIPM 如何转化为可执行的控制量
有了 LIPM 状态方程和 ZMP 目标轨迹之后,接下来要解决的是“怎么把 ZMP 误差换算成关节力矩”。常见做法是分两层:上层是质心轨迹跟踪,用 ZMP 误差做反馈修正质心加速度;下层是关节力矩映射,把质心加速度通过逆动力学换算成各关节力矩指令。
上层控制我常用的形式是比例-积分控制加前馈:
x_cmd_ddot = x_ref_ddot + Kp * (x_ref - x) + Kd * (x_ref_dot - x_dot) + Ki * integral(zmp_ref - zmp)这里x_ref_ddot是从规划器来的前馈加速度,Kp和Kd是质心位置和速度的反馈增益。Ki项针对 ZMP 误差做积分,用来消除脚底打滑或者斜坡导致的静态偏差。积分项的系数要很小,否则容易在支撑脚切换时产生较大的过冲。
下层转换到关节力矩,我一般直接用浮基逆动力学(Floating Base Inverse Dynamics),而不是逐关节做 PID。原因很简单:足式机器人是闭链系统加浮基,关节空间 PID 处理不了身体姿态和质心运动的耦合。具体实现上,可以用开源的任务空间控制库来解算,也可以手动推导拉格朗日方程后做数值求解。
实际调试中,Kp和Kd的初始值可以按临界比例法来定:先把Kd设为零,Kp从 0.5 开始逐步增加直到机器人出现等幅振荡,记录此时的Kp_crit和振荡周期T_crit,然后按经典 Ziegler-Nichols 的经验公式取Kp = 0.6 * Kp_crit,Kd取0.1 * Kp_crit * T_crit附近做微调。后面整定章节我会详细说这个过程的踩坑。
3. 从仿真到实机的平衡控制器实现路径
3.1 在 MuJoCo 里搭建一个四足机器人的平衡仿真环境
先说明,这里的仿真环境搭建目的不是做步态规划,而是专门验证平衡算法的鲁棒性。我的习惯是:先搭一个包含四条腿的简化刚体模型,躯干用一个大质量的长方体代替,每条腿三个自由度(髋关节横滚、髋关节俯仰、膝关节俯仰),这样既能体现足式机器人的主要动力学特性,又不会因为模型太复杂导致仿真速度过慢。
MuJoCo 的 XML 模型文件里,核心要设置好三个地方:关节的armature(电机转子惯量折算)、frictionloss(关节库仑摩擦)和接触参数。armature这个参数直接影响关节响应的快速性,设太小会让仿真里的关节像“空心轴”一样反应过度灵敏,实机上根本达不到。我通常会把电机转子惯量等效到关节上,大概占连杆惯量的 10%~20% 之间。
接触参数里最关键的solimp和solref分别控制接触的硬度和阻尼。这里有一个经验值:让接触硬度适中,避免仿真中脚底接触地面时出现高频振铃。如果仿真里脚底一落地就弹起来,多半是接触参数设置过刚,把solref的第二个值调大(增加阻尼)就能解决。
<default> <joint armature="0.05" damping="0.5" frictionloss="0.5"/> <geom friction="1.0" solimp="0.9 0.95 0.001" solref="0.01 1.0"/> </default>上面的friction设置的是库仑摩擦系数,solimp的前两个值决定接触力的非线性过渡形状,solref决定接触动力学的时间常数。仿真中如果发现机器人站不稳,先别急着调控制器参数,优先检查接触参数是否合理——这是最容易被忽视的坑。
3.2 单腿站立平衡的最小闭环控制代码
当模型建好之后,第一个要跑通的任务是“单腿站立平衡”。这里我直接用 LIPM 质心反馈作为外环,内环用关节空间的力矩映射。下面这段代码是一个最小可运行版本,重点展示状态获取、平衡控制律计算、力矩下发三个环节:
import mujoco import numpy as np # 获取状态 data.qpos # 关节位置 data.qvel # 关节速度 x, z = data.qpos[0], data.qpos[2] # 机身质心位置 dx, dz = data.qvel[0], data.qvel[2] # LIPM 平衡控制器 z_c = 0.45 # 质心高度目标 g = 9.81 x_ref = 0.0 # 期望质心位置 dx_ref = 0.0 # 期望质心速度 kp = 80.0 # 等效比例增益 kd = 2 * np.sqrt(kp) # 临界阻尼对应的微分增益 # 计算期望的质心水平加速度 x_ddot_ref = g / z_c * (x - x_ref) + kd * (dx - dx_ref) # 期望的足底反力 = 质量 * 加速度 + 重力补偿 m = data.body('trunk').mass f_x = m * x_ddot_ref f_z = m * g + m * dz # 垂直方向做重力补偿加阻尼 # 利用雅可比矩阵映射为关节力矩(简化版) J = np.zeros((2, 3)) # 此处应从 MuJoCo 获取对应雅可比 tau = J.T @ np.array([f_x, f_z]) data.ctrl = tau这段代码的核心逻辑是:外环把机器人当成一个倒立摆,计算出需要施加在质心上的水平力;内环通过雅可比矩阵把这个力分配到腿部各关节的力矩上。参数里最关键的是kp和kd的匹配关系,kd = 2*sqrt(kp)给出的是临界阻尼响应,也就是机器人回到平衡位置时既不超调也不振荡的理想状态。
实际运行中你会发现,单腿站立时机器人的髋关节横滚方向(侧向)比前后方向更难稳定,原因在于侧向的支撑多边形宽度更窄,ZMP 允许的移动范围更小。所以我会在侧向通道把kp适当调低 30% 左右,同时增大kd,用更“肉”的响应换取稳定裕度。
3.3 步态切换时的平衡保持策略:从静态到动态的过渡
单腿站稳之后,下一个难点是走路过程中支撑腿切换瞬间的平衡保持。这里最常见的设计模式是“分时控制”:单腿支撑期用 LIPM 平衡控制,双腿支撑期用位置插值过渡,切换时刻用冲击力补偿来处理载荷转移。
我做四足对角小跑时,通常把每个步态周期分成四个阶段:支撑前段(对角两条腿着地,质心向前移动)、支撑后段(后腿蹬地产生推进力)、腾空相(四条腿全部离地,靠躯干姿态控制维持姿态)、落地缓冲(前腿落地,吸收冲击)。其中腾空相对平衡算法最不友好,因为此时 ZMP 没有任何物理意义,必须切换到姿态控制模式。
落地缓冲的瞬间是最容易翻车的时刻。我常用的做法是:在落地前 30ms 预测落地腿的位置,提前将落地腿的关节力矩设为“软阻尼”模式,也就是只提供阻尼力、不提供位置跟踪力,让腿像弹簧一样缓冲落地冲击。落地完成后 50ms 再把控制权交还给 LIPM 平衡控制器。
# 落地缓冲状态切换伪代码 if leg_contact and not prev_contact: set_leg_impedance(low_stiffness, high_damping) enter_landing_buffer_mode() elif time_since_contact > 50ms: set_leg_impedance(normal_stiffness, normal_damping) enter_lipm_balance_mode()这个切换过程在仿真里很容易被忽略,但实机上极其重要——很多同学的四足机器人在仿真里走路很稳,一上实机每走一步都像踩棉花一样晃,大概率就是落地缓冲没有做,相当于每次落地都是一次随机冲击输入。
4. 平衡控制器增益整定:从仿真参数到实机鲁棒性
4.1 增益整定的三个层次:关节内环、姿态中环、平衡外环
足式机器人平衡控制器的参数整定,我的经验是必须分层进行,绝对不能在整定完关节内环之前就去调平衡外环。层次之间有一个不可跳过的顺序:关节力矩环(电流环)→ 关节位置环(PD 或 PID)→ 机身姿态环(横滚、俯仰、偏航)→ 质心平衡环(LIPM 控制器)。
每一层都有独立的响应带宽要求。关节力矩环带宽要最高,通常期望在 100Hz 以上;关节位置环带宽 10~30Hz;姿态环带宽 3~8Hz;质心平衡环带宽再低一些。如果上一层比下一层的带宽没有拉开 3 倍以上的差距,那么下一层会把上一层的动态耦合进来,整个系统就会出现“你调我的、我调你的”的相互干扰。
拿一个我做过的双足项目举例,某次机器人站立时出现持续横滚方向抖动,频率约 4Hz。我当时以为是 ZMP 反馈增益太高,把Kp调低了一倍,结果抖动反而加剧了。后来排查发现是姿态环带宽(当时约 12Hz)和平衡环带宽(约 5Hz)之间差距不够,姿态环的修正力矩干涉了平衡环的质心控制。把姿态环带宽降到 6Hz 之后,抖动立刻消失。
4.2 用轨迹圆滑度评估整定参数好坏的量化方法
调参不能靠手感,需要有可量化的评估指标。我常用的一个指标是“支撑腿关节力矩的平滑度”,具体计算方式是相邻两个控制周期内关节力矩差值的 RMS(均方根值):
τ_smoothness = RMS(τ(t) - τ(t - ΔT))这个值越小,说明控制器输出越平滑,也意味着机械磨损越少。如果这个值偏大,有两种可能:一是传感器噪声大、滤波不够;二是某个反馈环增益偏高导致输出出现“毛刺感”。在仿真里,我会把力矩平滑度和 ZMP 误差的 RMS 放在一起看,两个值同时低,才算全局最优。
另外有个很实用的技巧:记录机器人受到冲击后的恢复时间。具体做法是在机身后方用绳子拉住,然后释放,记录机身俯仰角从最大偏差恢复到 ±2° 以内所用的时间。这个时间如果小于 1 秒,说明姿态环的响应够快;如果大于 2 秒,说明增益偏低,或者摩擦模型不准导致控制力矩在前几拍被摩擦力吃掉了。
4.3 实机调试时参数需要妥协的三个地方:电机饱和、结构共振、地面打滑
仿真里可以无限追求跟踪精度,但实机上有三个物理约束,直接决定了你最终用的参数一定是一个“折中值”而不是“最优值”。
第一个约束是电机力矩饱和。LIPM 控制器给出的期望质心加速度,在仿真里可以放大Kp到任意值来增强抗扰能力,但实机上髋关节电机的峰值力矩是固定的。当 ZMP 误差较大时,期望力矩会超过电机饱和值,这时控制器的实际输出被截断,等效增益瞬间下降。解决方法是做“力矩限幅下的抗积分饱和”:在积分项里加入torque_saturated的反馈项,让控制器意识到输出已经限幅,停止继续积分。
第二个约束是结构共振。每个足式机器人都有机械共振频率,通常在 8~20Hz 之间。如果你的姿态环增益过高,修正力矩的频率可能正好落在共振点附近,机械本身会被激励起来,表现为机身持续高频振动,而且你会看到关节角度反馈正常、但机身传感器数据在剧烈波动。对策是在控制器输出前加一个低通滤波器,截止频率设在 15Hz,并避免在共振频率附近设置过高的增益。
第三个约束是地面打滑。脚底与地面的摩擦系数取决于接触材料,在干燥地毯上和潮湿瓷砖上差异很大。ZMP 控制的前提是支撑脚相对地面不打滑,一旦打滑,ZMP 反馈会给出错误的“平衡”信号,控制器会往错误方向加力,越拉越倒。实机上我至少会在两种以上地面测试一遍参数,如果最优参数差异超过 30%,就得做“地面自适应”:用一个滑动窗口估计脚底速度,检测到滑动时降低 ZMP 反馈增益。
5. 平衡算法实战避坑指南:三条血泪经验与排查路径
5.1 仿真里站得稳,实机上一站就倒:摩擦模型与接触刚度的差异
现象:仿真环境中单腿站立可以保持 5 分钟不晃,但同一套代码部署到实机后,机器人站上去 2 秒内就向某个方向倾倒,反复试都一样。
原因:90% 以上的情况是仿真里的接触参数和实机的脚底-地面接触差异过大。MuJoCo 默认的接触刚度很高,脚底微小的滑移会被模型自动约束掉,相当于“虚拟钉子”;而实机上橡胶脚垫有法向形变和切向柔顺,导致 ZMP 的实际支撑边界比仿真里小,等效支撑多边形缩水了。
解决:在仿真里故意降低接触刚度,同时调高摩擦系数到 1.2~1.5 左右,让模型“变笨”一点。具体做法是把solref的时间常数调大 3~5 倍,并减少friction到实际材料的保守值。如果调完参数后仿真里的机器人也开始偶尔滑动,恭喜你,仿真终于和实机“对齐”了。我通常还会在两个仿真环境下跑对比——一个默认参数、一个保守参数,控制器的参数必须同时通过这两个环境才算合格。
5.2 站立时持续低频抖动:姿态环与平衡环带宽打架
现象:机器人站立时看起来在“打哆嗦”,机身以 3~5Hz 频率持续前后或左右小幅振荡,振幅不大但一直不衰减。
原因:这是最典型的控制带宽耦合问题。姿态修正的响应带宽和质心平衡环的带宽重叠,姿态环检测到微小姿态偏差就产生修正力矩,这个力矩改变了支撑腿的受力分布,进而让 ZMP 发生变化,平衡环感知到 ZMP 变化后再次输出修正,形成闭环振荡。
解决:先把姿态环的带宽降下来,同时在 ZMP 反馈通道加带通滤波,把 3~5Hz 这个频段的信号衰减掉。需要注意的是,不要只调一个环的参数——要同时调整两个环的带宽比例,让姿态环带宽至少是平衡环带宽的 2 倍以上,或者反过来让平衡环慢到不会去响应姿态环的修正动作。还有一个容易被忽略的点:机身 IMU 的安装位置如果不在质心附近,角速度测量的耦合误差也会激发这种振荡,排查时绕着 IMU 底座加点减震垫往往有奇效。
5.3 单腿落地瞬间脚跟“软掉”或“硬顶”:切换逻辑时序不对
现象:机器人在行走步态中,支撑腿落地的那一刻,膝关节先是不受力一样地弯曲,然后突然变硬,机身明显顿挫一下,严重时直接把机身弹向反方向。
原因:落地缓冲和位置跟踪两种模式的切换时序不当。如果切换太早——脚还没真正触地就把关节切成低刚度模式,那么落地那一刻腿会因为自重快速弯曲,没有足够的支撑力;如果切换太晚——已经触地了还在执行高刚度的位置跟踪,那么落地冲击会被直接传导到机身,表现为硬碰硬的冲击。
解决:用脚底接触传感器(力传感器或触觉开关)作为模式切换的真值触发,而不要用时间估算。切换逻辑做成两段式:预测触地前 20ms 进入“快接近模式”(中等刚度、高阻尼),实际触地后 40ms 进入“缓冲模式”(低刚度、极高阻尼),等机身运动速度归零后再恢复位置跟踪。这里面有个小技巧:判断缓冲模式结束不能只看时间,要看机身垂直方向速度是否已经低于某个阈值(比如 0.05m/s),否则在楼梯等不规则地形上会因为固定时间过长而摔倒。
6. 更进一步的平衡技巧:用加速度反馈补偿足端力矩干扰
当你的机器人已经能站稳、能走直线之后,如果还想提升抗冲击能力,我推荐一个进阶方向:加速度反馈补偿。
基础版的 LIPM 控制器相当于一个质心位置/速度的 PD 控制,它只能被动地响应扰动。也就是说,机器人必须先被推一下(产生位置偏差),控制器才能开始修正。这在静平台上是够用的,但如果在行走中遇到地形突变或外力冲击,被动响应往往来不及。
我常用的做法是加入机身加速度前馈。具体实现:在机身质心位置安装一个三轴加速度计(或者从 IMU 中提取加速度信号),把这个加速度信号直接加到质心期望加速度上:
x_ddot_final = x_ddot_ref + K_ff * (x_ddot_measured - x_ddot_model)这里的K_ff通常在 0.1~0.4 之间。它做的事情本质上是:当外力让机身产生加速度变化时,控制器立刻感知到这个变化(通常是滞后 5ms 内),直接调整力矩输出,而不需要等位置积分出偏差。这个技术对于抗侧向踢腿效果非常明显——不加这个补偿时,侧向推一下大约要 300ms 才能恢复平衡;加上之后,100ms 内就能把机身拉回目标位置。
但要注意一个非常隐蔽的坑:加速度计测量到的信号包含重力分量,所以在做加速度反馈之前必须先做重力解耦。如果你直接把 IMU 的三轴加速度数值拿来用,K_ff只要大于 0.1,机器人就会因为重力分量被当成干扰反馈而持续产生错误力矩,表现就是站立时关节“发硬”且伴有低频哼声。我见过不止一个同学在这个问题上卡了一周,最后发现是忘了减掉[0, 0, 9.81]这个重力偏置。
另外一个实用技巧是:把加速度信号做 30Hz 左右的低通滤波之后再做反馈,因为加速度计的高频噪声很大,不过滤波引入的延迟也会让补偿效果打折扣。我的做法是用互补滤波——加速度信号经过低通、角速度信号经过高通,两者融合后得到一个比单独用加速度计噪声更小、比单独用角速度延迟更低的反馈信号。
方法层面讲到这里,最后分享一个自己总结的调试习惯:每次给足式机器人换一套平衡参数,我都会先在地面铺一层 3cm 厚的软垫上测试,然后把软垫换成硬木板,再到瓷砖地面。这样从阻尼最充足到最小摩擦的过渡过程中,你能清晰感觉到控制器在哪个环节开始力不从心——是电机饱和了,还是接触模型不对了,还是带宽耦合了。调平衡算法没有“一次到位”的灵丹妙药,但有了这条感知链路和上面的排查路径,至少每一轮迭代都知道该往哪个方向使劲。希望帮到你。
本文还有配套的精品资源,点击获取