FOC控制的整個過程如下:
論壇和貼子有很多講理論的,很少實際操作,本文理論聯系實際,為大家一步一步揭開SVPWM神秘的面紗。
本文基於ST-FOC 5.4 SVPWM函數分析,力爭讓讀者可以通過這篇文章了解SVPWM的本質:
SVPWM的目的如下:
SVPWM核心思想通過輸入的Vα 和Vβ 計算出三相 PWM占空比。
本文的目錄如下:
1.任意向量如何通過基本空間矢量合成?任意向量都可以通過相鄰的基本空間向量+V0+V7合成。
2.向量扇區的判斷?通過(α,β)坐標系下Vα和Vβ值的大小,判斷需要合成的向量位於哪個扇區。
3 .基本空間矢量作用時間計算?通過計算(α,β)坐標系下Vα和Vβ值的大小,計算出相鄰基本空間矢量作用的時間
4 .占空比計算如何計算?(高電平持續的時間or定時器寄存器的值),根據七段式SVPWM發波原理計算出:hTimePhA ,hTimePhB,hTimePhC的值。
5.總結 SVPWM原理總結。
6.附錄。
看一下SVPWM函數原型,輸入變量:
1 2 3 4 5 6 |
uint16_t PWMC_SetPhaseVoltage( PWMC_Handle_t * pHandle, alphabeta_t Valfa_beta ) typedef struct { int16_t alpha; int16_t beta; } alphabeta_t; |
1.任意向量如何通過基本空間矢量合成?
圖1 典型三相逆變器拓撲
定義[a,b,c]表示逆變橋上半開關管的狀態,當a,b,c為1是表示Q1,Q2,Q3導通,當a,b,c為0時表示Q1,Q2,Q3關閉,且同一橋臂的上管和下管狀態相反。根據a,b,C的狀態組合,開關管一共有8種狀態。如下圖所示:
序號 |
開關管狀態 |
|
|
相電壓 |
|
|
線電壓 |
|
|
|
a |
b |
c |
VAN |
VBN |
VCN |
VAB |
VBC |
VCA |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
-Vdc/3 |
-Vdc/3 |
2Vdc/3 |
0 |
-Vdc |
Vdc |
2 |
0 |
1 |
0 |
-Vdc/3 |
2Vdc/3 |
-Vdc/3 |
-Vdc |
Vdc |
0 |
4 |
1 |
0 |
0 |
2Vdc/3 |
-Vdc/3 |
-Vdc/3 |
Vdc |
0 |
-Vdc |
3 |
0 |
1 |
1 |
-2Vdc/3 |
Vdc/3 |
Vdc/3 |
-Vdc |
0 |
Vdc |
5 |
1 |
0 |
1 |
Vdc/3 |
-2Vdc/3 |
Vdc/3 |
Vdc |
-Vdc |
0 |
6 |
1 |
1 |
0 |
Vdc/3 |
Vdc/3 |
-2Vdc/3 |
0 |
Vdc |
-Vdc |
7 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
表1 開關狀態和相電壓,線電壓的關系
以上分析主要給予KCL和KVL定律,有疑問的小伙伴可以復習一下電路的相關知識。其中有兩個零矢量和六個非零矢量,整個空間也被划分為以下六個扇區。應用clark變換(三相-兩相變換或者3/2變換,詳見《電力拖動自動控制系統-運動控制系統 》第四版 P164),將VAN,VBN,VCN 旋轉的坐標系轉換到靜止的(α,β)坐標系。
可以得到Vα和Vβ的計算公式:
有Va+Vb+Vc=0,Vc=-Va-Vb。
得到計算公式如下:
a |
b |
c |
VAN |
VBN |
Vsα |
Vsβ |
Vector |
向量 |
角度 |
二進制 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
|
0 |
0[000] |
1 |
0 |
0 |
2Vdc/3 |
-Vdc/3 |
2Vdc/3 |
0 |
2Vdc/3 |
V1 |
60 |
4[100] |
1 |
1 |
0 |
Vdc/3 |
Vdc/3 |
Vdc/3 |
|
2Vdc/3 |
V2 |
120 |
6[110] |
0 |
1 |
0 |
-Vdc/3 |
2Vdc/3 |
-Vdc/3 |
|
2Vdc/3 |
V3 |
180 |
2[010] |
0 |
1 |
1 |
-2Vdc/3 |
Vdc/3 |
-2Vdc/3 |
0 |
2Vdc/3 |
V4 |
240 |
3[011] |
0 |
0 |
1 |
-Vdc/3 |
-Vdc/3 |
-Vdc/3 |
|
2Vdc/3 |
V5 |
300 |
1[001] |
1 |
0 |
1 |
Vdc/3 |
-2Vdc/3 |
Vdc/3 |
|
2Vdc/3 |
V6 |
360 |
5[101] |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
|
0 |
7[111] |
表2 clark變換后基本空間向量的計算【第一象限】
同理我們可以計算(α,β)在第四象限對應的轉換關系:
注意以上公式的變化點:
有Va+Vb+Vc=0,Vc=-Va-Vb。
系數K的作用是可以將裝換變為等幅值或者等功率轉換
在這里我們才管用等幅值變換K=2/3,可以得到:
因此可以得到,如下公式:
a |
b |
c |
VAN |
VBN |
Vsα |
Vsβ |
Vector |
向量 |
角度 |
二進制 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
|
0 |
0[000] |
1 |
0 |
0 |
2Vdc/3 |
-Vdc/3 |
2Vdc/3 |
0 |
2Vdc/3 |
V1 |
60 |
4[100] |
1 |
1 |
0 |
Vdc/3 |
Vdc/3 |
Vdc/3 |
|
2Vdc/3 |
V2 |
120 |
6[110] |
0 |
1 |
0 |
-Vdc/3 |
2Vdc/3 |
-Vdc/3 |
|
2Vdc/3 |
V3 |
180 |
2[010] |
0 |
1 |
1 |
-2Vdc/3 |
Vdc/3 |
-2Vdc/3 |
0 |
2Vdc/3 |
V4 |
240 |
3[011] |
0 |
0 |
1 |
-Vdc/3 |
-Vdc/3 |
-Vdc/3 |
|
2Vdc/3 |
V5 |
300 |
1[001] |
1 |
0 |
1 |
Vdc/3 |
-2Vdc/3 |
Vdc/3 |
|
2Vdc/3 |
V6 |
360 |
5[101] |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
|
0 |
7[111] |
表2 clark變換后基本空間向量的計算【第四象限】
綜上所示,以上兩種情況得到的基本矢量分布如下:
以上可以看到,基本向量的相對位置是沒有改變的。
如下圖所示:(根據ST-官方教程如下圖所示)
圖2 基本空間矢量圖
大家要特別關注Vα和Vβ的方向,本文以ST的FOC庫為准。
圖中2有幾個問題比較有意思:
1).為什么矢量的排列順序是按照4-6-2-3-1-5 或者是4-5-1-3-2-6順序排列那?這里面值得順序是二進制開關組合代表的組合。
答案:這六個矢量控制的是功率半導體-Mosfet或者IGBT;這些管子在開關和導通過程中會有熱量產生,也就是損耗。為了最大限度的降低損耗,每個扇區(包含扇區內部)的開關切換,都需要保證只改動一個橋臂的動作,這樣發熱量最小,功率密度才能做更高
2).矢量的排列順序是4-6-2-3-1-5 或者是4-5-1-3-2-6順序代表什么意思?
答案:這是電機正反轉的區別,假定4-6-2-3-1-5 逆時針為電機正傳,則4-5-1-3-2-6表示反轉。
備注:不同文檔中矢量的名稱可能會有改變,這里影響不大,只要調整a,b,c的排列順序,大家就可以發現其實都一樣的,大家看文檔的時候不用過於糾結。
SVPWM的目的是為了通過基本的空間的矢量組合得到一個旋轉的向量VOUT ,VOUT 可以用(α,β)軸分量Vα和Vβ表示(第四象限)。
2 .扇區的判斷
代碼如下:
//下面是查找定子電流的扇區號
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
if (wY<0) { if (wZ<0) { bSector = SECTOR_5; } else // wZ >= 0 if (wX<=0) { bSector = SECTOR_4; } else // wX > 0 { bSector = SECTOR_3; } } else // wY > 0 { if (wZ>=0) { bSector = SECTOR_2; } else // wZ < 0 if (wX<=0) { bSector = SECTOR_6; } else // wX > 0 { bSector = SECTOR_1; } } |
VOUT 這個矢量按照我們的設定在圓內依次運行,在每個扇區內VOUT 都是有兩個相鄰的矢量根據不同的時間合成的矢量,因此第一步我們需要知道VOUT 在哪個扇區。
扇區 |
判斷條件 |
X(wX) |
Y(wY) |
Z(wZ) |
SECTOR |
I |
|
>0 |
>0 |
<0 |
SECTOR_1 |
II |
|
>0 |
>0 |
>0 |
SECTOR_2 |
III |
|
>0 |
<0 |
>0 |
SECTOR_3 |
IV |
|
<0 |
<0 |
>0 |
SECTOR_4 |
V |
|
<0 |
<0 |
<0 |
SECTOR_5 |
VI |
|
<0 |
>0 |
<0 |
SECTOR_6 |
表3 扇區的判斷
設定:X>0則wX=1,否則wX=0.
設定:Y>0則wY=1,否則wY=0.
設定:Z>0則wZ=1,否則wZ=0.
程序中的定義:
1 2 3 4 5 6 |
wUAlpha = Stat_Volt_Input.qV_Component1 * T_SQRT3; wUBeta = -(Stat_Volt_Input.qV_Component2 * T);
wX = wUBeta; wY = (wUBeta + wUAlpha)/2; wZ = (wUBeta - wUAlpha)/2; |
圖3 基本空間矢量的作用時間計算
以上是理解下面運算的基礎。特別需要關注的是,(α,β)軸分量Vα和Vβ表示(第四象限),在第一象限用Vα'和Vβ'表示。
3 .基本空間矢量作用時間計算
知道扇區的位置,接下來計算矢量的作用時間。先討論一下扇區的發波問題,本文選取7段式SVPWM
關於更多SVPWM的發放方式,詳見:https://blog.csdn.net/michaelf/article/details/94013805
采用7段式SVPWM的優點是減少損耗,同時可以較少高次諧波含量,本文不再這里展開,大家了解一種即可
第一扇區計算:
圖4 第一扇區基本矢量作用是時間計算
其中T為PWM波形周期,T4是基本矢量V4持續的時間,T6是基本矢量V6持續的時 間。T-T6-T4為V0和V7矢量持續的時間。
將Vout投影到兩個相鄰的兩個矢量V4,V6上,在坐標系α'和β'坐標系下計算,可以得到如下關系:
為了后續計算,基本向量歸一化處理,步驟如下:
所有的基本空間向量的幅值都是2Vdc/3,當兩個零電壓矢量作用時間為0時,一個PWM周期內非零電壓矢量的作用時間最長,此時的合成空間矢量幅值最大,由下圖可以,其幅值最大不會超過圖中所示的正六邊形邊界,而當合成矢量落在該邊界之外是,將發生過調試,逆變器輸出電壓波形將失真。
以上計算得到最大不失真矢量電壓為幅值為
圖5 基本空間矢量歸一化
因此計算:
得到 :
計算結果匯總如下:
扇區 |
N |
T1 |
T2 |
T0+T7 |
I |
3 |
-Z |
X |
T+Z-X |
II |
1 |
Z |
Y |
T-Y-Z |
III |
5 |
X |
-Y |
T-X+Y |
IV |
4 |
-X |
Z |
T+X-Z |
V |
6 |
-Y |
-Z |
T+Y+Z |
VI |
2 |
Y |
-X |
T+X-Y |
表4 基本矢量作用時間計算
詳細的計算可以參考一下鏈接:https://blog.csdn.net/michaelf/article/details/94013805
需要關注兩點,一個是Vβ的方向,一個是基本矢量的歸一化。
表5基本矢量作用時間計算。
4 .占空比計算(高電平持續的時間or定時器寄存器的值)
根據3分析,我們可以計算出合成向量在不同的扇區,相鄰矢量作用的時間及 V0和V7作用的時間。
向上代碼,后分析:
FOC 5.0代碼分析如下:
1 2 3 4 5 6 7 8 9 |
uint16_t PWMperiod; /**< PWM period expressed in timer clock cycles unit: * @f$hPWMPeriod = TimerFreq_{CLK} / F_{PWM}@f$ */ #define PWM_PERIOD_CYCLES (uint16_t)(ADV_TIM_CLK_MHz*\ (unsigned long long)1000000u/((uint16_t)(PWM_FREQUENCY)))
#define ADV_TIM_CLK_MHz 72 #define PWM_FREQUENCY 16000
PWM_Handle_M1.PWMperiod = PWM_PERIOD_CYCLES,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
__weak uint16_t PWMC_SetPhaseVoltage( PWMC_Handle_t * pHandle, alphabeta_t Valfa_beta ) { int32_t wX, wY, wZ, wUAlpha, wUBeta, wTimePhA, wTimePhB, wTimePhC;
wUAlpha = Valfa_beta.alpha * ( int32_t )pHandle->hT_Sqrt3; wUBeta = -( Valfa_beta.beta * ( int32_t )( pHandle->PWMperiod ) ) * 2;
wX = wUBeta; wY = ( wUBeta + wUAlpha ) / 2; wZ = ( wUBeta - wUAlpha ) / 2;
/* Sector calculation from wX, wY, wZ */ if ( wY < 0 ) { if ( wZ < 0 ) { pHandle->Sector = SECTOR_5; wTimePhA = ( int32_t )( pHandle->PWMperiod ) / 4 + ( ( wY - wZ ) / ( int32_t )262144 ); wTimePhB = wTimePhA + wZ / 131072; wTimePhC = wTimePhA - wY / 131072; pHandle->lowDuty = wTimePhC; pHandle->midDuty = wTimePhA; pHandle->highDuty = wTimePhB; } else /* wZ >= 0 */ if ( wX <= 0 ) { pHandle->Sector = SECTOR_4; wTimePhA = ( int32_t )( pHandle->PWMperiod ) / 4 + ( ( wX - wZ ) / ( int32_t )262144 ); wTimePhB = wTimePhA + wZ / 131072; wTimePhC = wTimePhB - wX / 131072; pHandle->lowDuty = wTimePhC; pHandle->midDuty = wTimePhB; pHandle->highDuty = wTimePhA; } else /* wX > 0 */ { pHandle->Sector = SECTOR_3; wTimePhA = ( int32_t )( pHandle->PWMperiod ) / 4 + ( ( wY - wX ) / ( int32_t )262144 ); wTimePhC = wTimePhA - wY / 131072; wTimePhB = wTimePhC + wX / 131072; pHandle->lowDuty = wTimePhB; pHandle->midDuty = wTimePhC; pHandle->highDuty = wTimePhA; } } else /* wY > 0 */ { if ( wZ >= 0 ) { pHandle->Sector = SECTOR_2; wTimePhA = ( int32_t )( pHandle->PWMperiod ) / 4 + ( ( wY - wZ ) / ( int32_t )262144 ); wTimePhB = wTimePhA + wZ / 131072; wTimePhC = wTimePhA - wY / 131072; pHandle->lowDuty = wTimePhB; pHandle->midDuty = wTimePhA; pHandle->highDuty = wTimePhC; } else /* wZ < 0 */ if ( wX <= 0 ) { pHandle->Sector = SECTOR_6; wTimePhA = ( int32_t )( pHandle->PWMperiod ) / 4 + ( ( wY - wX ) / ( int32_t )262144 ); wTimePhC = wTimePhA - wY / 131072; wTimePhB = wTimePhC + wX / 131072; pHandle->lowDuty = wTimePhA; pHandle->midDuty = wTimePhC; pHandle->highDuty = wTimePhB; } else /* wX > 0 */ { pHandle->Sector = SECTOR_1; wTimePhA = ( int32_t )( pHandle->PWMperiod ) / 4 + ( ( wX - wZ ) / ( int32_t )262144 ); wTimePhB = wTimePhA + wZ / 131072; wTimePhC = wTimePhB - wX / 131072; pHandle->lowDuty = wTimePhA; pHandle->midDuty = wTimePhB; pHandle->highDuty = wTimePhC; } }
pHandle->CntPhA = ( uint16_t )wTimePhA; pHandle->CntPhB = ( uint16_t )wTimePhB; pHandle->CntPhC = ( uint16_t )wTimePhC;
return ( pHandle->pFctSetADCSampPointSectX( pHandle ) ); } |
先看第一扇區七段式SVPWM的順序是:0-4-6-7-6-4-0.重點V0和V7的作用時間相等。很重要。
扇區切換時間如下:X=1,Y=2
c橋臂: Tc=Tb-T2=Tb-X
同理可以推算出其他各個扇區的切換時間,匯總各個扇區的切換時間,對應關系如下:
扇區 |
I |
II |
III |
IV |
V |
VI |
備注 |
N |
3 |
1 |
5 |
4 |
6 |
2 |
程序對應的名稱 |
Ta |
|
|
|
|
|
|
hTimePhA |
Tb |
|
|
|
|
|
|
hTimePhB |
Tc |
Tb-X |
Tb-Y |
Tb-Y |
Tb-X |
Tb-Y |
Tb-Y |
hTimePhC |
表6 個橋臂作用時間計算
以上內容是結構ST的SVPWM教材分析,ST官方教程使用3頁PPT標識,詳細推到過程如以上所示。
結合代碼,我們會發現幾個問題點,
1) 為什么要除以4? pHandle->PWMperiod / 4
前面wUBeta 和wUAlpha 計算式PWMperiod *2,因此在這里需要除去2.實際結果是T/2.
參考PWM定時器配置:
1 2 3 4 5 6
|
htim1.Instance = TIM1;//設置頻率為16k htim1.Init.Prescaler = ((TIM_CLOCK_DIVIDER) - 1);//分頻系數為0 htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;//TIM中央對齊模式1計數模式 htim1.Init.Period = ((PWM_PERIOD_CYCLES) / 2);/*Period max 4500 設置了在下一個更新事件裝入活動的自動重裝載寄存器周期的值。value:0x0000~0xFFFF*/ htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV2;/*設置定時器時鍾CK_INT頻率與死區發生器以及數字濾波器采樣時鍾頻率分頻化。Value:*/ htim1.Init.RepetitionCounter = REP_RATE;/*是否使用重復定時器,當該值不為0的時候,計數器計數值達到周期數時,該值減1,計數器重新計數,當該值減到0的時候才會產生事件。*/ htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; |
正好配置TIM1為中央對齊模式1(TIM_COUNTERMODE_CENTERALIGNED1),在上面代碼的配置中,載波周期為16KHz,Period(ARR)=4500,CH1的CntPhA (CCR)=800。采用的PWM1模式,
即CNT小於CCR時,輸出有效電平,大於CCR小於ARR時,輸出無效電平,又配置CHx的有效電平為高電平,CHxN的有效電平為高電平,則可以得到下面的PWM波形:
如果CHxN的有效電平是低電平,則輸出的CHx和CHxN的波形是相同的。(可能CHx和CHxN有效電平的叫法相反)
從以上可T=(PWM_PERIOD_CYCLES) / 2.,PWM_PERIOD_CYCLES=2T.
2)為什么要除以131072?((((T + wX) - wZ)/2)/131072)
Q15,電流采用了Q15表示(左對齊),2^15 = 32768
3)為什么要除以262144 ?( ( wX - wZ ) / ( int32_t )262144 );
同問題2,262144 =32768X4=Q15*4.
5.總結
6.補充內容,PPT截圖如下: