4、增量式编码器详解:增量式编码器的结构、工作原理、零位脉冲(Z相)的作用
增量式编码器,说白了就是只告诉你“动了多少”,不告诉你“现在在哪”。它不像绝对式编码器那样一开机就知道位置,而是需要你从零开始数脉冲。很多刚入行的工程师觉得它不如绝对式高级,但我个人觉得,增量式在速度控制和低成本场景下,绝对是个好选择。
我最早接触增量式编码器是在一个伺服电机项目里。当时选型没注意Z相,结果每次断电重启都要手动回零,折腾了好几天。嗯,这里面的坑,咱们今天一个一个说清楚。
4.1 增量式编码器的结构
增量式编码器的内部结构其实不复杂。核心就三样东西:码盘、发光管和光敏接收管。码盘上刻着一圈一圈的透光和不透光条纹,像个小齿轮。发光管一照,接收管就能检测到光脉冲。
你想想看,码盘转一圈,接收管就输出一串脉冲。脉冲的个数,就代表了转过的角度。这就是增量式编码器最基础的原理。
核心结构组成:
- 码盘(光栅盘):玻璃或金属材质,上面刻有等间距的透光/不透光条纹。
- 发光源:通常是LED,发出稳定的光束。
- 光敏接收器:将光信号转换为电信号,输出方波脉冲。
- 信号处理电路:对原始信号进行整形、放大、差分输出等处理。
这里有个细节:码盘上的条纹数决定了编码器的分辨率。比如码盘有1000条条纹,那转一圈就输出1000个脉冲。但实际应用中,我们还会通过A、B相的倍频技术把分辨率翻倍甚至翻四倍。这个后面会细说。
4.2 工作原理:A、B相与相位差
增量式编码器通常输出两路信号:A相和B相。这两路信号是正交的,也就是相位差90度。为什么要有两路?因为单靠一路脉冲,你只能知道转了多少,但不知道往哪转。
我举个例子:A相上升沿时,如果B相是高电平,那就是正转;如果B相是低电平,那就是反转。这就是所谓的“正交解码”。
判断旋转方向的方法:
- A相超前B相90° → 正转(顺时针)
- B相超前A相90° → 反转(逆时针)
在实际项目中,我习惯用示波器同时抓A、B两路波形,一眼就能看出方向对不对。
为什么会这样?因为A、B相的相位关系是固定的。你只要检测A相和B相的边沿顺序,就能确定方向。这也是增量式编码器能实现“双向计数”的基础。
下面这张图展示了A、B相和Z相的关系,以及正反转时的波形差异。
4.3 零位脉冲(Z相)的作用
Z相,也叫零位脉冲或索引脉冲。它每圈只输出一个脉冲,位置固定。它的作用说白了就是:给你一个绝对的参考点。
增量式编码器本身没有绝对位置信息。断电后重新上电,你只知道从当前位置开始数脉冲,但不知道这个位置对应机械的哪个角度。这时候Z相就派上用场了。
Z相使用中的常见坑:
- Z相脉冲宽度很窄:通常只有1个A相脉冲的宽度,采样频率不够容易漏掉。
- Z相位置与机械零点的对应关系:安装时如果没对齐,每次回零都会偏一个固定角度。
- 多圈场景下的Z相使用:如果电机转了很多圈,Z相只能告诉你“这一圈”的零点,不能告诉你“第几圈”。
我曾经在一个AGV小车的轮子编码器上吃过亏。当时没接Z相,每次断电重启后,小车都不知道轮子转到哪了,只能靠限位开关硬碰硬找零。后来加了Z相,配合软件里的“找零程序”,问题就解决了。
4.4 增量式编码器的选型要点
选型时,我一般会关注这几个参数:
| 参数 | 说明 | 我的建议 |
|---|---|---|
| 分辨率(PPR) | 每圈脉冲数,决定位置精度 | 根据系统要求的定位精度反推,留20%余量 |
| 输出类型 | NPN、PNP、推挽、差分等 | 长距离传输用差分(RS-422),抗干扰好 |
| Z相有无 | 是否需要零位参考 | 只要断电后需要回零,就必须有Z相 |
| 防护等级 | IP等级,防尘防水 | 工业环境至少IP54,户外建议IP65以上 |
| 最高响应频率 | 编码器能输出的最高脉冲频率 | 要大于电机最高转速×分辨率,否则会丢脉冲 |
一个实用的选型公式:
最高转速(RPM)× 分辨率(PPR)÷ 60 = 最高输出频率(Hz)
这个频率必须小于控制器计数器的最大输入频率,否则会丢步。
4.5 信号处理实战:如何正确读取增量信号
读取增量式编码器的信号,看起来简单,但实际项目中容易出问题。我总结了几条经验:
- 硬件滤波不能省:编码器信号在长线传输时容易受干扰,建议在输入端加RC低通滤波,时间常数取1~10μs。
- 差分信号优先:如果距离超过2米,一定要用差分输出(A+、A-、B+、B-、Z+、Z-),抗共模干扰能力强。
- 软件去抖:在中断服务程序里做边沿检测时,加一个简单的延时去抖,防止毛刺导致误计数。
- Z相中断优先级最高:Z相脉冲很窄,如果被其他中断打断,可能就漏掉了。我习惯把Z相中断设为最高优先级。
下面是一个简单的STM32读取增量式编码器的代码片段,用的是定时器编码器模式:
// STM32 定时器编码器模式配置示例
void Encoder_Init(void)
{
TIM_EncoderInterfaceConfigTypeDef sEncoderConfig;
// 使能定时器时钟
__HAL_RCC_TIM3_CLK_ENABLE();
// 配置编码器模式
sEncoderConfig.EncoderMode = TIM_ENCODERMODE_TI12; // 4倍频模式
sEncoderConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sEncoderConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sEncoderConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sEncoderConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sEncoderConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sEncoderConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sEncoderConfig.IC1Filter = 0x0F; // 硬件滤波,降低干扰
sEncoderConfig.IC2Filter = 0x0F;
HAL_TIM_Encoder_Init(&htim3, &sEncoderConfig);
HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
}
// 读取当前计数值
int32_t Encoder_GetCount(void)
{
return (int32_t)__HAL_TIM_GET_COUNTER(&htim3);
}
这段代码里,我特别把滤波系数设成了0x0F,也就是最长的采样窗口。为什么?因为工业现场的电噪声太厉害了,不加滤波的话,计数器会莫名其妙地跳变。你想想看,一个本来静止的电机,编码器读数却在来回跳,那控制就没法做了。
4.6 避坑指南:增量式编码器常见问题
我曾经踩过的坑,分享给你:
- 坑1:Z相脉冲被漏掉——解决方案:用Z相触发外部中断,并在中断里锁存当前计数值。
- 坑2:长线传输导致信号畸变——解决方案:改用差分输出,并在接收端加终端电阻(120Ω)。
- 坑3:编码器安装偏心——解决方案:安装时用千分表打跳动,保证码盘与轴同心。
- 坑4:电源纹波干扰——解决方案:编码器电源单独供电,或加LC滤波。
嗯,这里要注意:增量式编码器虽然便宜好用,但它有个天生的缺点——断电丢失位置。如果你的系统要求断电后还能记住位置,那就得考虑绝对式编码器,或者加电池备份。
我个人习惯在项目初期就确定好:是否需要断电记忆位置?如果需要,那就别犹豫,直接上绝对式。如果不需要,增量式绝对够用,性价比还高。