3. FOC电角度校准

任务目标

在FOC控制中,电角度(转子磁极方向与定子A相轴线的夹角)必须与编码器读数建立精确对应关系。本案例通过向电机施加已知方向的电压矢量,使转子锁定到已知电角度位置,从而标定编码器零位偏移量,实现电角度校准。

原理简述

当向电机三相绕组施加固定电压矢量(如Vα=0, Vβ=Vmax,对应电角度90°)时,定子产生固定方向的磁场,转子会旋转并锁定到与该磁场对齐的位置。此时编码器读数θ_enc对应的真实电角度即为90°(或已知角度),由此可计算零位偏移:

offset = θ_enc - θ_electric

后续运行时,实际电角度 = 编码器读数 - offset。

硬件与依赖

  • 带增量式或值编码器的PMSM电机
  • FOC驱动板(如STM32 + DRV8301)
  • 串口输出用于读取编码器值

关键完整代码


// 电角度校准 - 施加已知电压矢量并检测转子锁定位置
// 硬件:STM32F405 + DRV8301 + 1000线增量编码器(4倍频后4000 counts/rev)
// 极对数:4(电机旋转一圈 = 4个电周期)

#include "foc_calib.h"

// 全局变量
int32_t encoder_raw;          // 编码器原始读数(0-3999)
int32_t zero_offset;          // 零位偏移量(编码器counts)
uint16_t pole_pairs = 4;      // 极对数

// 设置PWM占空比,输出指定电压矢量(电角度域)
void set_voltage_vector(float v_alpha, float v_beta) {
    // 使用SVPWM或直接开环方式输出
    // 此处简化为直接设置三相占空比(需根据实际驱动板适配)
    float v_u = v_alpha;
    float v_v = -0.5f * v_alpha + 0.866f * v_beta;
    float v_w = -0.5f * v_alpha - 0.866f * v_beta;
    
    // 归一化到PWM占空比(假设Vbus=12V,PWM满量程对应12V)
    pwm_set_duty(0, v_u / 12.0f);
    pwm_set_duty(1, v_v / 12.0f);
    pwm_set_duty(2, v_w / 12.0f);
}

// 读取编码器值(需实现)
int32_t encoder_read(void) {
    return TIM2->CNT;  // 示例:从定时器计数器读取
}

// 校准主函数
void angle_calibration(void) {
    float v_alpha = 0.0f;
    float v_beta = 2.0f;   // 施加电压幅值(伏特),方向为90°电角度
    
    // 1. 使能驱动,施加已知电压矢量
    motor_enable();
    set_voltage_vector(v_alpha, v_beta);
    
    // 2. 等待转子稳定(需足够时间让机械振荡衰减)
    delay_ms(2000);
    
    // 3. 读取当前编码器位置
    encoder_raw = encoder_read();
    
    // 4. 计算零位偏移
    // 施加的电压矢量对应电角度90°(π/2 rad)
    // 编码器读数对应的机械角度 = encoder_raw / 4000 * 360°
    // 电角度 = 机械角度 * pole_pairs(模360°)
    // 零位偏移 = 编码器读数 - 期望电角度对应的编码器读数
    float mech_angle_deg = (float)encoder_raw / 4000.0f * 360.0f;
    float elec_angle_deg = fmod(mech_angle_deg * pole_pairs, 360.0f);
    
    // 期望电角度为90°,计算偏移(编码器counts)
    // 偏移 = 当前编码器读数 - 90°对应的编码器读数
    float expected_enc_for_90 = (90.0f / pole_pairs) / 360.0f * 4000.0f;
    zero_offset = encoder_raw - (int32_t)expected_enc_for_90;
    
    // 5. 关闭驱动
    set_voltage_vector(0, 0);
    motor_disable();
    
    // 6. 输出结果
    printf("Calibration done.\r\n");
    printf("Encoder raw: %d\r\n", encoder_raw);
    printf("Zero offset: %d counts\r\n", zero_offset);
}

// 校准后获取电角度(供FOC使用)
float get_electric_angle(void) {
    int32_t enc = encoder_read();
    int32_t corrected = enc - zero_offset;
    // 转换为电角度(弧度)
    float mech_angle = (float)corrected / 4000.0f * 2.0f * 3.14159f;
    float elec_angle = fmod(mech_angle * pole_pairs, 2.0f * 3.14159f);
    if (elec_angle < 0) elec_angle += 2.0f * 3.14159f;
    return elec_angle;
}

运行与验证

  1. 接线:确保编码器信号正确接入MCU,电机三相线连接驱动板。
  2. 编译烧录:将上述代码集成到FOC工程中,编译后烧录至MCU。
  3. 执行校准:调用angle_calibration(),观察串口输出。
  4. 验证方法:
    • 校准完成后,手动旋转电机轴至任意位置,读取get_electric_angle()返回值。
    • 用万用表或示波器测量电机A相反电动势过零点,对比此时电角度是否接近0°或180°(误差应在±5°电角度内)。
    • 重复校准3次,比较zero_offset值,波动应小于±3 counts(对应约0.27°机械角)。

通过判定标准

  • 串口输出Zero offset为稳定值(多次校准偏差≤3 counts)。
  • 反电动势过零点与电角度读数对应误差在±5°电角度以内。
  • 电机在FOC开环模式下能平稳启动,无失步或明显振动。

常见问题

  • 转子未锁定:施加电压幅值过小或等待时间不足,增大v_beta至3-4V或延长等待至3秒。
  • 偏移计算错误:确认极对数设置正确,编码器线数与4倍频配置一致。
  • 校准后电角度跳变:检查编码器读数是否在零点附近穿越,需对zero_offset做模4000归一化处理。