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 梯形曲线的完整计算流程
在实际编程中,我一般按这个步骤来:
- 输入参数:总位移S、最大速度v_max、加速度a、减速度d
- 计算加速/减速所需位移:S_acc = v_max²/(2a),S_dec = v_max²/(2d)
- 判断是否达到最大速度:如果S_acc + S_dec > S,说明是三角曲线
- 计算各段时间:t_acc = v_max/a,t_dec = v_max/d,t_const = (S - S_acc - S_dec)/v_max
- 生成轨迹点:根据当前时间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形曲线虽然复杂一些,但在很多场景下效果确实更好。