4、梯形速度曲线:原理、数学模型、加减速过程分析

4.1 为什么是梯形?

做运动控制这么多年,我见过最多的速度曲线就是梯形。说白了,它就是让电机先匀加速、再匀速、最后匀减速。画出来像个梯形,所以叫这名。

你可能会问:为什么不用更平滑的S形曲线?嗯,这个问题问得好。梯形曲线虽然加速度有突变,但它简单、计算量小、响应快。在大多数工业场景中,比如点焊、搬运、码垛,梯形曲线完全够用。我早期做的一个码垛项目,用的就是梯形曲线,跑了三年没出过问题。

核心思想:梯形速度曲线将运动过程分为三个阶段——加速段、匀速段、减速段。每个阶段的速度变化都是线性的。

4.2 数学模型

我们先定义几个关键参数:

  • v₀:初始速度(通常为0)
  • v_max:最大速度(目标速度)
  • a:加速度(正值)
  • d:减速度(正值,但方向与加速度相反)
  • t_acc:加速时间
  • t_dec:减速时间
  • t_total:总运动时间
  • S:总位移

加速段的速度公式很简单:

v(t) = v₀ + a × t,  0 ≤ t ≤ t_acc

匀速段:

v(t) = v_max,  t_acc < t ≤ t_total - t_dec

减速段:

v(t) = v_max - d × (t - (t_total - t_dec)),  t_total - t_dec < t ≤ t_total

位移公式呢?其实就是速度曲线下的面积。我习惯这么算:

S_acc = ½ × v_max × t_acc          // 加速段位移
S_dec = ½ × v_max × t_dec          // 减速段位移
S_const = v_max × (t_total - t_acc - t_dec)  // 匀速段位移
S_total = S_acc + S_const + S_dec

我的小技巧:实际项目中,我通常先算加速和减速需要的位移,剩下的距离用匀速段补上。如果匀速段位移为负,说明距离太短,根本跑不到最大速度——这就是所谓的「三角速度曲线」情况。

4.3 加减速过程分析

4.3.1 加速阶段

加速阶段,电机从静止开始,以恒定加速度a加速到v_max。加速度a的选择很关键。我记得有一次在产线上调试,加速度设得太大了,结果机械臂末端抖得像筛子一样。后来我总结了一个经验:

  • 加速度不能超过电机额定转矩对应的加速度
  • 加速度不能超过机械结构的承受极限
  • 加速度要考虑负载变化——重载时加速度要适当降低

4.3.2 匀速阶段

匀速阶段是最舒服的阶段。电机以v_max稳定运行,电流平稳,振动最小。但这里有个坑:

我曾经踩过的坑:匀速段如果太长,电机长时间高速运行,温升会很明显。有一次在高温车间,电机直接过热报警了。后来我加了温度监控,并在匀速段适当降低速度。

4.3.3 减速阶段

减速阶段,电机从v_max匀减速到0。减速度d通常和加速度a相等,但也可以不等。我个人习惯让d = a,这样加减速对称,计算简单。

但有些场景需要不对称。比如:

  • 负载向下运动时,减速度要更大(防止溜车)
  • 精密定位时,末端减速度要小(减少过冲)
  • 紧急停止时,减速度可以很大(但别超过机械极限)

4.4 梯形曲线的完整计算流程

在实际编程中,我一般按这个步骤来:

  1. 输入参数:总位移S、最大速度v_max、加速度a、减速度d
  2. 计算加速/减速所需位移:S_acc = v_max²/(2a),S_dec = v_max²/(2d)
  3. 判断是否达到最大速度:如果S_acc + S_dec > S,说明是三角曲线
  4. 计算各段时间:t_acc = v_max/a,t_dec = v_max/d,t_const = (S - S_acc - S_dec)/v_max
  5. 生成轨迹点:根据当前时间t,判断处于哪个阶段,计算对应的速度和位置

代码实现大概是这样的:

// 梯形速度曲线规划
typedef struct {
    float v_max;    // 最大速度
    float a;        // 加速度
    float d;        // 减速度
    float S;        // 总位移
    float t_acc;    // 加速时间
    float t_dec;    // 减速时间
    float t_const;  // 匀速时间
    float t_total;  // 总时间
} TrapezoidProfile;

void Trapezoid_Init(TrapezoidProfile *prof, float S, float v_max, float a, float d) {
    prof->v_max = v_max;
    prof->a = a;
    prof->d = d;
    prof->S = S;
    
    float S_acc = v_max * v_max / (2.0f * a);
    float S_dec = v_max * v_max / (2.0f * d);
    
    if (S_acc + S_dec >= S) {
        // 三角曲线,达不到最大速度
        float v_peak = sqrtf(2.0f * a * d * S / (a + d));
        prof->t_acc = v_peak / a;
        prof->t_dec = v_peak / d;
        prof->t_const = 0.0f;
    } else {
        // 标准梯形曲线
        prof->t_acc = v_max / a;
        prof->t_dec = v_max / d;
        prof->t_const = (S - S_acc - S_dec) / v_max;
    }
    
    prof->t_total = prof->t_acc + prof->t_const + prof->t_dec;
}

float Trapezoid_GetVelocity(TrapezoidProfile *prof, float t) {
    if (t <= prof->t_acc) {
        // 加速段
        return prof->a * t;
    } else if (t <= prof->t_acc + prof->t_const) {
        // 匀速段
        return prof->v_max;
    } else if (t <= prof->t_total) {
        // 减速段
        float t_dec_elapsed = t - prof->t_acc - prof->t_const;
        return prof->v_max - prof->d * t_dec_elapsed;
    } else {
        return 0.0f;
    }
}

4.5 梯形曲线的优缺点

优点 缺点
计算简单,实时性好 加速度突变,有冲击
参数少,容易调试 高速时定位精度不如S形曲线
加减速对称,便于分析 不适合高精度、低振动的场景
占用CPU资源少 加减速段效率较低

我的建议:如果你的应用对振动不敏感(比如传送带、码垛、点焊),梯形曲线是最佳选择。但如果是精密加工、高速贴片机,建议用S形曲线或者更高阶的曲线。

4.6 实际项目中的避坑指南

最后,分享几个我踩过的坑:

  • 加速度单位搞混:有一次我把mm/s²和m/s²搞混了,结果加速度大了1000倍,电机直接飞车。后来我统一用mm作为单位,并在代码里加上了单位注释。
  • 浮点数精度问题:在嵌入式系统里,float的精度有限。长时间运行后,累积误差会越来越大。我一般会在每个运动周期结束时,强制修正位置。
  • 加减速时间太短:如果加速度很大,加减速时间可能只有几毫秒。这时候控制周期跟不上,实际速度曲线会严重变形。我建议加减速时间至少是控制周期的5倍以上。
  • 忽略负载变化:同一个加速度,空载和满载时电机的响应完全不同。我习惯在程序中加入自适应加速度调整,根据电流反馈动态调节。

嗯,梯形速度曲线就讲到这里。下一章我们会聊S形速度曲线,它解决了梯形曲线的加速度突变问题。到时候你会看到,S形曲线虽然复杂一些,但在很多场景下效果确实更好。