电机控制算法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_m}{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的各个模块和调试方法。

猜你喜欢

转载自blog.csdn.net/Viccj/article/details/106273341