4. 时间最优轨迹规划:梯形速度规划、S形速度规划原理与实现

时间最优,说白了就是让机器人跑得最快。但你不能让它猛冲猛停——电机受不了,末端还会抖。我早年做六轴机器人时,就吃过这个亏:梯形速度规划写得太糙,加减速段直接拉满,结果电机电流过冲,驱动器报警。从那以后,我对速度规划就特别较真。

这一章,我们聚焦两种最经典的规划方法:梯形速度规划和S形速度规划。它们不复杂,但很实用。你想想看,工业机器人里90%的点到点运动,底层用的就是这两套东西。

4.1 梯形速度规划:简单但够用

梯形速度曲线,顾名思义,速度-时间图是个梯形。加速段、匀速段、减速段,三段搞定。我习惯叫它「T型曲线」,因为形状像字母T。

4.1.1 核心参数与约束

做梯形规划前,你得先定好三个东西:

  • 最大速度 v_max —— 电机或关节能跑的上限
  • 最大加速度 a_max —— 电机力矩和结构刚度决定的
  • 总位移 s —— 起点到终点的距离

有了这三个,就能算出加速时间和减速时间。但有个坑:如果位移太短,速度还没到 v_max 就得开始减速。这种情况叫「三角型曲线」——没有匀速段。

关键判断条件:

如果 s ≥ v_max² / a_max,梯形成立;否则,退化为三角形。

4.1.2 分段公式与实现

梯形规划的分段公式其实很直观。我直接给代码,你一看就懂:

// 梯形速度规划:位置、速度、加速度计算
// 输入:总位移 s,最大速度 v_max,最大加速度 a_max,当前时间 t
// 输出:位置 pos,速度 vel,加速度 acc

double ta = v_max / a_max;          // 加速时间
double td = v_max / a_max;          // 减速时间
double sa = 0.5 * a_max * ta * ta;  // 加速段位移
double sd = sa;                     // 减速段位移

// 检查是否退化为三角形
if (sa + sd > s) {
    // 三角形规划:没有匀速段
    ta = sqrt(s / a_max);
    td = ta;
    v_max = a_max * ta;
}

double tv = (s - sa - sd) / v_max;  // 匀速段时间
double T = ta + tv + td;            // 总时间

if (t <= ta) {
    // 加速段
    acc = a_max;
    vel = a_max * t;
    pos = 0.5 * a_max * t * t;
} else if (t <= ta + tv) {
    // 匀速段
    acc = 0;
    vel = v_max;
    pos = sa + v_max * (t - ta);
} else if (t <= T) {
    // 减速段
    double t_dec = t - ta - tv;
    acc = -a_max;
    vel = v_max - a_max * t_dec;
    pos = sa + v_max * tv + v_max * t_dec - 0.5 * a_max * t_dec * t_dec;
} else {
    // 结束
    acc = 0;
    vel = 0;
    pos = s;
}

我的习惯:实际项目中,我会在梯形规划前加一个「最小位移检查」。如果 s 小于某个阈值(比如 0.001 rad),直接跳过规划,输出零速。这样可以避免除零和数值震荡。

4.1.3 梯形规划的局限性

梯形规划最大的问题:加速度在切换点突变。加速段结束瞬间,加速度从 a_max 跳到 0;减速段开始瞬间,又从 0 跳到 -a_max。这种突变会产生冲击力,轻则抖动,重则损坏机械结构。

我记得有一次做高速分拣机器人,梯形规划跑 2m/s 的传送带跟踪,结果末端执行器在加减速切换点有明显的「咯噔」声。后来换成 S 形规划,声音就没了。

4.2 S形速度规划:平滑但复杂

S形速度曲线,本质上是给加速度加了一个「缓变」过程。加速度不再跳变,而是线性变化。这样速度曲线就从折线变成了 S 形。

4.2.1 七段式结构

完整的 S 形规划有七段:

  1. 加加速段:加速度从 0 增加到 a_max
  2. 匀加速段:加速度保持 a_max
  3. 减加速段:加速度从 a_max 降到 0
  4. 匀速段:速度保持 v_max
  5. 加减速段:加速度从 0 降到 -a_max
  6. 匀减速段:加速度保持 -a_max
  7. 减减速段:加速度从 -a_max 升到 0

你想想看,七段听起来多,但核心就三个参数:最大速度 v_max、最大加速度 a_max、最大加加速度 j_max(也叫 jerk)。

注意:加加速度 j_max 是 S 形规划的灵魂。它决定了加速度变化的快慢。j_max 越大,曲线越接近梯形;j_max 越小,曲线越「软」。我一般建议 j_max 取 a_max 的 5~10 倍,具体看机械刚度。

4.2.2 参数计算与退化解

S 形规划的退化情况比梯形多得多。我整理了一张表,方便你对照:

条件 退化形态 说明
位移足够大 完整七段 所有段都存在
位移中等 五段(无匀加速/匀减速) 加速度刚升到 a_max 就得降
位移较小 三段(只有加/减加速段) 速度都到不了 v_max
位移极小 两段(直接加减速) 加速度还没升到 a_max 就开始降

我曾经在调试一个短行程(5mm)的直线模组时,忘了处理退化情况,结果规划出来的时间居然是负数。嗯,从那以后我就在代码里加了一个「退化检测」函数,先判断位移属于哪种情况,再分配段时间。

4.2.3 核心实现框架

S 形规划的代码比梯形长不少,但逻辑是清晰的。我给出核心框架:

// S形速度规划:核心参数计算
// 输入:s, v_max, a_max, j_max
// 输出:各段时间 ta1, ta2, ta3, tv, td1, td2, td3

// 1. 计算加/减速段能达到的最大加速度
double ta1 = a_max / j_max;  // 加加速段时间
double sa1 = (1.0/6.0) * j_max * ta1 * ta1 * ta1;

// 2. 检查是否能达到 a_max
if (sa1 * 2 > s) {
    // 退化:加速度达不到 a_max
    // 重新计算 ta1,使位移匹配
    ta1 = pow(s / (2.0 * j_max), 1.0/3.0);
    // 此时没有匀加速/匀减速段
    ta2 = 0;
    tv = 0;
    td2 = 0;
    td1 = ta1;
    td3 = ta1;
} else {
    // 3. 检查是否能达到 v_max
    double v_ramp = 0.5 * a_max * ta1 + a_max * ta1;  // 近似
    // 实际计算更复杂,这里简化
    // ...
}

避坑指南:我曾经在 S 形规划里用浮点数直接比较相等,结果因为精度问题,匀速段时间算出来是 -1e-12。后来我统一用「if (tv < 1e-9) tv = 0」来处理。浮点数比较,一定要给容差。

4.3 梯形 vs S形:怎么选?

很多初学者会问:到底用哪个?我的建议是:

  • 对冲击不敏感的场景(如大型重载机器人、低速搬运):梯形就够了,计算量小,实时性好。
  • 对平滑性要求高的场景(如高速分拣、精密装配、医疗机器人):必须上 S 形,否则抖动会毁掉精度。
  • 实时性受限的嵌入式系统:梯形更友好。S 形在退化判断上需要更多分支,容易出 bug。

我个人习惯是:默认用 S 形,但把 j_max 设大一点。这样既保留了平滑性,又不会让曲线太「软」导致时间过长。如果发现计算超时,再切回梯形。

4.4 知识体系总览

下面这张图,把本章的核心逻辑串起来了。你可以把它当作一个速查地图:

时间最优轨迹规划知识体系 时间最优轨迹规划知识体系 时间最优轨迹规划 梯形速度规划 S形速度规划 三段式结构 三角退化 加速度突变 计算量小 七段式结构 多级退化 加加速度控制 平滑无冲击 选择建议 冲击不敏感 → 梯形 | 平滑要求高 → S形 | 实时受限 → 梯形

4.5 工程落地要点

最后,我总结几个工程中容易踩的坑:

  • 浮点数精度:规划中大量使用除法、开方、三次方,浮点误差会累积。建议所有中间变量用 double,最终输出前做一次限幅。
  • 时间同步:多轴联动时,每个轴独立规划,但总时间必须对齐。我习惯先算出所有轴的时间,取最大值,然后让短轴「等」长轴。
  • 实时性保障:S 形规划的退化判断分支多,建议在离线阶段预计算好各段时间,在线只做插值查表。

一句话总结:梯形规划是「快」,S形规划是「稳」。选哪个,看你的机器人是去搬砖还是去绣花。