编码器与位置检测:从原理到实现

系列:电机控制系列 - 第 8 篇 目标平台:STM32F407ZGT6 阅读时间:25 分钟

前言

位置检测是 FOC 的导航仪

  • ❌ 位置不准 → 电流控制失败
  • ❌ 速度不准 → 转速环震荡

本篇目标

  • ✅ 掌握编码器接口
  • ✅ 理解位置/速度测量方法
  • ✅ 实现电角度计算

一、编码器类型

1.1 增量式编码器

结构

  • A 相、B 相(正交,90°)
  • Z 相(零位信号,每转一次)

输出波形

 A 相: ────┐   ┌───┐   ┌───
           └───┘   └───┘
 B 相: ──────┐   ┌───┐   ┌─
             └───┘   └───┘
       └─90°─┘
 
 正转:A 超前 B 90°
 反转:B 超前 A 90°

分辨率

  • 2500 线编码器:每转 2500 个脉冲
  • 四倍频后:2500 × 4 = 10000 线

1.2 绝对式编码器

特点

  • 每个位置对应唯一编码
  • 无需归零
  • 掉电后位置不丢失

接口

  • SSI(同步串行接口)
  • BiSS(双向串行接口)
  • EnDat(HEIDENHAIN)

1.3 磁编码器

常用芯片

  • AS5047(14-bit,SPI)
  • MA730(21-bit,SPI)
  • TLE5012(15-bit,SPI)

优点

  • ✅ 体积小
  • ✅ 成本低(¥20-50)
  • ✅ 抗污染

二、STM32 编码器接口

2.1 硬件编码器模式

定时器:TIM2/TIM3/TIM4/TIM5

配置步骤

 /**
  * @brief TIM3 编码器模式初始化
  */
 void Encoder_Init(void) {
     // 1. 使能时钟
     RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
     
     // 2. GPIO 配置(PA6/TIM3_CH1, PA7/TIM3_CH2)
     GPIOA->MODER |= (2 << GPIO_MODER_MODE6_Pos) |  // 复用功能
                     (2 << GPIO_MODER_MODE7_Pos);
     GPIOA->AFR[0] |= (2 << GPIO_AFRL_AFSEL6_Pos) |  // AF2
                      (2 << GPIO_AFRL_AFSEL7_Pos);
     
     // 3. 编码器模式配置
     TIM3->SMCR |= (3 << TIM_SMCR_SMS_Pos);  // 编码器模式 3(四倍频)
     
     // 4. 输入捕获配置
     TIM3->CCMR1 |= (1 << TIM_CCMR1_CC1S_Pos) |  // IC1 映射到 TI1
                    (1 << TIM_CCMR1_CC2S_Pos);   // IC2 映射到 TI2
     
     // 5. 极性配置
     TIM3->CCER |= TIM_CCER_CC1P | TIM_CCER_CC2P;  // 上升沿触发
     
     // 6. 计数器配置
     TIM3->ARR = 0xFFFF;  // 自动重装载值
     TIM3->CR1 |= TIM_CR1_CEN;  // 使能计数器
 }

2.2 读取编码器

 /**
  * @brief 读取编码器计数
  */
 int16_t Encoder_Get_Count(void) {
     return (int16_t)TIM3->CNT;
 }
 
 /**
  * @brief 读取方向
  */
 int Encoder_Get_Direction(void) {
     return (TIM3->CR1 & TIM_CR1_DIR) ? -1 : 1;
 }

三、位置测量

3.1 机械角度

 /**
  * @brief 计算机械角度
  */
 float Encoder_Get_Mech_Angle(uint16_t encoder_cnt, uint16_t ppr) {
     // ppr: 每转脉冲数(四倍频后)
     float angle = (float)encoder_cnt / ppr * 2.0f * M_PI;
     
     // 归一化到 [0, 2π)
     angle = fmodf(angle, 2.0f * M_PI);
     if (angle < 0) angle += 2.0f * M_PI;
     
     return angle;
 }

3.2 电角度

 /**
  * @brief 计算电角度
  */
 float Encoder_Get_Elec_Angle(float mech_angle, uint8_t pole_pairs) {
     float elec_angle = mech_angle * pole_pairs;
     
     // 归一化到 [0, 2π)
     elec_angle = fmodf(elec_angle, 2.0f * M_PI);
     if (elec_angle < 0) elec_angle += 2.0f * M_PI;
     
     return elec_angle;
 }

3.3 多圈计数

 /**
  * @brief 多圈计数(软件实现)
  */
 int32_t Encoder_Get_Multi_Turn(int16_t current_cnt) {
     static int16_t last_cnt = 0;
     static int32_t total_cnt = 0;
     
     int16_t delta = current_cnt - last_cnt;
     
     // 处理溢出
     if (delta > 32767) {
         delta -= 65536;  // 向下溢出
     } else if (delta < -32768) {
         delta += 65536;  // 向上溢出
     }
     
     total_cnt += delta;
     last_cnt = current_cnt;
     
     return total_cnt;
 }

四、速度测量

4.1 M 法(测频法)

原理:单位时间内的脉冲数

 /**
  * @brief M 法测速
  */
 float Encoder_Get_Speed_M(int16_t current_cnt, float dt, uint16_t ppr) {
     static int16_t last_cnt = 0;
     int16_t delta = current_cnt - last_cnt;
     last_cnt = current_cnt;
     
     // 速度(rpm)
     float speed_rpm = (float)delta / ppr / dt * 60.0f;
     
     return speed_rpm;
 }

适用:高速(脉冲数多,精度高)

4.2 T 法(测周法)

原理:脉冲间隔时间

 /**
  * @brief T 法测速(需要硬件定时器)
  */
 float Encoder_Get_Speed_T(uint32_t timer_cnt, float timer_freq, uint16_t ppr) {
     // timer_cnt: 相邻两个脉冲间的定时器计数
     if (timer_cnt == 0) return 0;
     
     float speed_rpm = timer_freq / timer_cnt / ppr * 60.0f;
     return speed_rpm;
 }

适用:低速(测量精度高)

4.3 M/T 法(混合法)

/**
 * @brief M/T 法测速
 */
float Encoder_Get_Speed_MT(int16_t delta_cnt, float dt, uint16_t ppr) {
    if (dt == 0) return 0;

    float speed_rpm = (float)delta_cnt / ppr / dt * 60.0f;
    return speed_rpm;
}

五、Z 信号校准

5.1 电角度零点对齐

目的:让编码器零点 = 电角度零点

/**
 * @brief Z 信号校准
 */
void Encoder_Calibrate_Z(void) {
    // 1. 通 id 电流,iq = 0(d 轴对齐)
    float id_ref = 1.0f;  // 1A
    float iq_ref = 0.0f;

    // 2. 等待稳定
    HAL_Delay(1000);

    // 3. 读取编码器
    int16_t cal_cnt = TIM3->CNT;

    // 4. 计算零点偏移
    encoder_offset = cal_cnt % ENCODER_PPR;

    // 5. 保存到 Flash
    Flash_Write(ENCODER_OFFSET_ADDR, &encoder_offset, 1);
}

5.2 自动校准流程

void Encoder_Auto_Calibrate(void) {
    printf("开始编码器校准...\n");

    // 1. 施加 id 电流
    id_ref = 2.0f;
    iq_ref = 0.0f;

    // 2. 等待转子对齐
    HAL_Delay(2000);

    // 3. 多次采样取平均
    int32_t sum = 0;
    for (int i = 0; i < 100; i++) {
        sum += TIM3->CNT;
        HAL_Delay(10);
    }
    encoder_offset = (sum / 100) % ENCODER_PPR;

    printf("校准完成,偏移 = %d\n", encoder_offset);
}

六、总结

编码器核心要点

  1. 增量式编码器 + 四倍频
  2. STM32 硬件编码器模式
  3. 电角度 = 机械角度 × 极对数
  4. Z 信号校准电角度零点

下一篇PID 控制器设计

最后修改:2026 年 03 月 14 日
如果觉得我的文章对你有用,请随意赞赏