直流无刷电机 FOC 控制算法原理与 STM32 实战
直流无刷电机(BLDC)及永磁同步电机(PMSM)的磁场定向控制(FOC)算法。涵盖电机基础结构、FOC 核心原理(Clarke/Park 变换、SVPWM)、STM32F103 硬件配置与软件实现流程。通过数学推导与代码示例,解析坐标变换、扇区判断、矢量作用时间计算及 PWM 占空比生成逻辑。提供性能测试数据与进阶优化方向,适合嵌入式开发者掌握高性能电机控制技术。

直流无刷电机(BLDC)及永磁同步电机(PMSM)的磁场定向控制(FOC)算法。涵盖电机基础结构、FOC 核心原理(Clarke/Park 变换、SVPWM)、STM32F103 硬件配置与软件实现流程。通过数学推导与代码示例,解析坐标变换、扇区判断、矢量作用时间计算及 PWM 占空比生成逻辑。提供性能测试数据与进阶优化方向,适合嵌入式开发者掌握高性能电机控制技术。

在直流无刷电机(BLDC)和永磁同步电机(PMSM)的控制领域,磁场定向控制(Field-Oriented Control,简称 FOC)凭借其转矩平稳、噪声低、效率高及动态响应快的核心优势,已成为高性能电机控制的主流方案。这种算法通过精准控制电机磁场的大小与方向,将复杂的三相交流控制问题转化为简单的直流控制模型,解决了传统六步换相控制中转矩脉动大的痛点。
本文从电机基础原理切入,系统拆解 FOC 算法的核心流程,结合数学推导、可视化图表与 STM32 实战代码,解析 FOC 控制技术的实现逻辑。
无刷电机由定子(三相绕组,星形连接)和转子(永磁体)组成,通过电子换向替代传统有刷电机的机械电刷,核心优势如下:
两者结构相似,最关键的差异在于反电动势波形,这直接决定了控制算法的选择:
| 特性 | 无刷直流电机(BLDC) | 永磁同步电机(PMSM) |
|---|---|---|
| 反电动势波形 | 梯形波 | 正弦波 |
| 控制方式 | 方波驱动(六步换相) | 正弦波驱动(FOC 矢量控制) |
| 电流波形 | 矩形波 | 正弦波 |
| 转矩脉动 | 中等(6%~10%) | 极低(<2%) |
以有感 BLDC 为例,传统六步换相与 FOC 的核心区别如下:

FOC 算法的本质是'坐标变换 + 闭环控制 + 脉冲调制'的组合,通过三次关键变换将三相交流量转化为可独立控制的直流量,核心流程如下:

将三相静止坐标系(Ia、Ib、Ic,相位差 120°)转化为两相静止坐标系(Iα、Iβ,正交 90°),利用基尔霍夫电流定律(Ia+Ib+Ic=0)简化计算。
变换公式推导: 基于矢量分解原理,将三相电流投影到α-β坐标系,等幅值变换结果为:
{ I_alpha = I_a
{ I_beta = (1/sqrt(3)) * I_a + (2/sqrt(3)) * I_b
逆变换公式(从α-β到三相):
{ I_a = I_alpha
{ I_b = (sqrt(3)*I_beta - I_alpha) / 2
{ I_c = (-I_alpha - sqrt(3)*I_beta) / 2
将两相静止坐标系(Iα、Iβ)转化为随转子旋转的 d-q 坐标系(Id、Iq),实现转矩与励磁的解耦控制:
变换公式推导: 设 d 轴与α轴夹角为θ(转子位置角),通过三角投影可得:
{ I_d = I_alpha * cos(theta) + I_beta * sin(theta)
{ I_q = -I_alpha * sin(theta) + I_beta * cos(theta)
逆变换公式(从 d-q 到α-β):
{ U_alpha = U_d * cos(theta) - U_q * sin(theta)
{ U_beta = U_d * sin(theta) + U_q * cos(theta)
空间矢量脉宽调制(SVPWM)是 FOC 的"执行终端",通过控制 6 个功率管的开关状态,合成接近圆形的磁链轨迹,核心实现分为四步:
三相逆变桥的 8 种开关状态(6 个非零矢量 +2 个零矢量)构成 6 个扇区,每个扇区对应 60°电角度:
| 矢量 | 开关状态(S1,S2,S3) | 电压幅值 | 扇区范围 |
|---|---|---|---|
| U0(000) | 0,0,0 | 0 | 零矢量 |
| U60(110) | 1,1,0 | 2Udc/3 | 扇区 I(0°~60°) |
| U120(010) | 0,1,0 | 2Udc/3 | 扇区 II(60°~120°) |
| U180(011) | 0,1,1 | 2Udc/3 | 扇区 III(120°~180°) |
| U240(001) | 0,0,1 | 2Udc/3 | 扇区 IV(180°~240°) |
| U300(101) | 1,0,1 | 2Udc/3 | 扇区 V(240°~300°) |
| U360(100) | 1,0,0 | 2Udc/3 | 扇区 VI(300°~360°) |
| U7(111) | 1,1,1 | 0 | 零矢量 |
通过 Uα、Uβ计算三个特征值,根据正负性确定扇区:
{ U1 = U_beta
{ U2 = (sqrt(3)*U_alpha - U_beta) / 2
{ U3 = -(sqrt(3)*U_alpha + U_beta) / 2
定义 A=(U1>0?1:0)、B=(U2>0?1:0)、C=(U3>0?1:0),扇区编码 N=A+2B+4C,对应关系如下:
| N 值 | 扇区 | A,B,C 状态 |
|---|---|---|
| 3 | I | 1,1,0 |
| 1 | II | 1,0,0 |
| 5 | III | 1,0,1 |
| 4 | IV | 0,0,1 |
| 6 | V | 0,1,1 |
| 2 | VI | 0,1,0 |
根据电压矢量合成原理,非零矢量作用时间需满足:
U_ref * T = U_x * T_x + U_y * T_y + U_z * T_z
其中 T 为 PWM 周期,Tx、Ty 为相邻非零矢量作用时间,Tz 为零矢量作用时间(T0=T7=Tz/2)。以扇区 I 为例,计算公式为:
{ T_4 = (sqrt(3)*U_2*T) / U_dc
{ T_6 = (sqrt(3)*U_1*T) / U_dc
{ T_0 = T_7 = (T - T_4 - T_6) / 2
采用七段式 SVPWM(谐波失真低),以扇区 I 为例,占空比计算如下:
value1 = (tpwm - ta - tb)/4; // 基础偏移量
value2 = value1 + ta / 2; // 中间值
value3 = value2 + tb / 2; // 最大值
ccr1 = value1; // A 相占空比
ccr2 = value2; // B 相占空比
ccr3 = value3; // C 相占空比
| 模块 | 配置参数 | 用途 |
|---|---|---|
| 时钟 | 外部晶振 8MHz→PLL→72MHz | 保证 ADC 与定时器同步 |
| 高级定时器 | TIM1,PWM 模式,频率 10kHz | 生成三路互补 PWM |
| ADC | 双通道,规则组,12 位分辨率 | 采样 Ia、Ib 电流 |
| GPIO | 推挽输出(驱动使能) | 控制功率桥使能信号 |
| 中断 | TIM1 更新中断(PWM 周期中断) | 触发 FOC 算法执行 |
#include "foc.h"
#include "math.h"
#define sqrt3 1.73205f
#define PI 3.14159f
// Clarke 变换:Ia,Ib → Iα,Iβ
void ClarkeTransform(float Ia, float Ib, float *Ialpha, float *Ibeta){
*Ialpha = Ia;
*Ibeta = (Ia + 2* Ib) / sqrt3;
}
// Park 变换:Iα,Iβ,θ → Id,Iq
void ParkTransform(float Ialpha, float Ibeta, float theta, float *Id, float *Iq){
float cos_theta = cos(theta);
float sin_theta = sin(theta);
*Id = Ialpha * cos_theta + Ibeta * sin_theta;
*Iq = -Ialpha * sin_theta + Ibeta * cos_theta;
}
// Park 逆变换:Ud,Uq,θ → Uα,Uβ
void RevParkTransform(float Ud, float Uq, float theta, float *Ualpha, float *Ubeta){
float cos_theta = cos(theta);
float sin_theta = sin(theta);
*Ualpha = Ud * cos_theta - Uq * sin_theta;
*Ubeta = Ud * sin_theta + Uq * cos_theta;
}
// 扇区判断
uint8_t SectorJudge(float Ualpha, float Ubeta){
float U1 = Ubeta;
float U2 = (sqrt3 * Ualpha - Ubeta)/2.0f;
float U3 = (-sqrt3 * Ualpha - Ubeta)/2.0f;
uint8_t A = (U1 > 0)?1:0;
uint8_t B = (U2 > 0)?1:0;
uint8_t C = (U3 > 0)?1:0;
return A + 2* B + 4* C;
}
// 矢量作用时间计算
void VectorActionTime(uint8_t sector, float Ualpha, float Ubeta, uint32_t Udcbus, uint32_t tpwm, float *ta, float *tb){
float K = (sqrt3 * tpwm) / Udcbus;
float U1 = Ubeta;
float U2 = (sqrt3 * Ualpha - Ubeta)/2.0f;
float U3 = (-sqrt3 * Ualpha - Ubeta)/2.0f;
switch(sector){
case 3: // 扇区 I
*ta = U2 * K;
*tb = U1 * K;
break;
case 1: // 扇区 II
*ta = -U2 * K;
*tb = -U3 * K;
break;
default:
*ta = 0;
*tb = 0;
break;
}
}
// CCR 寄存器计算
void CCRCalculate(uint8_t sector, float ta, float tb, uint32_t tpwm, uint32_t *ccr1, uint32_t *ccr2, uint32_t *ccr3){
if(ta + tb > tpwm){
float scale = tpwm /(ta + tb);
ta *= scale;
tb *= scale;
}
float v1 = (tpwm - ta - tb)/4.0f;
float v2 = v1 + ta /2.0f;
float v3 = v2 + tb /2.0f;
switch(sector){
case 3: // 扇区 I
*ccr1 = (uint32_t)v1;
*ccr2 = (uint32_t)v2;
*ccr3 = (uint32_t)v3;
break;
case 1: // 扇区 II
*ccr1 = (uint32_t)v2;
*ccr2 = (uint32_t)v1;
*ccr3 = (uint32_t)v3;
break;
}
}
#include "stm32f1xx_hal.h"
#include "foc.h"
// 全局变量 FOC_HandleTypeDef hfoc;
uint32_t tpwm = 7199; // PWM 周期(10kHz)
uint32_t Udcbus = 24; // 母线电压 24V
int main(void){
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_ADC1_Init();
// FOC 初始化
hfoc.Id_ref = 0; // 励磁电流给定(弱磁时非零)
hfoc.Iq_ref = 5.0f; // 转矩电流给定
hfoc.Kp_speed = 0.1f; // 速度环 PI 参数
hfoc.Ki_speed = 0.5f;
hfoc.Kp_current = 2.0f; // 电流环 PI 参数
hfoc.Ki_current = 10.0f;
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);
while(1){
// 1. 采样反馈信号
hfoc.Ia = ADC_GetValue(ADC_CHANNEL_0); // 采样 A 相电流
hfoc.Ib = ADC_GetValue(ADC_CHANNEL_1); // 采样 B 相电流
hfoc.theta = Hall_GetAngle(); // 霍尔传感器获取位置角
hfoc.speed = Hall_GetSpeed(); // 计算转速
// 2. 坐标变换
ClarkeTransform(hfoc.Ia, hfoc.Ib, &hfoc.Ialpha, &hfoc.Ibeta);
ParkTransform(hfoc.Ialpha, hfoc.Ibeta, hfoc.theta, &hfoc.Id, &hfoc.Iq);
// 3. 速度环 PI 控制
float speed_err = hfoc.speed_ref - hfoc.speed;
hfoc.Iq_ref = PI_Controller(speed_err, &hfoc.speed_i, hfoc.Kp_speed, hfoc.Ki_speed);
// 4. 电流环 PI 控制
float Id_err = hfoc.Id_ref - hfoc.Id;
float Iq_err = hfoc.Iq_ref - hfoc.Iq;
hfoc.Ud = PI_Controller(Id_err, &hfoc.Id_i, hfoc.Kp_current, hfoc.Ki_current);
hfoc.Uq = PI_Controller(Iq_err, &hfoc.Iq_i, hfoc.Kp_current, hfoc.Ki_current);
// 5. Park 逆变换
RevParkTransform(hfoc.Ud, hfoc.Uq, hfoc.theta, &hfoc.Ualpha, &hfoc.Ubeta);
// 6. SVPWM 计算
hfoc.sector = SectorJudge(hfoc.Ualpha, hfoc.Ubeta);
VectorActionTime(hfoc.sector, hfoc.Ualpha, hfoc.Ubeta, Udcbus, tpwm, &hfoc.ta, &hfoc.tb);
CCRCalculate(hfoc.sector, hfoc.ta, hfoc.tb, tpwm, &hfoc.ccr1, &hfoc.ccr2, &hfoc.ccr3);
// 7. 更新 PWM 输出
TIM1->CCR1 = hfoc.ccr1;
TIM1->CCR2 = hfoc.ccr2;
TIM1->CCR3 = hfoc.ccr3;
}
}
以 PWM 周期(100μs)为触发周期,FOC 算法的执行时序如下:
在 24V 母线电压、5A 转矩电流条件下,测试结果如下:
| 特性 | 说明 |
|---|---|
| 优点 | 转矩平稳、噪声低、调速范围宽、效率高 |
| 缺点 | 算法复杂、对 MCU 性能要求高(需 FPU) |
| 应用场景 | 工业机器人、无人机、电动汽车、精密机床 |
FOC 控制算法是嵌入式电机控制领域的核心技术,其本质是通过数学变换实现复杂系统的解耦控制。本文从原理推导到代码实现,结合多种可视化工具拆解了 FOC 的核心逻辑,为嵌入式开发者提供清晰的学习路径。
随着 MCU 性能的提升与算法的优化,FOC 技术正从工业领域向消费电子领域渗透,掌握这一技术将为智能硬件开发提供核心竞争力。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online