FOC 控制框架:电流环/速度环/位置环的完整实现
系列:电机控制系列 - 第 10 篇 目标平台:STM32F407ZGT6 阅读时间:40 分钟
前言
到前九篇为止,我们已经掌握了:
- ✅ 电机基础
- ✅ PWM 与逆变桥
- ✅ 数学模型
- ✅ 坐标变换
- ✅ SVPWM
- ✅ ADC 采样
- ✅ 编码器
- ✅ PID 控制
现在,是时候把它们组装起来,实现完整的 FOC 系统!
本篇目标:
- ✅ 理解三闭环控制架构
- ✅ 掌握时序与中断设计
- ✅ 实现完整的 FOC 代码
一、FOC 控制架构
1.1 三闭环结构
位置环(最外环,10Hz)
↓
速度环(中环,100Hz)
↓
电流环(最内环,20kHz)
↓
SVPWM → 三相 PWM层级关系:
| 环路 | 控制周期 | 带宽 | 输入 | 输出 |
|---|---|---|---|---|
| 位置环 | 10-100 Hz | 低 | 位置参考 | 速度参考 |
| 速度环 | 100-1000 Hz | 中 | 速度参考 | 电流参考 |
| 电流环 | 5-20 kHz | 高 | 电流参考 | 电压参考 |
1.2 坐标系关系
abc(三相静止)
↓ Clarke
αβ(两相静止)
↓ Park
dq(两相旋转)
↓ PID
vd, vq(电压)
↓ Inv_Park
vα, vβ(电压)
↓ SVPWM
PWM 占空比二、时序与中断设计
2.1 中断层级
优先级从高到低:
1. ADC 中断(20kHz)→ 电流环
2. 定时器中断(1kHz)→ 速度环
3. 主循环(100Hz)→ 位置环2.2 时序图
20kHz ADC 中断:
├── 读取 ADC(电流)
├── 读取编码器
├── Clarke 变换
├── Park 变换
├── 电流环 PID
├── 逆 Park 变换
└── SVPWM
每 20 次执行一次速度环
每 200 次执行一次位置环三、完整 FOC 代码
3.1 数据结构
/**
* @brief FOC 控制器
*/
typedef struct {
// 测量值
float ia, ib, ic; // 三相电流
float i_alpha, i_beta; // αβ 电流
float id, iq; // dq 电流
float theta_e; // 电角度
float omega_m; // 机械角速度
float theta_m; // 机械角度
// 参考值
float id_ref; // d 轴电流参考
float iq_ref; // q 轴电流参考
float omega_ref; // 速度参考
float theta_ref; // 位置参考
// 电压输出
float vd, vq; // dq 电压
float v_alpha, v_beta; // αβ 电压
// PID 控制器
PID_t pid_id;
PID_t pid_iq;
PID_t pid_speed;
PID_t pid_position;
// 电机参数
Motor_Params_t motor;
// 状态
uint8_t state; // 0=停机, 1=运行
uint32_t tick; // 时钟计数
} FOC_t;3.2 初始化
/**
* @brief FOC 初始化
*/
void FOC_Init(FOC_t *foc) {
// 1. 电机参数
Motor_Params_Init(&foc->motor);
// 2. PID 初始化
foc->pid_id = (PID_t){
.Kp = 2.0f,
.Ki = 200.0f,
.Kd = 0.0f,
.Ts = 0.00005f,
.integral_limit = 50.0f,
.output_limit = 24.0f
};
foc->pid_iq = foc->pid_id;
foc->pid_speed = (PID_t){
.Kp = 0.5f,
.Ki = 10.0f,
.Kd = 0.01f,
.Ts = 0.001f,
.integral_limit = 10.0f,
.output_limit = 10.0f
};
foc->pid_position = (PID_t){
.Kp = 10.0f,
.Ki = 1.0f,
.Kd = 0.1f,
.Ts = 0.01f,
.integral_limit = 5.0f,
.output_limit = 100.0f
};
// 3. 硬件初始化
PWM_GPIO_Init();
TIM1_PWM_Init();
ADC_Triple_Init();
Encoder_Init();
// 4. 状态
foc->state = 0;
foc->tick = 0;
}3.3 电流环(20kHz)
/**
* @brief 电流环控制(ADC 中断调用)
*/
void FOC_Current_Loop(FOC_t *foc) {
// 1. 读取 ADC
uint16_t adc_raw[3];
ADC_Read(adc_raw);
// 2. 转换为实际电流
Current_Get_Real(¤t_calib, adc_raw,
&foc->ia, &foc->ib, &foc->ic);
// 3. 读取编码器
uint16_t encoder_cnt = Encoder_Get_Count();
foc->theta_e = Encoder_Get_Elec_Angle(encoder_cnt, foc->motor.P);
// 4. Clarke 变换
Clarke_Transform(foc->ia, foc->ib, foc->ic,
&foc->i_alpha, &foc->i_beta);
// 5. Park 变换
Park_Transform(foc->i_alpha, foc->i_beta, foc->theta_e,
&foc->id, &foc->iq);
// 6. 电流环 PID
float id_error = foc->id_ref - foc->id;
float iq_error = foc->iq_ref - foc->iq;
foc->vd = PID_Position(&foc->pid_id, id_error);
foc->vq = PID_Position(&foc->pid_iq, iq_error);
// 7. 逆 Park 变换
Inv_Park_Transform(foc->vd, foc->vq, foc->theta_e,
&foc->v_alpha, &foc->v_beta);
// 8. SVPWM
SVPWM_Control(foc->v_alpha, foc->v_beta, VDC);
}3.4 速度环(1kHz)
/**
* @brief 速度环控制(定时器中断调用)
*/
void FOC_Speed_Loop(FOC_t *foc) {
// 1. 测速
foc->omega_m = Encoder_Get_Speed_M();
// 2. 速度环 PID
float speed_error = foc->omega_ref - foc->omega_m;
foc->iq_ref = PID_Position(&foc->pid_speed, speed_error);
// 3. id 参考(MTPA 或弱磁)
foc->id_ref = 0.0f; // 简单版本
}3.5 位置环(100Hz)
/**
* @brief 位置环控制(主循环调用)
*/
void FOC_Position_Loop(FOC_t *foc) {
// 1. 读取位置
foc->theta_m = Encoder_Get_Mech_Angle();
// 2. 位置误差(最短路径)
float theta_error = foc->theta_ref - foc->theta_m;
// 归一化到 [-π, π]
while (theta_error > M_PI) theta_error -= 2.0f * M_PI;
while (theta_error < -M_PI) theta_error += 2.0f * M_PI;
// 3. 位置环 PID
foc->omega_ref = PID_Position(&foc->pid_position, theta_error);
}3.6 ADC 中断服务函数
/**
* @brief ADC 中断(20kHz)
*/
void ADC_IRQHandler(void) {
static foc = {0}; // 全局 FOC 对象
if (foc.state == 1) { // 运行状态
// 电流环(每次都执行)
FOC_Current_Loop(&foc);
foc.tick++;
// 速度环(每 20 次执行)
if (foc.tick % 20 == 0) {
FOC_Speed_Loop(&foc);
}
// 位置环(每 200 次执行)
if (foc.tick % 200 == 0) {
FOC_Position_Loop(&foc);
}
}
}四、启动流程
/**
* @brief FOC 启动
*/
void FOC_Start(FOC_t *foc) {
// 1. 校准
Current_Calibrate();
Encoder_Auto_Calibrate();
// 2. 初始化参考
foc->id_ref = 0.0f;
foc->iq_ref = 0.0f;
foc->omega_ref = 0.0f;
foc->theta_ref = Encoder_Get_Mech_Angle();
// 3. 使能 PWM
TIM1->BDTR |= TIM_BDTR_MOE;
// 4. 启动状态
foc->state = 1;
printf("FOC 启动成功\n");
}五、总结
FOC 核心流程:
- ADC 采样 → 电流
- 编码器 → 位置/速度
- Clarke → Park → dq 电流
- PID 控制 → dq 电压
- 逆 Park → SVPWM → PWM
下一篇:无感 FOC:反电动势观测器