3. 编码器与速度反馈:增量式编码器原理、正交解码、速度测量方法(M法/T法)

做电机控制,速度反馈是闭环的「眼睛」。没有准确的速度值,PID 调得再好也是瞎忙活。这一章我们聊聊编码器——尤其是增量式编码器,以及怎么从它的脉冲信号里算出速度。

3.1 增量式编码器原理

增量式编码器,说白了就是一个「脉冲发生器」。它内部有个码盘,上面刻着明暗相间的条纹。电机一转,光耦或磁敏元件就会输出一串脉冲。脉冲的个数对应转过的角度,脉冲的频率对应转速。

我刚开始做项目时,总觉得绝对值编码器更高级,为什么大家还用增量式?原因很简单:便宜、接口简单、分辨率可以做得非常高。你想想看,一个 1024 线的增量式编码器,配上四倍频,一圈就能输出 4096 个脉冲,角度分辨率接近 0.1 度,很多场合完全够用了。

核心概念: 增量式编码器只输出相对位置变化,不输出绝对位置。上电后需要找零位(Z 信号)或执行回零操作。

3.2 正交解码(Quadrature Decoding)

增量式编码器通常输出两路信号:A 相和 B 相,相位差 90 度。这就是「正交」的由来。为什么要两路?为了判断方向。

判断逻辑很简单:

  • A 相上升沿时,B 相是高电平 → 正转
  • A 相上升沿时,B 相是低电平 → 反转

实际项目中,我习惯用 MCU 的定时器编码器模式(比如 STM32 的 TIM_EncoderMode)来硬件解码。这样 CPU 不用管脉冲计数,定时器自己就搞定了,省心省力。

我的经验: 如果 MCU 没有编码器接口,可以用两个外部中断加一个定时器捕获。但要注意中断频率——电机高速旋转时,脉冲频率可能达到几十 kHz,中断太频繁会拖死系统。

3.3 速度测量方法:M 法 vs T 法

有了脉冲信号,怎么算速度?主要有两种方法:M 法和 T 法。我当年刚入行时,以为随便用一种就行,结果低速时抖得不行,高速时又跟不上。后来才明白,这两种方法各有各的脾气。

方法 原理 适用场景 精度特点
M 法(测频法) 固定时间 T 内,计数脉冲个数 N 中高速(脉冲多) 高速时精度高,低速时误差大
T 法(测周法) 测量两个脉冲之间的时间间隔 Δt 低速(脉冲少) 低速时精度高,高速时误差大
M/T 法 结合两者,动态切换 全速域 综合最优,但实现稍复杂

3.3.1 M 法(测频法)

M 法的公式很简单:

速度 = (脉冲数 N) / (编码器线数 PPR × 测量时间 T) × 60  (单位:RPM)

举个例子:编码器 1024 线,测量时间 100ms,计到 512 个脉冲。那么:

速度 = 512 / (1024 × 0.1) × 60 = 300 RPM

M 法的问题在于低速。如果电机只转 10 RPM,100ms 内可能只抓到 1-2 个脉冲,算出来的速度跳来跳去。我曾经在一个低速伺服项目里用 M 法,速度反馈波动超过 30%,PID 根本稳不住。

避坑指南: 我曾经在低速时用 M 法,发现速度反馈值忽大忽小。后来一查,是因为测量时间太短,脉冲计数太少。解决办法是延长测量时间,或者改用 T 法。

3.3.2 T 法(测周法)

T 法反过来:测量两个脉冲之间的时间间隔。公式:

速度 = 1 / (时间间隔 Δt × 编码器线数 PPR) × 60  (单位:RPM)

低速时,脉冲间隔长,时间测量值大,相对误差小。但高速时,脉冲间隔极短,定时器分辨率不够,误差反而变大。

我个人的习惯是:低速用 T 法,高速用 M 法。如果项目要求全速域都稳定,那就上 M/T 法——根据当前速度动态切换测量策略。

3.4 实际项目中的速度反馈实现

下面是一个基于 STM32 定时器编码器模式的 M 法速度测量代码片段。我通常把测量周期设为 10ms 到 100ms 之间,具体看系统响应要求。

// 定时器编码器模式初始化(以 TIM2 为例)
void Encoder_Init(void) {
    TIM_EncoderInterfaceConfig(TIM2, TIM_ENCODERMODE_TI12, 
                                TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
    TIM_Cmd(TIM2, ENABLE);
}

// 每 50ms 调用一次,计算速度
float Get_Speed_RPM(void) {
    static uint16_t last_count = 0;
    uint16_t current_count = TIM_GetCounter(TIM2);
    uint16_t pulse_diff = current_count - last_count;
    last_count = current_count;
    
    // 假设编码器 1024 线,4 倍频后 PPR = 4096
    // 测量周期 50ms = 0.05s
    float speed = (float)pulse_diff / (4096 * 0.05f) * 60.0f;
    return speed;
}
注意: 代码里要处理计数器溢出。STM32 的 16 位定时器最大计数值 65535,如果电机高速旋转,50ms 内可能溢出多次。我一般用 32 位定时器,或者在中断里做溢出计数。

3.5 知识结构图:速度反馈系统

下面这张图展示了从编码器到速度反馈的完整链路。我画这张图时,特意把「测量方法选择」放在中间——因为这是最容易出问题的地方。

速度反馈系统知识结构图 增量式编码器 正交解码(A/B/Z) 速度测量方法选择 M 法(测频) T 法(测周) 适用:中高速 适用:低速 输出:速度值(RPM)→ PID 控制器

3.6 实际项目中的避坑经验

最后分享几个我在项目中踩过的坑:

  • 编码器线数不是越高越好。 线数高,分辨率高,但脉冲频率也高。MCU 处理不过来时,反而会丢脉冲。我一般根据最高转速和 MCU 性能来选:保证最高转速时脉冲频率不超过定时器捕获上限的 80%。
  • Z 信号一定要接。 虽然增量式编码器不输出绝对位置,但 Z 信号(零位脉冲)每圈输出一次,可以用来校准累计误差。我有个项目没接 Z 信号,运行久了位置漂移越来越严重。
  • 滤波不能省。 编码器信号在电机启停时容易抖动。我习惯在硬件上加 RC 滤波,软件里再做一次滑动平均。别指望 PID 能自动滤掉这些噪声——它只会让电机跟着抖。
一句话总结: 速度反馈的精度,决定了 PID 控制的天花板。编码器选型、解码方式、测量方法,每一步都值得认真对待。