FOC(磁场定向定向控制)驱动电机的完整流程可分为 6 个核心步骤,每个步骤对应具体代码实现,最终实现电机的高精度转速控制。以下是详细流程及代码对应关系:
FOC 控制电机的完整流程(结合代码)
步骤 1:硬件初始化(为 FOC 控制做准备)
作用:初始化 GPIO、PWM、串口、传感器等硬件,确保 FOC 算法能正常获取反馈和输出控制信号。
对应代码:
c
运行
// 主函数中的初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 使能GPIO时钟(控制驱动板使能等)
GPIO_Init(GPIOB,&GPIO_InitStructure); // 配置GPIO为推挽输出(如驱动板使能引脚)
Serial_Init(115200); // 初始化串口(调试用)
AD_Init(); // 初始化ADC(电流采样,FOC可选)
FOC_Init(12.6); // 核心FOC初始化(含PWM、传感器、校准)
Systick_CountMode(); // 初始化SysTick(计时用,计算转速)
// FOC_Init内部调用(DFOC.c)
void FOC_Init(float power) {
voltage_power_supply = power; // 记录电源电压(12.6V)
PWM_Init(); // 初始化PWM定时器(TIM2、TIM3,生成15kHz PWM波)
AS5600_Init(); // 初始化AS5600磁编码器(I2C通信,读取角度)
Check_Sensor(); // 电机零位校准(关键:建立电角度基准)
}关键细节:
PWM_Init()配置 6 路 PWM 输出(M0:PA0/PA1/PA2;M1:PA3/PA6/PA7),用于控制三相全桥驱动电路。AS5600_Init()初始化 I2C 接口,读取电机转子的机械角度,为后续电角度计算提供原始数据。
步骤 2:电机零位校准(建立电角度基准)
作用:FOC 控制需要准确的 “转子电角度”(与磁场方向对应),通过校准记录电角度零位,消除机械角度与电角度的偏差。
对应代码(DFOC.c 中的Check_Sensor函数):
c
运行
void Check_Sensor(void) {
// M0电机校准
SetPhaseVoltage(&M0, 3, _3PI_2); // 输出固定电压矢量(3V,电角度270°),锁定转子
delay_ms(3000); // 等待转子稳定
M0_zero_elc_Angle = M0_electricAngle(); // 记录此时的电角度作为零位
SetPhaseVoltage(&M0, 0, _3PI_2); // 关闭输出
// M1电机校准(同上)
SetPhaseVoltage(&M1, 3, _3PI_2);
delay_ms(3000);
M1_zero_elc_Angle = M1_electricAngle();
SetPhaseVoltage(&M1, 0, _3PI_2);
}
// 计算当前电角度(机械角度→电角度转换)
float M0_electricAngle(void) {
// 机械角度×极对数×方向 - 零位补偿 → 归一化到0~2π
return normalizeAngle((GetAngle_NoTrack(&Angle_Sensor0) * M0_PP * M0_DIR) - M0_zero_elc_Angle);
}关键细节:
- 电角度 = 机械角度 × 极对数(
M0_PP=7) ± 零位补偿,反映转子磁场的电气角度。 - 校准后,
M0_zero_elc_Angle和M1_zero_elc_Angle作为后续电角度计算的基准。
步骤 3:设置目标转速(主循环入口)
作用:确定电机需要运行的转速(单位:rad/s),作为闭环控制的输入。
对应代码(主函数循环):
c
运行
while(1) {
M0_Set_Velocity(3*2*PI); // M0目标转速:3圈/秒(2π rad=1圈,故3×2π rad/s)
M1_Set_Velocity(3*2*PI); // M1目标转速:3圈/秒
}关键细节:
- 目标转速通过
M0_Set_Velocity函数传入,后续步骤将围绕 “跟踪该目标” 展开。
步骤 4:获取电机实际状态(角度与转速)
作用:通过传感器实时检测电机当前的机械角度,计算实际转速,作为闭环控制的反馈。
对应代码(DFOC.c 中的M0_Set_Velocity函数及依赖函数):
c
运行
void M0_Set_Velocity(float Target) {
// 1. 读取实际转速(经低通滤波平滑)
Angle_Sensor0.velocity = Lowpassfilter(&M0_VEL_Filter, GetVelocity(&Angle_Sensor0));
// 2. 后续步骤:根据目标与实际转速的误差计算控制量
// ...
}
// 获取转速(AS5600.c)
float GetVelocity(struct AS5600_Sensor *AS5600_Vel) {
float dt = 0.0;
uint32_t Vel_ts = SysTick->VAL; // 读取当前SysTick值(计时)
// 计算两次采样的时间间隔dt(单位:秒)
if(Vel_ts < AS5600_Vel->Last_Vel_ts)
dt = (AS5600_Vel->Last_Vel_ts - Vel_ts)/9*1e-6f;
else
dt = (0xFFFFFF - Vel_ts + AS5600_Vel->Last_Vel_ts)/9*1e-6f;
// 计算角度变化量,转速=角度差/时间(rad/s)
float Vel_Angle = GetAngle(AS5600_Vel); // 获取当前角度
AS5600_Vel->velocity = (Vel_Angle - AS5600_Vel->Vel_Last_Angle)/dt;
// 更新历史数据
AS5600_Vel->Last_Vel_ts = Vel_ts;
AS5600_Vel->Vel_Last_Angle = Vel_Angle;
return AS5600_Vel->velocity;
}关键细节:
- 转速通过 “角度差 ÷ 时间间隔” 计算,
Lowpassfilter用于过滤噪声,使转速更平滑。 GetAngle函数会处理角度的 “圈数累积”(如转子转过 3 圈,角度 = 3×2π + 当前机械角度)。
步骤 5:速度环 PID 控制(计算所需电压)
作用:根据 “目标转速” 与 “实际转速” 的误差,通过 PID 算法计算出需要输出的电压(q 轴电压Uq,控制电机转矩)。
对应代码(DFOC.c 中的M0_Set_Velocity函数):
c
运行
void M0_Set_Velocity(float Target) {
// 1. 读取实际转速(步骤4已完成)
Angle_Sensor0.velocity = Lowpassfilter(&M0_VEL_Filter, GetVelocity(&Angle_Sensor0));
// 2. 计算PID输出(Uq:q轴电压,控制转矩)
float Uq = PID_Controller(&M0_VEL_PID, M0_DIR*(Target - Angle_Sensor0.velocity));
// 3. 输出电压到电机(步骤6)
SetPhaseVoltage(&M0, Uq, M0_electricAngle());
}
// PID算法(PID_Control.h)
float PID_Controller(struct _PID *pid, float error) {
float Ts = 0.001f; // 控制周期(约1ms)
// 比例项:Kp×误差
float proportion = pid->Kp * error;
// 积分项:Ki×误差×时间(累积误差)
float intergration = pid->Last_intergration + pid->Ki * 0.5f * Ts * error;
// 微分项:Kd×(当前误差-上一次误差)/时间(抑制超调)
float differential = pid->Kd * (error - pid->Last_Error) / Ts;
// 总输出:比例+积分+微分
float Output = proportion + intergration + differential;
// 限幅(避免电压过大)
Output = _constrain(Output, -voltage_limit, voltage_limit);
// 更新历史数据
pid->Last_Error = error;
pid->Last_intergration = intergration;
return Output;
}关键细节:
Uq是 FOC 控制的核心变量,与电机转矩成正比(Uq越大,转矩越大,转速越高)。- PID 参数(
Kp=0.4,Ki=2)决定了转速跟踪的快速性和稳定性。
步骤 6:FOC 坐标变换与 PWM 输出(驱动电机)
作用:将 PID 输出的Uq(旋转坐标系下的电压)通过坐标变换转换为三相 PWM 信号,控制电机绕组电流。
对应代码(DFOC.c 中的SetPhaseVoltage和SetPwm函数):
c
运行
// FOC核心变换:将Uq转换为三相电压
void SetPhaseVoltage(struct Motor_ *Motor, float Uq, float angle_el) {
// 1. Park逆变换:旋转坐标系→静止坐标系(Uq→Ualpha、Ubeta)
Motor->Ualpha = -Uq * sin(angle_el); // angle_el为当前电角度
Motor->Ubeta = Uq * cos(angle_el);
// 2. Clark逆变换:静止坐标系→三相坐标系(Ualpha、Ubeta→Ua、Ub、Uc)
// 叠加电源电压的1/2,适配单极性PWM
Motor->Ua = Motor->Ualpha + voltage_power_supply / 2;
Motor->Ub = (sqrt(3)*Motor->Ubeta - Motor->Ualpha) / 2 + voltage_power_supply / 2;
Motor->Uc = -(Motor->Ualpha + sqrt(3)*Motor->Ubeta) / 2 + voltage_power_supply / 2;
// 3. 输出PWM
SetPwm(Motor->Mot_num, Motor->Ua, Motor->Ub, Motor->Uc);
}
// 将三相电压转换为PWM占空比
void SetPwm(int Mot_num, float Ua, float Ub, float Uc) {
// 限幅:确保电压不超过电源电压
Ua = constrain(Ua, 0.0f, voltage_limit);
Ub = constrain(Ub, 0.0f, voltage_limit);
Uc = constrain(Uc, 0.0f, voltage_limit);
if(Mot_num == 0) { // M0电机PWM设置
// 占空比 = 相电压 / 电源电压 → 转换为TIM定时器的比较值(周期4800)
M0_PWM_A(M0.dc_a * 4800.0f); // TIM2_CH1(PA0)
M0_PWM_B(M0.dc_b * 4800.0f); // TIM2_CH2(PA1)
M0_PWM_C(M0.dc_c * 4800.0f); // TIM2_CH3(PA2)
}
// M1电机同理...
}关键细节:
- Park 逆变换:将旋转坐标系(与转子同步)的
Uq转换为静止坐标系的Ualpha、Ubeta,消除旋转耦合。 - Clark 逆变换:将两相电压转换为三相电压,直接对应电机的 A、B、C 三相绕组。
- PWM 占空比与相电压成正比,最终通过 TIM 定时器输出 PWM 波,控制驱动电路的 MOS 管导通 / 关断,驱动电机旋转。
总结:FOC 控制的核心逻辑
FOC 通过 “磁场定向” 将三相电机的复杂控制简化为 “转矩(Uq)” 和 “励磁(通常为 0)” 的独立控制,结合代码的完整流程为:
硬件初始化 → 零位校准(建立电角度基准) → 设置目标转速 → 检测实际转速 → PID计算Uq → FOC变换→三相PWM → 驱动电机 → (循环反馈调整)