脉冲输出性能优化:频率精度、加减速曲线与抖动抑制

脉冲输出,说白了就是运动控制卡跟电机驱动器之间的“对话语言”。你发的脉冲准不准,直接决定了电机走位精不精。我这些年调试过的设备,十有八九的问题都出在这个环节上。今天咱们就把脉冲输出的三个核心问题掰开揉碎了讲。

一、脉冲频率精度控制

先问个问题:你发的 100kHz 脉冲,实际到驱动器那里真的是 100kHz 吗?

很多工程师觉得,只要定时器中断里翻转 IO 就行了。嗯,没那么简单。我见过一个项目,客户说电机跑着跑着就丢步,查了半天,原来是脉冲频率波动太大,驱动器内部滤波直接把部分脉冲当噪声滤掉了。

核心原则:脉冲频率精度 = 定时器时钟源精度 + 中断响应抖动 + 软件补偿算法

1.1 定时器时钟源选择

我个人习惯,优先用专用的 PWM 定时器,而不是通用定时器。为什么?

  • 专用 PWM 定时器:硬件自动装载比较值,CPU 几乎不参与,频率稳定
  • 通用定时器:每次中断都要软件重装载,中断延迟直接导致频率抖动

举个例子,我用 STM32 的 TIM1 做脉冲输出,时钟源选内部 PLL 倍频到 180MHz,分频后做 1MHz 基准。这时候发 100kHz 脉冲,理论误差是 0。但如果你用 SysTick 做,每次进中断再翻转,误差至少 0.5% 起步。

时钟源方案 典型精度 CPU 占用 适用场景
专用 PWM 定时器 ±0.01% 几乎为 0 高速高精度运动
通用定时器中断 ±0.5%~2% 中等 低速或非关键运动
DMA + 定时器 ±0.05% 低 中等速度,波形灵活

1.2 频率补偿算法

实际晶振总有温漂,尤其是工业现场温度变化大的时候。我建议加一个简单的频率补偿:

// 频率补偿示例
// 实际频率 = 目标频率 * (1 + 补偿系数)
// 补偿系数通过测量实际晶振频率与标称值的偏差得到

float freq_compensation(float target_hz) {
    static float comp_factor = 0.0f;
    static int calibrated = 0;
    
    if (!calibrated) {
        // 测量 1 秒内实际脉冲数
        uint32_t measured = measure_pulses_in_1s();
        comp_factor = (float)REFERENCE_HZ / measured - 1.0f;
        calibrated = 1;
    }
    
    return target_hz * (1.0f + comp_factor);
}

我的经验:补偿系数建议每 10 分钟重新校准一次。工业现场温度变化没那么快,10 分钟足够跟踪温漂了。别每秒钟都校准,反而引入噪声。

二、加减速曲线算法

电机启动时直接给目标速度,你想想看会发生什么?轻则丢步,重则堵转。加减速曲线就是解决这个问题的。

我做过一个贴片机项目,最开始用梯形加减速,结果贴装头到终点时总是过冲。后来换成 S 形曲线,问题就解决了。选什么曲线,得看你的应用场景。

2.1 梯形加减速

梯形曲线最简单,加速段和减速段都是恒加速度。适合对启停时间要求不高的场合。

// 梯形加减速:速度分段计算
// t_acc: 加速时间, t_run: 匀速时间, t_dec: 减速时间
// v_max: 最大速度, a: 加速度

float trapezoid_speed(float t, float t_acc, float t_run, float t_dec, float v_max, float a) {
    if (t < t_acc) {
        // 加速段
        return a * t;
    } else if (t < t_acc + t_run) {
        // 匀速段
        return v_max;
    } else if (t < t_acc + t_run + t_dec) {
        // 减速段
        return v_max - a * (t - t_acc - t_run);
    } else {
        return 0.0f;
    }
}

注意:梯形曲线在加速结束和减速开始的瞬间,加速度突变。这会导致机械冲击。我曾经在一个精密平台上用梯形曲线,结果平台振动幅度直接超了规格书 3 倍。

2.2 S 形加减速

S 形曲线加了一个加加速度(Jerk)控制,让加速度平滑变化。说白了就是让速度变化更“温柔”。

我个人习惯用 7 段式 S 形曲线:加加速、匀加速、减加速、匀速、加减速、匀减速、减减速。每一段都有对应的加加速度值。

// S 形曲线:7 段式速度计算(简化版)
// jerk: 加加速度, a_max: 最大加速度, v_max: 最大速度

float s_curve_speed(float t, float jerk, float a_max, float v_max) {
    float t1 = a_max / jerk;           // 加加速段时间
    float t2 = v_max / a_max - t1;     // 匀加速段时间
    
    if (t < t1) {
        // 加加速段:速度 = 0.5 * jerk * t^2
        return 0.5f * jerk * t * t;
    } else if (t < t1 + t2) {
        // 匀加速段:速度 = 0.5 * jerk * t1^2 + a_max * (t - t1)
        float v1 = 0.5f * jerk * t1 * t1;
        return v1 + a_max * (t - t1);
    } else if (t < 2 * t1 + t2) {
        // 减加速段
        float v2 = 0.5f * jerk * t1 * t1 + a_max * t2;
        float dt = t - t1 - t2;
        return v2 + a_max * dt - 0.5f * jerk * dt * dt;
    }
    // ... 后续匀速、减速段类似
    return v_max;
}

2.3 多项式曲线

当 S 形曲线还不够平滑时,可以用多项式曲线。比如 5 次多项式,速度和加速度都连续,加加速度也连续。

我记得有个半导体封装项目,要求启停时振动小于 0.1g。梯形曲线根本做不到,S 形曲线勉强达标,最后用了 7 次多项式才搞定。

曲线类型 加速度连续性 加加速度连续性 计算量 适用场景
梯形 不连续 不连续 极低 低速、大惯量
S 形(7 段) 连续 分段常数 中等 通用运动控制
5 次多项式 连续 连续 较高 高精度、低振动
7 次多项式 连续 连续且平滑 高 极端精密场景

三、脉冲抖动(Jitter)抑制技术

脉冲抖动,就是每个脉冲的周期长度不一致。你想想看,如果第一个脉冲周期是 10μs,第二个变成 12μs,第三个又变成 9μs,电机能不抖吗?

我曾经调试一台激光切割机,切割边缘总是有锯齿。查了三天,最后发现是脉冲抖动导致激光功率和位置不同步。从那以后,我对抖动抑制格外重视。

3.1 硬件级抖动抑制

  • 专用脉冲发生器芯片:比如 TI 的 TMS320 系列内置 ePWM 模块,硬件产生脉冲,抖动几乎为 0
  • FPGA 实现:用 Verilog 写一个脉冲发生器,时钟域完全可控,抖动可以做到 ±1 个时钟周期
  • DMA 传输:用 DMA 把脉冲参数提前准备好,定时器自动装载,避免 CPU 干预

3.2 软件级抖动抑制

如果只能用 MCU 的定时器中断,那就要在软件上下功夫:

  1. 中断优先级提升:把脉冲定时器中断设为最高优先级,避免被其他中断打断
  2. 中断服务函数精简:ISR 里只做最必要的操作,其他计算放到主循环
  3. 双缓冲技术:准备两个比较值寄存器,一个当前在用,一个后台更新,切换时无缝隙
// 双缓冲脉冲输出示例
// 使用定时器的影子寄存器(shadow register)

void pulse_timer_isr() {
    // 清除中断标志
    TIM_ClearFlag(TIM1, TIM_FLAG_UPDATE);
    
    // 直接读取下一个比较值(已在后台准备好)
    // 不需要在这里计算,减少 ISR 耗时
    TIM_SetCompare1(TIM1, next_compare_value);
    
    // 通知主循环准备下一个值
    pulse_ready_flag = 1;
}

void main_loop() {
    while (1) {
        if (pulse_ready_flag) {
            pulse_ready_flag = 0;
            // 在这里计算下一个脉冲的比较值
            next_compare_value = calculate_next_pulse();
        }
    }
}

避坑指南:我曾经在 ISR 里做浮点运算,结果脉冲抖动直接飙到 5μs。后来把所有浮点运算都移到主循环,抖动降到了 0.2μs。记住,ISR 里只做整数赋值,别做复杂计算。

知识体系总览

下面这张图把脉冲输出优化的三个维度串起来了。你可以把它当作调试时的检查清单。

脉冲输出性能优化知识体系 脉冲输出性能优化核心框架 频率精度控制 加减速曲线算法 抖动抑制技术 时钟源选择 频率补偿算法 梯形曲线 S 形曲线 多项式曲线 硬件级抑制 软件级抑制 三个维度相互影响:频率精度是基础,加减速曲线决定运动品质,抖动抑制保证执行精度

实战建议

最后给几个实在的建议:

  • 先测抖动:用示波器看脉冲波形,测 1000 个脉冲的周期,算标准差。如果标准差超过平均周期的 5%,必须优化
  • 曲线选择:普通设备用梯形就够了,精密设备上 S 形,极端场景才上多项式。别杀鸡用牛刀
  • 预留裕量:脉冲频率别跑满定时器的极限,留 20% 裕量给中断延迟和温度变化

最后提醒一句:脉冲输出优化没有银弹。你必须在频率精度、曲线平滑度和抖动抑制之间做权衡。我见过太多人追求极致的 S 形曲线,结果 CPU 算不过来,抖动反而更大。适可而止,够用就好。