User Tools

Site Tools

study:foc-topic:241014-001:index

手寫STM32 FOC記錄-FOC + SVPWM

Local Backup

  • FOC控制演算法框圖:
  • 咋一看FOC控制框圖可能會發現不知道從哪入手,可以回想一下直流有刷電機的學習過程,首先是讓電機轉起來,然後進行速度控制,再進一步進行位置控制,同樣我們在FOC學習過程中依然可以這樣做,我們首先將位置環和速度環甚至是電流環去掉,然後就剩下SVPWM,既然只是讓電機轉起來那麼電流檢測也不需要了,我們就直接給電壓,開環運行,這時候控制框架就能簡化成下圖。
  • 逐一實現框圖中的每一個模組,首先是Park逆變換,將DQ座標系轉換成αβ座標系,如下:
  • 用C語言程式碼實作它:
    /************************************************* ************************************************** *******
    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原理,網路上關於SVPWM的資料超多,需要的時候自行查閱。這裡只針對我的理解,結合我寫的程式碼來說明具體的實作過程。
  • 第一步,計算u1、u2、u3。透過park逆變換得到的alpha,beta,計算得出u1、u2和u3,我理解這個其實是克拉克逆變換
  • 第二步,扇區判斷。根據u1、u2和u3的正負情況來決定所處的扇區。根據U1,U2,U3的符號計算N=4C+2B+A,再結合扇區表判斷所處的扇區,N的取值和對應的扇區關係如下表:
  • 第三步,計算基本向量電壓作用時間(佔空比),根據扇區確定相鄰兩個基本向量電壓及其作用時間,然後對作用時間進行等比例縮小處理,使得總的作用時間等於Ts採樣時間,或總的佔空比等於1,這樣計算出來的資料ta,tb,tc就是佔空比,剛好與PWM的輸出對應。這部分相對較複雜,這裡不做描述,請查閱專業的資料。推薦一篇部落格:https://blog.csdn.net/weixin_42887190/article/details/125464343
  • 第四步,6路PWM輸出,依a,b,c三項佔空比,計算PWM輸出,驅動馬達轉動,這個很簡單,就是用佔空比乘以PWM週期,寫set_PWM_value函數如下:
    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完成,現將完整的SVPWM程式碼貼進來:
    /************************************************* ************************************************** *******
    將座標轉換中的反Park變換得到的Valpha 、Vbeta 轉換六路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:磁區判斷
        // 根據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:6路PWM輸出
        set_PWM_value(PWM_PERIOD*svpwm->ta,PWM_PERIOD*svpwm->tb,PWM_PERIOD*svpwm->tc);
    }
  • 第五步,驗證SVPWM輸出是否正確。寫完SVPWM函數,需要做驗證,看看演算法是否正確可用。寫svpwm_test函數,給定d,q值,由於暫時還沒有完成電機編碼器的角度獲取,所以角度theta給以個固定增量,這樣正常情況下,接上電機會轉動起來。為了方便觀察將ta,tb,tc三個資料擴大100倍放到vofa輸出函數中,顯示三條曲線。

    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);
        }
    }
  • 最後主函數呼叫如下:
  • 接續硬體平台,編譯下載,開啟串口連接vofa+上位機,可以輸出三個相位相差120°的馬鞍波曲線,顯示如下,如此就證明SVPWM成功實現。
  • 同時,可以接上BLDC的UVW三項,可以看到電機會轉動,修改test_dq.d和theta的增量,馬達的轉動速度會改變。
  • 13 person(s) visited this page until now.

Permalink study/foc-topic/241014-001/index.txt · Last modified: 2024/10/14 16:09 by jethro

oeffentlich