拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32编码器电机速度控制与测速实现:从正交脉冲到PID闭环

简介面向STM32电机控制与编码器测速开发的完整工程资源适合嵌入式初学者及需要实现精确速度闭环的开发者。压缩包内含基于STM32F10x的脉冲读取、编码器接口与速度控制相关源码工程覆盖TIM输入捕获、PWM输出、PID调节等关键模块可直接参考工程结构进行二次开发。资源共287个文件包含c、h源码文件o、crf等编译中间文件以及hex、axf烧录文件、uvprojx工程配置等整体约14.68MB便于在Keil环境中打开查看与重新编译。已有504人学习下载。通过该压缩包可获取完整工程目录、初始化代码及外设配置思路有助于理解编码器脉冲计数与电机速度检测的实现流程并为进一步调试PID参数、优化控制效果提供实际参考。1. 编码器速度控制到底在解决什么问题做电机调速绕不开的就是“怎么知道转了多少、转得多快”。常见的方案里霍尔传感器只能给换相点采样电阻只能估电流真正能拿到角度和速度的高性价比方案就是编码器。把编码器接在电机轴上用 STM32 的定时器捕捉 A/B 两路脉冲通过单位时间内的脉冲增量反推转速再和期望速度做 PID 运算去调 PWM这就是标题里“编码电机速度控制和速度检测”最典型的落地路径。这个方案在 AGV 小车、桌面机械臂、云台、3D 打印机挤出机上都能见到属入门到中阶嵌入式开发者的必备技能。标题里的STM32 编码器、stm32脉冲读取指的是同一件事用 STM32 的定时器编码器模式或外部中断读取正交脉冲。这篇文章用增量式编码器作为主线给的代码基于标准外设库风格寄存器原理部分也能直接映射到 HAL 库。目标人群是能点灯、会用定时器输出 PWM但对编码器接口、测速公式、PID 闭环还缺一条线的开发者。下面从正交信号原理开始一步步把最小可用的速度控制闭环搭出来。2. 正交编码器信号原理与 STM32 硬件接口2.1 增量式编码器的 A 相、B 相与 Z 相增量式编码器输出的不是绝对角度而是两组相位差 90° 的方波。电机轴旋转一圈A、B 两相各输出线数(p/r)个完整周期。用 STM32 定时器的编码器模式同时检测 A、B 的上升沿和下降沿每个周期可以数出 4 个计数脉冲这就是常说的 4 倍频。假设电机自带编码器是 13 线的减速电机减速比 30:1输出轴转一圈电机轴转 30 圈编码器产生 13×30390 个周期。在 4 倍频下STM32 端每输出轴一圈能数到 390×41560 个计数。这个数字决定了测速分辨率也决定了 PID 控制周期的下限。Z 相索引脉冲每圈只出现一次用于找机械零点。速度控制场景一般用不到 Z 相只有在做位置归零或绝对值定位时才需要接。如果你的应用只做调速Z 相悬空即可。2.2 STM32 定时器编码器模式为什么首选它STM32 的通用定时器和高级定时器内置了编码器接口硬件自动解码正交信号。只要把 A、B 两相接在定时器的 CH1、CH2 引脚上配置好计数模式和滤波参数定时器计数器就会自动跟随电机正反转增减计数值完全不占 CPU。这是它相对外部中断方案最大的优势脉冲频率再高中断也不会丢。用外部中断读脉冲的方案把 A、B 接 EXTI在中断里计数在低速时能用但电机转速一高中断频繁触发CPU 忙于进出中断系统响应变差。常见做法是把外部中断方案留给没有编码器模式的低端 MCUSTM32 上优先用硬件编码器模式。2.3 常用定时器通道映射与接线参考以 STM32F103 为例编码器模式可用的定时器通道组合如下定时器CH1 引脚CH2 引脚最高计数频率TIM1PA8PA972 MHz 时钟下经预分频TIM2PA0PA1同上TIM3PA6PA7同上TIM4PB6PB7同上电机编码器输出一般是开漏或推挽注意看编码器手册的上拉要求。开漏输出必须外部上拉推挽则可以直接接 MCU。接线顺序没有强制要求A 相接 CH1、B 相接 CH2 是常规做法如果接反只是计数值方向反过来软件里检查到方向异常时交换定义即可不用改硬件。// 编码器接口引脚初始化TIM3, PA6CH1, PA7CH2 // 时钟使能放在 RCC 配置中统一处理 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; // 浮空输入编码器推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure);提示编码器输出是开漏时必须启用内部上拉GPIO_Mode_IPU否则电平不确定计数会乱跳。3. 用定时器编码器模式实现脉冲读取与方向判断3.1 编码器模式寄存器配置的完整步骤定时器编码器模式的核心配置在TIM_SMCR和TIM_CCER两个寄存器。TIM_SMCR的SMS位域选择计数方式TIM_CCER的CC1P、CC2P决定极性。STM32 参考手册给定了三种计数模式只在 TI1 边沿计数、只在 TI2 边沿计数、TI1 和 TI2 双沿计数4 倍频。速度控制场景下直接用 4 倍频模式分辨率最高。void Encoder_Init_TIM3(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; // 预分频 0时钟 72MHz计数周期设为最大值 0xFFFF // 让计数器纯粹做增减计数不产生溢出事件 TIM_TimeBaseStructure.TIM_Period 0xFFFF; TIM_TimeBaseStructure.TIM_Prescaler 0; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); // 编码器模式TI1TI2 双沿计数4 倍频 TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); // 输入滤波采样频率 72MHz/256持续 8 个采样周期 // 适合电机电刷产生的毛刺滤波太强会丢真实边沿 TIM_ICInitStructure.TIM_Channel TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter 0x08; TIM_ICInit(TIM3, TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel TIM_Channel_2; TIM_ICInitStructure.TIM_ICSelection TIM_ICSelection_DirectTI; TIM_ICInit(TIM3, TIM_ICInitStructure); TIM_Cmd(TIM3, ENABLE); }配置完成后TIM3-CNT就是一个带符号意义的自由运行计数器正转时递增反转时递减。读取当前值就能判断位置增量和运动方向。代码后每个参数都可按实际编码器调整TIM_ICFilter是毛刺滤波范围 0~0x0F值越大需要的持续高/低电平时间越长对有刷电机的换向火花效果明显TIM_Prescaler保持 0因为编码器计数必须一个边沿都不丢。3.2 读取计数值的方向判断与溢出保护编码器计数器的值是 16 位有符号容器超过 0x7FFF 就变成负数。速度控制中通常不用这个值本身而是周期性读取两次计数的差值。差值的正负自然对应方向但要处理 16 位回绕int16_t last_count 0; // 返回本次周期内的脉冲增量有符号 int16_t Encoder_GetDelta(void) { int16_t current (int16_t)(TIM3-CNT 0xFFFF); int16_t delta current - last_count; last_count current; return delta; }将TIM3-CNT强制转换为int16_t后current - last_count天然处理了回绕。比如上次是 32760本次是 -32760差值是 16恰好代表正向转过了 4 个整圈加 16 个脉冲。这个写法要依赖“两次采样间隔内脉冲增量不能超过 ±32767”的前提实际 500 rpm、13 线编码器 4 倍频下每秒脉冲数约 13000控制周期 10ms 内增量只有 130远远到不了回绕上限。3.3 信号抖动时的排查顺序编码器计数异常是初学者最常踩的坑按以下顺序排查能省不少时间先确认引脚电气连接用示波器或逻辑分析仪看 A、B 两相是否有稳定的方波以及 90° 相位差再查配置的滤波参数是不是过大滤波太强会把真实边沿滤掉表现为低速时计数正常、高速时计数明显偏少最后确认电源是否干净编码器供电和电机供电共地且没有大电容退耦时容易在 PWM 换向瞬间产生干扰脉冲导致计数跳动。把编码器电源单独用 100nF10uF 电容退耦能从硬件上解决大部分抖动。// 顺便说明外部中断读脉冲的替代写法只适合低速验证 // 每个脉冲进一次中断CPU 负载高正式项目不建议 void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) ! RESET) { pulse_count; EXTI_ClearITPendingBit(EXTI_Line0); } }4. 把脉冲换算成速度M 法、T 法与瞬时速度计算4.1 为什么固定周期数脉冲M 法最省事测速最常见的方法是 M 法测频法固定一个时间窗口数这个窗口内的脉冲个数用脉冲数 / 每转总脉冲数 / 窗口时间算出转速。窗口越长低速时脉冲数越多精度越高但实时性变差。M 法的天然局限在低速如果窗口内只有两三个脉冲量化误差会很大。对 13 线编码器 4 倍频输出轴每转 1560 个脉冲。转速 60 rpm 时1 秒内有 1560 个脉冲10ms 窗口只有 15 个脉冲误差接近 7%。所以控制周期和编码器线数是绑定的低速要求高的场景必须提高脉冲分辨率或改用 T 法。4.2 T 法测速与 M/T 法T 法测周法反过来测用高频时钟脉冲填满编码器相邻两个脉冲的间隔用时钟计数值除以高频时钟频率得到单个脉冲周期再换算转速。低速时单个脉冲间隔长测得更准高速时脉冲间隔短高频时钟计数少误差变大。M/T 法综合两者先用固定时间窗口计数同时在窗口首尾用高频时钟测时间速度和位置都能精确解算。实际速度控制中全范围 M/T 法实现复杂度高多数场景是低速用 T 法、中高速用 M 法做切换。判断依据很简单如果 M 法算出的速度在控制周期内脉冲数少于 10 个切换到 T 法。4.3 一个实用的速度计算函数把它放进 10ms 定时中断里做周期调用。以 STM32F103 的 72MHz 主频为参考控制周期DT_MS取 10编码器线数ENCODER_LINES取 13电机轴侧减速比GEAR_RATIO取 30#define DT_MS 10 // 控制周期 10ms #define ENCODER_LINES 13 // 编码器物理线数 #define GEAR_RATIO 30 // 减速比输出轴对电机轴 // 输出轴转速单位 rpm float Speed_Calculate(int16_t delta_pulse) { // delta_pulse 是电机轴侧 A/B 相 4 倍频计数值 // 输出轴每转 13*30*4 1560 个计数 float rpm (float)delta_pulse * 60000.0f / (float)(ENCODER_LINES * GEAR_RATIO * 4 * DT_MS); return rpm; }时间换算逻辑是delta_pulse / (线数×4×减速比)得到窗口内的输出轴转数除以窗口秒数DT_MS/1000得到每秒转数乘以 60 变 rpm分子就是60000 / (线数×4×减速比×DT_MS)。写代码时这个常量可以直接预编译算出来不用每次浮点除法但注释里要保留推导过程方便后面换不同减速比的电机。4.4 速度值做一阶低通滤波编码器输出的速度值在低速时量化阶梯明显直接给 PID 用会让 PWM 输出抖动。常见做法是给测量速度加一阶低通滤波static float speed_filtered 0.0f; #define FILTER_ALPHA 0.3f // 滤波系数越小越平滑但滞后越大 float Speed_Filter(float speed_raw) { speed_filtered FILTER_ALPHA * speed_raw (1.0f - FILTER_ALPHA) * speed_filtered; return speed_filtered; }FILTER_ALPHA的取值取决于控制周期和期望带宽。10ms 控制周期、0.3 的系数大约把速度环的测量带宽压到 30Hz 左右既滤掉了量化噪声又不至于让 PID 反应迟钝。注意滤波会引入相位滞后系数小于 0.1 时闭环系统容易震荡务必用控制周期内能接受的滞后量来反向设定。5. 速度闭环用增量式 PID 把 PWM 调到目标转速5.1 速度环的控制框图与 PID 选型速度控制闭环的常见做法是目标 rpm 与反馈 rpm 相减得到误差误差经过 PID 运算输出 PWM 占空比驱动电机。电流环比速度环快但如果只是调速不用太关心内部电流环直接把 PWM 限制在安全占空比范围内即可。PID 用位置式还是增量式的选择上速度环几乎都用增量式。增量式输出的是 PWM 占空比的增量不对累积误差直接积分限幅处理简单而且不会因为积分饱和导致大幅超调。对晶振频率漂移、控制周期抖动这类小扰动增量式的鲁棒性也比位置式好。typedef struct { float Kp; // 比例增益 float Ki; // 积分增益 float Kd; // 微分增益 float error_prev; // 上一次误差 float error_prev2; // 上上次误差 float output; // 当前输出占空比, 0~100 float output_max; // 输出限幅 float output_min; } SpeedPID_TypeDef; // 增量式 PID 核心运算输入目标转速和实际转速输出占空比增量 float SpeedPID_Calc(SpeedPID_TypeDef *pid, float target, float actual) { float error target - actual; float delta_outp; delta_outp pid-Kp * (error - pid-error_prev) pid-Ki * error pid-Kd * (error - 2.0f * pid-error_prev pid-error_prev2); // 递推误差历史 pid-error_prev2 pid-error_prev; pid-error_prev error; pid-output delta_outp; // 输出限幅防止 PWM 满占空比或反向冲击 if (pid-output pid-output_max) pid-output pid-output_max; if (pid-output pid-output_min) pid-output pid-output_min; return pid-output; }增量式 PID 的输出是增量累加到上一次输出上得到本次占空比。它天然自带“惯性”即使误差突变为零占空比也只是停止调整而非瞬间回零这对电机这种有惯性负载的对象是合理的。output_max和output_min通常设为本时刻允许的最小/最大占空比如 5 到 95避免电机启动时占空比过小无法克服静摩擦。5.2 参数初始值与整定步骤PID 参数没有万能值但初始值有套路。对额定电压 12V、空载转速 300 rpm、PWM 频率 10kHz 的小型编码电机调试时从纯 P 开始参数初始值调整方向Kp0.5增大直到出现轻微震荡然后回调 70%Ki0.1缓慢增大到静态误差消失Kd0.05只在超调明显时使用从小到大加手动整定步骤先令Ki0、Kd0给一个较低目标速度如 100 rpm逐步增大 Kp直到速度曲线出现等幅振荡记下临界 Kp 值然后取它的 60%-70% 作为工作 Kp。随后增大 Ki 消除稳态误差Ki 太大会让低速时速度来回波动表现是电机周期性“喘振”。最后如果启动超调明显加一点 KdKd 对编码器噪声敏感过大会引入高频抖动噪音。// 10ms 控制周期中采集编码器、算速度、查期望、执行 PID void TIM6_IRQHandler(void) // 基础定时器产生 10ms 时基 { int16_t delta; float rpm_actual, duty; if (TIM_GetITStatus(TIM6, TIM_IT_Update) ! RESET) { delta Encoder_GetDelta(); rpm_actual Speed_Filter(Speed_Calculate(delta)); duty SpeedPID_Calc(speed_pid, speed_target, rpm_actual); Motor_SetPWM((uint16_t)(duty * MOTOR_PWM_PERIOD / 100.0f)); TIM_ClearITPendingBit(TIM6, TIM_IT_Update); } }控制周期选 10ms 是基于常见 13 线减速电机的折中太短1ms时每个周期只有零星几个脉冲速度反馈噪声大太长50ms时系统对负载变化的响应迟钝。换用高线数编码器如 500 线伺服电机可以把周期降到 1ms-2ms此时 Kp 要相应缩小因为同样的误差在更短时间内得到修正。5.3 PWM 频率与电机驱动的关系PWM 频率选择影响电机噪音和电流纹波。常见做法是选 10kHz-20kHz高于人耳可听范围同时保证电流纹波不过大。PWM 分辨率则由定时器重载值决定72MHz 时钟、10kHz PWM 时计数周期是 7200占空比调节精度约 0.014%足够速度环使用。频率不要盲目提高频率越高MOS 管开关损耗越大驱动芯片发热越明显。电机驱动部分用 H 桥或集成驱动芯片如 DRV8871、TB6612。仅做单向调速时一个 N-MOS 管接低边即可但速度环通常需要刹车和反向制动H 桥的 BRAKE 模式能在减速时提供更好的动态响应。具体做法是在速度误差较大且希望快速减速时让 H 桥短接电机两端利用反电动势制动比单纯把 PWM 调到零更快。6. 编码器速度控制的验证方法与三个实用技巧速度环写完了别急着调 PID先验证测量链路的正确性。最直接的手段是开环给定一个固定占空比用手持转速表或另一个已知准确的编码器测实际转速和 STM32 算出的 rpm 对比。没有转速表的情况下可以用示波器量编码器单相波形测频率频率 × 60 / (线数 × 减速比)就是输出轴 rpm。验证正反向时把 PWM 阶跃到正负各一个值观察TIM3-CNT是否对应增减。这里重点检查编码器 A/B 相是否接反——接反后速度反馈符号错误PID 会越调越偏现象是电机“飞车”或朝反方向猛转。在确认反馈正确后再上闭环做阶跃响应测试给定一个从 0 到目标转速的阶跃用调试器的变量监视窗口或串口把速度采样值发出来画出转速曲线。合格的标准是上升过程中没有明显振荡稳态误差在目标值的 ±2% 以内超调量不超过 10%。如果出现等幅震荡多半是 Kp 过大或控制周期内速度反馈滞后太多先把 Kp 减半再试。最后给一个经验性的小技巧当电机轴上带了皮带轮或齿轮箱时负载惯量变化很大PID 参数要按最重负载的情况整定否则轻载时表现好的参数在重载下会震荡。另一个技巧是把控制周期和速度采样周期拆开——比如 5ms 执行一次 PID但速度计算用 20ms 窗口的平滑值——这样既能快速响应负载突变又不会因为单周期脉冲太少导致速度值乱跳。这两个做法实际工程里非常常见比盲目加大 Kp 同样有效。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门