目录
[1 磁场定向控制 (FOC)介绍](#1 磁场定向控制 (FOC)介绍)
[1.1 FOC控制模型介绍](#1.1 FOC控制模型介绍)
[1.2 模型功能](#1.2 模型功能)
[2 FOC模型的几个重要的转换关系](#2 FOC模型的几个重要的转换关系)
[2.1 Clarke Transform](#2.1 Clarke Transform)
[2.2 Inverse Clarke Transform](#2.2 Inverse Clarke Transform)
[2.3 Park Transform](#2.3 Park Transform)
[2.4 Inverse Park Transform](#2.4 Inverse Park Transform)
[3 STM32模拟实现FOC](#3 STM32模拟实现FOC)
[3.1 FOC算法的C语言实现](#3.1 FOC算法的C语言实现)
[3.2 测试代码实现](#3.2 测试代码实现)
[4 波形验证](#4 波形验证)
[5 源代码文件](#5 源代码文件)
概述
本文主要介绍磁场定向控制 (FOC)的控制原理框架结构,还简要介绍了模型中使用几个模块的数学实现方法,并使用C语言将这些数学模型实现出来,同时在STM32G4平台上对其做验证。
1 磁场定向控制 (FOC)介绍
1.1 FOC控制模型介绍
磁场定向控制 (FOC),又称矢量控制,是一种控制方法,用于在多种电机类型(包括感应电机、永磁同步电机 (PMSM 和无刷直流 (BLDC) 电机)的全转矩和转速范围内实现良好的控制能力。如果超出额定转速,则使用配合弱磁磁场磁场定向控制。
以下模块图显示了一个磁场定向控制架构,包括以下组件:

1.2 模型功能
设计磁场定向控制的电机控制工程师执行以下任务:
1)为电流回路开发具有两个 PI 控制器的控制器架构
2)为可选的转速外环和位置外环开发 PI 控制器
3)调节所有 PI 控制器的增益以满足性能要求
4)设计用于控制 PWM 的空间矢量调制器
5)如果使用无传感器控制,则设计观测器算法来估计转子位置和速度
6)设计每安培最大转矩或弱磁控制算法,以生成最佳 id_ref 和 iq_ref
7)实现在计算上高效的帕克变换、克拉克变换和帕克逆变换
8)设计故障检测和保护逻辑
9)验证和确认控制器在不同工况下的性能
10)在微控制器或 FPGA 上实现采用定点或浮点的控制器
2 FOC模型的几个重要的转换关系
2.1 Clarke Transform
Clarke Transform 模块计算 abc 参考系中平衡三相分量的克拉克变换,并输出静止 αβ 参考系中平衡两相正交分量。该模块也可以计算三相分量 a 、b 和 c 的克拉克变换,并输出分量 α 、β 和 0
。对于平衡系统,零分量等于零。使用输入数参数以使用两个或三个输入。
使用双输入配置时,该模块接受三相 (abc ) 中的两个信号,自动计算第三个信号,并输出 αβ 参考系中的对应分量。例如,该模块接受 a 和 b 输入值或多路复用输入值 abc ,其中相位 a 轴与 α 轴对齐。

数学模型如下:

2.2 Inverse Clarke Transform
Inverse Clarke Transform 模块计算静止 αβ 参考系中平衡的两相正交分量的克拉克逆变换,并输出静止 abc 参考系中平衡的三相分量。该模块也可以计算分量 α 、β 和 0
的克拉克逆变换,以输出三相分量 a 、b 和 c 。对于平衡系统,零分量等于零。使用输入数参数以使用两个或三个输入。
该模块接受 α-β 轴分量作为输入,并输出对应的三相信号,其中相位 a 轴与 α 轴对齐。

数学模型:

2.3 Park Transform
Park Transform 模块计算静止 αβ 参考系中两相正交分量(α 、β )或多路复用 αβ0 分量的帕克变换。该模块接受以下输入:
静止参考系中的 α-β 轴分量或多路复用分量 αβ0 。使用输入数参数以使用两个或三个输入。
对应变换角度的正弦值和余弦值。
在使用双输入配置时,它会输出旋转 dq 参考系中的正交直轴 (d ) 和交轴 (q ) 分量。在使用三输入配置时,它输出多路复用分量 dq0。
对于平衡系统,零分量等于零。
可以配置模块,使 d 轴或 q 轴在时间 t = 0 处与 α 轴对齐。
下列各图显示在以下情形下 αβ 参考系和旋转 dq 参考系中的 α-β 轴分量:

数学模型:
当 q 轴与 α 轴对齐时:

其中:
fα 和 fβ 是静止 αβ 参考系中的两相正交分量。
fd 和 fq 是旋转 dq 参考系中的直轴和交轴正交分量。
2.4 Inverse Park Transform
Inverse Park Transform 模块计算正交直轴 (d ) 和正交轴 (q ) 分量或旋转 dq 参考系中的多路复用 dq0 分量的帕克逆变换。可以对该模块进行配置,使 d 轴或 q 轴在时间 t = 0 处与 α 轴对齐。
该模块接受以下输入:
旋转参考系中的 d-q 轴分量或多路复用分量 dq0 。使用输入数参数以使用两个或三个输入。
对应变换角度的正弦值和余弦值。
在使用双输入配置时,它输出静止 αβ 参考系中的两相正交分量。在使用三输入配置时,它输出多路复用分量 αβ0。对于平衡系统,零分量等于零。
下列各图显示在以下情形下的旋转 dq 参考系和 αβ 参考系中的 α-β 轴分量:
当 d 轴与 α 轴对齐时:

当 q 轴与 α 轴对齐时:

其中:
fd 和 fq 是旋转 dq 参考系中的直轴和交轴正交分量。
fα 和 fβ 是静止 αβ 参考系中的两相正交分量。
3 STM32模拟实现FOC
3.1 FOC算法的C语言实现
1)Clarke变换

2)Clarke逆变换

- Park变换

- park逆变换

3.2 测试代码实现
代码216行: 设置d轴的值为0.2f
代码217行: 设置q轴的值为0
代码221行: 设置电机DQ轴上运行的角度值范围(0~2π)
代码224行: 实现park逆变换,将D,Q坐标转变至α、β坐标系上
代码229行: 将经过逆park生成的ia,ib,ic,生成svpwm

4 波形验证
1)D,Q坐标系中的值经过逆park变换生成的α、β坐标系上的波形图,α、β的波形相位相差90°

2)α、β坐标系经过逆Clark生成的ia,ib,ic的波形图,ia,ib,ic三个波形之间相位相差120°

3)将 α、β坐标系和ia,ib,ic的波形图放在同一个图像中进行参照

5 源代码文件
创建foc.c文件,编写如下代码:
cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : foc_ctrl.h
* Description : foc driver base on stm32f446
******************************************************************************
* @attention
*
* COPYRIGHT: Copyright (c) 2024 tangminfei2013@126.com
* DATE: JUL 05th, 2024
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "foc_ctrl.h"
#define SQRT_3 1.7320508f
#define SQRT_3_DIV_2 0.8660254f
#define DIV_1 0.5f
FOC_T FOC;
/*****************************************************************************
Clarke变换 输入三相电流,输出alpha,bate电流
Iα = Ia
Iβ = (Ia + 2Ib) / sqrt(3)
******************************************************************************/
void clarkeTransform(Phase_T *abc, AlphaBeta_T *alphaBeta)
{
alphaBeta->alpha = abc->Ua;
alphaBeta->beta = (abc->Ua + 2 * abc->Ub) * SQRT_3;
}
/****************************************************************************
Park变换,输入电角度、Ialpha和Ibeta,经过Park变换得到Iq、Id
Id = Iα · cosθ + Iβ · sinθ
Iq = Iα · sinθ + Iβ · cosθ
*****************************************************************************/
void parkTransform(const AlphaBeta_T *alphaBeta, float angle, DQ_T *dq)
{
float sinAngle = sin(angle);
float cosAngle = cos(angle);
dq->d = cosAngle * alphaBeta->alpha + sinAngle * alphaBeta->beta;
dq->q = -sinAngle * alphaBeta->alpha + cosAngle * alphaBeta->beta;
}
/***************************************************************************
park逆变换,输入Uq、Ud得到Ualpha、Ubeta
Uα = Ud · cosθ - Uq · sinθ
Uβ = Ud · sinθ + Uq · cosθ
****************************************************************************/
void inverseParkTransform(DQ_T *dq, AlphaBeta_T *alphaBeta, float angle)
{
float cosAngle = cos(angle);
float sinAngle = sin(angle);
alphaBeta->alpha = dq->d * cosAngle - dq->q * sinAngle;
alphaBeta->beta = dq->d * sinAngle + dq->q * cosAngle;
}
/**********************************************************************************************************
Clarke逆变换,输入Ualpha、Ubeta,得到Ua,Ub,Uc
Ua = Uα
Ub = -1/2 * Uα + sqrt(3)/2 * Uβ
Ub = -1/2 * Uα - sqrt(3)/2 * Uβ
**********************************************************************************************************/
void inverseClarkeTransform(AlphaBeta_T *abVoltage, Phase_T *abc)
{
abc->Ua = abVoltage->alpha;
abc->Ub = -DIV_1 * abVoltage->alpha + SQRT_3_DIV_2 * abVoltage->beta;
abc->Uc = -DIV_1 * abVoltage->alpha - SQRT_3_DIV_2 * abVoltage->beta;
}
void SVPWM(SVPWM_T *svpwm, Phase_T *abc)
{
float sum;
float k_svpwm;
// step-1: 设置象限电压值
svpwm->Ts = 1.0f; // SVPWM的采样周期
svpwm->u1 = abc->Ua;
svpwm->u2 = abc->Ub;
svpwm->u3 = abc->Uc;
// step2:扇区判断
// 根据u1、u2和u3的正负情况确定所处的扇区
svpwm->sector = (svpwm->u1 > 0.0f) + ((svpwm->u2 > 0.0f) << 1) + ((svpwm->u3 > 0.0f) << 2); // N=4*C+2*B+A
// step3:计算基本矢量电压作用时间(占空比)
// 根据扇区的不同,计算对应的t_a、t_b和t_c的值,表示生成的三相电压的时间
switch (svpwm->sector)
{
case 5:
// 扇区5
svpwm->t4 = svpwm->u3;
svpwm->t6 = svpwm->u1;
sum = svpwm->t4 + svpwm->t6;
if (sum > svpwm->Ts)
{
k_svpwm = svpwm->Ts / sum; //
svpwm->t4 = k_svpwm * svpwm->t4;
svpwm->t6 = k_svpwm * svpwm->t6;
}
svpwm->t7 = (svpwm->Ts - svpwm->t4 - svpwm->t6) / 2;
svpwm->ta = svpwm->t4 + svpwm->t6 + svpwm->t7;
svpwm->tb = svpwm->t6 + svpwm->t7;
svpwm->tc = svpwm->t7;
break;
case 1:
// 扇区1
svpwm->t2 = -svpwm->u3;
svpwm->t6 = -svpwm->u2;
sum = svpwm->t2 + svpwm->t6;
if (sum > svpwm->Ts)
{
k_svpwm = svpwm->Ts / sum; // 计算缩放系数
svpwm->t2 = k_svpwm * svpwm->t2;
svpwm->t6 = k_svpwm * svpwm->t6;
}
svpwm->t7 = (svpwm->Ts - svpwm->t2 - svpwm->t6) / 2;
svpwm->ta = svpwm->t6 + svpwm->t7;
svpwm->tb = svpwm->t2 + svpwm->t6 + svpwm->t7;
svpwm->tc = svpwm->t7;
break;
case 3:
// 扇区3
svpwm->t2 = svpwm->u1;
svpwm->t3 = svpwm->u2;
sum = svpwm->t2 + svpwm->t3;
if (sum > svpwm->Ts)
{
k_svpwm = svpwm->Ts / sum; //
svpwm->t2 = k_svpwm * svpwm->t2;
svpwm->t3 = k_svpwm * svpwm->t3;
}
svpwm->t7 = (svpwm->Ts - svpwm->t2 - svpwm->t3) / 2;
svpwm->ta = svpwm->t7;
svpwm->tb = svpwm->t2 + svpwm->t3 + svpwm->t7;
svpwm->tc = svpwm->t3 + svpwm->t7;
break;
case 2:
// 扇区2
svpwm->t1 = -svpwm->u1;
svpwm->t3 = -svpwm->u3;
sum = svpwm->t1 + svpwm->t3;
if (sum > svpwm->Ts)
{
k_svpwm = svpwm->Ts / sum;
svpwm->t1 = k_svpwm * svpwm->t1;
svpwm->t3 = k_svpwm * svpwm->t3;
}
svpwm->t7 = (svpwm->Ts - svpwm->t1 - svpwm->t3) / 2;
svpwm->ta = svpwm->t7;
svpwm->tb = svpwm->t3 + svpwm->t7;
svpwm->tc = svpwm->t1 + svpwm->t3 + svpwm->t7;
break;
case 6:
// 扇区6
svpwm->t1 = svpwm->u2;
svpwm->t5 = svpwm->u3;
sum = svpwm->t1 + svpwm->t5;
if (sum > svpwm->Ts)
{
k_svpwm = svpwm->Ts / sum; //
svpwm->t1 = k_svpwm * svpwm->t1;
svpwm->t5 = k_svpwm * svpwm->t5;
}
svpwm->t7 = (svpwm->Ts - svpwm->t1 - svpwm->t5) / 2;
svpwm->ta = svpwm->t5 + svpwm->t7;
svpwm->tb = svpwm->t7;
svpwm->tc = svpwm->t1 + svpwm->t5 + svpwm->t7;
break;
case 4:
// 扇区4
svpwm->t4 = -svpwm->u2;
svpwm->t5 = -svpwm->u1;
sum = svpwm->t4 + svpwm->t5;
if (sum > svpwm->Ts)
{
k_svpwm = svpwm->Ts / sum; //
svpwm->t4 = k_svpwm * svpwm->t4;
svpwm->t5 = k_svpwm * svpwm->t5;
}
svpwm->t7 = (svpwm->Ts - svpwm->t4 - svpwm->t5) / 2;
svpwm->ta = svpwm->t4 + svpwm->t5 + svpwm->t7;
svpwm->tb = svpwm->t7;
svpwm->tc = svpwm->t5 + svpwm->t7;
break;
default:
break;
}
// step4:3路PWM输出
}
void foc_test(void)
{
int run_cnt = 10;
float theta = 0;
float ta,tb,tc;
DQ_T dq_t;
AlphaBeta_T alphaBeta_t;
SVPWM_T svpwm_out;
Phase_T phase_t;
dq_t.d = 0.2f;
dq_t.q = 0.0f;
while( run_cnt--)
{
for ( theta = 0; theta < 6.2831853f; theta += 0.275f )
{
// 逆Park变换
inverseParkTransform(&dq_t,&alphaBeta_t,theta);
// 逆Clark变换
inverseClarkeTransform(&alphaBeta_t, &phase_t);
// swpwm 转换
SVPWM( &svpwm_out,&phase_t );
ta = 100.0f*svpwm_out.ta;
tb = 100.0f*svpwm_out.tb;
tc = 100.0f*svpwm_out.tc;
// printf("%.4f,%.4f,%.4f,%.4f,%.4f\n", alphaBeta_t.alpha*100.0f ,
// alphaBeta_t.beta*100.0f ,ta,tb,tc);
printf("%.4f,%.4f,%.4f,%.4f,%.4f \n", alphaBeta_t.alpha,alphaBeta_t.beta,
phase_t.Ua,phase_t.Ub,phase_t.Uc );
}
}
}
/* End of this file */
创建foc.h文件,编写如下代码:
cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : foc_ctrl.h
* Description : foc driver base on stm32f446
******************************************************************************
* @attention
*
* COPYRIGHT: Copyright (c) 2024 tangminfei2013@126.com
* DATE: JUL 05th, 2024
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#ifndef __FOC_CTRL_H
#define __FOC_CTRL_H
/*****************************************************************************/
/* Includes */
/*****************************************************************************/
#include "main.h"
#include "utils_types.h"
#ifdef _cplusplus
extern "C" {
#endif
typedef struct
{
float Ia; // Phase A current
float Ib; // Phase B current
float Ic; // Phase C current
float Ua; // Phase A Voltage
float Ub; // Phase B Voltage
float Uc; // Phase C Voltage
} Phase_T;
typedef struct
{
float alpha; // alpha-axis current
float beta; // beta-axis current
} AlphaBeta_T;
typedef struct
{
float d; // d-axis current
float q; // q-axis current
} DQ_T;
typedef struct
{
int sector;
float u1;
float u2;
float u3;
float ta;
float tb;
float tc;
float Ts;
float t0;
float t1;
float t2;
float t3;
float t4;
float t5;
float t6;
float t7;
} SVPWM_T;
typedef struct
{
float U_d;
float U_q;
float theta;
float U_alpha;
float U_bate;
Phase_T Phase_Curr;
AlphaBeta_T AlphaBeta;
DQ_T DQ;
} FOC_T;
extern FOC_T FOC;
void foc_test(void);
#ifdef _cplusplus
}
#endif
#endif /* __FOC_CTRL_H */