電機控制算法FOC-研究英飛凌AP32013i_Inverter_TC27xC_1_22心得(一)

本文參照英飛凌AP32013i_Inverter_TC27xC_1_22相關的文檔,研究FOC算法的原理,模塊組成和調試過程。


一,什麼是FOC

FOC(Field Oriented Control)是採用數學方法實現三相電機的力矩與勵磁的解耦控制。

主要是對電機的控制電流進行矢量分解,變成勵磁電流Id和交軸電流Iq ,勵磁電流主要是產生勵磁,控制的是磁場的強度(勵磁就是控制定子的電壓使其產生的磁場變化,改變直流電機的轉速,改變勵磁同樣起到改變轉速的作用。),而交軸電流是用來控制力矩。

二,FOC的框圖和模塊劃分

這裏借用https://blog.csdn.net/wofreeo/article/details/82968439的框圖。

從框圖上可以分成下面的幾個部分:

1. 電機的d-q模型:clark變換,park變換,park逆變換。

2. 外部的對象:三相逆變驅動器(3-phase inverter)(通常指三相全橋驅動電路)和電機(直流無刷電機BLDC)

3. 空間矢量脈衝調製模塊:SVPWM

4. PI調節器:基於小信號控制理論,把電流差值轉換成電壓的調節器。

5. 給定的Iq_ref和Id-ref:

需要說明的地方:

1. FOC算法是電機控制算法的一部分,具有可替代性,比如DTC。而整體的電機控制算法是基於小信號控制理論的。

2. FOC算法可用於異步電機,直流無刷電機,永磁同步電機。

3. 給定的Iq_ref和Id-ref要根據電機相關模型和參數計算得出。

4. 該算法受物理定律,軟件框架,軟件相應速度等限制。

三,各個模塊的簡介

1. 電機數學模型-座標變換:clark和park變換。

座標軸變換-clark變換:

{\color{Red} i_\alpha =i_u}

{\color{Red} i_\beta =\frac{i_w-i_v}{\sqrt{3}}}

座標軸變換-park變換:

下面的公式,x可以是電壓也可以是電流

park 正變換:

{\color{Red} \begin{bmatrix} x_d\\ x_q \end{bmatrix}= \begin{bmatrix} \cos \psi & \sin \psi\\ -\sin\psi&\cos\psi \end{bmatrix} \begin{bmatrix} x_\alpha\\ x_\beta\end{bmatrix}}

park逆變換:

{\color{Red} \begin{bmatrix} x_\alpha\\ x_\beta\end{bmatrix}= \begin{bmatrix} \cos \psi & -\sin \psi\\ \sin\psi&\cos\psi \end{bmatrix} \begin{bmatrix} x_d\\ x_q \end{bmatrix}}

2. SVPWM模塊:

參見https://blog.csdn.net/u010671230/article/details/79478648,這裏不在敘述了。

3. 如何給定的Iq_ref和Id-ref:

在AP32013i_Inverter_TC27xC_1_22中英飛凌給出是MTPA-電壓極限圓-MTPF的方法,如下;

A-B:MTPA(Maximum-torque-per-ampere)

{\color{Red} i^4_{qmtpa}.\Delta ^2_L+\Gamma _n.i_{qmtpa}.\psi_m-\Gamma ^2_n=0 with \Gamma _n=\frac{\Gamma _{ref}}{\frac{3}{2}.p}}

{\color{Red} i_{dmpta}=\frac{\psi_m-\sqrt{\psi^2_m+(2\Delta L.i_{qmtpa})}}{2\Delta L}}

C:保持要求扭矩時的減弱磁場

{\color{Red} i_{dfw}=\frac{-\psi}{L_d}+\frac{1}{L_d}.\sqrt{\frac{V_{max}^2}{\omega _r}+(L_q.i_{qmtpa})^{2}},i_{dmax}\leq i_{dfw}\leq0 }

{\color{Red} i_{qfw}=i_{qmtpa}+\frac{\Delta L.(i_{dfw}-i_{dmtpa}).i_{qmtpa}}{\psi_m-\Delta L.i_{dfw}}}

D:弱磁在極限轉矩時

{\color{Red} i^2_d+i_q^2\leq I^2_{max}}

E-F:MTPF(Maximum-torque-per-flux)

{\color{Red} \frac{2\rho ^2.L^3_d.\Gamma _e.(\rho -1)}{(\psi_m-(\rho -1).L_d.i_{dmtpf})^3}+2L_d.(\psi_m+L_d.i_{dmtpf})=0,\rho =\frac{L_q}{L_d}}

程序如下:

Ifx_MotorModelPmsmF32_Data *p      = &model->params;
    float32                       deltaL = p->ld - p->lq;
    float32                       fluxM  = p->fluxM;
    sint16                        n;
    float32                       p_1_5  = 1.5 * p->polePair;
    float32                       iq_mtpa, id_mtpa;
    cfloat32                      idqRef;

    float32                       T_ref = input->ref.torque;

    /* Actual torque (estimated) -------------------------------------------------------*/
    output->torque = p_1_5 * model->foc.idq.imag * (fluxM + model->foc.idq.real * deltaL);

    {   /* MAXIMUM TORQUE PER AMPERE (MTPA) ------------------------------------------- */
        float32 Tn_ref, Tn_f;
        float32 deltaL_sq, fluxM_sq;
        float32 Iq_n, Iq_n_3;
        float32 f_n, df_n;

        Tn_ref    = (T_ref < 0) ? (-T_ref / p_1_5) : (T_ref / p_1_5);

        deltaL_sq = deltaL * deltaL;

        Tn_f      = Tn_ref * fluxM;
        Iq_n      = Tn_ref / (p_1_5 * fluxM);

        if (Iq_n > 0)
        {
            for (n = 0; n < 3; n++)
            {
                Iq_n_3 = Iq_n * Iq_n * Iq_n;

                f_n    = (Iq_n * (Iq_n_3 * deltaL_sq + Tn_f)) - (Tn_ref * Tn_ref);
                df_n   = (4 * Iq_n_3 * deltaL_sq) + Tn_f;
                Iq_n   = Iq_n - (f_n / df_n);
            }
        }

        iq_mtpa  = Iq_n;

        fluxM_sq = (fluxM * fluxM);
        id_mtpa  = (fluxM - __sqrtf(fluxM_sq + (4 * deltaL_sq * Iq_n * Iq_n))) / (-2 * deltaL);
    }

    if (model->fieldWeakeningEnabled)
    {   /* FIELD-WEAKENING ------------------------------------------------------------ */
        // Maximum flux from nominal DC voltage, rotation speed and correction-feedback */
        float32 absSpeed   = __absf(model->electricalSpeed) + 1.0e-9;
        float32 fluxMaxAbs = model->vdc / (IFX_SQRT_THREE / IFX_FOCF32_VDQ_THRESHOLD) / absSpeed;
        float32 fluxMax    = __maxf(fluxMaxAbs + model->foc.fw.fbValue, 0.0);
        model->fluxMax = fluxMax;
        float32 fluxMax_sq = fluxMax * fluxMax;

        // Q-axis flux
        float32 Lq       = p->lq;
        float32 Ld       = p->ld;
        float32 fluxQ    = Lq * iq_mtpa;
        float32 fluxQ_sq = fluxQ * fluxQ;

        // Calculate Id_fw from the following conditions:
        //     fluxQ_sq  + fluxD_sq          <= fluxMax_sq
        //     (Lq*Iq)^2 + (fluxM + Ld*Id)^2 <= fluxMax_sq
        //     Id <= -fluxPM /Ld + sqrtf(fluxMax_sq - fluxQ_sq)/Ld;

        float32 id_fw;
        float32 deltaFlux_sq = fluxMax_sq - fluxQ_sq;

        id_fw = (deltaFlux_sq > 0) ? ((-fluxM + __sqrtf(deltaFlux_sq)) / Ld) : (-fluxM / Ld);

        float32 deltaId = id_fw - id_mtpa;

        if (deltaId < 0)
        {
            // We are in field-weakening, check whether torque should be reduced
            float32 idqLimit = Ifx_FocF32_getIdqLimit(&model->foc);
            id_fw   = __maxf(id_fw, -idqLimit);
            iq_mtpa = (T_ref < 0) ? -iq_mtpa : iq_mtpa;
            float32 iq_fw    = iq_mtpa;
            float32 fluxD    = ((Ld * id_fw) + fluxM);
            float32 fluxD_sq = fluxD * fluxD;

            deltaFlux_sq = fluxMax_sq - fluxD_sq;
            iq_fw        = (deltaFlux_sq > 0) ? (__sqrtf(deltaFlux_sq) / Lq) : 0;

            float32 deltaIq       = (-iq_mtpa * deltaId * deltaL) / ((id_fw * deltaL) + fluxM);
            float32 iq_corr       = iq_mtpa + deltaIq;

            boolean reducedTorque = (iq_corr < iq_fw);
            iq_fw = reducedTorque ? iq_corr : iq_fw;
            //if (reducedTorque) __debug();

            iq_fw       = (iq_fw < iq_mtpa) ? iq_fw : iq_mtpa;
            iq_fw       = __minf(__sqrtf(__sqrf(idqLimit) - __sqrf(id_fw)), iq_fw);

            idqRef.real = id_fw;
            idqRef.imag = (T_ref < 0) ? -iq_fw : iq_fw;

            model->fw   = idqRef;
        }
        else
        {
            idqRef.real = id_mtpa;
            idqRef.imag = (T_ref < 0) ? -iq_mtpa : iq_mtpa;
        }
    }
    else
    {
        idqRef.real = id_mtpa;
        idqRef.imag = (T_ref < 0) ? -iq_mtpa : iq_mtpa;
    }

    /* Transfer the current references to current controller (FOC) block ---------------*/
    input->ref.current = idqRef;

四,關於調試:

本調試方法來自http://bbs.elecfans.com/forum.php?mod=viewthread&tid=1865932&page=1

(一),測試相電流採樣電路的功能。

1、不接電機,連續採樣相電流,此時採樣值爲相電流爲0時的值,此時值應該比較穩定,變化不大,如果變化比較大,說明有問題。

2、接上電機,給U相設置佔空比爲5%,V、W佔空比爲0,此時可以用萬用表測量取樣電阻上的電壓值,應該已經有值。再用adc採樣相電流,計算相電流採樣極性和大小是否正常。根據正電壓產生正電流的電動機原則,U相電流應該是正的,V、W兩相電流應該是負的,且V、W兩相電流應基本相同。若正負號不對,需要進行調整。

 

(二)、測試變換程序的正確性。矢量控制的核心其實就在Clark與Park變換上,通過這兩個變換實現了直軸與交軸的解耦。如果使用的是官方的庫,可以不管本步驟。如果是自己寫的,需要通過仿真測試等手段,確保程序的正確性。

 

(三)、調試SVPWM模塊。通過SVPWM模塊可以把FOC的控制結果轉換爲定時器6個通道的佔空比,從而驅動三相逆變模塊控制定子繞組產生旋轉磁場,拖動轉子旋轉。爲了驗證SVPWM模塊的功能,需要使用上圖中的6、7即反park變換和SVPWM模塊,產生開環的旋轉磁場。

1、把FOC其他部分註釋掉,只保留反park變換和svpwm函數。

2、反park變換的輸入參數有3個:Vq、Vd、Angle,將Vq=0,Vd設置爲一個較小的值,Angle=0,然後接電機上電,此時svpwm會有輸出,電機有力,轉子被鎖定在當前電角度位置。如果沒有力,說明Vd值太小了。同時可以查看CH1、CH2、CH3的波形和svpwm的標準波形做對比。比如,處於svpwm的扇區1,那麼CH1、CH2、CH3上的波形關係如下:

你可以在仿真模式下,把CH1、CH2、CH3通道的比較值加入watch窗口,然後手動改變Vd的值。改變Vd的值,3個通道的比較值也會跟着改變。

3、將Angle由0開始,每次增加30°左右,此時電機會跟着旋轉,且每次旋轉的角度應該是相同的,記錄下這個旋轉方向,這就是此係統固有的正方向。此時還可以驗證電機的極對數,若Angle重複增加N個週期後電機回到起始點(可用記號筆進行標註),電機的極對數即爲N。

 

(四)、整定d軸、q軸PID參數。

1、把電流採樣模塊、clark變換、park變換、PID控制器加入。

2、此時系統的輸入參數有3個:PID控制器的參考量IQREF、IDREF和Angle。設置Angle=0,IQREF=0,IDREF設置爲一個較小值(可以與上面的Vd一樣)。

3、d軸和q軸都有PID控制器,將兩個PID的參數都設置爲0。之後保持q軸的PID爲0,然後整定d軸的參數(整定的過程中,可以給電機上電觀察效果,目的是使電流採樣值經過clark、park變換後的Iq、Id值接近於IQREF、IDREF)。d軸整定完後,把q軸的PID參數設置爲相同即可。

 

(五)、使用編碼器輸出角度替代Angle值

(六)、驗證電流閉環

我們先來驗證d軸,將IqRef設置爲0,IdRef設置爲一個合適的正值,此時電機是不會旋轉的,用手轉動電機也是可以轉動的,只是不同於自由轉動狀態,此時旋轉電機時會感到阻力較大,有一個力始終在維持電機處於當前位置。

驗證q軸時同理將IdRef設置爲0,IqRef設置爲一個合適的值即可。需要注意的是,因爲啓動電流明顯大於穩態電流,如果IqRef的值設置得過小,電機無法旋轉起來,而增大IqRef,使電機可以旋轉起來後,電機會一直加速到最高轉速。爲保證安全,需要對輸出電壓佔空比進行限幅。

將IqRef設置爲正值時電機應該正轉,設置爲負值時電機應該反轉,且正反轉速應該是相同的。此時還可以進一步觀察轉速採樣結果,如果和實際情況符合的話就可以進行下一步了。


總結:本文簡述了FOC的各個模塊和調試方法。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章