NOTE

FOC控制电机流程

2026/04/19 5 min read NOTE 类 论文项目 项目 毕业论文 FOC电机控制 毕业论文

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_AngleM1_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.4Ki=2)决定了转速跟踪的快速性和稳定性。
步骤 6:FOC 坐标变换与 PWM 输出(驱动电机)

作用:将 PID 输出的Uq(旋转坐标系下的电压)通过坐标变换转换为三相 PWM 信号,控制电机绕组电流。

对应代码(DFOC.c 中的SetPhaseVoltageSetPwm函数):

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转换为静止坐标系的UalphaUbeta,消除旋转耦合。
  • Clark 逆变换:将两相电压转换为三相电压,直接对应电机的 A、B、C 三相绕组。
  • PWM 占空比与相电压成正比,最终通过 TIM 定时器输出 PWM 波,控制驱动电路的 MOS 管导通 / 关断,驱动电机旋转。

总结:FOC 控制的核心逻辑

FOC 通过 “磁场定向” 将三相电机的复杂控制简化为 “转矩(Uq)” 和 “励磁(通常为 0)” 的独立控制,结合代码的完整流程为:

硬件初始化 → 零位校准(建立电角度基准) → 设置目标转速 → 检测实际转速 → PID计算Uq → FOC变换→三相PWM → 驱动电机 → (循环反馈调整)