在电机控制领域,加减速算法是决定运动平稳性和精度的关键因素。作为一名长期从事MCU电机控制开发的工程师,我经常需要为不同应用场景选择合适的加减速算法。本文将分享两种经典的柔性加减速控制算法——梯形和指数算法,从理论推导到实际实现的全过程。
这两种算法各有特点:梯形算法计算简单,适合资源有限的微控制器;指数算法运动更平滑,适合对振动敏感的高精度场合。我在数字舵机控制项目中都实践过这两种方案,下面就把我的实现经验和踩过的坑详细分享给大家。
梯形算法将运动过程划分为三个明显阶段:
这种分段方式在速度-时间图上呈现明显的梯形特征,因此得名。在实际工程中,我们需要考虑两种情况:
判断逻辑可以用这个伪代码表示:
python复制if 剩余位移 < 减速所需位移:
进入减速段
elif 当前速度 < 最大速度:
保持加速
else:
维持匀速
减速所需位移是算法实现的关键参数。根据运动学公式:
减速时间 t = V₀/a (V₀为当前速度,a为减速度)
减速位移 s = V₀t - 1/2at² = V₀²/(2a)
但在离散系统中,我们需要使用等差数列求和公式:
s = Σ(V₀ - a*i) = V₀(V₀ + a)/(2a) (i从0到V₀/a)
实际应用中发现:离散化计算比连续公式更准确,特别是在低采样率的系统中。我在STM32F103上测试时,离散公式的位置误差比连续公式小约3%。
剩余位移 = |目标位置 - 当前位置|
这个计算看似简单,但在实际应用中要注意:
使用Python模拟数字舵机的20ms控制周期:
python复制import threading
import matplotlib.pyplot as plt
class TrapezoidalController:
def __init__(self, start_pos, target_pos, max_speed, acc):
self.current_pos = start_pos
self.target_pos = target_pos
self.max_speed = max_speed
self.acc = acc
self.current_speed = 0
self.dir = 1 if target_pos > start_pos else -1
# 数据记录
self.time_log = []
self.pos_log = []
self.speed_log = []
self.acc_log = []
python复制def update(self):
remaining_dist = abs(self.target_pos - self.current_pos)
# 计算减速所需距离(离散公式)
decel_dist = 0.5 * self.current_speed * (self.current_speed + self.acc) / self.acc
if remaining_dist <= decel_dist:
# 减速段
self.current_speed -= self.acc
if self.current_speed < 0:
self.current_speed = 0
elif self.current_speed < self.max_speed:
# 加速段
self.current_speed += self.acc
else:
# 匀速段
self.current_speed = self.max_speed
# 位置更新
self.current_pos += self.dir * self.current_speed
# 数据记录
self.time_log.append(len(self.time_log))
self.pos_log.append(self.current_pos)
self.speed_log.append(self.current_speed)
self.acc_log.append(self.acc if self.current_speed < self.max_speed else 0)

从仿真曲线可以看出:
实测中发现:加速度突变会导致机械振动。在要求较高的场合,需要加入加速度变化率(Jerk)限制。
c复制typedef struct {
float current_pos;
float target_pos;
float max_speed;
float acc;
float current_speed;
int8_t dir;
} TrapezoidalController;
void trapezoidal_update(TrapezoidalController* ctrl) {
float remaining = fabsf(ctrl->target_pos - ctrl->current_pos);
float decel_dist = 0.5f * ctrl->current_speed *
(ctrl->current_speed + ctrl->acc) / ctrl->acc;
if (remaining <= decel_dist) {
ctrl->current_speed -= ctrl->acc;
} else if (ctrl->current_speed < ctrl->max_speed) {
ctrl->current_speed += ctrl->acc;
} else {
ctrl->current_speed = ctrl->max_speed;
}
ctrl->current_pos += ctrl->dir * ctrl->current_speed;
}
工程经验:在MCU实现时,建议:
- 使用定点数运算提升性能
- 加入速度限幅防止超调
- 对关键变量进行范围检查
指数算法通过指数函数实现速度的平滑变化:
其中τ是时间常数,决定变化快慢:
这个公式与电容充放电曲线类似。工程上认为5τ时间后速度达到稳定值(99.3%Vmax)。
总位移可以通过积分求得:
S = ∫v(t)dt = Vmax * τ (1 - e^(-t/τ))
但更实用的方法是面积法:
python复制if 总位移 < Vmax*5τ:
# 无匀速段
t1 = t2 = 总位移/Vmax
else:
# 有匀速段
t1 = 5τ
t_cruise = (总位移 - Vmax*5τ)/Vmax
t2 = t1 + t_cruise
python复制class ExponentialController:
def __init__(self, total_dist, max_speed, tau):
self.S = total_dist
self.Vmax = max_speed
self.tau = tau
# 计算时间分段
self.t1 = 5 * tau
Sa = max_speed * self.t1
if Sa >= total_dist:
self.t1 = total_dist / max_speed
self.t2 = self.t1
else:
self.Sc = total_dist - Sa
self.t_cruise = self.Sc / max_speed
self.t2 = self.t1 + self.t_cruise
self.T_total = self.t2 + 5 * tau

与梯形算法相比:
实测数据:在相同参数下,指数算法的振动幅度比梯形算法降低约60%,但运动时间增加约15%。
| 特性 | 梯形算法 | 指数算法 |
|---|---|---|
| 计算复杂度 | 低 | 中 |
| 运动平滑性 | 一般 | 优秀 |
| 定位时间 | 较短 | 较长 |
| 振动幅度 | 较大 | 很小 |
| 适用场景 | 普通定位 | 高精度场合 |
根据我的工程经验:
资源受限系统:选择梯形算法
高精度系统:选择指数算法
折中方案:可以考虑7段S曲线算法
参数整定方法:
异常处理:
c复制// 在C实现中加入安全判断
if (isnan(current_pos) || isinf(current_pos)) {
emergency_stop();
}
动态调整:
现象:电机运动超过目标位置
原因:
现象:运动过程中产生明显振动
原因:
现象:变量值异常或系统崩溃
原因:
c复制// 变量范围限制
#define LIMIT(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
current_speed = LIMIT(current_speed, 0, max_speed);
根据负载惯量自动调整算法参数:
结合两种算法的优点:
加入速度和加速度前馈,提高跟踪精度:
c复制output = PID() + Kv*velocity + Ka*acceleration;
经过多个项目的实践验证,这些电机控制算法不仅能用于舵机控制,同样适用于工业机械臂、CNC机床等高精度运动控制场合。关键是要根据具体应用场景选择合适的算法,并通过充分的测试和调参达到最佳性能。