1. 编码器基础从分类到工作原理第一次接触编码器是在大四的智能车竞赛上当时为了精确控制小车的电机转速不得不硬着头皮研究这个会输出脉冲的小玩意儿。现在回想起来编码器确实是运动控制系统中不可或缺的眼睛。编码器本质上是一种将机械运动转换为电信号的传感器。根据不同的分类标准编码器可以分为多种类型按工作原理增量式、绝对式、霍尔式按检测方式光电式、磁电式按运动形式旋转式、直线式1.1 增量式编码器工业界的脉搏计数器增量式编码器就像是一个精密的脉搏计数器。它通过输出连续的脉冲信号来反映位移变化每转过一个固定角度就会输出一个脉冲。我实验室里用的逐飞科技编码器就是典型的增量式有A、B、Z三相信号A、B相相位差90°的方波用于计数和判断方向Z相每转一圈输出一个脉冲作为零位参考实际使用中发现这种编码器的优势是结构简单、成本低但断电后需要重新找零位。在智能车项目中我们就是通过Z相信号来校准初始位置的。1.2 绝对式编码器自带记忆功能的高端货绝对式编码器更像是自带GPS定位的装置。它的码盘上刻有独特的二进制编码每个位置都有唯一的数字编码。这就好比摩斯电码不同的排列组合对应不同的信息。我拆解过一个多圈绝对编码器内部采用齿轮组结构记录圈数。这种编码器最大的优点就是断电后位置信息不会丢失在工业机械臂上应用广泛。不过价格也确实美丽一个高精度的绝对编码器能顶我一个月工资。1.3 霍尔编码器经济实用的选择霍尔编码器利用霍尔效应检测磁场变化输出两路正交信号。相比前两种它的结构更简单、抗干扰能力更强。我在淘宝上买的20元一个的霍尔编码器用在直流减速电机上效果就很不错。实测发现霍尔编码器虽然分辨率较低常见16-64PPR但对于一般的速度控制已经足够。而且它不怕灰尘油污在恶劣环境下也能稳定工作这点在工业现场特别重要。2. 编码器测速原理深度剖析2.1 脉冲计数编码器测速的核心编码器测速的本质就是统计脉冲数量。想象一下高速公路的车辆计数器通过统计单位时间内经过的车辆数就能估算出车流量。编码器测速也是这个道理只不过车辆变成了电脉冲。在实际项目中我常用三种测速方法M法频率法固定时间内统计脉冲数T法周期法测量相邻脉冲的时间间隔M/T法结合前两种的优势2.2 四倍频技术精度提升的秘诀刚开始做项目时我发现编码器的实际分辨率比标称值高很多。后来才知道这是用了四倍频技术——通过检测A、B相的上升沿和下降沿将分辨率提高4倍。以常见的100PPR编码器为例原始分辨率100脉冲/转四倍频后400脉冲/转这个技巧在低速测量时特别有用。我曾经用STM32的编码器模式实现了四倍频代码配置后面会详细介绍。2.3 方向判断AB相的秘密AB相信号的相位差不仅用于倍频还能判断旋转方向。这里有个简单规律正转时A相上升沿对应B相低电平反转时A相上升沿对应B相高电平在STM32中通过读取计数器的计数方向CR1寄存器的DIR位就能知道电机转向非常方便。3. STM32硬件配置实战3.1 定时器编码器模式配置STM32的定时器外设内置了编码器接口配置起来比用外部中断方便多了。以TIM3为例最关键的几个配置步骤GPIO初始化配置PA6、PA7为浮空输入时基设置ARR设为最大值PSC为0编码器模式选择TI1和TI2边沿计数滤波器设置根据信号质量调整ICF值void Encoder_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 1. 时钟使能 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 2. GPIO配置 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_TIM3); GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_TIM3); // 3. 时基配置 TIM_TimeBaseStructure.TIM_Period 0xFFFF; TIM_TimeBaseStructure.TIM_Prescaler 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); // 4. 编码器接口配置 TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_ICStructInit(TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter 6; TIM_ICInit(TIM3, TIM_ICInitStructure); TIM_Cmd(TIM3, ENABLE); }3.2 中断配置与计数处理为了处理计数器溢出我们需要配置更新中断。在中断服务函数中记录溢出次数volatile int32_t Encoder_Overflow 0; void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_Update) SET) { if(TIM_GetCounter(TIM3) 0x7FFF) Encoder_Overflow--; else Encoder_Overflow; TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } }3.3 速度计算从脉冲到转速速度计算的关键是理解几个参数的关系PPR编码器每转脉冲数物理分辨率倍频数通常为4四倍频采样周期计算速度的时间窗口速度计算公式转速(rpm) (ΔCount / (PPR×4)) × (60 / 采样时间(s))实现代码int32_t Get_Speed(uint32_t PPR, float sample_time) { static int32_t last_count 0; int32_t current_count TIM_GetCounter(TIM3) Encoder_Overflow * 65536; int32_t delta current_count - last_count; last_count current_count; return (delta * 60) / (PPR * 4 * sample_time); }4. 常见问题与优化技巧4.1 信号抖动硬件滤波很重要在第一个智能车项目上我遇到过信号抖动导致计数不准的问题。后来发现是接线太长引入了干扰。解决方法有两个硬件方面使用屏蔽线在信号线上加100pF电容缩短走线距离软件方面调整输入捕获滤波器增加软件去抖算法4.2 低速测量T法的优势当电机转速很低时M法会出现分辨率不足的问题。这时可以切换到T法测量float Get_Speed_T_Method(uint32_t PPR) { uint32_t period Get_Pulse_Period(); // 获取脉冲周期(us) return 60000000.0f / (PPR * 4 * period); // rpm }4.3 多电机同步定时器资源分配在四轮驱动项目中需要同时测量四个电机的转速。STM32F103的定时器资源有限我是这样分配的电机定时器引脚左前TIM2PA0-PA1右前TIM3PA6-PA7左后TIM4PB6-PB7右后TIM5PA0-PA1(重映射)对于没有足够编码器接口的MCU可以考虑使用外部中断定时器的方式不过这会增加CPU负担。5. 完整项目实战智能车测速系统5.1 硬件连接示意图[电机] - [编码器] A相 - TIMx_CH1 B相 - TIMx_CH2 VCC - 5V GND - GND5.2 软件架构设计底层驱动层编码器初始化定时器配置算法层速度计算滤波处理应用层PID控制数据传输5.3 关键代码实现typedef struct { int32_t raw_count; int32_t last_count; float speed_rpm; uint32_t ppr; } Encoder_TypeDef; Encoder_TypeDef encoder[4]; void Encoder_Update(Encoder_TypeDef *enc, TIM_TypeDef *TIMx) { enc-raw_count TIMx-CNT Encoder_Overflow * 65536; int32_t delta enc-raw_count - enc-last_count; enc-last_count enc-raw_count; // 低通滤波 enc-speed_rpm 0.2f * (delta * 60.0f / (enc-ppr * 4 * 0.01f)) 0.8f * enc-speed_rpm; } void Control_Loop(void) { for(int i0; i4; i) { Encoder_Update(encoder[i], TIMx[i]); PID_Calculate(pid[i], encoder[i].speed_rpm, target_speed); Set_PWM(MOTOR[i], pid[i].output); } }6. 进阶话题从速度到位置控制当项目要求更高时单纯的测速就不够用了。我在机械臂项目中实现了位置控制关键点包括位置计算position (count_total / (PPR×4)) × 轮周长多圈计数利用Z相信号校准软件记录圈数闭环控制位置环速度环双闭环梯形加减速算法这些年在不同项目中使用过各种编码器从几块钱的霍尔编码器到上万元的绝对式编码器都有接触。实测下来STM32的编码器接口模式确实稳定可靠配合适当的滤波算法完全能满足大多数工业应用的需求。