User Tools

Site Tools


Action disabled: source
study:foc-topic:241014-001:index

手寫STM32 FOC記錄-FOC + SVPWM

Local Backup

  • FOC控制演算法框圖:
  • 咋一看FOC控制框圖可能會發現不知道從哪入手,可以回想一下直流有刷電機的學習過程,首先是讓電機轉起來,然後進行速度控制,再進一步進行位置控制,同樣我們在FOC學習過程中依然可以這樣做,我們首先將位置環和速度環甚至是電流環去掉,然後就剩下SVPWM,既然只是讓電機轉起來那麼電流檢測也不需要了,我們就直接給電壓,開環運行,這時候控制框架就能簡化成下圖。
  • 逐一實現框圖中的每一個模組,首先是Park逆變換,將DQ座標系轉換成αβ座標系,如下:
  • 用C語言程式碼實作它:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /************************************************* ************************************************** *******
    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函數如下:
    1
    2
    3
    4
    5
    6
    7
    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程式碼貼進來:
    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
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    /************************************************* ************************************************** *******
    將座標轉換中的反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輸出函數中,顯示三條曲線。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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的增量,馬達的轉動速度會改變。
  • 3 person(s) visited this page until now.

study/foc-topic/241014-001/index.txt · Last modified: 2024/10/14 16:09 (external edit)