编码器测速全解析:从脉冲计数到M/T法速度计算实战
发布时间:2026/10/20 1:01:56 锦皓数字建站

1. 从一个被问烂了的问题说起“编码器读到的到底是什么”这个问题我在不同场合被问过不下几十次。问的人有刚入行的嵌入式新人有做运动控制多年的老工程师也有做机器人算法的朋友。大家的困惑点其实不太一样新人往往卡在“为什么我读到的数一直在跳”老工程师关心的是“同样的脉冲数为什么算出来的速度不一样”做算法的则更在意“这个速度值到底能不能直接喂给控制环”。先把结论摆在前面编码器本身只输出脉冲速度是算出来的不是读出来的。这句话听起来像废话但真正理解它的人不多。你从定时器寄存器里读到的那个数字是脉冲计数你从CAN总线上收到的那个转速值是别人用脉冲和时间算出来的你在上位机看到的曲线是再经过一次滤波和单位换算的结果。每一层都有信息损失每一层都可能引入误差。这篇内容我打算把从脉冲到速度这条链路完整拆一遍。适合谁看如果你正在做电机控制、机器人关节、传送带测速、云台稳定这类项目或者你只是单纯想搞清楚编码器到底怎么回事那这篇应该能帮你省下不少翻手册和试错的时间。我会尽量用生活化的类比把原理讲透同时给出可以直接抄的参数和代码最后再聊聊那些手册上不会写的坑。2. 编码器输出的物理本质脉冲不是数字2.1 增量式编码器的A/B/Z三兄弟市面上最常见的增量式编码器输出三路信号A相、B相、Z相。很多人一上来就盯着定时器的计数值看却忽略了这三路信号本身的物理含义。A相和B相是两路方波频率相同但相位差90度。这个90度不是随便定的它决定了你能分辨方向。想象两个人并排走路左边的人先迈步还是右边的人先迈步你就能判断他们是往前走还是往后走。编码器同理A相超前B相90度是一个方向B相超前A相90度是另一个方向。Z相是每转一圈输出一个脉冲叫零位信号。它的作用不是用来计数的而是用来校准绝对位置的。你可以把它理解成钟表上的12点刻度没有它你只知道走了多少格不知道现在几点。注意Z相脉冲的宽度通常很窄如果定时器采样率不够或者没有做边沿捕获很容易漏掉。我在一个云台项目里就吃过这个亏开机找零找了半天找不到最后发现是Z相脉冲只有几十微秒宽主循环轮询根本抓不住。2.2 脉冲计数到底在计什么定时器的编码器模式本质上是在对A相和B相的边沿做计数。这里有个关键选择计的是单边沿还是双边沿。单边沿计数只对A相的上升沿计数一圈的脉冲数等于编码器线数。双边沿计数对A相和B相的上升沿、下降沿都计数一圈的脉冲数是线数的4倍。假设你用一个1000线的编码器单边沿计数一圈就是1000个脉冲双边沿就是4000个脉冲。很多新手看到手册上写“1000线”以为一圈就是1000个脉冲结果程序里算出来速度差了4倍就是这里没搞清楚。我个人的习惯是能用双边沿就用双边沿。原因很简单分辨率高了4倍低速时速度估算更平滑。代价是定时器的计数频率高了4倍如果你的转速很高要确认定时器不会溢出。2.3 分辨率、精度、准确度三个容易混的概念这三个词经常被混用但在编码器语境下必须分清楚概念含义受什么影响分辨率能分辨的最小变化量线数、计数方式单/双边沿精度测量值与真实值的接近程度安装偏心、码盘刻线误差准确度多次测量的一致性信号质量、电磁干扰一个1000线的编码器双边沿计数分辨率是4000脉冲/转但如果安装时偏心0.1毫米精度可能只有±5个脉冲。分辨率高不代表精度高这是两码事。我在一个传送带测速项目里客户抱怨“速度曲线毛刺大”换了更高线数的编码器也没用最后发现是联轴器同心度差振动导致脉冲抖动。换了个弹性联轴器问题直接消失。3. 从脉冲到速度三种主流算法拆解3.1 M法测速固定时间数脉冲M法是最直观的固定一个采样周期T数这段时间内有多少个脉冲然后除以T和每转脉冲数得到转速。公式很简单转速(rpm) (脉冲数 / 每转脉冲数) / T * 60举个例子每转4000脉冲采样周期10毫秒读到200个脉冲。转速 (200 / 4000) / 0.01 * 60 300 rpmM法的优点是实现简单低速时也能用。缺点是低速时误差大。因为采样周期固定转速越低脉冲数越少量化误差占比越大。比如同样10毫秒转速只有30 rpm时脉冲数只有20个±1个脉冲就是±5%的误差。实操心得M法适合中高速场合。如果转速低于额定转速的10%建议换T法或者M/T法。我一般会在代码里设一个阈值高于阈值用M法低于阈值切T法。3.2 T法测速固定脉冲数测时间T法反过来固定数N个脉冲测这段时间有多长然后算转速。转速(rpm) (N / 每转脉冲数) / 时间 * 60还是上面的例子每转4000脉冲数20个脉冲测得时间1毫秒。转速 (20 / 4000) / 0.001 * 60 300 rpmT法的优点是低速时精度高因为脉冲数固定时间测量精度取决于定时器频率。缺点是高速时时间太短定时器分辨率不够误差反而大。这里有个关键参数定时器频率。假设定时器频率1 MHz分辨率1微秒。转速3000 rpm时每转4000脉冲20个脉冲对应的时间是时间 20 / (3000/60 * 4000) 0.1 毫秒 100 微秒100微秒用1微秒分辨率测量化误差1%还能接受。但如果转速到6000 rpm时间只有50微秒误差就变成2%了。3.3 M/T法取长补短M/T法结合了前两者的思路同时测脉冲数和时间用高频时钟去数脉冲周期再用低速时钟去数固定时间内的脉冲数。这样高速时用M法的思路低速时用T法的思路全速段精度都比较均衡。具体实现上常见做法是设定一个采样周期T在这个周期内数脉冲数M1同时用高频时钟数第一个脉冲到最后一个脉冲之间的时间T1。转速计算转速(rpm) (M1 / 每转脉冲数) / T1 * 60注意这里用的是T1而不是T。T1是实际测到的时间比T更准确因为它剔除了采样周期起止点与脉冲边沿不对齐带来的误差。M/T法的代价是逻辑复杂一些需要两个定时器配合。但在STM32这类MCU上用编码器模式加一个输入捕获通道就能实现不算太麻烦。3.4 三种方法怎么选一张表说清楚方法适用转速范围优点缺点典型场景M法中高速10%额定实现简单响应快低速误差大风扇、高速主轴T法低速10%额定低速精度高高速误差大云台、精密转台M/T法全速段精度均衡逻辑复杂机器人关节、伺服我个人的经验是如果MCU资源够直接上M/T法。省得后面切换逻辑写一堆if-else调试起来还容易出bug。如果资源紧张那就根据实际转速范围选一个别硬上。4. 手把手实现从定时器配置到速度输出4.1 硬件连接与定时器配置以常见的STM32为例编码器A相接定时器的CH1B相接CH2Z相接一个外部中断引脚。定时器配置成编码器模式计数方式选双边沿。关键配置项定时器预分频器设为0让计数器跑最快。计数模式Encoder ModeTI1和TI2都计数。计数器周期设为6553516位定时器最大值避免频繁溢出。输入滤波根据信号质量设一般设6-10个时钟周期。注意输入滤波设太大高速时会丢脉冲设太小干扰会导致误计数。我一般先用示波器看信号质量再决定滤波参数。如果信号干净滤波设2-4就行。4.2 速度计算的代码实现下面是一个M/T法的简化实现基于定时器中断每10毫秒执行一次#define PPR 4000 // 每转脉冲数双边沿 #define SAMPLE_MS 10 // 采样周期10ms #define CLK_MHZ 72 // 定时器时钟72MHz volatile int32_t last_count 0; volatile uint32_t last_time 0; void speed_calc(void) { int32_t curr_count TIM1-CNT; uint32_t curr_time TIM2-CNT; // 高频时钟计数 int32_t delta_count curr_count - last_count; uint32_t delta_time curr_time - last_time; // 防止溢出处理 if (delta_count 0) delta_count 65536; float rpm 0; if (delta_count 0 delta_time 0) { // M/T法用实际时间算 float time_s (float)delta_time / (CLK_MHZ * 1000000); rpm ((float)delta_count / PPR) / time_s * 60.0f; } last_count curr_count; last_time curr_time; // 输出rpm }这段代码有几个细节值得说第一delta_count可能为负因为编码器反转时计数器会减。这里用if (delta_count 0) delta_count 65536处理溢出但更严谨的做法是用有符号数直接算让硬件自动处理。第二delta_time用的是高频时钟计数不是采样周期。这是M/T法的核心用实际脉冲间隔时间而不是固定周期精度更高。第三转速为0时delta_count为0直接输出0避免除零。4.3 参数计算采样周期怎么定采样周期不是随便定的它决定了速度环的带宽。根据香农采样定理采样频率至少是信号最高频率的2倍。实际工程中一般取5-10倍。假设你的速度环带宽是100 Hz那采样频率至少500 Hz采样周期就是2毫秒。但采样周期太短低速时脉冲数太少量化误差大。所以有个折中采样周期 max(1 / (10 * 速度环带宽), 最小脉冲数 / (最高转速 * PPR / 60))举个例子速度环带宽100 Hz最高转速3000 rpmPPR4000。第一项1 / (10 * 100) 1 毫秒第二项假设最小脉冲数1010 / (3000 * 4000 / 60) 0.05 毫秒取大的那个1毫秒。但1毫秒时低速脉冲数可能不够所以实际取5-10毫秒比较稳妥。我一般用10毫秒兼顾低速精度和响应速度。4.4 单位换算rpm、rad/s、脉冲/秒不同场合用的单位不一样控制算法里常用rad/s上位机显示常用rpm底层调试常用脉冲/秒。换算关系1 rpm 2π / 60 rad/s ≈ 0.1047 rad/s 1 rad/s 60 / (2π) rpm ≈ 9.549 rpm 脉冲/秒 rpm / 60 * PPR我习惯在代码里统一用rad/s输出到上位机时再转rpm。这样控制算法里不用反复换算减少出错概率。5. 那些手册不会告诉你的坑5.1 计数溢出最隐蔽的bug16位定时器最大计数值65535如果转速高、PPR大很容易溢出。比如PPR4000转速3000 rpm每秒脉冲数3000 / 60 * 4000 200000 脉冲/秒10毫秒采样周期内就是2000个脉冲不会溢出。但如果采样周期1秒就是20万肯定溢出。所以采样周期和定时器位数要匹配。更隐蔽的是溢出发生在两次采样之间你读到的delta_count可能是负数。如果代码里没处理速度就会突然跳变。我见过一个项目电机高速时速度曲线每隔几秒出现一个尖峰查了两天才发现是计数器溢出。避坑技巧用32位定时器或者用定时器的溢出中断累加高位。STM32的TIM2和TIM5是32位的能省不少事。5.2 信号抖动脉冲边沿的毛刺编码器信号经过长线传输容易引入毛刺。毛刺会导致多计数速度曲线出现高频噪声。解决方法有三个层次硬件滤波在A/B相线上加RC低通滤波截止频率根据最高转速定。定时器输入滤波STM32的编码器模式支持数字滤波设几个时钟周期的滤波窗口。软件滤波速度算出来后做滑动平均或低通滤波。我一般先用硬件滤波和定时器滤波软件滤波作为最后一道防线。软件滤波会引入相位滞后对控制环不利能少用就少用。5.3 低速时的速度估算从脉冲到速度的鸿沟低速时采样周期内可能只有几个脉冲甚至一个都没有。这时候速度估算就变成了“有脉冲就是有速度没脉冲就是零速”曲线像台阶一样。解决办法有两个一是用T法测脉冲间隔时间。但脉冲间隔太长时定时器可能溢出。二是用观测器比如龙伯格观测器或卡尔曼滤波用电流和电压估算速度。这属于进阶内容但效果确实好。我在一个直驱电机项目里低速0.1 rpm时用脉冲法根本没法看上了观测器之后曲线平滑多了。5.4 常见问题速查表现象可能原因排查方法解决方案速度一直为0编码器没接好示波器看A/B相检查接线速度方向反了A/B相接反交换A/B相改接线或改代码速度跳变计数器溢出看delta_count符号换32位定时器速度毛刺大信号干扰看脉冲波形加滤波低速不准量化误差看脉冲数换T法或M/T法高速不准定时器分辨率不够算时间量化误差提高定时器频率6. 从速度到控制编码器数据的下游用法6.1 速度环的输入从哪来速度算出来之后通常直接喂给PID速度环。但这里有个细节速度值的更新率和控制环的执行率要匹配。如果速度环跑1 kHz但速度值10毫秒才更新一次那控制环有9次用的是旧值。这会导致相位滞后降低带宽。解决办法是让速度计算和控制环同频或者用预测算法补值。我一般让速度计算在定时器中断里跑频率和控制环一致这样最简单。6.2 位置和速度的关系位置是速度的积分速度是位置的微分。编码器直接给的是位置脉冲数速度是算出来的。所以位置环和速度环的信息来源其实是一个只是处理方式不同。位置环用脉冲数直接算精度取决于编码器分辨率。速度环用脉冲数差分精度取决于采样周期和算法。两者要协调好否则位置环和速度环会打架。6.3 编码器分辨率对控制性能的影响分辨率越高速度估算越平滑低速性能越好。但分辨率高也意味着脉冲频率高对MCU的定时器频率和中断处理能力要求更高。选型时要算一笔账最高脉冲频率 最高转速 / 60 * PPR假设最高转速6000 rpmPPR4000最高脉冲频率400 kHz。STM32的定时器在72 MHz时钟下编码器模式能处理这个频率但中断里不能做太多事。如果PPR10000频率就到1 MHz很多MCU就吃不消了。这时候要么降速要么换更高性能的MCU要么用硬件计数器加DMA。7. 实测数据与经验总结7.1 一组实测对比我在一个测试台上用同一个电机、同一个编码器PPR4000分别用M法、T法、M/T法测速采样周期10毫秒结果如下设定转速M法误差T法误差M/T法误差3000 rpm0.5%3%0.6%300 rpm2%0.8%0.9%30 rpm15%1.5%1.8%3 rpm无法测3%3.5%数据很直观M法高速好T法低速好M/T法全段均衡。3 rpm时M/T法误差略大是因为脉冲间隔太长定时器分辨率不够了。7.2 几个我踩过的坑第一个坑编码器线数不是脉冲数。我刚开始做的时候以为1000线就是1000脉冲结果程序里算出来速度差4倍。后来才知道双边沿计数是4倍频。第二个坑Z相找零的时机。Z相脉冲很窄如果主循环轮询很容易错过。我后来改成外部中断触发才稳定找到零位。第三个坑速度方向判断。A/B相接反了速度方向就反了。但有时候接反了也能计数只是方向不对。调试时一定要手动转一下电机看速度符号对不对。第四个坑滤波参数设太大。输入滤波设太大高速时丢脉冲速度偏低。我一般先用示波器看信号再决定滤波参数。7.3 给新手的建议如果你刚开始做编码器测速我的建议是先用示波器看A/B/Z波形确认信号质量。用M法先跑通确认计数和方向正确。再根据转速范围决定要不要换M/T法。速度算出来后先在上位机看曲线别急着闭环。低速性能要求高的话考虑加观测器。最后再分享一个小技巧在代码里加一个脉冲计数溢出检测。每次采样时判断delta_count是否超过阈值超过就报警。这个简单的检查能帮你提前发现很多问题比如编码器松动、信号干扰、计数器溢出等。这个内容后续还可以这样扩展如果你用的是绝对值编码器那读到的就不是脉冲而是位置码处理方式完全不同。另外多圈绝对值编码器涉及圈数计数和电池备份又是另一个话题。有机会再聊。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。