第3章:编码器与反馈——伺服系统的“眼睛”

做伺服驱动这么多年,我越来越觉得,编码器就是整个系统的眼睛。你算法写得再漂亮,电流环调得再顺,如果反馈回来的位置数据是错的,那一切都是白搭。今天咱们就好好聊聊编码器这回事。

3.1 编码器类型:增量式 vs 绝对式

先说说最基础的问题。编码器分两大类:增量式和绝对式。这俩有啥区别?说白了,一个只知道“动了多少”,一个知道“在哪儿”。

增量式编码器

增量式编码器输出的是脉冲信号。电机一转,它就咔咔往外蹦脉冲。你通过数脉冲个数,就知道电机转了多少角度。

优点:

  • 结构简单,成本低
  • 响应速度快
  • 分辨率可以做得比较高

缺点:

  • 断电后位置丢失
  • 上电需要找零位(回零操作)
  • 抗干扰能力相对弱一些
我的经验: 增量式编码器在工业机器人上用得少,但在一些简单的传送带、包装机上还是很常见的。我做过一个项目,客户为了省钱用了增量式,结果每次断电重启都要回零,操作工烦得要死。后来还是换了绝对式。

绝对式编码器

绝对式编码器就高级多了。它每个位置都有一个唯一的编码值。你读到的数据直接就是当前位置,不需要累计,不需要回零。

优点:

  • 断电不丢位置
  • 上电即用,无需回零
  • 抗干扰能力强

缺点:

  • 价格贵
  • 接口复杂
  • 多圈绝对式编码器有机械齿轮,寿命有限
注意: 绝对式编码器分单圈和多圈。单圈的只能记录一圈内的位置,多圈的可以记录多圈。选型时一定要搞清楚你的应用需要几圈。我曾经有个客户,选了单圈绝对式,结果用在丝杠上,一圈走5mm,行程1米,你说这能行吗?

3.2 编码器分辨率与精度

这两个概念经常被混淆。我见过不少工程师把分辨率当精度用,结果系统怎么调都调不好。

分辨率 是编码器能分辨的最小角度变化。比如一个17位的编码器,分辨率就是 360° / 2^17 ≈ 0.0027°。说白了,就是它能“看到”多小的变化。

精度 是编码器实际输出值和真实值之间的误差。这个受机械安装、码盘刻划误差、电子噪声等因素影响。

参数 分辨率 精度
含义 能分辨的最小变化 实际值与真实值的偏差
单位 位(bit)或线数(CPR) 角秒(arcsec)或角度
影响因素 码盘刻线数、电子细分 机械安装、码盘质量、温度
典型值 17位、23位 ±20角秒、±5角秒
核心观点: 分辨率高不代表精度高。你用一个23位的编码器,分辨率是0.00015°,但如果安装偏心0.1mm,精度可能差到几角分。我调试过一个高精度转台,编码器分辨率23位,但装上去就是抖,最后发现是联轴器有间隙。换了刚性联轴器,问题就解决了。

3.3 编码器接口:ABZ、SSI、BiSS

接口这块,我把它分成三类来说。每种接口都有自己的脾气。

ABZ 增量式接口

这是最经典的接口。A相和B相相差90°电角度,用来判断方向。Z相是零位信号,每转一圈出一个脉冲。

读取方式很简单:用定时器捕获或者外部中断计数。我一般用STM32的定时器编码器模式,硬件自动处理,省心。

// STM32 定时器编码器模式配置示例
void Encoder_Init(void)
{
    TIM_EncoderInterfaceConfigTypeDef sEncoderConfig;
    
    sEncoderConfig.EncoderMode = TIM_ENCODERMODE_TI12;  // 双沿触发
    sEncoderConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
    sEncoderConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
    
    HAL_TIM_Encoder_Init(&htim3, &sEncoderConfig);
    HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
}
避坑指南: ABZ接口要注意信号抖动。我曾经在一个电机上发现,电机静止时计数器还在跳,查了半天发现是电缆屏蔽没做好。后来加了磁环和滤波电容,问题就解决了。

SSI 同步串行接口

SSI是绝对式编码器常用的接口。它用时钟线和数据线,主设备发时钟,编码器在时钟的驱动下逐位输出数据。

SSI的时序其实很简单:

  1. 主设备拉低时钟线,准备开始
  2. 时钟线产生一串脉冲(比如25个,对应25位数据)
  3. 编码器在每个时钟上升沿输出一位数据
  4. 传输完成后,时钟线保持高电平一段时间(叫“单稳态时间”)
// SSI 读取示例(伪代码)
uint32_t SSI_Read(void)
{
    uint32_t data = 0;
    
    // 拉低时钟,开始传输
    CLK_LOW();
    delay_us(1);
    
    for(int i = 0; i < 25; i++)
    {
        CLK_HIGH();
        delay_us(0.5);
        
        data <<= 1;
        if(DATA_READ() == HIGH)
            data |= 0x01;
        
        CLK_LOW();
        delay_us(0.5);
    }
    
    // 等待单稳态时间
    delay_us(20);
    
    return data;
}
注意: SSI的时钟频率不能太高,一般不超过5MHz。线缆长了还要降频。我试过用10米线跑4MHz,数据全是错的。降到1MHz就正常了。

BiSS 双向同步串行接口

BiSS是这几年比较火的接口,可以理解为SSI的升级版。它支持双向通信,不仅能读位置,还能写参数、读诊断信息。

BiSS的时序和SSI类似,但多了几个特点:

  • 支持CRC校验,数据可靠性高
  • 支持多编码器级联
  • 传输速率高,可达10MHz以上
  • 有寄存器读写功能

我个人比较喜欢BiSS,尤其是做高精度伺服的时候。CRC校验能帮你省掉很多排查故障的时间。有一次系统偶尔报位置错误,查了三天没找到原因。后来发现是编码器线缆有轻微破损,BiSS的CRC校验直接报错,一下子就定位到了。

3.4 编码器数据读取与处理

数据读回来了,怎么用?这里有几个关键点。

数据滤波

编码器数据难免有噪声。尤其是增量式,一个毛刺就能让位置跳一大截。我常用的滤波方法:

  • 限幅滤波: 相邻两次位置变化超过阈值就丢弃
  • 滑动平均: 取最近N个值的平均
  • 中值滤波: 取中间值,适合去除脉冲噪声
// 限幅滤波示例
#define MAX_DELTA 100  // 最大允许变化量

int32_t Position_Filter(int32_t new_pos, int32_t last_pos)
{
    int32_t delta = new_pos - last_pos;
    
    if(abs(delta) > MAX_DELTA)
    {
        // 变化太大,认为是噪声,返回上次值
        return last_pos;
    }
    
    return new_pos;
}

速度计算

位置数据有了,速度怎么算?最直接的方法就是差分:

// M法测速:计算单位时间内的脉冲数
float Speed_M_Method(int32_t pos_now, int32_t pos_last, float dt)
{
    float speed = (float)(pos_now - pos_last) / dt;
    return speed;
}

但这里有个坑。如果dt太小,差分结果会放大噪声。我一般会加一个低通滤波器:

// 一阶低通滤波
float Speed_LPF(float speed_raw, float speed_last, float alpha)
{
    // alpha 取 0.1~0.3 比较合适
    return alpha * speed_raw + (1 - alpha) * speed_last;
}
我的习惯: 低速时用T法(测脉冲周期),高速时用M法(测脉冲数)。中速时两者结合,叫M/T法。这个后面讲速度环的时候会详细说。

多圈处理

用增量式编码器做位置控制,一定要处理多圈溢出。比如一个16位的编码器,一圈65536个脉冲。转两圈就溢出了。你得在软件里维护一个“圈数计数器”。

// 多圈位置处理
int32_t g_total_position = 0;
int16_t g_last_raw = 0;

void Position_Update(int16_t raw_value)
{
    int16_t delta = raw_value - g_last_raw;
    
    // 处理溢出
    if(delta > 32767)
        delta -= 65536;
    else if(delta < -32768)
        delta += 65536;
    
    g_total_position += delta;
    g_last_raw = raw_value;
}
总结一下: 编码器选型要看应用场景。精度要求高的用绝对式BiSS,成本敏感的用增量式ABZ。不管用哪种,数据处理都要做好滤波和溢出处理。这些基础打好了,后面的位置环、速度环才能调得顺。

嗯,今天就先聊到这儿。下一章咱们讲电流环,那才是真正考验算法功底的地方。