news 2026/10/20 1:01:54

编码器测速全解析:从脉冲计数到M/T法速度计算实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
编码器测速全解析:从脉冲计数到M/T法速度计算实战

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 rpm

M法的优点是实现简单,低速时也能用。缺点是低速时误差大。因为采样周期固定,转速越低,脉冲数越少,量化误差占比越大。比如同样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 rpm

T法的优点是低速时精度高,因为脉冲数固定,时间测量精度取决于定时器频率。缺点是高速时时间太短,定时器分辨率不够,误差反而大。

这里有个关键参数:定时器频率。假设定时器频率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相接定时器的CH1,B相接CH2,Z相接一个外部中断引脚。定时器配置成编码器模式,计数方式选双边沿。

关键配置项:

  • 定时器预分频器:设为0,让计数器跑最快。
  • 计数模式:Encoder Mode,TI1和TI2都计数。
  • 计数器周期:设为65535(16位定时器最大值),避免频繁溢出。
  • 输入滤波:根据信号质量设,一般设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 rpm,PPR=4000。

  • 第一项:1 / (10 * 100) = 1 毫秒
  • 第二项:假设最小脉冲数10,10 / (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 计数溢出:最隐蔽的bug

16位定时器最大计数值65535,如果转速高、PPR大,很容易溢出。比如PPR=4000,转速3000 rpm,每秒脉冲数:

3000 / 60 * 4000 = 200000 脉冲/秒

10毫秒采样周期内就是2000个脉冲,不会溢出。但如果采样周期1秒,就是20万,肯定溢出。所以采样周期和定时器位数要匹配。

更隐蔽的是,溢出发生在两次采样之间,你读到的delta_count可能是负数。如果代码里没处理,速度就会突然跳变。我见过一个项目,电机高速时速度曲线每隔几秒出现一个尖峰,查了两天才发现是计数器溢出。

避坑技巧:用32位定时器,或者用定时器的溢出中断累加高位。STM32的TIM2和TIM5是32位的,能省不少事。

5.2 信号抖动:脉冲边沿的毛刺

编码器信号经过长线传输,容易引入毛刺。毛刺会导致多计数,速度曲线出现高频噪声。

解决方法有三个层次:

  1. 硬件滤波:在A/B相线上加RC低通滤波,截止频率根据最高转速定。
  2. 定时器输入滤波:STM32的编码器模式支持数字滤波,设几个时钟周期的滤波窗口。
  3. 软件滤波:速度算出来后做滑动平均或低通滤波。

我一般先用硬件滤波和定时器滤波,软件滤波作为最后一道防线。软件滤波会引入相位滞后,对控制环不利,能少用就少用。

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 rpm,PPR=4000,最高脉冲频率400 kHz。STM32的定时器在72 MHz时钟下,编码器模式能处理这个频率,但中断里不能做太多事。

如果PPR=10000,频率就到1 MHz,很多MCU就吃不消了。这时候要么降速,要么换更高性能的MCU,要么用硬件计数器加DMA。

7. 实测数据与经验总结

7.1 一组实测对比

我在一个测试台上用同一个电机、同一个编码器(PPR=4000),分别用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 给新手的建议

如果你刚开始做编码器测速,我的建议是:

  1. 先用示波器看A/B/Z波形,确认信号质量。
  2. 用M法先跑通,确认计数和方向正确。
  3. 再根据转速范围决定要不要换M/T法。
  4. 速度算出来后,先在上位机看曲线,别急着闭环。
  5. 低速性能要求高的话,考虑加观测器。

最后再分享一个小技巧:在代码里加一个脉冲计数溢出检测。每次采样时判断delta_count是否超过阈值,超过就报警。这个简单的检查能帮你提前发现很多问题,比如编码器松动、信号干扰、计数器溢出等。

这个内容后续还可以这样扩展:如果你用的是绝对值编码器,那读到的就不是脉冲而是位置码,处理方式完全不同。另外,多圈绝对值编码器涉及圈数计数和电池备份,又是另一个话题。有机会再聊。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 1:01:50

可重构低功耗AI芯片落地智能门锁:从功耗账本到选型经验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/20 1:01:40

读懂汽车传感器与执行器:从静态特性到实战波形诊断

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/20 1:01:24

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/19 23:25:36

开源代码评审工作流:本地化、可审计、可定制的LLM实践方案

1. 项目概述&#xff1a;这不是一个“工具”&#xff0c;而是一套可落地的开源代码评审工作流“open-code-review”这个名字乍一听像某个新发布的 CLI 工具&#xff0c;或者某家大厂刚开源的 AI 代码审查平台。但实际翻遍 GitHub、Hugging Face 和主流技术社区&#xff0c;你会…

作者头像 李华
网站建设 2026/10/19 22:00:01

open-code-review:可编程、可审计的开源式代码评审新范式

1. 什么是 open-code-review&#xff1a;一个被严重低估的工程实践新范式“open-code-review”这个词最近在开发者社区里频繁出现&#xff0c;但它不是某个具体工具的名字&#xff0c;也不是某家公司的私有产品&#xff0c;而是一种正在成型的、以开源精神重构代码评审流程的新…

作者头像 李华
网站建设 2026/10/19 21:58:28

从零构建技能管理项目:技能建模、可视化与团队应用实践

1. 当“skills”成为一个项目标题&#xff1a;我看到的不是空壳&#xff0c;而是留白第一次看到这个项目标题的时候&#xff0c;我盯着屏幕愣了几秒。就一个词&#xff0c;“skills”&#xff0c;没有正文&#xff0c;没有关键词&#xff0c;没有摘要描述。换作几年前&#xff…

作者头像 李华