2. 梯形加减速算法原理:速度曲线模型、加速/匀速/减速三段式、数学推导

大家好,欢迎来到第二章。

上一章我们聊了运动控制里为什么要做加减速。说白了,就是为了让电机不丢步、不抖动,跑得又稳又快。那具体怎么做?最简单、最经典的方法,就是梯形加减速。

我个人习惯把梯形加减速叫做「入门级但永不过时」的算法。为什么?因为它逻辑清晰,计算量小,在资源受限的嵌入式系统里特别好用。我在做早期的步进电机项目时,用的就是它。嗯,咱们今天就把它彻底吃透。

2.1 速度曲线模型:长什么样?

梯形加减速,名字就告诉你了——速度曲线像个梯形。

你想想看,一个梯形有上底、下底和两个腰。对应到速度曲线上:

  • 下底:时间轴,从启动到停止。
  • 左边的腰:加速阶段,速度从0线性增加到目标速度。
  • 上底:匀速阶段,速度保持目标值不变。
  • 右边的腰:减速阶段,速度从目标值线性减到0。

说白了,就是「先匀加速,再匀速,最后匀减速」。简单粗暴,但非常有效。

下面我画了一张图,帮你直观理解这个模型:

t (时间) v (速度) O 加速段 匀速段 减速段 t₁ t₂ t₃ v_max

图中横轴是时间 t,纵轴是速度 v。三段清晰明了:红色加速、蓝色匀速、绿色减速。这就是梯形加减速的核心骨架。

核心要点:梯形加减速的本质是「三段式线性速度规划」。加速和减速阶段的加速度大小相等、方向相反,这是最常见的对称形式。当然,也可以不对称,但对称形式计算最简单,我用得最多。

2.2 加速/匀速/减速三段式:具体怎么跑?

咱们把三段拆开,逐一分析。这样你写代码时心里才有底。

2.2.1 加速阶段

从启动速度 v₀(通常是0)开始,以恒定加速度 a 加速到目标速度 v_max。

  • 速度公式:v(t) = v₀ + a·t,其中 t ∈ [0, t₁]
  • 位移公式:s(t) = v₀·t + ½·a·t²
  • 加速时间:t₁ = (v_max - v₀) / a
  • 加速距离:s₁ = v₀·t₁ + ½·a·t₁²

这里有个细节:实际项目中,v₀ 不一定是0。比如你从低速切换到高速,v₀ 可能是某个中间值。我曾经在一条产线上遇到过启动抖动问题,排查半天发现是 v₀ 设成了0,但电机其实还有残余速度。嗯,这个坑我踩过。

2.2.2 匀速阶段

速度达到 v_max 后,保持匀速运行。这是效率最高的阶段。

  • 速度公式:v(t) = v_max,其中 t ∈ [t₁, t₂]
  • 位移公式:s(t) = s₁ + v_max·(t - t₁)
  • 匀速时间:t₂ - t₁ = (总位移 - s₁ - s₃) / v_max
  • 匀速距离:s₂ = v_max·(t₂ - t₁)

你想想看,如果总位移不够大,可能就没有匀速段了。这就是后面要讲的「三角形加减速」——梯形变成了三角形。但那是另一章的内容,咱们先不跑题。

2.2.3 减速阶段

从 v_max 开始,以恒定减速度 a(大小与加速阶段相同)减速到 v₀。

  • 速度公式:v(t) = v_max - a·(t - t₂),其中 t ∈ [t₂, t₃]
  • 位移公式:s(t) = s₁ + s₂ + v_max·(t - t₂) - ½·a·(t - t₂)²
  • 减速时间:t₃ - t₂ = (v_max - v₀) / a = t₁
  • 减速距离:s₃ = v_max·t₁ - ½·a·t₁² = s₁

个人经验:对称梯形(加速和减速时间相等)在代码实现上最省事。你只需要算一次加速参数,减速直接复用。我在做STM32项目时,就是这么干的,省了不少RAM和Flash。

2.3 数学推导:从公式到代码

光有公式还不够,得能算。实际应用中,我们通常已知:

  • 总位移 S(比如电机要走10000个脉冲)
  • 最大速度 v_max
  • 加速度 a
  • 启动/停止速度 v₀(通常为0)

需要求解:加速时间 t₁、匀速时间 t₂-t₁、减速时间 t₃-t₂,以及各阶段的位置点。

推导过程其实很简单,我带你走一遍:

  1. 先算加速/减速所需距离:s₁ = s₃ = v₀·t₁ + ½·a·t₁²,其中 t₁ = (v_max - v₀)/a
  2. 判断是否有匀速段:如果 S > s₁ + s₃,则有匀速段;否则,就是三角形加减速。
  3. 计算匀速段距离:s₂ = S - s₁ - s₃
  4. 计算匀速段时间:t₂ - t₁ = s₂ / v_max
  5. 总时间:t₃ = t₁ + (t₂ - t₁) + t₁ = 2·t₁ + s₂/v_max

你看,是不是很清晰?

下面我给出一个C语言的伪代码片段,这是我在实际项目中用过的简化版:

// 梯形加减速参数计算
typedef struct {
    float v0;       // 启动速度
    float v_max;    // 最大速度
    float a;        // 加速度
    float S;        // 总位移
    float t1;       // 加速时间
    float t2;       // 匀速结束时间
    float t3;       // 总时间
    float s1, s2, s3; // 各段位移
} TrapezoidProfile;

void CalcTrapezoidProfile(TrapezoidProfile *p) {
    // 1. 计算加速时间
    p->t1 = (p->v_max - p->v0) / p->a;
    // 2. 计算加速/减速位移
    p->s1 = p->v0 * p->t1 + 0.5f * p->a * p->t1 * p->t1;
    p->s3 = p->s1;  // 对称梯形
    // 3. 判断是否有匀速段
    if (p->S > p->s1 + p->s3) {
        p->s2 = p->S - p->s1 - p->s3;
        p->t2 = p->t1 + p->s2 / p->v_max;
        p->t3 = p->t2 + p->t1;
    } else {
        // 三角形加减速(无匀速段)
        // 这里先留个空,后面章节会细讲
        p->s2 = 0;
        p->t2 = p->t1;
        p->t3 = 2 * p->t1;
    }
}

注意:上面的代码假设了对称梯形。如果你的系统要求加速和减速的加速度不同,那 s₁ 和 s₃ 就得分开算。我曾经在某个项目中为了省事用了对称假设,结果减速时过冲了——因为负载变化导致实际减速度不够。所以,关键参数一定要根据实际工况标定。

2.4 梯形加减速的优缺点

任何算法都有两面性。梯形加减速虽然经典,但也不是万能的。

方面 优点 缺点
计算量 极低,只有加减乘除,没有三角函数、指数运算。在8位单片机上都能跑得飞起。 无显著缺点。
实现难度 逻辑简单,代码量少,调试方便。我一般半小时就能写完一个可用的版本。 速度曲线有拐点(加速度突变),在高速时可能引起机械振动。
运动平滑性 对于大多数步进电机应用,平滑度足够。 加加速度(Jerk)在拐点处为无穷大,不适合高精度、低振动的场景(如3D打印、CNC)。
适用场景 点对点定位、传送带控制、简易机械臂等。 对平滑度要求极高的场合,建议用S形加减速。

说白了,梯形加减速是「够用就好」的典型代表。如果你的项目对成本敏感、对实时性要求高,它绝对是首选。但如果你在做高端数控机床,嗯,那还是看看后面的S形加减速吧。

2.5 避坑指南:我踩过的几个坑

最后,分享几个我在项目中实际遇到的问题,希望能帮你少走弯路。

  • 坑一:速度单位搞混。我曾经把脉冲/秒和转/分钟混在一起算,结果电机飞车了。建议统一用「脉冲/秒」作为速度单位,位移用「脉冲数」,这样最直观。
  • 坑二:浮点数精度。在低端MCU上,float运算可能很慢。我建议能转成整数运算的就转。比如把速度放大1000倍,用int32算,最后再除回来。
  • 坑三:加减速距离超过总位移。这就是前面说的三角形情况。一定要在代码里做边界检查,否则电机还没加速到目标速度就得开始减速,逻辑会乱掉。
  • 坑四:中断里做浮点运算。我曾经在定时器中断里直接算梯形参数,结果中断执行时间太长,导致丢步。后来我把参数计算放到主循环里,中断只做简单的比较和赋值,问题就解决了。

我的建议:梯形加减速虽然简单,但它是理解更复杂算法(S形、多项式、指数型)的基础。把这个吃透了,后面学什么都快。嗯,下一章我们会深入代码实现,看看怎么在定时器中断里精确控制每一步。

好,这一章就到这里。梯形加减速的原理你掌握了吗?有什么问题欢迎交流。