2. 梯形加减速算法原理:速度曲线模型、加速/匀速/减速三段式、数学推导
大家好,欢迎来到第二章。
上一章我们聊了运动控制里为什么要做加减速。说白了,就是为了让电机不丢步、不抖动,跑得又稳又快。那具体怎么做?最简单、最经典的方法,就是梯形加减速。
我个人习惯把梯形加减速叫做「入门级但永不过时」的算法。为什么?因为它逻辑清晰,计算量小,在资源受限的嵌入式系统里特别好用。我在做早期的步进电机项目时,用的就是它。嗯,咱们今天就把它彻底吃透。
2.1 速度曲线模型:长什么样?
梯形加减速,名字就告诉你了——速度曲线像个梯形。
你想想看,一个梯形有上底、下底和两个腰。对应到速度曲线上:
- 下底:时间轴,从启动到停止。
- 左边的腰:加速阶段,速度从0线性增加到目标速度。
- 上底:匀速阶段,速度保持目标值不变。
- 右边的腰:减速阶段,速度从目标值线性减到0。
说白了,就是「先匀加速,再匀速,最后匀减速」。简单粗暴,但非常有效。
下面我画了一张图,帮你直观理解这个模型:
图中横轴是时间 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₂,以及各阶段的位置点。
推导过程其实很简单,我带你走一遍:
- 先算加速/减速所需距离:s₁ = s₃ = v₀·t₁ + ½·a·t₁²,其中 t₁ = (v_max - v₀)/a
- 判断是否有匀速段:如果 S > s₁ + s₃,则有匀速段;否则,就是三角形加减速。
- 计算匀速段距离:s₂ = S - s₁ - s₃
- 计算匀速段时间:t₂ - t₁ = s₂ / v_max
- 总时间: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形、多项式、指数型)的基础。把这个吃透了,后面学什么都快。嗯,下一章我们会深入代码实现,看看怎么在定时器中断里精确控制每一步。
好,这一章就到这里。梯形加减速的原理你掌握了吗?有什么问题欢迎交流。