4. 编码器与速度测量:增量式编码器原理、STM32编码器接口模式、M法/T法/M-T法测速
做电机控制,速度反馈是闭环的灵魂。没有准确的速度测量,PID调得再好也是瞎忙活。今天咱们就聊聊编码器——这个在电机控制里最常见的“眼睛”。
我个人习惯把编码器比作电机的“码表”。它不直接告诉你速度,而是告诉你“转了多少格”。你得自己算,才能知道跑多快。这“怎么算”就是M法、T法、M-T法的核心。
4.1 增量式编码器工作原理
增量式编码器,说白了就是一个带光栅的圆盘。圆盘转,光栅切光,传感器输出脉冲。你数脉冲个数,就知道转了多少角度。
它通常输出两路信号:A相和B相。这两路信号相位差90度。通过判断谁先跳变,就能知道电机是正转还是反转。
核心概念:增量式编码器只输出“变化量”,不输出“绝对位置”。断电再上电,位置就丢了。需要配合归零校准。
我遇到过不少新手,上来就问:“编码器分辨率怎么算?”其实很简单:
分辨率 = 360° / (线数 × 4)
为什么乘以4?因为A相和B相的上升沿和下降沿都能用,这叫“四倍频”。比如一个1000线的编码器,四倍频后一圈就是4000个脉冲,每个脉冲对应0.09°。
| 参数 | 说明 | 常见值 |
|---|---|---|
| 线数(PPR) | 编码器每圈输出的脉冲数 | 500、1000、2000、4096 |
| 四倍频后 | 实际可用的计数脉冲 | 4 × PPR |
| Z相(零位) | 每圈输出一个脉冲,用于归零 | 1个/圈 |
4.2 STM32编码器接口模式
STM32的定时器有个很聪明的功能——编码器接口模式。你不需要在中断里手动判断A相B相,硬件全帮你干了。
配置起来其实就三步:
- 选定时器:STM32的TIM1~TIM8、TIM2~TIM5都支持编码器模式。具体看数据手册。
- 配GPIO:把A相接TI1,B相接TI2。注意要复用功能。
- 设模式:TIM_SlaveMode_Encoder,再选TI1和TI2都计数(这样就是四倍频)。
// STM32编码器模式配置示例(基于HAL库)
TIM_Encoder_InitTypeDef sEncoderConfig = {0};
sEncoderConfig.EncoderMode = TIM_ENCODERMODE_TI12; // TI1和TI2都计数
sEncoderConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sEncoderConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sEncoderConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sEncoderConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sEncoderConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sEncoderConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sEncoderConfig.IC1Filter = 0;
sEncoderConfig.IC2Filter = 0;
HAL_TIM_Encoder_Init(&htim3, &sEncoderConfig);
HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
我的经验:编码器线缆长的时候,信号容易抖动。我习惯在ICFilter里设个2~4的滤波值,能有效滤掉毛刺。别设太大,否则会丢失高速脉冲。
配置好之后,定时器的CNT寄存器会自动加减。正转加1,反转减1。你只需要定时读CNT值,再清零,就能算出这段时间的脉冲增量。
4.3 M法、T法、M-T法测速
有了脉冲数,怎么算速度?这就引出了三种经典方法。我当年刚入行时也纠结过该用哪个,后来发现——没有最好的,只有最合适的。
先看一张对比图,心里有个底:
4.4 三种方法的详细实现
M法(测频法)
M法是最直观的。你固定一个时间窗口,比如10ms,数这个窗口内来了多少个脉冲。脉冲多,说明转得快。
// M法测速示例
uint32_t pulse_count;
float speed_rpm;
void M_Method_Timer_IRQ(void) {
// 每10ms进入一次
pulse_count = __HAL_TIM_GET_COUNTER(&htim3);
__HAL_TIM_SET_COUNTER(&htim3, 0);
// 速度 = 脉冲数 / (时间 × 编码器分辨率)
// 假设编码器1000线,四倍频后4000脉冲/圈
speed_rpm = (float)pulse_count / (0.01f * 4000.0f) * 60.0f;
}
注意:M法在低速时误差很大。比如10ms内只来了1个脉冲,那速度就是1.5 RPM。但如果实际是1.4 RPM呢?你测不出来。这就是“量化误差”。
T法(测周法)
T法反过来。我不固定时间,而是等固定数量的脉冲来了,再算用了多少时间。比如等10个脉冲,用定时器量这段时间。
低速时,脉冲间隔长,时间测得很准。但高速时,10个脉冲一瞬间就过去了,时间分辨率不够,误差反而大。
// T法测速思路(伪代码)
uint32_t time_interval; // 单位us
uint32_t pulse_target = 10; // 等10个脉冲
void T_Method_Edge_IRQ(void) {
static uint32_t last_time = 0;
static uint32_t pulse_cnt = 0;
pulse_cnt++;
if(pulse_cnt >= pulse_target) {
time_interval = current_time - last_time;
last_time = current_time;
pulse_cnt = 0;
// 速度 = 脉冲数 / (时间 × 分辨率)
speed_rpm = (float)pulse_target / (time_interval * 1e-6f * 4000.0f) * 60.0f;
}
}
M-T法
M-T法是我个人最推荐的方法。它同时测脉冲数和时间,用高频时钟来填充时间间隔。不管高速低速,精度都很好。
具体做法:启动一个高频定时器(比如1MHz),同时开始数编码器脉冲。当编码器脉冲数达到预设值(比如10个)时,停掉高频定时器,读出计数值。这样既知道脉冲数,又知道精确时间。
避坑指南:我曾经在一个项目里直接用M法测低速,结果速度反馈抖得像心电图。换成M-T法后,波形平滑多了。如果你的电机需要从零速到高速平稳运行,别犹豫,上M-T法。
4.5 实际项目中的选型建议
说了这么多,到底怎么选?我总结了一个简单的决策表:
| 应用场景 | 推荐方法 | 理由 |
|---|---|---|
| 高速风机(>3000 RPM) | M法 | 脉冲密集,量化误差小,实现简单 |
| 低速云台(< 10 RPM) | T法 | 脉冲间隔长,时间测量精度高 |
| 伺服电机(0~3000 RPM) | M-T法 | 全速域精度一致,适合闭环控制 |
| 低成本玩具 | M法 | 代码量小,MCU资源占用少 |