记录自己手写FOC算法流程之SVPWM实现。
SVPWM的原理就不过多赘述,SVPWM模块的输入是Uα和Uβ,输出的三相占空比,这里直接上代码:
结构体及宏定义如下
#define PI (3.141526535f) //Pi的数学的定义
#define DIV_1_2 (0.5f) //1除2
#define Sqrt_3_div_2 (0.86602f) //根号3除2
#define Vdc (5.0f) //假设的VBus,这个根据自己的代码修改
#define Ts (2400 * 2) //周期,PWM计数器
#define SVPWM_K (1.732f * Ts / Vdc) //这个是SVPWM的算法公式
//DQ轴坐标系结构体
struct DQ_Aix
{
float V_d; //d-q坐标系,d轴电压分量
float V_q; //d-q坐标系,q轴电压分量
float theta; //转子电气角度
};
//α-β轴坐标系结构体
struct AlphaBeta_Aix
{
float V_Alpha; //α-β坐标系,α轴电压分量
float V_Beta; //α-β坐标系,β轴电压分量
};
struct SVPWM_Control
{
uint8_t sector; //扇区
float X; //SVPWM算法中间变量
float Y; //SVPWM算法中间变量
float Z; //SVPWM算法中间变量
float T_First; //SVPWM算法中间变量
float T_Second; //SVPWM算法中间变量
float T0; //SVPWM算法中间变量
float Ta; //SVPWM算法中间变量
float Tb; //SVPWM算法中间变量
float Tc; //SVPWM算法中间变量
};
//三个占空比控制结构体
struct SVPWM_Time
{
float Ta; //A相占空比PWM寄存器的值
float Tb; //B相占空比PWM寄存器的值
float Tc; //C相占空比PWM寄存器的值
};
1、Uα和Uβ获取,使用逆帕克变换
//逆克拉克变换
void AntiParkTransfer(struct DQ_Aix dq ,struct AlphaBeta_Aix* ptr)
{
ptr->V_Alpha = dq.V_d * cosf(dq.theta) - dq.V_q * sinf(dq.theta);
ptr->V_Beta = dq.V_d * sinf(dq.theta) + dq.V_q * cosf(dq.theta);
}
//这里我偷懒用的是C语言的math库,实际使用可以根据自己的情况使用查表法后者是优化后的正余弦算法
2、根据Uα和Uβ得到当前的扇区
//当前扇区计算
void CalculateSector(struct AlphaBeta_Aix ab, struct SVPWM_Control* ptr)
{
int8_t A = 0;
int8_t B = 0;
int8_t C = 0;
int8_t N = 0;
float VA = ab.V_Beta;
float VB = ab.V_Alpha * Sqrt_3_div_2 - DIV_1_2 * ab.V_Beta;
float VC = 0 - ab.V_Alpha * Sqrt_3_div_2 - DIV_1_2 * ab.V_Beta;
ptr->X = SVPWM_K * ab.V_Beta;
ptr->Y = SVPWM_K * (ab.V_Alpha * Sqrt_3_div_2 + DIV_1_2 * ab.V_Beta);
ptr->Z = SVPWM_K * (0 - ab.V_Alpha * Sqrt_3_div_2 + DIV_1_2 * ab.V_Beta);
if(VA > 0)
{
A = 1;
}
else
{
A = 0;
}
if(VB > 0)
{
B = 1;
}
else
{
B = 0;
}
if(VC > 0)
{
C = 1;
}
else
{
C = 0;
}
N = 4*C + 2*B + A;
switch(N)
{
case 3:
ptr->sector = 1;
break;
case 1:
ptr->sector = 2;
break;
case 5:
ptr->sector = 3;
break;
case 4:
ptr->sector = 4;
break;
case 6:
ptr->sector = 5;
break;
case 2:
ptr->sector = 6;
break;
}
}
3、计算占空比
//计算出占空比
void CalulateDutyCycle(struct SVPWM_Control* ctrl,struct SVPWM_Time* ptr)
{
switch(ctrl->sector)
{
case 2:
ctrl->T_First = ctrl->Z;
ctrl->T_Second = ctrl->Y;
break;
case 6:
ctrl->T_First = ctrl->Y;
ctrl->T_Second = 0 - ctrl->X;
break;
case 1:
ctrl->T_First = 0 - ctrl->Z;
ctrl->T_Second = ctrl->X;
break;
case 4:
ctrl->T_First = 0 - ctrl->X;
ctrl->T_Second = ctrl->Z;
break;
case 3:
ctrl->T_First = ctrl->X;
ctrl->T_Second = 0 - ctrl->Y;
break;
case 5:
ctrl->T_First = 0 - ctrl->Y;
ctrl->T_Second = 0 - ctrl->Z;
break;
}
if((ctrl->T_First + ctrl->T_Second) > Ts)
{
ctrl->T_First = ctrl->T_First * Ts / (ctrl->T_First + ctrl->T_Second);
ctrl->T_Second = ctrl->T_Second * Ts / (ctrl->T_First + ctrl->T_Second);
}
ctrl->T0 = (Ts - ctrl->T_First - ctrl->T_Second) / 2;
ctrl->Ta = ctrl->T0 / 2;
ctrl->Tb = ctrl->Ta + ctrl->T_First / 2;
ctrl->Tc = ctrl->Tb + ctrl->T_Second / 2;
switch(ctrl->sector)
{
case 2:
ptr->Ta = ctrl->Tb;
ptr->Tb = ctrl->Ta;
ptr->Tc = ctrl->Tc;
break;
case 6:
ptr->Ta = ctrl->Ta;
ptr->Tb = ctrl->Tc;
ptr->Tc = ctrl->Tb;
break;
case 1:
ptr->Ta = ctrl->Ta;
ptr->Tb = ctrl->Tb;
ptr->Tc = ctrl->Tc;
break;
case 4:
ptr->Ta = ctrl->Tc;
ptr->Tb = ctrl->Tb;
ptr->Tc = ctrl->Ta;
break;
case 3:
ptr->Ta = ctrl->Tc;
ptr->Tb = ctrl->Ta;
ptr->Tc = ctrl->Tb;
break;
case 5:
ptr->Ta = ctrl->Tb;
ptr->Tb = ctrl->Tc;
ptr->Tc = ctrl->Ta;
break;
}
}
4、测试代码
static void TestSVPMW(void)
{
AntiParkTransfer(dq_Aix ,&ab_Aix);
CalculateSector(ab_Aix ,&Svpwm_Ctrl);
CalulateDutyCycle(&Svpwm_Ctrl ,&PhaseTime);
//模拟角度变化
if(Tick >= 1.00f)
{
Tick = 0.001f;
}
else
{
Tick += 0.001f;
}
dq_Aix.theta = 2 * PI * Tick;
}
5、测试结果
逆帕克变换测试,上面红色的是角度,下面的是Uα和Uβ
SVPWM波形测试
最上面的是角度,中间的是扇区计算,最下面的是三相占空比,可以看出来是标准的马鞍波