1. 项目概述
在工业自动化控制领域,步进电机的精准定位控制一直是个经典课题。传统的梯形加减速算法虽然实现简单,但在启停阶段容易产生机械振动和冲击噪声。这次我要分享的是基于STM32F103的S曲线加减速算法实现,这种算法能让电机速度变化更加平滑,特别适合对运动平稳性要求高的应用场景。
我最初接触这个需求是在一个精密点胶设备项目中,客户反馈设备在启停时存在明显的机械抖动,导致点胶位置出现毫米级偏差。经过多次调试发现,问题的根源就在于使用了传统的梯形加减速算法。改用S曲线算法后,不仅解决了抖动问题,还将定位精度提升到了0.1mm以内。
2. 核心算法原理
2.1 S曲线与梯形曲线的对比
传统梯形加减速的速度曲线在加速度突变点(即梯形拐角处)会产生冲击(jerk),这是导致机械振动的根本原因。而S曲线通过三次函数实现加速度的连续变化,将冲击值控制在合理范围内。
具体来看,完整的S曲线包含7个阶段:
- 加加速阶段(加速度逐渐增大)
- 匀加速阶段(加速度恒定)
- 减加速阶段(加速度逐渐减小)
- 匀速阶段
- 加减速阶段(减速度逐渐增大)
- 匀减速阶段(减速度恒定)
- 减减速阶段(减速度逐渐减小)
2.2 数学建模关键
实现S曲线控制需要建立三个关键方程:
- 加加速度方程:j(t) = J(常数)
- 加速度方程:a(t) = ∫j(t)dt
- 速度方程:v(t) = ∫a(t)dt
- 位置方程:s(t) = ∫v(t)dt
在实际工程实现中,我们通常采用离散化的递推公式来计算各时刻的速度值。以加加速阶段为例:
v[n] = v[n-1] + (a[n-1] * T + 0.5 * J * T²)
其中T为定时器中断周期
3. STM32硬件实现方案
3.1 硬件配置要点
使用STM32F103C8T6最小系统板,关键外设配置:
- 定时器TIM1用于产生PWM脉冲信号
- 定时器TIM2用于速度曲线计算中断
- GPIO引脚配置为推挽输出驱动步进电机驱动器
特别注意:PWM频率应根据电机最高转速选择。以常见的1.8°步距角电机为例:
最高转速600rpm对应的脉冲频率 = (600/60)*200 = 2000Hz
因此PWM频率应至少设置为2kHz以上
3.2 驱动器接口设计
常见的步进电机驱动器(如DM542)需要两个控制信号:
- PUL脉冲信号:每个上升沿使电机走一步
- DIR方向信号:高低电平控制转向
接线示例:
c复制// STM32引脚配置
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_0; // PUL
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_1; // DIR
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
4. 软件算法实现细节
4.1 速度曲线生成算法
c复制typedef struct {
uint32_t step_count; // 总步数
uint32_t current_step; // 当前步数
float current_speed; // 当前速度(步/秒)
float max_speed; // 最大速度
float acceleration; // 加速度
float jerk; // 加加速度
uint16_t timer_arr; // 定时器重载值
} MotorControl;
void TIM2_IRQHandler(void) {
static uint8_t phase = 0;
MotorControl* mc = &motor;
// 计算下一脉冲间隔
switch(phase) {
case 0: // 加加速
mc->current_speed += mc->jerk * T;
if(mc->current_speed >= mc->max_speed/4) phase++;
break;
case 1: // 匀加速
mc->current_speed += mc->acceleration * T;
if(mc->current_speed >= mc->max_speed*0.75) phase++;
break;
// 其他阶段类似...
}
// 更新定时器ARR值
mc->timer_arr = (uint16_t)(SystemCoreClock / mc->current_speed);
__HAL_TIM_SET_AUTORELOAD(&htim1, mc->timer_arr);
// 步数统计
mc->current_step++;
if(mc->current_step >= mc->step_count) {
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
}
}
4.2 参数整定方法
关键参数的计算公式:
- 最小加加速时间:t₁ = √(v_max/(2*J))
- 匀加速时间:t₂ = (v_max - 2Jt₁²)/a
- 总加速时间:T_acc = 2*t₁ + t₂
实际调试时建议先用较低速度测试:
- 先设置较小的J值(如100 steps/s³)
- 逐步增加J值直到出现振动,然后回退20%
- 最后调整最大速度v_max
5. 实际应用中的优化技巧
5.1 动态调整策略
在长距离移动时,可能出现未达到最大速度就需要减速的情况。此时需要重新计算速度曲线:
c复制void recalculate_curve(MotorControl* mc) {
float decel_distance = mc->current_speed * mc->current_speed / (2 * mc->acceleration);
if((mc->step_count - mc->current_step) < decel_distance) {
// 进入减速阶段
mc->phase = 4; // 跳转到加减速阶段
}
}
5.2 抗振颤措施
- 在PCB布局时,PUL信号线要尽量短
- 在驱动器输入端并联100Ω电阻和100nF电容滤波
- 软件上可增加脉冲边沿平滑处理:
c复制void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
// 在脉冲结束时轻微延时
if(htim->Instance == TIM1) {
delay_us(2);
}
}
6. 性能测试数据
使用1000步的移动距离进行测试:
| 参数 | 梯形曲线 | S曲线 |
|---|---|---|
| 定位时间 | 1.2s | 1.35s |
| 最大振动幅度 | 0.5mm | 0.05mm |
| 重复定位精度 | ±0.2mm | ±0.05mm |
| 运行噪声 | 65dB | 52dB |
实测表明,S曲线虽然增加了约12.5%的运动时间,但振动和噪声水平显著降低,精度提升明显。
7. 常见问题排查
7.1 电机不转动
检查顺序:
- 确认驱动器供电正常(测量V+电压)
- 检查PUL信号是否有波形(示波器观察)
- 确认DIR信号电平正确
- 检查电机绕组电阻(通常几欧姆)
7.2 定位不准
可能原因及解决:
- 丢步现象:增加驱动电流或降低最大速度
- 机械回差:检查联轴器和导轨间隙
- 曲线参数不当:减小加加速度J值
7.3 异常噪声
处理方案:
- 在速度突变点增加平滑过渡
- 尝试不同的微步设置(如1/8步改为1/16步)
- 检查机械装配是否松动
8. 进阶优化方向
- 自适应参数调整:根据负载自动调整曲线参数
c复制void auto_tune_parameters(MotorControl* mc) {
// 通过检测电流波动自动调整J值
float current_ripple = get_current_ripple();
if(current_ripple > threshold) {
mc->jerk *= 0.9; // 减小加加速度
}
}
-
前馈补偿:根据已知负载特性预先补偿曲线
-
双闭环控制:增加编码器实现位置闭环
这个项目我从最初的梯形曲线升级到S曲线用了近一个月时间调试,最大的体会是:参数整定需要耐心,建议准备一个记录本,每次调整都记录下参数和效果。另外,用手机慢动作视频拍摄电机运动状态是个很实用的调试技巧,能直观看到振动情况。