AB相编码器是现代位移测量中最常见的传感器之一,它的工作原理其实很像我们生活中的自行车码表。想象一下自行车轮转动时,固定在辐条上的磁铁每经过传感器就会产生一个信号——编码器也是类似的原理,只不过更加精密。
这种编码器内部有一个旋转的光栅盘,当轴转动时,光电传感器会检测到光栅的明暗变化,从而产生两路相位差90度的方波信号,这就是A相和B相信号。我拆解过几个不同品牌的编码器,发现核心结构都大同小异,但信号质量差异很大,这也是为什么有些便宜的编码器测量不准。
判断旋转方向的关键在于两路信号的相位关系:
这个特性让我们不仅能计数脉冲,还能判断运动方向。在实际项目中,我遇到过因为相位关系理解错误导致测量值忽大忽小的问题,后来用示波器观察信号才找到原因。
选择编码器时,我通常会先看三个关键参数:工作电压、脉冲数和机械尺寸。以常见的400脉冲编码器为例,如果测量轮周长是50mm,那么每个脉冲对应的位移就是50/400=0.125mm。这个分辨率对于大多数DIY项目已经足够用了。
接线时要注意:
这里有个实用技巧:一定要使用INPUT_PULLUP模式,这样可以省去外部上拉电阻。我曾经为了省事直接接IO口,结果信号抖动严重,后来加上10k上拉电阻才稳定。
测量轮的选择也很关键。在机床监控项目中,我试过用不同材质的测量轮,发现橡胶包覆的轮子比金属的测量更精准,因为能避免打滑。轮子直径要精确测量,最好用游标卡尺多次测量取平均值。
Arduino处理编码器的精髓在于中断的使用。UNO只有引脚2和3支持外部中断,这就是为什么我们选择这两个引脚。在setup()中,我们需要配置:
arduino复制pinMode(APin, INPUT_PULLUP);
pinMode(BPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(APin), count_A, CHANGE);
这里使用CHANGE模式意味着A相电平的任何变化都会触发中断。在中断服务函数count_A()中,我们读取A、B相当前状态:
arduino复制void count_A() {
int a = digitalRead(APin);
int b = digitalRead(BPin);
if(a == b) {
count--;
} else {
count++;
}
}
实测中发现,中断函数执行时间要尽可能短。我曾经在中断里加了串口打印,结果丢失了大量脉冲。另一个坑是volatile关键字——计数变量必须这样声明,否则可能得到错误的结果。
将脉冲数转换为实际位移很简单:位移=计数×分辨率。但实际应用中要考虑更多因素:
数据类型选择:long类型可以计数到±2,147,483,647,对于400脉冲/转的编码器,相当于约671公里行程——足够大多数应用了。
方向处理:正转时计数增加,反转时减少。在升降平台项目中,我发现机械回差会导致正反转读数不一致,后来通过软件补偿解决了这个问题。
滤波处理:添加简单的去抖逻辑可以大幅提高稳定性:
arduino复制if(millis() - lastTime > 5) { // 5ms防抖
validCount = count;
lastTime = millis();
}
结合前面所有知识点,这是我在多个项目中验证过的稳定版本:
arduino复制#define APin 3
#define BPin 2
volatile long count = 0;
float resolution = 0.125; // mm/脉冲
void setup() {
Serial.begin(115200);
pinMode(APin, INPUT_PULLUP);
pinMode(BPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(APin), count_A, CHANGE);
}
void loop() {
static unsigned long lastPrint = 0;
if(millis() - lastPrint > 100) {
Serial.print("脉冲数:");
Serial.print(count);
Serial.print("\t位移(mm):");
Serial.println(count * resolution);
lastPrint = millis();
}
}
void count_A() {
static unsigned long lastTime = 0;
if(micros() - lastTime < 200) return; // 硬件防抖
lastTime = micros();
int a = digitalRead(APin);
int b = digitalRead(BPin);
if(a == b) {
count--;
} else {
count++;
}
}
几个优化点值得注意:
在模型升降平台项目中,这套代码实现了±0.2mm的重复定位精度,完全满足需求。遇到问题时的排查顺序建议:先查电源是否稳定,再查信号线是否接触良好,最后用示波器看信号波形。