第三章 脉冲信号与运动控制:脉冲频率与转速关系、加减速曲线原理、脉冲数计算与定位控制
各位工程师朋友,这一章咱们聊聊步进电机运动控制的核心——脉冲信号。说白了,步进电机就是个“数字电机”,你给它多少脉冲,它就转多少角度。但实际项目中,光给脉冲可不够,你得会算频率、会画加减速曲线、还得算准定位脉冲数。我刚开始搞运动控制时,就吃过“脉冲数算错,位置跑偏”的亏。今天咱们把这些坑一个个填上。
3.1 脉冲频率与转速的关系
先搞清楚最基础的关系。步进电机每收到一个脉冲,就转动一个步距角。比如常见的1.8°步进电机,转一圈需要200个脉冲(360° ÷ 1.8° = 200)。
转速公式很简单:
转速 (r/min) = (脉冲频率 × 60) ÷ (每转脉冲数 × 细分数)
举个例子:你给驱动器发10kHz的脉冲,细分数设为4(即每转800个脉冲),那么转速就是:
转速 = (10000 × 60) ÷ (200 × 4) = 600000 ÷ 800 = 750 r/min
嗯,这里要注意:细分数越大,电机转动越平滑,但同样频率下转速会变慢。我有个项目是做贴片机,为了兼顾速度和精度,最后选了16细分,脉冲频率跑到200kHz才够用。
核心公式速记:
- 每转脉冲数 = 360° ÷ 步距角 × 细分数
- 转速(r/min) = 脉冲频率(Hz) × 60 ÷ 每转脉冲数
- 脉冲频率(Hz) = 转速(r/min) × 每转脉冲数 ÷ 60
3.2 加减速曲线原理
为什么需要加减速?你想想看,电机从静止直接跳到高速,或者从高速突然刹停,步进电机很容易丢步。说白了就是惯性太大,电机转子跟不上脉冲指令。
我个人习惯把加减速曲线分成两种:梯形和S形。
3.2.1 梯形加减速曲线
梯形曲线是最简单的。加速段、匀速段、减速段,三段拼起来像个梯形。加速和减速都是恒定的加速度。
// 梯形加减速的伪代码示例
void trapezoidal_move(int target_speed, int total_steps) {
int accel_steps = total_steps * 0.3; // 加速段占30%
int decel_steps = total_steps * 0.3; // 减速段占30%
int const_steps = total_steps * 0.4; // 匀速段占40%
// 加速阶段:频率从0线性增加到target_speed
for (int i = 0; i < accel_steps; i++) {
current_speed = (target_speed * i) / accel_steps;
send_pulse(current_speed);
}
// 匀速阶段
for (int i = 0; i < const_steps; i++) {
send_pulse(target_speed);
}
// 减速阶段
for (int i = decel_steps; i > 0; i--) {
current_speed = (target_speed * i) / decel_steps;
send_pulse(current_speed);
}
}
梯形曲线的优点是计算简单,适合大多数场合。但缺点也很明显:加速度突变。在加速开始和结束的瞬间,电机会有冲击感。我曾经在一个高速贴标机上用梯形曲线,结果标签总是贴歪——后来发现是加减速切换时的振动导致的。
3.2.2 S形加减速曲线
S形曲线就优雅多了。它的加速度不是恒定的,而是从0逐渐增加到最大值,再逐渐减小到0。这样电机启停非常平滑,几乎没有冲击。
S形曲线的核心是“加加速度”(Jerk)的概念。说白了就是加速度的变化率。加加速度越小,曲线越平滑。
// S形加减速的简化实现(使用正弦函数近似)
void s_curve_move(int target_speed, int total_steps) {
for (int i = 0; i < total_steps; i++) {
float t = (float)i / total_steps; // 归一化时间 0~1
// 使用正弦函数生成S形速度曲线
float speed_factor = 0.5 * (1 - cos(M_PI * t));
current_speed = target_speed * speed_factor;
send_pulse(current_speed);
}
}
我的经验:
梯形曲线适合对速度要求不高、负载较轻的场合。S形曲线适合精密定位、高速运动、或者负载变化大的场合。如果MCU算力有限,可以用查表法预计算S形曲线数据,运行时直接查表,省去实时计算的开销。
3.3 脉冲数计算与定位控制
定位控制说白了就是:你要让电机转多少角度,就得发多少个脉冲。这个计算本身不难,但容易出错的地方在于——你忘了考虑机械传动比。
完整的脉冲数计算公式:
脉冲数 = (目标位移角度 ÷ 步距角) × 细分数 × 传动比
举个例子:你要让丝杠滑台移动100mm,丝杠导程是5mm(即丝杠转一圈,滑台移动5mm),步进电机步距角1.8°,细分数8。那么:
电机需要转的圈数 = 100mm ÷ 5mm/圈 = 20圈
每圈脉冲数 = 360° ÷ 1.8° × 8 = 1600 脉冲/圈
总脉冲数 = 20 × 1600 = 32000 脉冲
避坑指南:
我曾经在一个项目中,把传动比算反了。结果电机转了20圈,滑台只走了5mm。排查了半天才发现是公式里的传动比写成了倒数。所以我的习惯是:先用手动模式让电机转一圈,实测滑台移动距离,反过来验证传动比对不对。
3.4 实际项目中的定位控制策略
搞定了脉冲数计算,接下来就是怎么发脉冲的问题。我常用的方法有三种:
- 定时器中断法:用定时器产生精确的PWM脉冲,频率可调。适合需要连续变速的场合。
- 专用运动控制芯片法:比如TMC5160、TB6600等,硬件自动处理加减速和脉冲计数,MCU只需发指令。
- 软件延时法:用delay或循环延时产生脉冲。简单但不精确,只适合低速场合。
我个人最推荐定时器中断法。你看,用STM32的定时器,可以轻松实现脉冲频率的实时调整,配合加减速算法,效果非常好。
// STM32定时器配置示例(产生可变频率脉冲)
void TIM_Pulse_Init(void) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef TIM_Init;
TIM_Init.TIM_Prescaler = 72 - 1; // 72MHz分频到1MHz
TIM_Init.TIM_Period = 1000 - 1; // 初始频率1kHz
TIM_Init.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_Init);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
// 在中断服务函数中翻转IO口产生脉冲
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
GPIO_ToggleBits(GPIOA, GPIO_Pin_0); // 脉冲输出
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
pulse_count++; // 脉冲计数
if (pulse_count >= target_pulses) {
TIM_Cmd(TIM2, DISABLE); // 到达目标位置,停止
}
}
}
定位控制的关键点:
- 脉冲计数一定要用硬件计数器,不要用软件变量累加——中断可能丢脉冲
- 加减速的步数要预留足够,别让电机还没加速到目标速度就开始减速
- 定位完成后,建议加一段“保持电流”,防止电机被外力推动
3.5 本章小结
这一章的内容其实就三件事:算频率、画曲线、数脉冲。但实际项目中,这三件事环环相扣,哪一个出问题都会导致运动控制失败。
我记得有一次调试一台三轴点胶机,Z轴老是定位不准。查了三天,最后发现是加减速曲线里的加速段太短,电机还没到目标速度就开始匀速,导致实际走的步数比理论值少了几十步。后来把加速段从20%调整到40%,问题就解决了。
所以我的建议是:先在小范围内验证你的脉冲数和加减速参数,确认无误后再上高速。别一上来就全速跑,丢步了都不知道。
下一章咱们聊聊步进电机的驱动电路和电流控制,那又是另一番天地了。