共计 1584 个字符,预计需要花费 4 分钟才能阅读完成。
为什么需要编码器?
在电机控制中,我们经常需要知道电机的转速和位置。想象一下遥控车的轮子——如果不知道轮子转了多少圈,就没办法精确控制它走多远。这就是编码器的用武之地。
新手最常遇到的坑就是 信号抖动问题。编码器输出的信号本应该是干净的方波,但由于机械振动或电路干扰,实际信号可能像老式电视机雪花屏一样充满毛刺。这会导致系统误计数,明明只转了一格,计数器却跳了好几下。
硬件工作原理
增量式编码器通常输出三路信号:
- A 相 和 B 相:两路相位差 90 度的方波(这叫正交信号)
- Z 相(索引信号):每转一圈输出一个脉冲

判断旋转方向的秘诀在于:当 A 相上升沿时,如果 B 相是高电平,说明正转;如果是低电平,就是反转。这就好比两个人并排走路,总是一个在前一个在后。
动手写代码
下面是用 Arduino Uno 实现的经典方案,重点看中断处理和防抖:
// 定义编码器接口引脚
#define ENCODER_A 2 // 必须接中断引脚(UNO 的 2 或 3)
#define ENCODER_B 4
volatile long encoderCount = 0; // volatile 确保中断中安全访问
void setup() {pinMode(ENCODER_A, INPUT_PULLUP);
pinMode(ENCODER_B, INPUT_PULLUP);
// 设置 A 相上升沿和下降沿都触发中断
attachInterrupt(digitalPinToInterrupt(ENCODER_A), updateEncoder, CHANGE);
Serial.begin(9600);
}
void loop() {Serial.println(encoderCount);
delay(100);
}
// 中断服务函数要尽可能快!void updateEncoder() {
static unsigned long lastTime = 0;
unsigned long now = micros();
// 防抖处理:两次中断间隔小于 50us 则忽略
if (now - lastTime < 50) return;
lastTime = now;
// 核心逻辑:读 B 相电平决定方向
if (digitalRead(ENCODER_A) == digitalRead(ENCODER_B)) {encoderCount++;} else {encoderCount--;}
}
关键点说明:
CHANGE模式可以捕获 A 相的所有边沿变化- 防抖时间 50us 需要根据实际编码器调整(优质编码器可缩短)
- 中断函数中避免使用
Serial.print()等耗时操作
性能优化实战
我做了组对比测试,使用 1000 线编码器在 3000RPM 转速下:
| 方法 | CPU 占用率 | 最大跟踪转速 |
|---|---|---|
| 查询法 | 85% | 1200 RPM |
| 中断法 | 12% | 4500 RPM |
| 硬件计数器 | <5% | 10000 RPM |
高速场景建议:
- 对于 UNO,可以启用 PCINT 引脚中断(所有 IO 都可作为中断源)
- 使用 Arduino Due 等 32 位板卡,中断响应更快
- 终极方案是用定时器的编码器接口模式(如 STM32 的 TIMx)
血泪避坑指南
-
电源噪声:示波器看到信号上有毛刺?在编码器电源脚加个 100uF 电解电容 +0.1uF 瓷片电容组合
-
线缆过长:超过 1 米的信号线要用双绞线,最好带屏蔽层。曾经有个项目因为 2 米平行线导致计数错乱
-
接地环路:编码器和单片机要单点接地。遇到过因为两地电压差导致信号畸变的案例
调试技巧:用示波器的 XY 模式同时看 A / B 相信号,正常应该呈现完美的圆形李萨如图形。如果图形变形扭曲,说明信号质量有问题。
下一步挑战
现在你已经能让编码器稳定计数了,试试进阶任务:
- 如何用编码器测量转速?(提示:定时采样计数值差)
- 怎样实现位置闭环 PID 控制?(需要设置目标位置和反馈调节)
- 尝试用中断优先级解决多个编码器冲突问题
编码器就像电机的 ” 眼睛 ”,用好它能让你的机器人项目获得精准的运动控制能力。遇到问题别慌,用示波器观察信号,往往能快速定位故障根源。
正文完
