跳到主要内容直流无刷电机 FOC 控制算法详解 | 极客日志C算法
直流无刷电机 FOC 控制算法详解
详细解析了直流无刷电机 FOC 控制算法的原理与实现。内容涵盖 FOC 基本概念、无刷电机与永磁同步电机的区别、Clarke 与 Park 坐标变换推导、SVPWM 空间矢量调制策略以及 STM32 硬件配置。重点展示了基于 C 语言的 FOC 代码实现,包括反 Park 变换、扇区判断、矢量作用时间计算及 PWM 占空比生成,并通过中断回调演示了位置环、速度环与电流环的闭环控制流程。
清心1 浏览 1. FOC 概述
1.1 FOC 控制算法介绍
FOC(Field-Oriented Control),直译为磁场定向控制,也被称为矢量控制。这是目前无刷直流电机(BLDC)和永磁同步电机(PMSM)高效控制的最优方案之一。FOC 算法的核心在于精确控制磁场的大小与方向,使得电机的运动转矩平稳、噪声小、效率高,并且具备高速的动态响应能力。
2. 无刷电机基础
2.1 无刷电机介绍
无刷电机(Brushless Motor)相比传统有刷电机,去除了机械换向器和电刷,因此具有更高的效率和可靠性。它采用电子换向技术,通过控制器调整转子磁场与定子磁场的相对位置来实现旋转。
无刷电机主要由定子和转子组成。定子上的线圈通电产生磁场,转子上的永磁体与之相互作用产生力矩。其优势包括高效率、高功率密度、宽速度范围、高可靠性和低噪音。
2.2 无刷电机和永磁同步电机的区别
无刷直流电机(BLDC)和永磁同步电机(PMSM)结构相似,主要区别在于反电动势波形。BLDC 的反电动势接近梯形波,而 PMSM 的反电动势接近正弦波。

2.3 无刷电机的控制原理
2.3.1 无刷电机工作原理
根据安培定则(右手螺旋定则),电流流过螺线管会产生磁场。无刷电机利用通电线圈产生的磁场与转子永磁体的相互作用来驱动旋转。
典型的 BLDC 电机有三根引出线:U 相(黄)、V 相(绿)、W 相(蓝)。三相绕组的一端连接在一起(星形接法),另一端引出。任意两根相线通电即可导通对应的线圈。
2.3.2 直流无刷电机驱动原理
有感直流无刷电机六步换相驱动原理
驱动电路通常由三个半桥组成,通过控制上下桥臂的导通实现逆变,将直流电转换为交流电以驱动电机。所谓的三相逆变电路,就是由 A+/-、B+/-、C+/- 三个半桥分别控制三相绕组。
由于绕组两两导通的特性,三相线圈只有 6 种有效的通电组合。按照合理顺序切换这些组合,即可让转子跟随磁场旋转。这种依赖霍尔传感器反馈转子位置的方式称为有感驱动。
霍尔传感器通常以 120°电角度安装,输出高低电平信号指示扇区。通过霍尔波形可以判断当前转子位置,对应三相逆变电路的特定导通状态。
直流无刷电机 FOC 控制原理
与传统的六步换相相比,FOC 在实际控制中有显著差异:
- 控制信号:FOC 采用正弦波驱动,六步换相采用方波驱动。
- 控制方式:FOC 中三个半桥的 MOS 管采用三三导通,而六步换相是两两导通。
3. 无刷电机 FOC 控制算法
3.1 FOC 控制算法整体流程
FOC 算法的实现流程大致如下:
- 采集电机三相相电流 Ia, Ib, Ic。
- 通过 Clarke 变换将三相电流转换为静止坐标系下的 Iα, Iβ。
- 通过 Park 变换将 Iα, Iβ 转换为旋转坐标系下的 Id, Iq。
- 根据 Id, Iq 和目标值进行 PID 计算,得到电压指令 Uq, Ud(电流环)。
- 通过反 Park 变换将 Uq, Ud 转换回 Uα, Uβ。
- 将 Uα, Uβ 作为 SVPWM 输入,生成三路 PWM 占空比。
3.2 FOC 算法 Clarke 变换
Clarke 变换的作用是将三相静止坐标系(Ia, Ib, Ic)转化为两相静止直角坐标系(Iα, Iβ)。这类似于力的矢量分解,将三相映射到两轴之上。
根据基尔霍夫电流定律(Ia + Ib + Ic = 0),我们只需采样两相电流即可推算出第三相。等幅值变换下,系数 K = 2/3,最终公式可简化为矩阵形式。
3.2.1 Clarke 变换公式推导


3.2.2 Clarke 逆变换公式推导
为了恢复三相电流,我们需要进行 Clarke 逆变换。已知 Iα, Iβ,结合基尔霍夫定律即可推导出 Ia, Ib, Ic 的表达式。
3.3 FOC 算法 Park 变换
Park 变换将静止坐标系下的 Iα, Iβ 转换为随转子旋转的 Id, Iq 坐标系。在这个旋转坐标系下,原本时变的交流量变成了直流量,便于控制。
3.3.1 Park 变换公式推导
角度 θ 是 d 轴与 α 轴的夹角。通过三角投影关系,可以得出 Id 和 Iq 的计算公式。其中 d 轴与转子磁链重合,q 轴垂直于磁链。
经过这一步变换,匀速旋转向量在该坐标系下变为定值,实现了控制变量的线性化。
3.3.2 Park 逆变换公式推导
逆变换则是将计算出的电压指令从旋转坐标系还原回静止坐标系,以便生成 PWM。
3.4 FOC 算法 SVPWM 原理及实现
SVPWM(Space Vector Pulse Width Modulation)空间矢量脉宽调制,旨在模拟三相正弦波电流形成的电压矢量圆,产生接近圆形的磁链轨迹。
- 基本矢量电压分解。
- 扇区判断(将 360 度分为六个扇区)。
- 计算相邻两个基本矢量电压的作用时间。
- 计算三路 PWM 占空比。
3.4.1 SVPWM 原理及实现
SVPWM 通过逆变器 6 个功率管的开关组合,合成所需的电压矢量。
3.4.2 七段式 SVPWM 和五段式 SVPWM 的区别
七段式使用七个向量,效率更高但计算复杂;五段式仅用五个向量,计算简单但谐波略大。本设计采用七段式 SVPWM。
3.4.3 至 3.4.6 矢量与占空比计算
根据扇区不同,基本矢量的作用时间分配也不同。例如在第一扇区,通过计算 Ta 和 Tb,结合 PWM 周期 Tpwm,可以得出三路 PWM 的 CCR 值。
计算公式示例(以扇区一为例):
value_a = (tpwm - Ta - Tb)/4;
value_b = value_a + Ta/2;
value_c = value_b + Tb/2;
3.5 FOC 矢量控制 C 语言代码
下面是核心的算法实现代码,包含反 Park 变换、扇区判断、矢量作用时间计算及 CCR 配置。
3.5.1 foc.c 文件代码
#include"foc.h"
void RevParkOperate(float vd,float vq,float theta,float*valpha,float*vbeta){
*valpha = vd *cos(theta)- vq *sin(theta);
*vbeta = vd *sin(theta)+ vq *cos(theta);
}
unsigned char SectorJude(float valpha,float vbeta){
float A = vbeta;
float B = valpha * sqrt3/2.0f- vbeta/2.0f;
float C =-valpha * sqrt3/2.0f- vbeta/2.0f;
unsigned char N =0;
if(A >0){ N = N+1;}
if(B >0){ N = N+2;}
if(C >0){ N = N+4;}
return N;
}
void VectorActionTime(uint8_t n,float valpha,float vbeta,uint32_t udc,uint32_t tpwm,float*ta,float*tb){
float X =(sqrt3 * tpwm)/ udc * vbeta;
float Y =(sqrt3 * tpwm)/ udc *((sqrt3/2.0f)*valpha - vbeta/2.0f);
float Z =(sqrt3 * tpwm)/ udc *(-(sqrt3/2.0f)*valpha - vbeta/2.0f);
if(n ==3)
{*ta = Y;*tb = X;}
if(n ==1)
{*ta =-Y;*tb =-Z;}
if(n ==5)
{*ta = X;*tb = Z;}
if(n ==4)
{*ta =-X;*tb =-Y;}
if(n ==6)
{*ta = Z;*tb = Y;}
if(n ==2)
{*ta =-Z;*tb =-X;}
}
void CCRCalculate(uint8_t n,float ta,float tb,uint32_t tpwm,uint32_t*ccr1,uint32_t*ccr2,uint32_t*ccr3){
float temp = ta + tb;
if(temp > tpwm){ ta = ta / temp*tpwm; tb = tb / temp*tpwm;}
float value1 =(tpwm - ta - tb)/4.0f;
float value2 = value1 + ta/2.0f;
float value3 = value2 + tb/2.0f;
switch(n){
case3:*ccr1 = value1;*ccr2 = value2;*ccr3 = value3;break;
case1:*ccr1 = value2;*ccr2 = value1;*ccr3 = value3;break;
case5:*ccr1 = value3;*ccr2 = value1;*ccr3 = value2;break;
case4:*ccr1 = value3;*ccr2 = value2;*ccr3 = value1;break;
case6:*ccr1 = value2;*ccr2 = value3;*ccr3 = value1;break;
case2:*ccr1 = value1;*ccr2 = value3;*ccr3 = value2;break;
}
}
void SvpwmAlgorithm(float vd,float vq,float theta,uint32_t udc,uint32_t tpwm){
float valpha =0;
float vbeta =0;
RevParkOperate(vd,vq,theta,&valpha,&vbeta);
unsigned char n = SectorJude(valpha,vbeta);
float ta =0;
float tb =0;
VectorActionTime(n,valpha,vbeta,udc,tpwm,&ta,&tb);
uint32_t ccr1 =0;
uint32_t ccr2 =0;
uint32_t ccr3 =0;
CCRCalculate(n,ta,tb,tpwm,&ccr1,&ccr2,&ccr3);
__HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_1,ccr1);
__HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,ccr2);
__HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_3,ccr3);
}
3.5.2 foc.h 文件代码
#ifndef__FOC_H__
#define__FOC_H__
#include"tim.h"
#include<math.h>
#include<stdio.h>
#define sqrt3 1.7320508f
void SvpwmAlgorithm(float vd,float vq,float theta,uint32_t udc,uint32_t tpwm);
void Clark_Park(float Electronic_Angle);
float motor_ramp(float t_value,float acc_time);
#endif
4. STM32CUBEMX 配置
4.1 时钟原配置
4.2 调试接口配置
4.3 时钟配置
4.4 高级定时器配置
用于 FOC 算法产生 PWM,需配置为互补输出模式。
4.5 ADC 配置
5. 代码讲解
5.1 foc.c 文件逻辑
这里再次展示核心算法文件的完整实现,重点在于 Clark_Park 变换和中断回调中的闭环控制逻辑。
#include"foc.h"
#include"spi.h"
#include"adc.h"
FOC_CURRENT foc_current;
void RevParkOperate(float vd,float vq,float theta,float*valpha,float*vbeta){
*valpha = vd *cos(theta)- vq *sin(theta);
*vbeta = vd *sin(theta)+ vq *cos(theta);
}
unsigned char SectorJude(float valpha,float vbeta){
float A = vbeta;
float B = valpha * sqrt3/2.0f- vbeta/2.0f;
float C =-valpha * sqrt3/2.0f- vbeta/2.0f;
unsigned char N =0;
if(A >0){ N = N+1;}
if(B >0){ N = N+2;}
if(C >0){ N = N+4;}
return N;
}
void VectorActionTime(uint8_t n,float valpha,float vbeta,uint32_t udc,uint32_t tpwm,float*ta,float*tb){
float X =(sqrt3 * tpwm)/ udc * vbeta;
float Y =(sqrt3 * tpwm)/ udc *((sqrt3/2.0f)*valpha - vbeta/2.0f);
float Z =(sqrt3 * tpwm)/ udc *(-(sqrt3/2.0f)*valpha - vbeta/2.0f);
if(n ==3){*ta = Y;*tb = X;}
if(n ==1){*ta =-Y;*tb =-Z;}
if(n ==5){*ta = X;*tb = Z;}
if(n ==4){*ta =-X;*tb =-Y;}
if(n ==6){*ta = Z;*tb = Y;}
if(n ==2){*ta =-Z;*tb =-X;}
}
void CCRCalculate(uint8_t n,float ta,float tb,uint32_t tpwm,uint32_t*ccr1,uint32_t*ccr2,uint32_t*ccr3){
float temp = ta + tb;
if(temp > tpwm){ ta = ta / temp*tpwm; tb = tb / temp*tpwm;}
float value1 =(tpwm - ta - tb)/4.0f;
float value2 = value1 + ta/2.0f;
float value3 = value2 + tb/2.0f;
switch(n){
case3:*ccr1 = value1;*ccr2 = value2;*ccr3 = value3;break;
case1:*ccr1 = value2;*ccr2 = value1;*ccr3 = value3;break;
case5:*ccr1 = value3;*ccr2 = value1;*ccr3 = value2;break;
case4:*ccr1 = value3;*ccr2 = value2;*ccr3 = value1;break;
case6:*ccr1 = value2;*ccr2 = value3;*ccr3 = value1;break;
case2:*ccr1 = value1;*ccr2 = value3;*ccr3 = value2;break;
}
}
void SvpwmAlgorithm(float vd,float vq,float theta,uint32_t udc,uint32_t tpwm){
float valpha =0;
float vbeta =0;
RevParkOperate(vd,vq,theta,&valpha,&vbeta);
unsigned char n = SectorJude(valpha,vbeta);
float ta =0;
float tb =0;
VectorActionTime(n,valpha,vbeta,udc,tpwm,&ta,&tb);
uint32_t ccr1 =0;
uint32_t ccr2 =0;
uint32_t ccr3 =0;
CCRCalculate(n,ta,tb,tpwm,&ccr1,&ccr2,&ccr3);
__HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_1,ccr1);
__HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,ccr2);
__HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_3,ccr3);
}
void Clark_Park(int APhaseCurrent,int BPhaseCurrent,float Electronic_Angle,FOC_CURRENT *Current){
int I_alfa = APhaseCurrent;
int I_beta =1/sqrt3 *(2*BPhaseCurrent + APhaseCurrent);
Current->Id = I_alfa*cos(Electronic_Angle)+ I_beta*sin(Electronic_Angle);
Current->Iq =-(I_alfa *sin(Electronic_Angle))+ I_beta*cos(Electronic_Angle);
Current->Id = Current->Id/1600.0;
Current->Iq = Current->Iq/1600.0;
}
5.2 foc.h 头文件定义
#ifndef__FOC_H__
#define__FOC_H__
#include"tim.h"
#include<math.h>
#include<stdio.h>
#define sqrt3 1.7320508f
typedef struct{
float Id;
float Iq;
}FOC_CURRENT;
extern FOC_CURRENT foc_current;
void SvpwmAlgorithm(float vd,float vq,float theta,uint32_t udc,uint32_t tpwm);
void Clark_Park(int APhaseCurrent,int BPhaseCurrent,float Electronic_Angle,FOC_CURRENT *Current);
#endif
5.3 主要运行代码
在中断回调中执行完整的控制环路,包括位置获取、PID 运算及 FOC 变换。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if(htim->Instance==TIM3){
if(motor_state ==1){
Get_Pos();
Encoder_TypeDef.angle =Get_Elec_Angle();
if(Encoder_TypeDef.angle>=Encoder_TypeDef.angle_rad_offset){
Encoder_TypeDef.angle = Encoder_TypeDef.angle - Encoder_TypeDef.angle_rad_offset;
}else{
Encoder_TypeDef.angle =6.2831852f- Encoder_TypeDef.angle_rad_offset + Encoder_TypeDef.angle;
}
Encoder_TypeDef.electronic_angle = Encoder_TypeDef.angle*7;
Encoder_TypeDef.electronic_angle =fmod(Encoder_TypeDef.electronic_angle,6.2831852f);
PID_Position_Out =PID_Function(&pid_position_TypeDef,target_location,encoder.rad_comulative);
vel_LPF =LPF_velocity(MT6701_GetVelocity());
PID_Speed_Out =PID_Function(&pid_speed_TypeDef,PID_Position_Out,vel_LPF);
Clark_Park(PhaseACurrent,PhaseBCurrent,Encoder_TypeDef.electronic_angle,&foc_current);
PID_Out_Value =PID_Function(&pid_current_Typedef,PID_Speed_Out,foc_current.Iq);
SvpwmAlgorithm(0,PID_Out_Value,Encoder_TypeDef.electronic_angle,12,3600);
}
}
}
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online