步态规划入门:静步态与动步态、Trot步态原理、步态周期与占空比

各位工程师朋友,欢迎来到步态规划这一章。说实话,步态是四足机器人能否“走起来”和“走得好”的分水岭。很多同学代码写得飞起,电机调得顺滑,但机器人一迈腿就原地翻车——问题多半出在步态上。

我个人习惯把步态比作舞蹈编排。你想想看,四条腿怎么配合、什么时候抬、什么时候落、重心怎么移,这些都得有章法。今天我们就从最基础的静步态和动步态讲起,再到经典的Trot步态,最后把步态周期和占空比这两个核心参数说透。

静步态 vs 动步态:本质区别在哪?

先问一个问题:机器人走路时,重心投影必须始终落在支撑多边形内吗?

如果是,那就是静步态。如果不是,那就是动步态。就这么简单。

核心区别一句话:

  • 静步态:任何时候至少三条腿着地,重心投影在支撑三角形内 → 稳定,但慢
  • 动步态:允许只有两条腿甚至一条腿着地 → 快,但需要动态平衡控制

我在项目中遇到过这样一个坑:刚开始做四足时,我图省事直接上了静步态,想着“稳字当头”。结果机器人走一步要花2秒,跟乌龟似的。后来换Trot步态,速度直接翻了三倍。但代价是什么?控制器的复杂度上了一个台阶。

对比项 静步态 动步态
支撑腿数 ≥3 ≤2(甚至1)
稳定性 静态稳定,可随时急停 动态稳定,需持续控制
典型速度 0.1~0.3 m/s 0.5~2.0 m/s
适用场景 精细操作、狭窄空间 快速移动、多地形
控制复杂度 低 高

避坑提醒:我曾经在湿滑地面上用静步态,以为三条腿着地就万无一失。结果每条腿都在打滑,重心投影虽然落在支撑多边形内,但摩擦力不够,机器人照样滑倒。静步态只保证“几何稳定”,不保证“物理稳定”。

Trot步态原理:对角线的默契

Trot步态,说白了就是对角腿同起同落。左前腿+右后腿是一组,右前腿+左后腿是另一组。两组交替摆动,像马的小跑一样。

为什么选对角?你想想看,如果同侧腿一起抬(比如左前+左后),机器人会往另一边猛倒,重心偏移太大。对角腿组合刚好把重心夹在中间,动态平衡更容易维持。

我习惯用状态机来描述Trot步态:

// Trot步态状态机(简化版)
enum TrotPhase {
  PHASE_1,  // 左前+右后 摆动,右前+左后 支撑
  PHASE_2   // 右前+左后 摆动,左前+右后 支撑
};

// 每个相位持续 T/2 时间,T为步态周期
void trotGaitUpdate(float t) {
  float phase = fmod(t, T);  // 当前周期内的时间
  if (phase < T/2) {
    // PHASE_1: 对角组1摆动
    setLegSwing(LF, true);  // 左前摆动
    setLegSwing(RH, true);  // 右后摆动
    setLegSwing(RF, false); // 右前支撑
    setLegSwing(LH, false); // 左后支撑
  } else {
    // PHASE_2: 对角组2摆动
    setLegSwing(LF, false);
    setLegSwing(RH, false);
    setLegSwing(RF, true);
    setLegSwing(LH, true);
  }
}

个人经验:刚开始调Trot步态时,我犯过一个低级错误——两组腿的相位切换太生硬,导致机器人每走一步都“点头”。后来加了正弦轨迹做摆动腿的抬放过渡,瞬间顺滑了。记住:步态不只是“抬”和“落”,更是“怎么抬”和“怎么落”。

步态周期与占空比:两个调参利器

好,现在我们把Trot步态拆成两个关键参数:步态周期 T 和 占空比 D。

  • 步态周期 T:完成一个完整步态循环所需的时间。T越小,步频越快,速度越快。
  • 占空比 D:每条腿在一个周期内处于支撑相的时间比例。D=0.5表示一半时间支撑,一半时间摆动。

对于Trot步态,标准占空比是0.5。但实际项目中,我很少用死板的0.5。

占空比 D 效果 适用场景
D > 0.5 支撑相更长,更稳但更慢 湿滑地面、上坡
D = 0.5 标准Trot,速度与稳定平衡 平坦硬地
D < 0.5 摆动相更长,速度快但易失稳 冲刺、下坡

注意:占空比不能无限调。D < 0.3时,每条腿只有不到30%的时间着地,机器人几乎在“飞”。这时候需要IMU和角速度反馈做动态补偿,否则必摔。我曾经在D=0.25时试过,机器人跑了三步就侧翻了——教训深刻。

步态参数关系图

下面这张图把步态周期、占空比、支撑相和摆动相的关系画清楚了。我建议你把它存下来,调参时对照着看。

步态周期与占空比关系图 一个步态周期 T LF 支撑相 (D·T) 摆动相 ((1-D)·T) RH RF LH 对角组1 (LF+RH) 支撑 对角组2 (RF+LH) 支撑 D·T(支撑相) (1-D)·T(摆动相) Trot步态:对角腿同相,占空比 D=0.5 时支撑相=摆动相

调参实战:从理论到代码

理论讲完了,咱们来点实际的。假设你现在要调一个Trot步态,从零开始,你会怎么做?

我的习惯是三步走:

  1. 定周期 T:先根据目标速度估算。比如目标速度 0.5 m/s,步长 0.2 m,那 T = 0.2 / 0.5 × 2 = 0.8 秒。为什么乘2?因为一个周期内每条腿只摆动一次,但机器人需要两组腿交替才能走一步。
  2. 定占空比 D:平坦地面从 0.5 开始。如果发现机器人有“拖腿”现象(摆动腿落地时蹭地),适当增大 D 到 0.55~0.6。
  3. 微调相位偏移:Trot步态的标准相位偏移是 0.5(两组腿相差半个周期)。但实际中,如果机器人转弯或侧倾,可以微调这个偏移量来补偿。
// 步态参数结构体
typedef struct {
  float period;       // 步态周期 T (秒)
  float dutyCycle;    // 占空比 D (0~1)
  float phaseOffset;  // 相位偏移 (0~1),Trot标准为0.5
} GaitParams;

// 初始化Trot步态
GaitParams trotParams = {
  .period = 0.8,      // 0.8秒一个周期
  .dutyCycle = 0.5,   // 50%支撑,50%摆动
  .phaseOffset = 0.5  // 两组腿相差半个周期
};

// 计算每条腿的相位
float getLegPhase(LegID leg, float time) {
  float basePhase = fmod(time, trotParams.period) / trotParams.period;
  switch (leg) {
    case LF: return basePhase;
    case RH: return basePhase;
    case RF: return fmod(basePhase + trotParams.phaseOffset, 1.0);
    case LH: return fmod(basePhase + trotParams.phaseOffset, 1.0);
  }
}

// 判断腿是否在支撑相
bool isInStance(float legPhase) {
  return legPhase < trotParams.dutyCycle;
}

调参小技巧:我习惯在调试时把步态周期和占空比做成可在线修改的参数,通过串口或无线实时调整。这样不用每次改代码重新烧录,效率高很多。你想想看,调一次参数要30秒,一天调100次就是50分钟——省下来的时间干点啥不好?

静步态 vs 动步态:什么时候用哪个?

最后,我根据实际项目经验,给你一个选择建议:

  • 用静步态的场景:机器人需要在狭窄空间内精确移动(比如钻管道)、负载很重需要稳定支撑、或者你刚开始做四足想先跑通基础逻辑。
  • 用动步态的场景:需要快速移动、跨越障碍、在不平地面行走。Trot是最常用的入门动步态,再往上还有Pace、Bound、Gallop等。

我的建议:先做静步态把运动学和控制框架跑通,再切到Trot步态。不要一上来就搞动步态,否则你连问题出在运动学还是步态上都分不清。我曾经带过一个实习生,上来就写Trot,结果机器人原地转圈——查了两天发现是腿序搞反了。嗯,这种坑踩一次就够了。

好了,步态规划的基础就讲到这里。记住三个核心:静步态求稳、动步态求快、Trot步态是动步态的入门钥匙。步态周期和占空比是你手里最直接的两个旋钮,调好了,机器人就能稳稳当当地跑起来。