跳到主要内容

5. 步态控制

站立控制只需维持一组关节目标,行走则要持续回答三个问题:哪条腿着地、摆动腿向哪里落、执行器如何跟踪关节目标。一条可解释的模型驱动管线可以把它们拆成:

步态时钟 -> 足端轨迹 -> 逆运动学 -> 关节目标角 -> 关节 PD 伺服

本章使用 §4 的真实 Pupper v3 mesh 模型,同一个脚本支持 walk、trot、pace、bound 和 gallop。它是 开环步态生成 + 关节闭环 PD 跟踪:步态层不使用足端接触、机身姿态或速度反馈,关节层会根据实际关节角和角速度修正跟踪误差。

本章目标

  • 用周期、占空比和相位偏移描述四足步态
  • 生成连续的支撑相与摆动相足端轨迹
  • 用数值 IK 将足端目标转为十二个关节目标角
  • 在 MuJoCo viewer 中对比 walk、trot、pace、bound 和 gallop
  • 区分步态层开环控制与关节层闭环 PD

前置阅读

5.1 识别常见步态

四足步态的核心区别是落足顺序支撑时长。walk 通常保留三足支撑,trot 使用两组对角腿,pace 改为两组同侧腿,bound 让前腿对与后腿对交替,gallop 用非对称四拍顺序留出腾空相。walk、trot、bound 和 gallop 的典型节奏如图 1 所示,pace 可以与 trot 对照理解。

四足常见步态的着地节奏与占空比。
四足常见步态的着地节奏与占空比。
步态落足关系占空比稳定性典型用途
walkRL → FL → RR → FR0.75低速、谨慎落足
trotFL + RR,FR + RL0.50常规巡航
paceFL + RL,FR + RR0.50中低相位对照、侧倾分析
boundRL + RR,FL + FR0.42高速直行、跳跃
gallopRR → RL → FL → FR0.28高速奔跑

表中的占空比来自本章 demo 预设,是用于比较相位关系的起点,不是所有机器人通用的唯一参数。高速步态还需要自由基座动力学、落地缓冲和姿态稳定;固定基座 demo 只检查相位和足端轨迹。

5.2 编排步态相位

步态图把一轮周期 展开到时间轴上,每行对应一条腿。以 trot 为例,FL 和 RR 同相,FR 和 RL 同相,两组相差半个周期,如图 2 所示。

trot 步态图:两组对角腿相差半个周期。
trot 步态图:两组对角腿相差半个周期。

步态时钟由三类参数决定:

  • 周期 :完成一轮步态的时间,周期越短,步频越高。
  • 占空比 :单腿在一个周期内处于支撑相的比例。
  • 相位偏移 :第 条腿进入支撑相的时刻,用周期比例表示。

本章脚本把 定义为支撑相起点。仿真时间为 时,单腿局部相位为

时腿处于支撑相,否则处于摆动相。减号使 时恰好得到 ,因此配置表可以直接按落足时刻阅读。

def leg_phase(t: float, leg: str, t_cycle: float, gait: Gait):
t_local = ((t / t_cycle) - gait.phase_offsets[leg]) % 1.0
if t_local < gait.duty:
return True, t_local / gait.duty
return False, (t_local - gait.duty) / (1.0 - gait.duty)

返回值中的 s 是当前支撑段或摆动段内的归一化进度。下游足端轨迹只需处理 in_stances,不需要知道当前是哪种步态。

5.3 生成足端轨迹

足端目标在髋关节局部坐标系中运动。支撑相内,脚相对地面保持不动,从机身看就是由前向后扫;摆动相内,脚抬起并从后方回到前方。两段轨迹如图 3 所示。

单腿足端轨迹的落地、支撑、离地与摆动中点。
单腿足端轨迹的落地、支撑、离地与摆动中点。

设步长为 ,抬腿高度为 ,站高为 ,段内进度为 。支撑相的足端目标为

摆动相的足端目标为

对应实现直接返回三维目标:

def foot_trajectory(s, in_stance, step_length, step_height, stand_height):
if in_stance:
x = step_length * (0.5 - s)
z = -stand_height
else:
x = step_length * (s - 0.5)
z = -stand_height + step_height * np.sin(np.pi * s)
return np.array((x, 0.0, z), dtype=float)

sin(\pi s) 保证摆动相起点和终点回到地面高度,但它不保证端点垂向速度为零。自由基座和真机落地需要用摆线、五次多项式或 Bezier 曲线进一步限制端点速度和加速度。

5.4 串联 IK 与 PD

足端轨迹输出的是几何位置,MuJoCo actuator 接收的是关节目标。整条控制链如图 4 所示。

足端目标经数值 IK 转为关节目标角,再由关节 PD 伺服跟踪。
足端目标经数值 IK 转为关节目标角,再由关节 PD 伺服跟踪。

PupperLegIK 使用 mj_jacSite 读取真实模型的足端雅可比,通过 DLS 迭代求解 HAA、HFE 和 KFE 三个关节角。四条腿的髋偏置、左右镜像和关节限位全部来自 pupper_v3.xml,避免了解析玩具 IK 与 mesh 几何不一致。

def gait_step(kin, t, step_length, seed, *, t_cycle,
step_height, stand_height, gait):
target = {}
for leg in LEG_ORDER:
in_stance, s = leg_phase(t, leg, t_cycle, gait)
foot_base = HIP_OFFSETS[leg] + foot_trajectory(
s, in_stance, step_length, step_height, stand_height
)
q = kin.ik(foot_base, leg=leg, q_seed=seed[leg])
seed[leg] = q
target[leg] = q
return target

seed 保留每条腿上一帧的 IK 结果,使数值迭代从附近解开始,减少关节角在不同解支之间跳变。

关节目标角写入 data.ctrl 后,真实模型的 affine actuator 等价于

demo 在编译模型时使用 。这是关节空间闭环 PD,没有积分项,也不会自动修正步态相位、落足位置或机身姿态。

5.5 配置五种步态

run_gait_control.py 用同一个 Gait 数据结构保存相位偏移、占空比、周期、步长、步高和前进速度。步态切换不会改变足端轨迹、IK 或 PD 代码。

步态相位偏移周期步高前进步长
walkRL 0.00、FL 0.25、RR 0.50、FR 0.750.90 s3.0 cm3.5 cm
trotFL/RR 0.00、FR/RL 0.500.50 s3.5 cm5.0 cm
paceFL/RL 0.00、FR/RR 0.500.50 s3.5 cm5.0 cm
boundRL/RR 0.00、FL/FR 0.500.45 s4.5 cm6.5 cm
gallopRR 0.00、RL 0.12、FL 0.45、FR 0.570.50 s5.0 cm8.0 cm

表中周期和步高对应 forward 预设;inplace 将步长置零,并对部分步态使用稍低的步高。每种步态都有两个实验方向:

  • inplace:保持 weld 位置不变,用于检查抬腿顺序。
  • forward:按预设速度平移 weld,用于观察足端的前后扫动。

5.6 检查原地步态

codes/practices/quadruped/cs123 目录运行:

# macOS 的交互 viewer 必须用 mjpython
uv run mjpython 5.gait-control/run_gait_control.py \
--gait trot --viewer inplace

# Linux 和 Windows 使用 python
uv run python 5.gait-control/run_gait_control.py \
--gait trot --viewer inplace

原地 trot 如图 5 所示。step_length=0 使足端只做垂向抬落,适合检查 FL + RR 与 FR + RL 的对角同步关系。

原地 trot:足端步长为零,两组对角腿交替抬落。
原地 trot:足端步长为零,两组对角腿交替抬落。

检查顺序为:

  1. 确认机身和 mesh 位置没有错位。
  2. 确认应同相的腿同时抬起和落下。
  3. 确认摆动腿的足端有明显离地间隙。
  4. 确认关节连续运动,没有 IK 解支跳变。

机身被 weld 固定,因此 base z std 接近零是约束的结果,不能当作真实地面稳定性证据

5.7 检查前进轨迹

uv run mjpython 5.gait-control/run_gait_control.py \
--gait trot --viewer forward

forward 预设给足端一个非零步长,并按预设速度平移 base weld。从跟随机身的相机中可以看到支撑相向后扫、摆动相向前回收,如图 6 所示。

前进 trot 轨迹预览:base 由移动 weld 带动,足端按前后轨迹运动。
前进 trot 轨迹预览:base 由移动 weld 带动,足端按前后轨迹运动。

forward 是轨迹可视化,不是自由基座行走证明。机身位移由 weld 直接给定,不是足地接触力积分出的运动;因此不能用它评估实际速度误差、能耗、打滑或抗扰性。

不带 --viewer 时,脚本会为指定步态同时生成 inplaceforward 两段 GIF:

uv run python 5.gait-control/run_gait_control.py --gait walk
uv run python 5.gait-control/run_gait_control.py --gait trot
uv run python 5.gait-control/run_gait_control.py --gait pace
uv run python 5.gait-control/run_gait_control.py --gait bound
uv run python 5.gait-control/run_gait_control.py --gait gallop

剩余四种前进步态的轨迹对比如图 7 所示。walk 依次落足,pace 两条同侧腿同步,bound 前后腿对交替,gallop 按非对称四拍运动。 组合图可以用以下命令重新生成:

uv run python 5.gait-control/run_gait_control.py --comparison
前进轨迹对比:左上 walk,右上 pace,左下 bound,右下 gallop。base 由移动 weld 带动。
前进轨迹对比:左上 walk,右上 pace,左下 bound,右下 gallop。base 由移动 weld 带动。

输出文件遵循 lab5_<direction>_<gait>.gif 命名,例如 lab5_inplace_trot.giflab5_forward_gallop.gif。如果需要先生成 GIF 再打开 viewer,可以组合 --gif--viewer

5.8 划定控制边界

“开环”和“有 PD”可以同时成立,因为它们描述的是不同控制层。

控制层输入输出是否闭环
步态时钟仿真时间、预设相位stance/swing 和段内进度
足端轨迹步长、步高、站高足端位置
数值 IK足端目标、上一帧解关节目标角几何迭代,不是运动反馈
关节 PD目标角、实际角、角速度关节力矩
base weld预设位置机身约束力强制约束

当前步态层没有使用以下反馈:

  • 足端接触检测和接触时刻的相位重置
  • IMU 测得的 roll、pitch 和 yaw
  • 期望速度与实际速度的误差
  • 足端打滑、法向力和落地冲击

解除 weld 后,仅靠时钟驱动的 walk/trot/pace/bound/gallop 很容易走偏、打滑或翻倒。一条完整的模型驱动运动控制器通常还需要机身姿态稳定、落足点调整、接触力分配或 MPC。RL 步态 则将这些修正能力融入策略学习。

5.9 对比全局时钟与 CPG

本章控制器使用统一的全局相位,四条腿通过固定偏移读取局部相位。CPG 通常为每条腿配置振荡器,通过振荡器间的耦合保持相位关系,如图 8 所示。

全局时钟与耦合振荡器网络的步态相位生成方式。
全局时钟与耦合振荡器网络的步态相位生成方式。

全局时钟参数少、易于调试,适合建立 baseline。CPG 能通过耦合调整节奏,但只有在接触或姿态信号真正反馈到振荡器时,才能比固定时钟更好地恢复协调。把全局相位作为强化学习观测也很常见,因为它为策略提供了明确的周期结构。

参考资料