四、手寫STM32 FOC記錄-----FOC + SVPWM
FOC控制算法框圖:

????????咋一看FOC控制框圖可能會發(fā)現(xiàn)不知道從哪入手,可以回想一下直流有刷電機的學習過程,首先是讓電機轉(zhuǎn)起來,然后進行速度控制,再進一步進行位置控制,同樣我們在FOC學習過程中依然可以這樣做,我們首先將位置環(huán)和速度環(huán)甚至是電流環(huán)去掉,然后就剩下SVPWM,既然只是讓電機轉(zhuǎn)起來那么電流檢測也不需要了,我們就直接給電壓,開環(huán)運行,這時候控制框架就能簡化成下圖所示。

????????逐一實現(xiàn)框圖中的每一個模塊,首先是Park逆變換,將DQ坐標系轉(zhuǎn)換成αβ坐標系,如下:

????????用C語言代碼實現(xiàn)它:

/**********************************************************************************************************
park逆變換,輸入Uq、Ud得到Ualpha、Ubeta
Uα = Ud · cosθ - Uq · sinθ
Uβ = Ud · sinθ + Uq · cosθ
**********************************************************************************************************/
void inverseParkTransform(DQ_Def *dq, AlphaBeta_Def *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;
}
????????接下來便是FOC控制最復雜也是最難理解的一部分SVPWM,不介紹SVPWM原理,網(wǎng)上關(guān)于SVPWM的資料超級多,需要的時候自行查閱。這里只針對我的理解,結(jié)合我編寫的代碼來說明具體的實現(xiàn)過程。
????????第一步,計算u1、u2、u3。通過park逆變換得到的alpha,beta,計算得出u1、u2和u3,我理解這個其實是克拉克逆變換

????????第二步,扇區(qū)判斷。根據(jù)u1、u2和u3的正負情況確定所處的扇區(qū)。根據(jù)U1,U2,U3的符號計算N=4C+2B+A,再結(jié)合扇區(qū)表判斷所處的扇區(qū),N的取值和所對應的扇區(qū)關(guān)系如下表:

????????第三步,計算基本矢量電壓作用時間(占空比),根據(jù)扇區(qū)確定相鄰兩個基本矢量電壓及其作用時間,然后對作用時間進行等比例縮小處理,使得總的作用時間等于Ts采樣時間,或總的占空比等于1,這樣計算出來的數(shù)據(jù)ta,tb,tc就是占空比,正好與PWM的輸出對應。這部分相對較復雜,這里不做描述,請查閱專業(yè)的資料。推薦一篇博客:https://blog.csdn.net/weixin_42887190/article/details/125464343

????????第四步,6路PWM輸出,根據(jù)a,b,c三項占空比,計算PWM輸出,驅(qū)動電機轉(zhuǎn)動,這個很簡單,就是用占空比乘以PWM周期,編寫set_PWM_value函數(shù)如下:


void set_PWM_value(uint16_t pwm_u,uint16_t pwm_v,uint16_t pwm_w)
{
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pwm_u); ?
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, pwm_v);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, pwm_w);
?
}
至此SVPWM完成,現(xiàn)將完整的SVPWM代碼粘貼進來:
/**********************************************************************************************************
將坐標變換中的反Park變換得到的 Valpha 、Vbeta 轉(zhuǎn)換六路PWM輸出。
?
**********************************************************************************************************/
void SVPWM(AlphaBeta_Def *U_alphaBeta, SVPWM_Def *svpwm)
{
????float sum;
????float k_svpwm;
????
????svpwm->Ts = 1.0f; // SVPWM的采樣周期
?
????svpwm->U_alpha = U_alphaBeta->alpha;
????svpwm->U_beta = U_alphaBeta->beta;
?
??// step1 計算u1、u2和u3
????// 計算SVPWM算法中的三個控制電壓u1、u2和u3
????svpwm->u1 = U_alphaBeta->beta;
????svpwm->u2 = -0.8660254f * U_alphaBeta->alpha - 0.5f * U_alphaBeta->beta; // sqrt(3)/2 ≈ 0.86603
????svpwm->u3 = 0.8660254f * U_alphaBeta->alpha - 0.5f * U_alphaBeta->beta;
?// step2:扇區(qū)判斷
????// 根據(jù)u1、u2和u3的正負情況確定所處的扇區(qū)
????svpwm->sector = (svpwm->u1 > 0.0f) + ((svpwm->u2 > 0.0f) << 1) + ((svpwm->u3 > 0.0f) << 2); // N=4*C+2*B+A
????// step3:計算基本矢量電壓作用時間(占空比)
????// 根據(jù)扇區(qū)的不同,計算對應的t_a、t_b和t_c的值,表示生成的三相電壓的時間
????switch (svpwm->sector)
????{
????case 5:
????????// 扇區(qū)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:
????????// 扇區(qū)1
????????svpwm->t2 = -svpwm->u3;
????????svpwm->t6 = -svpwm->u2;
????????sum = svpwm->t2 + svpwm->t6;
????????if (sum > svpwm->Ts)
????????{
????????????k_svpwm = svpwm->Ts / sum; // 計算縮放系數(shù)
????????????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:
????????// 扇區(qū)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:
????????// 扇區(qū)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:
????????// 扇區(qū)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:
????????// 扇區(qū)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:6路PWM輸出
????set_PWM_value(PWM_PERIOD*svpwm->ta,PWM_PERIOD*svpwm->tb,PWM_PERIOD*svpwm->tc);
}
????????第五步,驗證SVPWM輸出是否正確。寫完SVPWM函數(shù),需要做驗證,看看算法是否正確可用。編寫svpwm_test函數(shù),給定d,q值,由于暫時還沒有完成電機編碼器的角度獲取,所以角度theta給以個固定增量,這樣正常情況下,接上電機會轉(zhuǎn)動起來。為了便于觀察將ta,tb,tc三個數(shù)據(jù)擴大100倍放到vofa輸出函數(shù)中,顯示三條曲線。

void svpwm_test(void)
{
????float theta = 0;
????DQ_Def test_dq;
????AlphaBeta_Def test_ab;
????SVPWM_Def svpwm_out;
?
????test_dq.d = 0.0f;
????test_dq.q = 0.2f;
?
????
?
????for (theta = 0; theta < 6.2831853f; theta += 0.275f)
?????{
????????inverseParkTransform(&test_dq,&test_ab,theta);
????????SVPWM(&test_ab,&svpwm_out);
? vofa_JustFloat_output(100.0f*svpwm_out.ta,100.0f*svpwm_out.tb,100.0f*svpwm_out.tc,0.0f);
// ? ?HAL_Delay(1);
????}
}
????????最后主函數(shù)調(diào)用如下:
?

????????接上硬件平臺,編譯下載,打開串口連接vofa+上位機,可以輸出三條相位相差120°的馬鞍波曲線,顯示如下,如此就證明SVPWM成功實現(xiàn)。

?
????????同時,可以接上BLDC的UVW三項,可以看到電機會轉(zhuǎn)動,修改test_dq.d和theta的增量,電機的轉(zhuǎn)動速度會改變。
觀看視頻效果請轉(zhuǎn)至https://www.bilibili.com/video/BV1pu4y1y7jK/?spm_id_from=333.999.0.0&vd_source=4c57166c6142504cc135c2135bf9844e
????????本文已將重要的代碼貼到文章中了,涉及到的所有源碼可以都開源,需要的小伙伴點贊關(guān)注后可以私信獲取