FOC笔记整理(变换+SVPWM+PID+磁链圆限制)+数学推导及算法实现

28 篇文章 6 订阅
27 篇文章 3 订阅

文中有部分公式了图片转换失败了,原文是用语雀文档写的,需要的自行下载PDF下载链接或者浏览原文链接:
pdf链接: FOC笔记整理
语雀文档链接: FOC笔记整理

以下是文中一页的截图:
在这里插入图片描述

以下是FOC控制框图:
image.png

1、Clark变换

所谓克拉克变换,实际上就是降维解耦的过程,把难以辨明和控制的三相相位差120°电机波形降维为两维矢量

1.1 数学推导

首先因为由基尔霍夫电流定律(KCL),在任一时刻,流入节点的电流之和等于流出节点的电流之和,也就是说
I a + I b + I c = 0 I_a+I_b+I_c=0 Ia+Ib+Ic=0

由上图三维变换为二维的变换公式:
{ I α = k ( I a − I b s i n 30 ° − I c s i n 30 ) I β = k ( I b s i n 60 ° − I c s i n 60 ° ) ⇒ { I α = k ( I a − I b 2 − I c 2 ) I β = k ( 3 2 I b − 3 2 I c ) \left \{ \begin{array}{c} I_\alpha=k(I_a-I_bsin30°-I_csin30) \\ I_\beta=k(I_bsin60°-I_csin60°) \end{array} \right. \Rightarrow \left \{ \begin{array}{c} I_\alpha=k(I_a-\frac{I_b}{2}-\frac{I_c}{2}) \\ I_\beta=k(\frac{\sqrt {3}}{2}I_b-\frac{\sqrt {3}}{2}I_c) \end{array} \right. {Iα=k(IaIbsin30°Icsin30)Iβ=k(Ibsin60°Icsin60°){Iα=k(Ia2Ib2Ic)Iβ=k(23 Ib23 Ic)
{ I α = k ( I a − I b 2 − I c 2 ) I β = k ( 3 2 I b − 3 2 I c ) I c = − I a − I b ⇒ { I α = k 3 2 I a I β = 3 k 2 ( I a + 2 I b ) \left \{ \begin{array}{c} I_\alpha=k(I_a-\frac{I_b}{2}-\frac{I_c}{2}) \\ I_\beta=k(\frac{\sqrt {3}}{2}I_b-\frac{\sqrt {3}}{2}I_c) \\ I_c=-I_a-I_b \end{array} \right. \Rightarrow \left \{ \begin{array}{c} I_\alpha=k\frac{3}{2} I_a\\ I_\beta=\frac{\sqrt {3}k}{2}(I_a+2I_b) \end{array} \right. Iα=k(Ia2Ib2Ic)Iβ=k(23 Ib23 Ic)Ic=IaIb{Iα=k23IaIβ=23 k(Ia+2Ib)

k k k是变换系数,有等幅值变换何恒功率变换
等幅值变换: k = 2 3 k=\frac{2}{3} k=32
恒功率变换: k = 2 3 k=\sqrt{\frac{2}{3}} k=32

1.2 等幅值变换系数

假设变换前 I a = 1 , I b = − 0.5 , I c = − 0.5 I_a=1,I_b=-0.5,I_c=-0.5 Ia=1,Ib=0.5,Ic=0.5
变换后 I α = k 3 2 I a = k 3 2 × 1 = k 3 2 I_\alpha=k\frac{3}{2} I_a=k\frac{3}{2}\times1=k\frac{3}{2} Iα=k23Ia=k23×1=k23
前后幅值相等于是 k 3 2 = 1 ⇒ k = 2 3 k\frac{3}{2}=1 \Rightarrow k=\frac{2}{3} k23=1k=32

1.3 恒功率变换系数

假设变换前 I a = 1 , I b = − 0.5 , I c = − 0.5 I_a=1,I_b=-0.5,I_c=-0.5 Ia=1,Ib=0.5,Ic=0.5
P = I a 2 R + I b 2 R + I c 2 R = 3 2 R , ( R 为相阻抗) P=I_a^2R+I_b^2R+I_c^2R=\frac{3}{2}R,(R为相阻抗) P=Ia2R+Ib2R+Ic2R=23R,(R为相阻抗)
变换后 I α = k 3 2 I a = k 3 2 × 1 = k 3 2 , I β = 3 k 2 ( I a + 2 I b ) = 0 I_\alpha=k\frac{3}{2} I_a=k\frac{3}{2}\times1=k\frac{3}{2},I_\beta=\frac{\sqrt {3}k}{2}(I_a+2I_b)=0 Iα=k23Ia=k23×1=k23Iβ=23 k(Ia+2Ib)=0
P = I α 2 R + I β 2 R = 9 4 k 2 R P=I_\alpha^2R+I_\beta^2R=\frac{9}{4}k^2R P=Iα2R+Iβ2R=49k2R
前后幅值相等于是 3 2 R = 9 4 k 2 R ⇒ k = 2 3 \frac{3}{2}R=\frac{9}{4}k^2R \Rightarrow k=\sqrt{\frac{2}{3}} 23R=49k2Rk=32
实际FOC设计中我们一般采用等幅值变换,因为在SVPWM设计中假如采用等功率变换,得到的矢量 U t U_t Ut为相电压峰值的1.5倍,将会超出空间矢量的圆圈,产生过调制。
于是最终变换公式为:
KaTeX parse error: \tag works only in display equations
上式消掉了 I c I_c Ic,这样在设计采样电路时就只需要采集A相和B相的电流就可以了,减少成本和复杂度。

1.4 程序实现

alphaBeta_t Clark(abc_t input)
{
  alphaBeta_t output;
	 
	output.i_alpha=input.ia;                          //I_alpha=Ia
	output.i_beta=(input.ia+2*input.ib)*0.577350269;  //I_beta=(Ia+2Ib)/sqrt(3)
	
	return output;
}

2、Park变换

在Clark变换中,对ABC三维坐标系进行降维处理,但 α − β \alpha-\beta αβ依然是非线性的,依然很难处理,Park变换就是对 α − β \alpha-\beta αβ进行线性化处理的过程。在Park变换过程中,把旋转的 d − q d-q dq轴作为参考坐标系,实际上对于转子来说, d − q d-q dq坐标系就是静止的坐标系,且 i d i_d id i q i_q iq幅值是固定值,于是 d − q d-q dq轴呈现出来的效果就是直线(线性化),如下图。角度 θ \theta θ就是转子当前旋转的电角度。

2.1 数学推导

faee9e28630b9b320364e508922d436.jpg
由上图可得:
KaTeX parse error: \tag works only in display equations

2.2 程序实现

dq_t Park(alphaBeta_t input,int16_t theta)
{
  dq_t output;
  Trig_Components	TempSinCos;
  float cos_da,sin_da;
  float I_alpha_ratio,I_beta_ratio;

  TempSinCos = Trig_Functions(theta);             //get sin_cos value
  cos_da = (float)(TempSinCos.hCos*DIV_Q15);      //sin_cos is 0-32768 or -32768-0,sin_cos/32768 is 0-1
  sin_da = (float)(TempSinCos.hSin*DIV_Q15);      //sin_cos is 0-32768 or -32768-0,sin_cos/32768 is 0-1

  output.id = input.i_alpha*cos_da +  input.i_beta*sin_da;
  output.iq = -input.i_alpha*sin_da +  input.i_beta*cos_da;

  return ( output );	
}

3、Park逆变换

Park逆变换其实就是将PI控制器调整后的 I d I_d Id I q I_q Iq d − q d-q dq轴转换到 α − β \alpha-\beta αβ轴。

3.1 数学推导

此为正方形矩阵它的逆矩阵则为:($$ ψ = θ \psi =\theta ψ=θ)

因此Park的逆变换公式如下:

3.2 程序实现

alphaBeta_t Rev_Park(dq_t input,int16_t theta)
{
  alphaBeta_t output;
  Trig_Components	TempSinCos;
  float cos_da,sin_da;
  float I_alpha_ratio,I_beta_ratio;

  TempSinCos = Trig_Functions(theta);             //get sin_cos value
  cos_da = (float)(TempSinCos.hCos*DIV_Q15);      //sin_cos is 0-32768 or -32768-0,sin_cos/32768 is 0-1
  sin_da = (float)(TempSinCos.hSin*DIV_Q15);      //sin_cos is 0-32768 or -32768-0,sin_cos/32768 is 0-1

  output.i_alpha = input.id*cos_da -  input.iq*sin_da;
  output.i_beta = input.id*sin_da +  input.iq*cos_da;

  return ( output );	
}

4、SVPWM技术

4.1 原理

** SVPWM 的理论基础是平均值等效原理**,即在一个开关周期内通过对基本电压矢量加以组合,使其平均值与给定电压矢量相等。在某个时刻,电压矢量旋转到 某个区域中,可由组成这个区域的两个相邻的非零矢量和零矢量在时间上的不同 组合来得到。两个矢量的作用时间在一个采样周期内分多次施加,从而控制各个 电压矢量的作用时间,使电压空间矢量接近按圆轨迹旋转,通过逆变器的不同开关状态所产生的实际磁通去逼近理想磁通圆,并由两者的比较结果来决定逆变器 的开关状态,从而形成 PWM 波形。
655260b57d0d613129d0dd9a854eac4.jpg

使用这6个空间电压矢量作为基向量来合成任意矢量。在每一个扇区,选择相邻两个电压矢量以及零矢量,按照伏秒平衡原则来合成每个扇区内的任意电压矢量,即:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OdoJR3ES-1687592100934)(null#clientId=u31621085-f95b-4&from=paste&id=u08c5fa00&originHeight=60&originWidth=483&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u7896028c-0ae2-4d23-ad16-2903d69d3d3&title=)]
离散化后等效为下式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-34d6yfMq-1687592100417)(null#clientId=u31621085-f95b-4&from=paste&id=u25a6ab92&originHeight=26&originWidth=336&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u036a431e-2fb7-4c86-a176-a2778d6945c&title=)]
式子中的 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMw6aQng-1687592101297)(null#clientId=u31621085-f95b-4&from=paste&id=udf58ac18&originHeight=26&originWidth=38&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=ud71e4f99-6086-48e6-81b4-d4aa1167575&title=)] 是我们期望得到的电压矢量,T是一个PWM周期。

由正弦定理可得:
KaTeX parse error: \tag works only in display equations
其中 ∣ U 4 ∣ = ∣ U 6 ∣ = 2 3 U d c |U_4|=|U_6|=\frac{2}{3}U_{dc} U4=U6=32Udc
整理可得在扇区I内, U 4 U_4 U4 U 6 U_6 U6以及零矢量所占时间:
KaTeX parse error: \tag works only in display equations
其中 m = 3 ∣ U r e f ∣ U d c m=\frac{\sqrt{3}|U_{ref}|}{U_{dc}} m=Udc3 Uref为SVPWM调制系数比(调制比)。在电流环控制过程中 m m m设置得越大代表了期望力矩越大。
当然 m m m并不是越大越好,需要使得合成矢量在线性区域内调制,则m满足以下条件:
KaTeX parse error: \tag works only in display equations
SPWM调制比为1,这就是SVPWM相比与SPWM对于直线母线电压利用率更高的原因,差不多高出 15.47 % 15.47\% 15.47%的效率。
在式(4)中得到了扇区I中任意合成矢量 U r e f U_{ref} Uref的作用时间 T 0 ( T 7 ) T_0(T_7) T0(T7) T 4 T_4 T4 T 6 T_6 T6,零矢量 T 0 ( T 7 ) T_0(T_7) T0(T7)的选择最具灵活性,分配的顺序也具有一定的灵活性,理论上一个周期内切换的顺序可以是任意的,只要满足条件即可,但是需要最大限度地减少开关损耗。
基本矢量作用顺序的分配原则:每次开关状态转换时,只改变其中一相的开关状态,并且对零矢量的时间进行平均分配,使产生的PWM对称,从而有效地降低PWM的谐波分量。
以下是各个扇区的 U r e f U_{ref} Uref开关顺序如下表:
image.png

4.2 算法实现

4.2.1 参考电压矢量的扇区判断

image.png
U α U_\alpha Uα U β U_\beta Uβ表示参考电压矢量 U r e f U_{ref} Uref α \alpha α β \beta β轴上的分量,定义 U r e f 1 U_{ref1} Uref1 U r e f 2 U_{ref2} Uref2 U r e f 3 U_{ref3} Uref3三个变量,于是有:
KaTeX parse error: \tag works only in display equations
如果 U r e f 1 > 0 U_{ref1}>0 Uref1>0,则变量A=1,否则A=0;
如果 U r e f 2 > 0 U_{ref2}>0 Uref2>0,则变量B=1,否则B=0;
如果 U r e f 3 > 0 U_{ref3}>0 Uref3>0,则变量C=1,否则C=0;
N = A × 2 0 + B × 2 1 + C × 2 2 = A + 2 B + 4 C N=A\times2^0+B\times2^1+C\times2^2=A+2B+4C N=A×20+B×21+C×22=A+2B+4C,则可以得到N与扇区的关系如下表所示:

N315462
扇区IIIIIIIVVVI

4.2.2 非零矢量和零矢量作用时间计算

根据式(4)可在扇区I时:
{ T 6 = m T s s i n θ T 4 = m T s s i n ( π 3 − θ ) ⇒ { T 6 = 3 T s U d c U r e f s i n θ T 4 = 3 T s U d c U r e f s i n ( π 3 − θ ) ⇒ \left \{ \begin{array}{c} T_6=mT_ssin\theta \\ T_4=mT_ssin(\frac{\pi}{3}-\theta) \end{array} \right. \Rightarrow \left \{ \begin{array}{c} T_6=\frac{\sqrt{3}T_s}{U_{dc}}U_{ref}sin\theta \\ T_4=\frac{\sqrt{3}T_s}{U_{dc}}U_{ref}sin(\frac{\pi}{3}-\theta) \end{array} \right. \Rightarrow {T6=mTssinθT4=mTssin(3πθ){T6=Udc3 TsUrefsinθT4=Udc3 TsUrefsin(3πθ)
{ T 6 = 3 T s U d c U r e f s i n θ T 4 = 3 T s U d c U r e f ( s i n π 3 c o s θ − c o s π 3 s i n θ ) ⇒ { T 6 = 3 T s U d c U r e f s i n θ T 4 = 3 T s U d c ( 3 2 U r e f c o s θ − 1 2 U r e f s i n θ ) U α = U r e f c o s θ , U β = U r e f s i n θ ⇒ \left \{ \begin{array}{c} T_6=\frac{\sqrt{3}T_s}{U_{dc}}U_{ref}sin\theta \\ T_4=\frac{\sqrt{3}T_s}{U_{dc}}U_{ref}(sin\frac{\pi}{3}cos \theta- cos\frac{\pi}{3}sin \theta) \end{array} \right. \Rightarrow \left \{ \begin{array}{c} T_6=\frac{\sqrt{3}T_s}{U_{dc}}U_{ref}sin\theta \\ T_4=\frac{\sqrt{3}T_s}{U_{dc}}(\frac{\sqrt{3}}{2}U_{ref}cos \theta- \frac{1}{2}U_{ref}sin \theta) \\ U_\alpha=U_{ref}cos \theta, U_\beta=U_{ref}sin \theta\end{array} \right. \Rightarrow {T6=Udc3 TsUrefsinθT4=Udc3 TsUref(sin3πcosθcos3πsinθ) T6=Udc3 TsUrefsinθT4=Udc3 Ts(23 Urefcosθ21Urefsinθ)Uα=Urefcosθ,Uβ=Urefsinθ
KaTeX parse error: \tag works only in display equations
同理可得在扇区II时:
KaTeX parse error: \tag works only in display equations
同理可得在扇区III时:
KaTeX parse error: \tag works only in display equations
同理可得在扇区IV时:
KaTeX parse error: \tag works only in display equations
同理可得在扇区V时:
KaTeX parse error: \tag works only in display equations
同理可得在扇区VI时:
KaTeX parse error: \tag works only in display equations
由上面计算的结果,可以总结出以下表格:
image.png

4.2.3 过调制处理

在SVPWM的扇区I中,非零矢量 T 4 T_4 T4 T 6 T_6 T6和零矢量的作用时间总和应为 T s T_s Ts,以保持一个电机周期的完整性。然而,有时候 T 4 + T 6 > T s T_4+T_6>T_s T4+T6>Ts,这可能会导致电机控制出现问题,例如电流畸变或转速不稳定。
为了解决这个问题,需要进行过调制处理,将 T 4 T_4 T4 T 6 T_6 T6进行缩小,以保持电机周期的完整性,并确保控制信号的正确性,也可以保持SVPWM算法的准确性和稳定性,避免电机控制出现问题。
在扇区I中,如果 T 4 + T 6 > T s T_4+T_6>T_s T4+T6>Ts则过调制处理过程为:
KaTeX parse error: \tag works only in display equations
其他扇区类似处理即可。

4.2.4 扇区矢量切换点的确定

在扇区I中七段式SVPWM的顺序是0-4-6-7-7-6-4-0,对称结构,由式(7)可得到非零矢量作用时间 T 4 T_4 T4 T 6 T_6 T6和零矢量作用时间 T 0 T_0 T0 T 7 T_7 T7
image.png
且由上图可以得到扇区I三相桥臂的切换时间
SA桥臂切换时间: T a = 1 2 T 0 = 1 4 ( T s − T 4 − T 6 ) T_a=\frac{1}{2}T_0=\frac{1}{4}(T_s-T_4-T_6) Ta=21T0=41(TsT4T6)
SB桥臂切换时间: T b = 1 2 T 0 + 1 2 T 4 = 1 4 ( T s + T 4 − T 6 ) T_b=\frac{1}{2}T_0+\frac{1}{2}T_4=\frac{1}{4}(T_s+T_4-T_6) Tb=21T0+21T4=41(Ts+T4T6)
SC桥臂切换时间: T c = 1 2 T 0 + 1 2 T 4 + 1 2 T 6 = 1 4 ( T s + T 4 + T 6 ) T_c=\frac{1}{2}T_0+\frac{1}{2}T_4+\frac{1}{2}T_6=\frac{1}{4}(T_s+T_4+T_6) Tc=21T0+21T4+21T6=41(Ts+T4+T6)
同理可得扇区II三相桥臂的切换时间
SA桥臂切换时间: T a = 1 2 T 0 + 1 2 T 2 = 1 4 ( T s + T 2 − T 6 ) T_a=\frac{1}{2}T_0+\frac{1}{2}T_2=\frac{1}{4}(T_s+T_2-T_6) Ta=21T0+21T2=41(Ts+T2T6)
SB桥臂切换时间: T b = 1 2 T 0 = 1 4 ( T s − T 2 − T 6 ) T_b=\frac{1}{2}T_0=\frac{1}{4}(T_s-T_2-T_6) Tb=21T0=41(TsT2T6)
SC桥臂切换时间: T c = 1 2 T 0 + 1 2 T 2 + 1 2 T 6 = 1 4 ( T s + T 2 + T 6 ) T_c=\frac{1}{2}T_0+\frac{1}{2}T_2+\frac{1}{2}T_6=\frac{1}{4}(T_s+T_2+T_6) Tc=21T0+21T2+21T6=41(Ts+T2+T6)
同理可得扇区III三相桥臂的切换时间
SA桥臂切换时间: T a = 1 2 T 0 + 1 2 T 2 + 1 2 T 3 = 1 4 ( T s + T 2 + T 3 ) T_a=\frac{1}{2}T_0+\frac{1}{2}T_2+\frac{1}{2}T_3=\frac{1}{4}(T_s+T_2+T_3) Ta=21T0+21T2+21T3=41(Ts+T2+T3)
SB桥臂切换时间: T b = 1 2 T 0 = 1 4 ( T s − T 2 − T 3 ) T_b=\frac{1}{2}T_0=\frac{1}{4}(T_s-T_2-T_3) Tb=21T0=41(TsT2T3)
SC桥臂切换时间: T c = 1 2 T 0 + 1 2 T 2 = 1 4 ( T s + T 2 − T 3 ) T_c=\frac{1}{2}T_0+\frac{1}{2}T_2=\frac{1}{4}(T_s+T_2-T_3) Tc=21T0+21T2=41(Ts+T2T3)
同理可得扇区IV三相桥臂的切换时间
SA桥臂切换时间: T a = 1 2 T 0 + 1 2 T 1 + 1 2 T 3 = 1 4 ( T s + T 1 + T 3 ) T_a=\frac{1}{2}T_0+\frac{1}{2}T_1+\frac{1}{2}T_3=\frac{1}{4}(T_s+T_1+T_3) Ta=21T0+21T1+21T3=41(Ts+T1+T3)
SB桥臂切换时间: T b = 1 2 T 0 + 1 2 T 1 = 1 4 ( T s + T 1 − T 3 ) T_b=\frac{1}{2}T_0+\frac{1}{2}T_1=\frac{1}{4}(T_s+T_1-T_3) Tb=21T0+21T1=41(Ts+T1T3)
SC桥臂切换时间: T c = 1 2 T 0 = 1 4 ( T s − T 1 − T 3 ) T_c=\frac{1}{2}T_0=\frac{1}{4}(T_s-T_1-T_3) Tc=21T0=41(TsT1T3)
同理可得扇区V三相桥臂的切换时间
SA桥臂切换时间: T a = 1 2 T 0 + 1 2 T 1 = 1 4 ( T s + T 1 − T 5 ) T_a=\frac{1}{2}T_0+\frac{1}{2}T_1=\frac{1}{4}(T_s+T_1-T_5) Ta=21T0+21T1=41(Ts+T1T5)
SB桥臂切换时间: T b = 1 2 T 0 + 1 2 T 1 + 1 2 T 5 = 1 4 ( T s + T 1 + T 5 ) T_b=\frac{1}{2}T_0+\frac{1}{2}T_1+\frac{1}{2}T_5=\frac{1}{4}(T_s+T_1+T_5) Tb=21T0+21T1+21T5=41(Ts+T1+T5)
SC桥臂切换时间: T c = 1 2 T 0 = 1 4 ( T s − T 1 − T 5 ) T_c=\frac{1}{2}T_0=\frac{1}{4}(T_s-T_1-T_5) Tc=21T0=41(TsT1T5)
同理可得扇区VI三相桥臂的切换时间
SA桥臂切换时间: T a = 1 2 T 0 = 1 4 ( T s − T 4 − T 5 ) T_a=\frac{1}{2}T_0=\frac{1}{4}(T_s-T_4-T_5) Ta=21T0=41(TsT4T5)
SB桥臂切换时间: T b = 1 2 T 0 + 1 2 T 1 + 1 2 T 5 = 1 4 ( T s + T 4 + T 5 ) T_b=\frac{1}{2}T_0+\frac{1}{2}T_1+\frac{1}{2}T_5=\frac{1}{4}(T_s+T_4+T_5) Tb=21T0+21T1+21T5=41(Ts+T4+T5)
SC桥臂切换时间: T c = 1 2 T 0 + 1 2 T 4 = 1 4 ( T s + T 4 − T 5 ) T_c=\frac{1}{2}T_0+\frac{1}{2}T_4=\frac{1}{4}(T_s+T_4-T_5) Tc=21T0+21T4=41(Ts+T4T5)
汇总可得各个扇区的切换时刻表如下所示:
image.png
除了用切换时刻还可以使用开关切换的总时间即占空比来处理,两种方法在本质上差不多。
得到扇区I三相桥臂的占空比
SA桥臂占空比: T a = ( T 4 + T 6 + T 7 ) / T s T_a=(T_4+T_6+T_7)/T_s Ta=(T4+T6+T7)/Ts
SB桥臂占空比: T b = ( T 6 + T 7 ) / T s T_b=(T_6+T_7)/Ts Tb=(T6+T7)/Ts
SC桥臂占空比: T c = T 7 / T s T_c=T_7/T_s Tc=T7/Ts
同理可得扇区II三相桥臂的占空比
SA桥臂占空比: T a = ( T 6 + T 7 ) / T s T_a=(T_6+T_7)/T_s Ta=(T6+T7)/Ts
SB桥臂占空比: T b = ( T 2 + T 6 + T 7 ) / T s T_b=(T_2+T_6+T_7)/Ts Tb=(T2+T6+T7)/Ts
SC桥臂占空比: T c = T 7 / T s T_c=T_7/T_s Tc=T7/Ts
同理可得扇区III三相桥臂的占空比
SA桥臂占空比: T a = T 7 / T s T_a=T_7/T_s Ta=T7/Ts
SB桥臂占空比: T b = ( T 2 + T 3 + T 7 ) / T s T_b=(T_2+T_3+T_7)/Ts Tb=(T2+T3+T7)/Ts
SC桥臂占空比: T c = ( T 3 + T 7 ) / T s T_c=(T_3+T_7)/T_s Tc=(T3+T7)/Ts
同理可得扇区IV三相桥臂的占空比
SA桥臂占空比: T a = T 7 / T s T_a=T_7/T_s Ta=T7/Ts
SB桥臂占空比: T b = ( T 3 + T 7 ) / T s T_b=(T_3+T_7)/Ts Tb=(T3+T7)/Ts
SC桥臂占空比: T c = ( T 1 + T 3 + T 7 ) / T s T_c=(T_1+T_3+T_7)/T_s Tc=(T1+T3+T7)/Ts
同理可得扇区V三相桥臂的占空比
SA桥臂占空比: T a = ( T 5 + T 7 ) / T s T_a=(T_5+T_7)/T_s Ta=(T5+T7)/Ts
SB桥臂占空比: T b = T 7 / T s T_b=T_7/Ts Tb=T7/Ts
SC桥臂占空比: T c = ( T 1 + T 5 + T 7 ) / T s T_c=(T_1+T_5+T_7)/T_s Tc=(T1+T5+T7)/Ts
同理可得扇区VI三相桥臂的占空比
SA桥臂占空比: T a = ( T 4 + T 5 + T 7 ) / T s T_a=(T_4+T_5+T_7)/T_s Ta=(T4+T5+T7)/Ts
SB桥臂占空比: T b = T 7 / T s T_b=T_7/Ts Tb=T7/Ts
SC桥臂占空比: T c = ( T 5 + T 7 ) / T s T_c=(T_5+T_7)/T_s Tc=(T5+T7)/Ts

4.2.5 程序实现

首先实现基础的FOC算法:

#include <iostream>
#include "foc.h"
#include <fstream>
#include <cstdint>

int main()
{ 
    foc_val_t foc_val;
    std::ofstream fout("test_foc.csv");
    std::cout << "This is Foc test!";
    for (double theta = 0; theta < 10; theta+=0.01)//开环
        {
            foc_val.Udq.q = 0;
            foc_val.Udq.d = 0.01;

            alphaBeta_t alphaBeta= Rev_Park(foc_val.Udq, theta);
            tabc_t tabc=svpwm(alphaBeta, theta);
            double u_a = tabc.ta - 0.5 * (tabc.tb + tabc.tc);
            double u_b = tabc.tb - 0.5 * (tabc.ta + tabc.tc);
            double u_c =  - (tabc.ta + tabc.tb);
            fout << tabc.ta << ',' << tabc.tb << ',' << tabc.tc << '\n';
            std::cout << tabc.ta << ',' << tabc.tb << ',' << tabc.tc << '\n';
        }
}
#pragma once
#include <iostream>

typedef struct
{
    int16_t hCos;
    int16_t hSin;
} Trig_Components;

/* Macros ---------------------------------------------------------------- */
typedef struct
{
    float ia;
    float ib;
    float ic;
}abc_t;

typedef struct
{
    float ta;
    float tb;
    float tc;
}tabc_t;

typedef struct
{
    float d;
    float d_filter;
    float q;
    float q_filter;
}dq_t;

typedef struct
{
    float alpha;
    float beta;
}alphaBeta_t;


typedef struct
{
    abc_t Iab;
    alphaBeta_t I_alphaBeta;
    dq_t  Idq_ref;
    dq_t  Idq;
    tabc_t tabc;
    alphaBeta_t U_alphaBeta;
    dq_t  Udq;
}foc_val_t;

extern foc_val_t foc_val;


Trig_Components Trig_Functions(int16_t hAngle);
alphaBeta_t Clark(abc_t input);
dq_t Park(alphaBeta_t input, int16_t theta);
//alphaBeta_t Rev_Park(dq_t input, int16_t theta);
//tabc_t svpwm(alphaBeta_t input, int16_t theta);
alphaBeta_t Rev_Park(dq_t input, float theta);
tabc_t svpwm(alphaBeta_t input, float theta);
#include "foc.h"
#include "cmath"

/**********************************************function************************************************************************/
alphaBeta_t Rev_Park(dq_t input, float theta)
{
    alphaBeta_t output;
    Trig_Components	TempSinCos;
    float cos_da, sin_da;
    float I_alpha_ratio, I_beta_ratio;

    //TempSinCos = Trig_Functions(theta);             //get sin_cos value
    //cos_da = (float)(TempSinCos.hCos * DIV_Q15);      //sin_cos is 0-32768 or -32768-0,sin_cos/32768 is 0-1
    //sin_da = (float)(TempSinCos.hSin * DIV_Q15);      //sin_cos is 0-32768 or -32768-0,sin_cos/32768 is 0-1

    //output.alpha = input.d * cos_da - input.q * sin_da;
    //output.beta = input.d * sin_da + input.q * cos_da;
    output.alpha = input.d * cos(theta) - input.q * sin(theta);
    output.beta = input.d * sin(theta) + input.q * cos(theta);

    return (output);
}

tabc_t svpwm(alphaBeta_t input, float theta)
{
    float u1 = 0, u2 = 0, u3 = 0;
    float ts = 1;
    float k = 1.732 * ts / 1;  //Ts=1,Uds=1
    tabc_t output;
    float t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t0 = 0, t7 = 0;

    u1 = input.beta;
    u2 = input.alpha * 0.866 - 0.5 * input.beta;    //sqrt(3)*U_alpha/2-U_beta/2
    u3 = - input.alpha * 0.866 - 0.5 * input.beta;  //-sqrt(3)*U_alpha/2-U_beta/2

    uint8_t index = (u1 > 0) + (u2 > 0 ) * 2 + (u3 > 0 ) * 4;

    switch (index)
    { 
        case 3://sector 1
            t4 = k * u2;
            t6 = k * u1;
            if (fabs(t4 + t6) > ts)
            {
                t4 = t4 * ts / (t4 + t6);
                t6 = t6 * ts / (t4 + t6);
            }
            t7 = 0.5 * (ts - t4 - t6);
            output.ta = t4 + t6 + t7;
            output.tb = t6 + t7;
            output.tc = t7;
           break;

        case 1://sector 2
            t2 = - k * u2;
            t6 = - k * u3;
            if (fabs(t2 + t6) > ts)
            {
                t2 = t2 * ts / (t2 + t6);
                t6 = t6 * ts / (t2 + t6);
            }
            t7 = 0.5 * (ts - t2 - t6);
            output.ta = t6 + t7;
            output.tb = t2 + t6 + t7;
            output.tc = t7;
           break;

        case 5://sector 3
            t2 = k * u1;
            t3 = k * u3;
            if (fabs(t2 + t3) > ts)
            {
                t2 = t2 * ts / (t2 + t3);
                t3 = t3 * ts / (t2 + t3);
            }
            t7 = 0.5 * (ts - t2 - t3);
            output.ta = t7;
            output.tb = t2 + t3 + t7;
            output.tc = t3 + t7;
            break;

        case 4://sector 4
            t3 = - k * u2;
            t1 = - k * u1;
            if (fabs(t1 + t3) > ts)
            {
                t3 = t3 * ts / (t1 + t3);
                t1 = t1 * ts / (t1 + t3);
            }
            t7 = 0.5 * (ts - t1 - t3);
            output.ta = t7;
            output.tb = t3 + t7;
            output.tc = t1 + t3 + t7;
            break;


        case 6://sector 5
            t1 = k * u3;
            t5 = k * u2;
            if (fabs(t1 + t5) > ts)
            {
                t1 = t1 * ts / (t1 + t5);
                t5 = t5 * ts / (t1 + t5);
            }
            t7 = 0.5 * (ts - t1 - t5);
            output.ta = t5 + t7;
            output.tb = t7;
            output.tc = t1 + t5 + t7;
            break;

        case 2://sector 6
            t4 = - k * u3;
            t5 = - k * u1;
            if (fabs(t4 + t5) > ts)
            {
                t4 = t4 * ts / (t4 + t5);
                t5 = t5 * ts / (t4 + t5);
            }
            t7 = 0.5 * (ts - t4 - t5);
            output.ta = t4 + t5 + t7;
            output.tb = t7;
            output.tc = t5 + t7;
            break;

        default:
            output.ta = 0.5;
            output.tb = 0.5;
            output.tc = 0.5;
            break;
    }
    return output;
}

分析数据验证:
image.png
由结果可以得到svpwm产生了三相相位差120°的马鞍波电压,说明算法是正确的。
到这里只是说明svpwm算法是正确的,但在单片机里如何处理呢,单片机的计算能力和存储能力都不及强大的CPU,在单片机里处理算法有很多问题需要考虑,要考虑计算量,计算时间以及处理的方便性。
其实处理也简单,归一化处理,例如将正余弦0-1范围的小数,折算到0-32768的数值计算,避免了浮点数的计算。

5、PID控制

PID控制器(比例-积分-微分控制器),由比例单元(Proportional)、积分单元(Integral)和微分单元(Derivative)组成。可以透过调整这三个单元的增益来调定其特性。PID控制器主要适用于基本上线性,且动态特性不随时间变化的系统。
{ U ( x ) = k p ( e r r ( t ) + 1 T I ∫ e r r ( t ) d t + T D d e r r ( t ) d t ) = K p e r r ( t ) + K i ∫ e r r ( t ) d t + K d d e r r ( t ) d t e r r ( t ) = r i n t ( t ) − r o u t ( t ) \left \{ \begin{array}{c} U(x)=k_p(err(t)+\frac{1}{T_I}\int err(t)dt+\frac{T_Dderr(t)}{dt})=K_perr(t)+K_i\int err(t)dt+K_d\frac{derr(t)}{dt} \\ err(t)=rint(t)-rout(t) \end{array} \right. {U(x)=kp(err(t)+TI1err(t)dt+dtTDderr(t))=Kperr(t)+Kierr(t)dt+Kddtderr(t)err(t)=rint(t)rout(t)
从信号变换的角度而言,超前校正、滞后校正、滞后-超前校正可以总结为比例、积分、微分三种运算及其组合。

  • 比例控制可快速、及时、按比例调节偏差,提高控制灵敏度,但有静差,控制精度低。

  • 积分控制能消除偏差,提高控制精度、改善稳态性能,但易引起震荡,造成超调。

  • 微分控制是一种超前控制,能调节系统速度、减小超调量、提高稳定性,但其时间常数过大会引入干扰、系统冲击大,过小则调节周期长、效果不显著。

    比例、积分、微分控制相互配合,合理选择PID调节器的参数,即比例系数KP、积分时间常数τi和微分时间常数τD,可迅速、准确、平稳的消除偏差,达到良好的控制效果。
    上面的公式是连续的,在计算机及单片机中处理时需要离散化处理。

5.1 离散化处理

假设采样间隔为 T T T,则在第 k k k T T T时刻:
偏差 e r r ( k ) = r i n ( k ) − r o u t ( k ) err(k)=rin(k)-rout(k) err(k)=rin(k)rout(k);
积分环节用加和的形式表示,即 e r r ( k ) + e r r ( k + 1 ) + … … err(k)+err(k+1)+…… err(k)+err(k+1)+……;
微分环节用斜率的形式表示,即 [ e r r ( k ) − e r r ( k − 1 ) ] / T [err(k)-err(k-1)]/T [err(k)err(k1)]/T;
从而形成如下PID离散表示形式:
KaTeX parse error: {equation} can be used only in display mode.
PID有位置式和增量式两种,上式就是位置式,增量式计算如下,在第 k − 1 k-1 k1 T T T时刻:
KaTeX parse error: \tag works only in display equations
式(1)减去式(2) U ( k ) − U ( k − 1 ) U(k)-U(k-1) U(k)U(k1)且有:
KaTeX parse error: \tag works only in display equations
那么增量式PID就有:
KaTeX parse error: \tag works only in display equations

5.2 位置式PID和增量式PID控制算法的区别

位置式 PID 和增量式 PID 控制算法的区别:
(1)位置式PID控制的输出与整个过去的状态有关,用到了误差的累加值;而增量式PID的输出只与当前拍和前两拍的误差有关,因此位置式PID控制的累积误差相对更大;
(2)增量式PID控制输出的是控制量增量,并无积分作用,因此该方法适用于执行机构带积分部件的对象,如步进电机等,而位置式PID适用于执行机构不带积分部件的对象,如电液伺服阀。
(3)由于增量式PID输出的是控制量增量,如果计算机出现故障,误动作影响较小,而执行机构本身有记忆功能,可仍保持原位,不会严重影响系统的工作,而位置式的输出直接对应对象的输出,因此对系统影响较大。

5.3 程序实现

在这里我们不使用微分,主要是因为微分如果参数调的不好的话,在有噪声的系统中会放大噪声,因此在这里只使用比例和积分调节。
以位置式PID为例:

#pragma once
#include <iostream>

typedef struct
{
	float rin;
	float rout;
	float err;
	float kp;
	float ki;
	float kd;
	float interal;
}pid_t;

extern pid_t pid;

float PID_Calc(pid_t input, float err);
#include "pid.h"
#include "cmath"

float PID_Calc(pid_t input,float err)
{
	float Proportional_temp = 0, Integral_temp = 0, Integral_sum_temp = 0;

	Proportional_temp = input.kp * err;
	Integral_temp = input.ki * err;
	Integral_sum_temp = input.interal+ Integral_temp;
	input.rout = Integral_sum_temp + Proportional_temp;

	return input.rout;
}

5.4 参数整定

工程整定方法(工程经验调参):
PID控制器参数的工程整定方法,主要有临界比例法、反应曲线法和衰减法。三种方法各有其特点,其共同点都是通过试验,然后按照工程经验公式对控制器参数进行整定。但无论采用哪一种方法所得到的控制器参数,都需要在实际运行中进行最后调整与完善。现在一般采用的是临界比例法。
PID调试一般原则

  • 在输出不振荡时,增大比例增益P
  • 在输出不振荡时,减小积分时间常数Ti
  • 在输出不振荡时,增大微分时间常数Td

1)确定比例增益P
确定比例增益P 时,首先去掉PID的积分项和微分项,一般是令Ti=0、Td=0(具体见PID的参数设定说明),使PID为纯比例调节。输入设定为系统允许的最大值的60%70%,由0逐渐加大比例增益P,直至系统出现振荡;再反过来,从此时的比例增益P逐渐减小,直至系统振荡消失,记录此时的比例增益P,设定PID的比例增益P为当前值的**60%70%**。比例增益P调试完成。

2)确定积分时间常数Ti
比例增益P确定后,设定一个较大的积分时间常数Ti的初值,然后逐渐减小Ti,直至系统出现振荡,之后在反过来,逐渐加大Ti,直至系统振荡消失。记录此时的Ti,设定PID的积分时间常数Ti为当前值的150%~180%。积分时间常数Ti调试完成。
3)确定微分时间常数Td
微分时间常数Td一般不用设定,为0即可。若要设定,与确定P和Ti的方法相同,取不振荡时的30%。
4)系统空载、带载联调,再对PID参数进行微调,直至满足要求。
变速积分的基本思想是,设法改变积分项的累加速度,使其与偏差大小相对应:偏差越大,积分越慢;反之则越快,有利于提高系统品质。

5.5 参数自整定算法

预留

6、磁链圆限制

磁链圆限制(flux circle limitation)是一种保护电机和控制系统的技术。它的目的是限制电机的磁链(flux)在一定的范围内,避免电机工作在过饱和或过磁饱和状态下。
磁链圆限制是为了防止以下情况发生:

  1. 磁链过饱和(Magnetic Flux Saturation):当电机磁链超过一定的限制值时,磁链的增长速率会变得非常缓慢,这会导致电机性能下降,并可能产生不可预测的响应。过饱和可能会引起电机振动、噪声和损坏,还可能导致电机控制系统的不稳定。
  2. 磁链过磁饱和(Magnetic Flux Demagnetization):过磁饱和是指磁链降低到不可接受的水平,导致电机无法提供预期的输出扭矩。过磁饱和可能会导致电机失去控制、失去动力或停机。

通过限制磁链在合适的范围内,磁链圆限制可以确保电机在正常工作范围内运行,提供稳定的性能和可靠性。它通常通过对电机控制系统中的电流、电压或磁场进行限制来实现。

6.1 确定MAX_MODULE值

image.png
假设磁链圆的最大矢量值为1,但因FOC控制中需要设定PWM死区时间,以及PWM载波频率对AD采样时间的影响,使PWM占空比值最大不能达到100%,需要留出时间给ADC采样电机电流,所以矢量的最大值不能达到1。
因FOC中使用Q15格式处理小数,所以设定矢量为1时(PWM最大占空比100%) MAX_MODULE = 32767;若PWM占空比最大为97%,则MAX_MODULE = 0.97 × 32767;

6.2 磁链限制原理

磁链限制函数在FOC流程框架图中,在PID控制器之后,而PID控制器是单独对 V d V_d Vd V q V_q Vq进行PID控制的,所以为了使 V d V_d Vd V q V_q Vq合成的电压矢量小于等于单位圆的边,即 V d ² + V q ² ≤ M A X _ M O D U L E ² V_d^² + V_q^² ≤ MAX\_MODULE^² Vd²+Vq²MAX_MODULE²
其原理公式如下,若 V d V_d Vd V q V_q Vq合成的矢量大于圆的最大矢量,则将其乘以一个缩放倍数 i 2 i² i2,使其等于圆的最大矢量, i i i就是需要缩放的系数。
( V d ² + V q ² ) × i ² = M A X _ M O D U L E ² ⇒ i = M A X _ M O D U L E ² / ( V d ² + V q ² ) (V_d^² + V_q^² ) × i^² = MAX\_MODULE^² \Rightarrow i = \sqrt{ MAX\_MODULE^² / (V_d^² + V_q^²) } (Vd²+Vq²)×i²=MAX_MODULE²i=MAX_MODULE²/Vd²+Vq²)
STM32在计算开根号上比较费时,所以ST电机库中引入查表去查开根号值。

6.3 确定 START_INDEX值

V d V_d Vd V q V_q Vq都是int_16类型,其最大值为S16_MAX (32767)
所以需要限制的范围满足 V d ² + V q ² ≤ M A X _ M O D U L E ² V_d^² + V_q^² ≤ MAX\_MODULE^² Vd²+Vq²MAX_MODULE² 也就是 ( M A X _ M O D U L E ² + 1 ) ~ ( 2 × S 16 _ M A X ² ) ( MAX\_MODULE^² + 1 )~ ( 2 × S16\_MAX^² ) (MAX_MODULE²+1)(2×S16_MAX²)
即需要制表的值 i = M A X _ M O D U L E ² / ( M A X _ M O D U L E ² + 1 ) ~ M A X _ M O D U L E ² / ( 2 × S 16 _ M A X ² ) i = \sqrt{ MAX\_MODULE^² / ( MAX\_MODULE^² + 1) ~ MAX\_MODULE^² / ( 2 × S16\_MAX^² ) } i=MAX_MODULE²/(MAX_MODULE²+1)MAX_MODULE²/(2×S16_MAX²) ;
i ⊆ ( 0 , 1 ) i \subseteq(0,1) i(0,1) 这段内容则不需要进行制表,所以需要定制一个制表的开头位置。
START_INDEX 是为了求出在全段范围内,不需要进行限制的个数。
设定 M A X _ M O D U L E ² / ( M A X _ M O D U L E ² + 1 ) ~ M A X _ M O D U L E ² / ( 2 × S 16 _ M A X ² ) MAX\_MODULE^² / ( MAX\_MODULE^² + 1) ~ MAX\_MODULE^² / ( 2 × S16\_MAX^² ) MAX_MODULE²/(MAX_MODULE²+1)MAX_MODULE²/(2×S16_MAX²)需要制定count个值,则不需要限制范围个数 S T A R T _ I N D E X = ( M A X _ M O D U L E 2 ) / ( 2 × S 16 _ M A X ² ) × c o u n t START\_INDEX = (MAX\_MODULE²) / (2 \times S16\_MAX^²) \times count START_INDEX=(MAX_MODULE2)/(2×S16_MAX²)×count
则制表需要数据个数 n = c o u n t − S T A R T _ I N D E X n = count - START\_INDEX n=countSTART_INDEX
将制表的范围用MIN,MAX表示:
{ M I N = M A X _ M O D U L E 2 / ( M A X _ M O D U L E 2 + 1 ) × Q 15 M A X = M A X _ M O D U L E 2 / ( 2 × S 1 6 M A X 2 ) × Q 15 \left \{ \begin{array}{c} MIN = \sqrt{ MAX\_MODULE² / ( MAX\_MODULE² + 1 ) } × Q15 \\ MAX = \sqrt{ MAX\_MODULE² / ( 2 × S16_MAX ² ) } × Q15 \end{array} \right. {MIN=MAX_MODULE2/(MAX_MODULE2+1) ×Q15MAX=MAX_MODULE2/(2×S16MAX2) ×Q15
则制表数组每个数值 i = M I N + ( M A X − M I N ) / n × x i = MIN + (MAX - MIN)/n × x i=MIN+MAXMIN/n×x
设定count = 128, 当PWM占空比最大为100%时, MAX_MODULE = 32767,START_INDEX = 64,n = 128 - 64 = 64.

6.4 程序实现

uint16_t Circle_limit_table[65] = { 	32768, 32515 ,32268 ,32026 ,31790 ,31558 ,31332 ,31111 ,30894 ,30682 ,
                                   30474 ,30270 ,30070 ,29874 ,29682 ,29494 ,29309 ,29127 ,28949 ,28774 ,
                                   28602 ,28434 ,28268 ,28105 ,27945 ,27787 ,27632 ,27480 ,27330 ,27183 ,
                                   27038 ,26895 ,26755 ,26617 ,26481 ,26346 ,26214 ,26084 ,25956 ,25830 ,
                                   25705 ,25583 ,25462 ,25342 ,25225 ,25109 ,24994 ,24882 ,24770 ,24660 ,
                                   24552 ,24445 ,24339 ,24235 ,24132 ,24031 ,23930 ,23831 ,23733 ,23637 ,
                                   23541 ,23447 ,23354 ,23262 ,23170 }; //i = sqrt(64/m)*Q15   Q15 = 32768														
uint16_t MaxModule = 32767;	/**<  Circle limitation maximum allowed module */
uint8_t  Start_index = 64;	/**<  Circle limitation table indexing start */

qd_t Circle_Limitation(qd_t Vqd)
{
  uint16_t table_element;
  uint32_t uw_temp;
  int32_t  sw_temp;
  qd_t local_vqd = Vqd;

	sw_temp = ( int32_t )( Vqd.q ) * Vqd.q +			//temp = vq2 + vd2;
            ( int32_t )( Vqd.d ) * Vqd.d;

  uw_temp = ( uint32_t ) sw_temp;
  
  /* uw_temp min value 0, max value 32767*32767 */
  if ( uw_temp > ( MaxModule * MaxModule ))
  {
    uw_temp = (uw_temp - 64) / (MaxModule * MaxModule / 64) + 1;

    /* wtemp min value pHandle->Start_index, max value 127 */
    uw_temp -= Start_index;

    /* uw_temp min value 0, max value 127 - pHandle->Start_index */
    table_element = Circle_limit_table[uw_temp];

    sw_temp = Vqd.q * (int32_t)table_element;
    local_vqd.q = (int16_t)(sw_temp / 32768);

    sw_temp = Vqd.d * (int32_t)( table_element );
    local_vqd.d = (int16_t)(sw_temp / 32768);
  }
  return ( local_vqd );
}

参考:
有感FOC算法学习与实现总结:https://www.cnblogs.com/unclemac/p/12783366.html?share_token=2D91E9DE-C18B-43B8-B2AD-304FFE17B3AB&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_ios&utm_campaign=client_share&wxshare_count=1

FOC中的Clarke变换和Park变换详解(动图+推导+仿真+附件代码):
https://www.cnblogs.com/unclemac/p/12783309.html

内置式永磁同步电机PMSM的矢量控制:
https://blog.csdn.net/jaysur/article/details/103865717

FOC中的SVPWM原理细讲:
https://zhuanlan.zhihu.com/p/466883185

FOC算法与SVPWM技术:
https://blog.csdn.net/qq_41990294/article/details/128405398?share_token=7AC0CFF2-CC2D-4571-B0B3-1AD1622B77F8&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_ios&utm_campaign=client_share&wxshare_count=1

【SVPWM原理与搭建思路-哔哩哔哩】 https://b23.tv/YPa07W5

简述FOC电机控制之SVPWM原理(下)
https://m.elecfans.com/article/2063607.html?share_token=980CCA7A-35DD-49B8-8726-73204B6E65F4&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_ios&utm_campaign=client_share&wxshare_count=1

FOC入门教程:
https://blog.csdn.net/qq_35947329/article/details/115483413

  • 12
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
FOC(Field Oriented Control)算法是一种用于交流电机控制的高级控制算法。下面是一个用C语言实现FOC算法的示例代码: ```c #include <math.h> // 定义PI常量 #define PI 3.14159265358979323846 // 定义结构体,存储FOC算法所需的变量 typedef struct { float alpha; // alpha轴电流 float beta; // beta轴电流 float theta; // 电机转子角度 float i_d; // d轴电流 float i_q; // q轴电流 float v_d; // d轴电压 float v_q; // q轴电压 float v_alpha; // alpha轴电压 float v_beta; // beta轴电压 float i_alpha; // alpha轴电流 float i_beta; // beta轴电流 } foc_vars_t; // 定义函数,用于计算FOC算法所需的电压和电流 void foc_calc(foc_vars_t* vars, float v_u, float v_v, float v_w, float theta) { // 计算alpha轴电压和beta轴电压 vars->v_alpha = (2.0 / 3.0) * (v_u - 0.5 * v_v - 0.5 * v_w); vars->v_beta = (2.0 / 3.0) * (0.866 * v_v - 0.866 * v_w); // 计算alpha轴电流和beta轴电流 vars->i_alpha = vars->alpha * cos(theta) - vars->beta * sin(theta); vars->i_beta = vars->alpha * sin(theta) + vars->beta * cos(theta); // 计算d轴电流和q轴电流 vars->i_d = vars->i_alpha * cos(theta) + vars->i_beta * sin(theta); vars->i_q = -vars->i_alpha * sin(theta) + vars->i_beta * cos(theta); // 计算d轴电压和q轴电压 vars->v_d = vars->v_alpha * cos(theta) + vars->v_beta * sin(theta); vars->v_q = -vars->v_alpha * sin(theta) + vars->v_beta * cos(theta); } // 主函数 int main() { foc_vars_t vars; float v_u, v_v, v_w, theta; // 初始化FOC算法所需的变量 vars.alpha = 0.0; vars.beta = 0.0; vars.theta = 0.0; vars.i_d = 0.0; vars.i_q = 0.0; vars.v_d = 0.0; vars.v_q = 0.0; vars.v_alpha = 0.0; vars.v_beta = 0.0; vars.i_alpha = 0.0; vars.i_beta = 0.0; // 读取电压和转子角度 scanf("%f %f %f %f", &v_u, &v_v, &v_w, &theta); // 计算FOC算法所需的电压和电流 foc_calc(&vars, v_u, v_v, v_w, theta); // 输出d轴电流和q轴电流 printf("%f %f\n", vars.i_d, vars.i_q); return 0; } ``` 上述代码仅提供了FOC算法的基本实现,实际应用中可能需要进一步优化算法以提高控制精度和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hlpinghcg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值