本文參照英飛凌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變換:
座標軸變換-park變換:
下面的公式,x可以是電壓也可以是電流
park 正變換:
park逆變換:
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)
C:保持要求扭矩時的減弱磁場
D:弱磁在極限轉矩時
E-F:MTPF(Maximum-torque-per-flux)
程序如下:
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的各個模塊和調試方法。