步态规划入门:静步态与动步态、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时试过,机器人跑了三步就侧翻了——教训深刻。
步态参数关系图
下面这张图把步态周期、占空比、支撑相和摆动相的关系画清楚了。我建议你把它存下来,调参时对照着看。
调参实战:从理论到代码
理论讲完了,咱们来点实际的。假设你现在要调一个Trot步态,从零开始,你会怎么做?
我的习惯是三步走:
- 定周期 T:先根据目标速度估算。比如目标速度 0.5 m/s,步长 0.2 m,那 T = 0.2 / 0.5 × 2 = 0.8 秒。为什么乘2?因为一个周期内每条腿只摆动一次,但机器人需要两组腿交替才能走一步。
- 定占空比 D:平坦地面从 0.5 开始。如果发现机器人有“拖腿”现象(摆动腿落地时蹭地),适当增大 D 到 0.55~0.6。
- 微调相位偏移: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步态是动步态的入门钥匙。步态周期和占空比是你手里最直接的两个旋钮,调好了,机器人就能稳稳当当地跑起来。