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;
}
运行与验证
- 接线:确保编码器信号正确接入MCU,电机三相线连接驱动板。
- 编译烧录:将上述代码集成到FOC工程中,编译后烧录至MCU。
- 执行校准:调用
angle_calibration(),观察串口输出。 - 验证方法:
- 校准完成后,手动旋转电机轴至任意位置,读取
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归一化处理。