3、零位校准:编码器零位与电机电气零位的对齐方法、Z信号捕获与软件对齐策略

零位校准,说白了就是让编码器告诉控制器:「电机现在转到了哪个电气角度」。这件事如果做不准,后面所有电流环、速度环都是白搭。我刚开始做FOC时,就吃过这个亏——电机能转,但一加负载就抖,查了三天才发现是零位偏了5度。

这一节,我们重点解决两个问题:怎么找到电气零位,以及怎么让编码器记住这个位置。

3.1 电气零位 vs 编码器零位

先理清两个概念。你想想看,电机绕组通电后会产生磁场,这个磁场方向由电流矢量决定。当A相电流最大时,转子应该停在某个固定位置——这个位置就是电气零位。

而编码器零位,是传感器自己定义的参考点。对于增量式编码器,通常就是Z信号出现的那个位置。对于绝对值编码器,则是出厂设定的机械零点。

我们的目标很简单:让编码器零位和电气零位重合,或者在软件里记下它们之间的偏差。

概念 定义 来源
电气零位 A相通电时转子稳定位置 电机绕组结构决定
编码器零位 传感器参考点(Z信号或出厂零点) 编码器安装位置决定
对齐偏差 两者之间的角度差 安装误差+磁极位置

核心原则:对齐偏差一旦确定,在正常运行中不会改变。除非你拆了电机或编码器,否则只需要校准一次。

3.2 手动对齐法:给电机通直流电

这是最原始也最可靠的方法。我在实验室调试第一版驱动板时,用的就是这招。

步骤很简单:

  1. 给电机A相通正向直流电,B、C相悬空或接负端
  2. 转子会自己转到某个固定位置——这就是电气零位
  3. 手动旋转编码器外壳,让Z信号刚好出现
  4. 锁紧编码器,完成

但这里有个坑:通多大电流?电流太小,转子转不到位;电流太大,可能烧线圈。我个人习惯用额定电流的30%~50%,持续不超过2秒。

注意:手动对齐只适用于可以旋转编码器外壳的场合。如果编码器已经固定死,那就只能用软件对齐了。

3.3 Z信号捕获策略

增量式编码器每转一圈会输出一个Z脉冲。这个脉冲很窄,通常只有1~2个计数周期。如果MCU没及时捕获,你就得再转一圈。

我常用的捕获策略有两种:

  • 中断法:Z信号接MCU的外部中断引脚,上升沿触发。中断里记录当前编码器计数值。
  • 定时查询法:在主循环里轮询Z信号电平,检测到跳变后记录。适合低速场合。

实际项目中,我推荐用中断法。为什么呢?因为电机高速旋转时,Z脉冲持续时间可能只有几十微秒,主循环根本来不及响应。

// Z信号中断服务程序示例
void Z_Signal_ISR(void)
{
    // 记录当前编码器计数值
    z_capture_value = encoder_get_count();
    
    // 标记Z信号已捕获
    z_captured_flag = 1;
    
    // 清除中断标志
    EXTI_ClearITPendingBit(EXTI_Line_Z);
}

小技巧:如果电机启动时不知道位置,可以先强制开环运行一小段,等捕获到Z信号后再切回FOC。我管这叫「盲启动+Z校准」策略。

3.4 软件对齐策略:记下偏差

如果编码器已经固定死,没法手动调零,那就用软件补偿。具体做法是:

  1. 给A相通电,让转子转到电气零位
  2. 读取当前编码器角度值,记为 offset_angle
  3. 在FOC运算中,每次读取编码器角度后都减去这个偏移量

公式很简单:

电气角度 = 编码器角度 - offset_angle

但要注意:offset_angle 必须是单圈内的值。如果编码器是多圈的,你得取模处理。

我曾经在一个项目中,因为忘了对 offset_angle 做归一化,导致电机每转一圈就跳变一次。排查了半天才发现是角度溢出问题。

3.5 自动校准流程

手动校准太麻烦,Z信号捕获又依赖硬件。有没有更省事的办法?有——自动校准。

思路是这样的:

  • 给电机施加一个旋转磁场(开环强拖)
  • 同时读取编码器角度和反电动势相位
  • 找到两者之间的固定偏差

这个方法不需要Z信号,也不需要手动调编码器。但实现起来有点复杂,需要ADC采样相电压。

下面是我画的一张自动校准流程图,帮你理清逻辑:

编码器零位自动校准流程图 编码器零位自动校准流程 开始校准 施加旋转磁场(开环强拖) 同步采样编码器角度 + 反电动势相位 计算偏差:offset = θ_enc - θ_bemf 偏差是否稳定? 不稳定 稳定 保存 offset 到非易失存储器 校准完成 注意: 强拖速度建议在 50~200 RPM 电流限制在额定值的 30%

3.6 保存与校验

校准完成后,offset 值必须保存到非易失存储器里。我一般存在EEPROM或Flash的最后几个扇区。

但有个问题:你怎么知道保存的值是对的?

我的做法是加一个校验机制:

  • 保存时同时写入一个固定的魔数(比如 0xA5A5)
  • 上电时读取魔数,如果匹配才使用保存的 offset
  • 如果不匹配,触发重新校准流程
// 保存校准参数
void save_calibration(int32_t offset)
{
    uint32_t magic = 0xA5A5;
    eeprom_write(ADDR_OFFSET, (uint8_t*)&offset, 4);
    eeprom_write(ADDR_MAGIC, (uint8_t*)&magic, 4);
}

// 加载校准参数
int32_t load_calibration(void)
{
    uint32_t magic;
    int32_t offset;
    
    eeprom_read(ADDR_MAGIC, (uint8_t*)&magic, 4);
    if (magic != 0xA5A5) {
        return 0;  // 未校准,返回0
    }
    
    eeprom_read(ADDR_OFFSET, (uint8_t*)&offset, 4);
    return offset;
}

经验之谈:我习惯在产线最后一步做零位校准,然后把 offset 值打印出来贴在产品标签上。万一客户反馈有问题,可以快速比对。

3.7 常见问题与避坑

做零位校准这么多年,踩过的坑不少。挑几个典型的说说:

  • Z信号抖动:编码器Z信号在过零点附近可能有毛刺。我一般加一个20μs的软件去抖。
  • 温度漂移:有些编码器受温度影响,零位会漂。如果产品工作温度范围大,建议做温度补偿。
  • 多极对电机:对于4对极、8对极电机,电气零位每90度、45度就有一个。校准时要确认是对应哪个极对。

警告:绝对不要在电机高速旋转时写入EEPROM!写入操作会阻塞CPU,导致电流环失控。我都是先停电机,再保存参数。