编码器电机控制正交编码器信号原理电机控制项目里测速是闭环控制的前提。没有准确的转速反馈PID就是盲调。正交编码器Quadrature Encoder是工业电机最常用的转速反馈元件两路脉冲信号A和B相差90度相位差通过比较两路信号的相位关系判断转向通过计数脉冲数计算转速。A和B两路信号的工作时序很直观。正转时A超前B 90度反转时B超前A 90度。STM32的定时器编码器模式硬件自动完成相位比较和方向判断不需要软件干预这在高速旋转场景下比外部中断方式可靠得多。STM32定时器编码器模式配置STM32的通用定时器TIM2-TIM5都支持编码器模式。以STM32F407为例使用TIM3的CH1和CH2通道连接编码器的A和B信号#includestm32f4xx_hal.hTIM_HandleTypeDef htim3;int32_tencoder_count0;int32_tlast_count0;voidMX_TIM3_Init(void){TIM_Encoder_InitTypeDef encoder_config{0};TIM_MasterConfigTypeDef master_config{0};htim3.InstanceTIM3;htim3.Init.Prescaler0;htim3.Init.CounterModeTIM_COUNTERMODE_UP;htim3.Init.Period65535;htim3.Init.ClockDivisionTIM_CLOCKDIVISION_DIV1;encoder_config.EncoderModeTIM_ENCODERMODE_TI12;encoder_config.IC1PolarityTIM_ICPOLARITY_RISING;encoder_config.IC1SelectionTIM_ICSELECTION_DIRECTTI;encoder_config.IC1PrescalerTIM_ICPSC_DIV1;encoder_config.IC1Filter0x0F;encoder_config.IC2PolarityTIM_ICPOLARITY_RISING;encoder_config.IC2SelectionTIM_ICSELECTION_DIRECTTI;encoder_config.IC2PrescalerTIM_ICPSC_DIV1;encoder_config.IC2Filter0x0F;HAL_TIM_Encoder_Init(htim3,encoder_config);master_config.MasterOutputTriggerTIM_TRGO_RESET;master_config.MasterSlaveModeTIM_MASTERSLAVEMODE_DISABLE;HAL_TIMEx_MasterConfigSynchronization(htim3,master_config);}几个关键参数需要说明。TIM_ENCODERMODE_TI12表示同时计数CH1和CH2的跳变分辨率是单路脉冲的4倍这是精度最高的模式。IC1Filter设为0x0F是输入滤波在工业环境里电机附近的电磁干扰会导致编码器信号毛刺不滤波的话计数值会跳变。Filter值越大抗干扰越强但延迟越高实际项目里0x0A到0x0F之间是比较好用的范围。测速算法实现编码器测速有三种经典算法M法测频法、T法测周法和M/T法。各有适用场景方法原理优点缺点适用场景M法固定时间计脉冲数高速段精度好低速段分辨率差高速电机T法固定脉冲计时间低速段精度好高速段易超时低速电机M/T法同时计脉冲和时间全速段精度均衡实现稍复杂通用场景实际项目里M/T法用得最多兼顾高速和低速#defineSAMPLE_PERIOD_MS10#defineENCODER_PPR1000// 编码器每转脉冲数volatileuint32_tcapture_count0;volatileuint32_tcapture_time0;voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim){if(htim-InstanceTIM3){capture_count__HAL_TIM_GET_COUNTER(htim3);capture_timeSAMPLE_PERIOD_MS;__HAL_TIM_SET_COUNTER(htim3,0);}}floatcalculate_rpm(void){// M/T法脉冲数和时间都参与计算// 4倍频计数每转脉冲数 PPR * 4floatpulses_per_revENCODER_PPR*4.0f;if(capture_time0)return0.0f;floatrpm(capture_count*60000.0f)/(pulses_per_rev*capture_time);// 处理负值反转if(capture_count32767){// 16位计数器溢出处理rpm((65536-capture_count)*60000.0f)/(pulses_per_rev*capture_time);rpm-rpm;}returnrpm;}SAMPLE_PERIOD_MS的选择直接影响测速精度和实时性。10ms采样周期在3000RPM的电机上每次采样约500个脉冲精度足够。如果电机转速只有几十RPM10ms内可能只采到几个脉冲误差很大这时候要加大采样周期或者改用T法。PID闭环控制有了转速反馈就可以做闭环了。增量式PID代码量小且不需要积分限幅是嵌入式平台的首选typedefstruct{floatkp;floatki;floatkd;floaterr;floaterr_last;floaterr_prev;floatoutput;}pid_t;pid_tspeed_pid{2.5f,0.08f,0.5f,0,0,0,0};floatpid_compute(pid_t*pid,floattarget,floatactual){pid-errtarget-actual;pid-outputpid-kp*(pid-err-pid-err_last)pid-ki*pid-errpid-kd*(pid-err-2*pid-err_lastpid-err_prev);pid-err_prevpid-err_last;pid-err_lastpid-err;// 输出限幅对应PWM占空比范围if(pid-output999.0f)pid-output999.0f;if(pid-output-999.0f)pid-output-999.0f;returnpid-output;}voidmotor_control_task(void){floattarget_rpm1500.0f;floatactual_rpmcalculate_rpm();floatpwmpid_compute(speed_pid,target_rpm,actual_rpm);// PWM输出正值正转负值反转if(pwm0){__HAL_TIM_SET_COMPARE(htim1,TIM_CHANNEL_1,(uint16_t)pwm);__HAL_TIM_SET_COMPARE(htim1,TIM_CHANNEL_2,0);}else{__HAL_TIM_SET_COMPARE(htim1,TIM_CHANNEL_1,0);__HAL_TIM_SET_COMPARE(htim1,TIM_CHANNEL_2,(uint16_t)(-pwm));}}PID参数整定是电机控制的核心难点。工程上一般先调P让电机转起来有响应再加D抑制超调最后加I消除稳态误差。P太大会震荡D太大系统会卡顿I太大会积分饱和导致超调严重。一个实用的整定技巧是先让电机空载运行用串口把目标转速和实际转速都打出来观察响应曲线。我在调试电机项目时用虎王科技开源的硬件调试工具做串口数据监控它支持多串口同时打开一边看STM32的PID调试日志一边看驱动板的反馈数据比开多个串口终端方便不少。项目在Gitee上开源做电机和通信模组调试的可以试试。调试中的常见问题编码器计数值跳变是最常见的坑。原因无非三个信号线没接滤波电容、定时器输入滤波值太小、编码器电源不稳。前两个通过硬件和寄存器配置解决第三个需要在编码器供电端加LC滤波。16位计数器溢出也要处理。电机反转时计数器从0减到65535直接读值会得到一个很大的正数。上面的代码里用32767作为判断阈值超过就认为是溢出反转取补码得到真实负值。电机控制这块坑确实多从编码器信号到PID整定每一步都可能踩雷。如果觉得这篇内容对你有用收藏一下方便后面查代码后面会继续更新电机驱动和电流环的实战内容关注了就不会错过。