无感FOC学习笔记

clark变换/park变换和Clark逆变换/park逆变换,三相坐标系转换成αβ坐标系,然后转换成dq坐标系,让dq逼近磁链环

Park变换原理 Park变换可以将正弦变量线性化,将α一β坐标系旋转度变为d-q坐标系,d指向转子中心,Q指向切线方向,其中θ是转子当前的角度。如下图:

01086bfb3d789c60ef3b5d1148df08e8.png

/*
**Clarkb变换
**Ia+Ib+Ic=0
**等幅值变换
**Alpha=Iu
**Beta=sqrt(3)/3*Iu+x*sqrt(3)/3*Iv
*/


void CLARKE_Cale(M_CLARKE pv)
{
    pv->Alpha = -(pv->Iv + pv->Iw);
    pv->Beta =  0.5773502691896f*(pv->Iv - pv->Iw);
}


/*
**Park变换
*/
void PARK_Cale(M_PARK pv)
{
    pv->Ds = arm_cos_f32(pv->Theta) * pv->Alpha + arm_sin_f32(pv->Theta) * pv->Beta;
    pv->Qs = arm_cos_f32(pv->Theta) * pv->Beta - arm_sin_f32(pv->Theta) * pv->Alpha;
}




/*
**IPark变换
*/
void IPARK_Cale(M_IPARK pv)
{
    pv->Alpha = arm_cos_f32(pv->Theta) * pv->Ds - arm_sin_f32(pv->Theta) * pv->Qs;
    pv->Beta = arm_sin_f32(pv->Theta) * pv->Ds + arm_cos_f32(pv->Theta) * pv->Qs;
}

PI控制器

//增量式pid
void  PI_Controller(M_PI_Control  pv)
{
    /* proportional term */
    pv->err = pv->Ref - pv->Fbk; // 偏差计算


    pv->Up = pv->Kp * pv->err;


    pv->Ui = Limit_Sat(pv->Ui +(pv->Ki * pv->Up), pv->Umax, pv->Umin);
    /* integral term */
    pv->Out = Limit_Sat(pv->Up +pv->Ui, pv->Umax, pv->Umin); // 限制PI输出,超出最大值
    
//    pv->Ui +=  pv->Ki * pv->Up + pv->Out - (pv->Up + pv->Ui);
}

SvPWM

SVPWM 的理论基础是平均值等效原理,即在一个开关周期内通过对基本电压矢量加以组合,使其平均值与给定电压矢量相等。在某个时刻,电压矢量旋转到某个区域中,可由组成这个区域的两个相邻的非零矢量和零矢量在时间上的不同组合来得到。两个矢量的作用时间在一个采样周期内分多次施加,从而控制各个电压矢量的作用时间,使电压空间矢量接近按圆轨迹旋转,通过逆变器的不同开关状态所产生的实际磁通去逼近理想磁通圆,并由两者的比较结果来决定逆变器的开关状态,从而形成PWM 波形

fd1855752f34d45d505625cdbc1d059b.png

1f343afe8cdbf2d0b6cbe35b4f0ae57e.png

传统SvPWM

void SVPWM_Cale(M_SVPWM    pv)
{
    pv->u1 = pv->Ubeta;
    pv->u2 = 0.866025f*pv->Ualpha  - 0.5f*pv->Ubeta;
    pv->u3 = -0.866025f*pv->Ualpha  - 0.5f*pv->Ubeta;

    pv->VecSector = 4*((pv->u3 > 0) ? 1:0) + 2*((pv->u2 > 0) ? 1:0) + ((pv->u1 > 0) ? 1:0);

    if(pv->VecSector == 3)                            //sector 1
    {
        pv->Ta = Svpwm_Km_Backw * TS * pv->u2;
        pv->Tb = Svpwm_Km_Backw * TS * pv->u1;

        MODIFY_TX_TY(pv->Ta, pv->Tb, pv->Tc, TS);

        pv->T0 = TS - pv->Ta - pv->Tb;

        TIM1->CCR1 = (u16)(pv->T0/2/TS*TIM1_Period);
        TIM1->CCR2 = (u16)((pv->T0/2 + pv->Ta)/TS*TIM1_Period);
        TIM1->CCR3 = (u16)((pv->T0/2 + pv->Ta + pv->Tb)/TS*TIM1_Period);
    }
    else if(pv->VecSector == 1)                    //sector 2
    {
        pv->Ta = Svpwm_Km_Backw * TS * (-pv->u2);
        pv->Tb = Svpwm_Km_Backw * TS * (-pv->u3);

        MODIFY_TX_TY(pv->Ta, pv->Tb, pv->Tc, TS);

        pv->T0 = TS - pv->Ta - pv->Tb;

        TIM1->CCR1 = (u16)((pv->T0/2 + pv->Ta)/TS*TIM1_Period);
        TIM1->CCR2 = (u16)(pv->T0/2/TS*TIM1_Period);
        TIM1->CCR3 = (u16)((pv->T0/2 + pv->Ta + pv->Tb)/TS*TIM1_Period);
    }
    else if(pv->VecSector == 5)                    //sector 3
    {
        pv->Ta = Svpwm_Km_Backw * TS * pv->u1;
        pv->Tb = Svpwm_Km_Backw * TS * pv->u3;

        MODIFY_TX_TY(pv->Ta, pv->Tb, pv->Tc, TS);

        pv->T0 = TS - pv->Ta - pv->Tb;

        TIM1->CCR1 = (u16)((pv->T0/2 + pv->Ta + pv->Tb)/TS*TIM1_Period);
        TIM1->CCR2 = (u16)(pv->T0/2/TS*TIM1_Period);
        TIM1->CCR3 = (u16)((pv->T0/2 + pv->Ta)/TS*TIM1_Period);
    }
    else if(pv->VecSector == 4)                    //sector 4
    {
        pv->Ta = Svpwm_Km_Backw * TS * (-pv->u1);
        pv->Tb = Svpwm_Km_Backw * TS * (-pv->u2);

        MODIFY_TX_TY(pv->Ta, pv->Tb, pv->Tc, TS);

        pv->T0 = TS - pv->Ta - pv->Tb;

        TIM1->CCR1 = (u16)((pv->T0/2 + pv->Ta + pv->Tb)/TS*TIM1_Period);
        TIM1->CCR2 = (u16)((pv->T0/2 + pv->Ta)/TS*TIM1_Period);
        TIM1->CCR3 = (u16)(pv->T0/2/TS*TIM1_Period);
    }
    else if(pv->VecSector == 6)                    //sector 5
    {
        pv->Ta = Svpwm_Km_Backw * TS * pv->u3;
        pv->Tb = Svpwm_Km_Backw * TS * pv->u2;

        MODIFY_TX_TY(pv->Ta, pv->Tb, pv->Tc, TS);

        pv->T0 = TS - pv->Ta - pv->Tb;

        TIM1->CCR1 = (u16)((pv->T0/2 + pv->Ta)/TS*TIM1_Period);
        TIM1->CCR2 = (u16)((pv->T0/2 + pv->Ta + pv->Tb)/TS*TIM1_Period);
        TIM1->CCR3 = (u16)(pv->T0/2/TS*TIM1_Period);
    }
    else if(pv->VecSector == 2)                    //sector 6
    {
        pv->Ta = Svpwm_Km_Backw * TS * (-pv->u3);
        pv->Tb = Svpwm_Km_Backw * TS * (-pv->u1);

        MODIFY_TX_TY(pv->Ta, pv->Tb, pv->Tc, TS);

        pv->T0 = TS - pv->Ta - pv->Tb;

        TIM1->CCR1 = (u16)(pv->T0/2/TS*TIM1_Period);
        TIM1->CCR2 = (u16)((pv->T0/2 + pv->Ta + pv->Tb)/TS*TIM1_Period);
        TIM1->CCR3 = (u16)((pv->T0/2 + pv->Ta)/TS*TIM1_Period);
    }
    else
    {
        TIM1->CCR1 = 0;
        TIM1->CCR2 = 0;
        TIM1->CCR3 = 0;
    }
}

中点平移法svpwm

中点平移法svpwm则是先把电压矢量分解为三相电压,然后根据三相电压的大小上下平移电压,使电压输出范围最大化。相比较经典的svpwm实现方法,该方法计算量小,并且个人认为,原理更容易理解。

void SVPWM_Cale(M_SVPWM    pv)
{
    float Vmax_pu = 0,Vmin_pu = 0,Vcom_pu;

    float oneOverDcBus_invV = 1.0f/ADC_Sample_F_Para.VBUS;
//        float oneOverDcBus_invV = 1.0f/Standard_Work_Voltage_V;

    float Valpha_pu = pv->Ualpha*oneOverDcBus_invV;
    float Vbeta_pu = pv->Ubeta*oneOverDcBus_invV;

    float Va_tmp = (float)(0.5f) * Valpha_pu;
    float Vb_tmp = SVGEN_SQRT3_OVER_2 * Vbeta_pu;

    float Va_pu = Valpha_pu;
    float Vb_pu = -Va_tmp + Vb_tmp;                        // -0.5*Valpha + sqrt(3)/2 * Vbeta
    float Vc_pu = -Va_tmp - Vb_tmp;                        // -0.5*Valpha - sqrt(3)/2 * Vbeta

    // Find Vmax and Vmin
    if(Va_pu > Vb_pu)
    {
        Vmax_pu = Va_pu;
        Vmin_pu = Vb_pu;
    }
    else
    {
        Vmax_pu = Vb_pu;
        Vmin_pu = Va_pu;
    }

    if(Vc_pu > Vmax_pu)
    {
        Vmax_pu = Vc_pu;
    }
    else if(Vc_pu < Vmin_pu)
    {
        Vmin_pu = Vc_pu;
    }

    Vcom_pu = (float)0.5f * (Vmax_pu + Vmin_pu);            // Compute Vcom = 0.5*(Vmax+Vmin)

    // Subtract common-mode term to achieve SV modulation
    pv->Ta = (Va_pu - Vcom_pu);
    pv->Tb = (Vb_pu - Vcom_pu);
    pv->Tc = (Vc_pu - Vcom_pu);

    TIM1->CCR1 = (u16)((Limit_Sat(pv->Ta, 0.5f, -0.5f)+ 0.5f)*TIM1_Period);
    TIM1->CCR2 = (u16)((Limit_Sat(pv->Tb, 0.5f, -0.5f)+ 0.5f)*TIM1_Period);
    TIM1->CCR3 = (u16)((Limit_Sat(pv->Tc, 0.5f, -0.5f)+ 0.5f)*TIM1_Period);

    return;
}

滑模变阻抗跟踪观测器

在永磁同步电机(PMSM)的磁场定向控制(FOC)中,降低磁通量,尤其是气隙磁通量,实际上可以提高电机的运行速度。这个过程通常被称为弱磁控制或磁通减弱(Flux Weakening, FW)。在标准的恒定转矩运行区域,电机的转速与磁通量成正比,因此降低磁通量确实会减缓电机速度。但是,在恒定功率运行区域,情况则有所不同。

在恒定功率运行区域,电机的运行速度不再受限于磁通量,而是受限于逆变器能够提供的最高电压。当电机运行在基本转速之上时,由于反电动势(BEMF)随转速的增加而增加,逆变器输出电压也必须随之增加以克服不断增加的BEMF。然而,逆变器的电压输出是有限的,这就限制了电机能够达到的最高转速。

为了突破这一限制,弱磁控制通过降低气隙磁通量,减小了电机的磁通常数(KΦ)。磁通常数的减小意味着电机的反电动势(BEMF)减小,这使得在相同的逆变器输出电压下,电机可以运行在更高的转速上。在弱磁模式下,电机的d轴电流Id被设置为一个负值,这将削弱气隙磁链,进而降低磁通量。因此,尽管降低了磁通量,电机的转速反而可以提升。

值得注意的是,弱磁控制会导致电机的转矩降低,因为转矩与磁通量成正比。然而,在恒定功率运行区域,通过提高转速,可以维持电机的输出功率不变,即使是在磁通量减小的情况下。这也意味着在弱磁模式下,电机将运行在较低的转矩和较高的转速下,以保持恒定的输出功率。

此外,弱磁控制的实现涉及到一系列复杂的电机参数调整,包括d轴电流的设定、电压常数的调整以及电机电感的动态补偿等。这些参数的调整需要精确的电机模型和实时的电机状态监测,以确保弱磁控制的平稳和安全运行。在弱磁模式下,电机可能会运行在接近或超过其标称转速的两倍,但这也带来了退磁或机械损坏的风险,因此需要谨慎操作并采取相应的保护措施。

在实施弱磁控制时,逆变器的电压限制变得尤为重要。如果电机转速过高,导致BEMF超出逆变器能够支持的电压范围,那么逆变器可能会过压,从而损坏功率电子元件。因此,弱磁控制的实现往往需要逆变器具备过压保护功能,以防止在高速运行时发生过压情况

结合滑模观测器(Sliding Mode Observer, SMO)、高频注入(High Frequency Injection, HFI)与锁相环(Phase-Locked Loop, PLL)的控制策略,确实可以提供更全面的性能和鲁棒性,尤其是在永磁同步电机(PMSM)的无传感器控制领域。下面详细解释这一组合策略的优越性:

  1. 滑模观测器(SMO)的鲁棒性:SMO是一种非线性控制策略,能够处理系统不确定性、扰动和参数变化,尤其适用于需要快速响应和高精度控制的场合。SMO能够提供快速收敛到滑模面的能力,即使在外界干扰下也能保持系统的稳定性。62589ea13d8d6d0a28a222ee588a774f.jpeg

  2. 高频注入(HFI)的精确位置估计:在低速或零速时,传统的方法如反电动势(Back Electromotive Force, BEMF)很难估计转子位置,因为BEMF信号很弱或消失。HFI通过注入高频信号来激发电机的磁路饱和效应,从而获取电机的磁性特性信息,以此来估计转子位置。HFI技术在低速时表现尤为突出,能够提供准确的转子位置信息,弥补了SMO在低速时可能存在的不足。

  3. 锁相环(PLL)的速度和角度估计:PLL是一种用于同步信号的控制技术,可以跟踪并锁定到输入信号的频率和相位。在电机控制中,PLL被用于估计电机的转速和转子位置,通过跟踪反电动势的频率来实现。PLL的使用可以进一步提高转速和位置估计的精度,特别是在高速运行时,PLL可以提供稳定的转速反馈,这对于实现精确的电机控制非常重要。

综合这三种技术,SMO+HFI+PLL能够提供以下优势:

  • 全速度范围内的精确控制:HFI在低速时提供准确的位置信息,而PLL在高速时提供稳定的转速反馈,SMO则在整个速度范围内提供鲁棒性和快速响应。

  • 鲁棒性与灵活性:SMO的非线性特性使得系统对扰动和参数变化具有很好的适应性,而HFI和PLL的加入增强了在不同工况下的控制性能。

  • 无传感器控制:这种组合策略完全不需要位置或速度传感器,降低了系统成本,提高了可靠性和鲁棒性。

  • 优化性能:通过SMO的快速响应、HFI的低速性能和PLL的高速稳定性,可以实现更优的电机控制性能,特别是在需要精确转速和位置控制的应用中。

然而,实现这一策略也存在一些挑战,比如复杂的系统设计和参数调整、算法的实时计算需求等。在实际应用中,需要综合考虑系统复杂度、计算资源和实时性要求,以确保控制策略的有效性和可行性。尽管如此,SMO+HFI+PLL的组合确实为PMSM的无传感器控制提供了一个更全面、更强大的解决方案。

2015bc4863f91b6428027e84dd944aed.png

23ab849b63aafe9b9a3f642465f2bdbc.png

电阻、电感、电流、电压对位置估算误差的影响

d2a031488ab53e58312d9eb6cc14bf68.png

可以看出当电阻和电压偏大的时候,得到的估算位置超前实际位置,而电感和电流偏大的时候,估算转子位置滞后实际转子位置。结论可以归纳为:电阻和电压的误差正相关于估算位置误差角,电流和电感的误差反相关于估算位置误差角

电机定子阻值会受温度和电流频率的影响。电机在低速运行时,通常负载比较小,定子电流也较小,温升效果不明显,电阻参数本身变化不大;随着转速升高和负载增大,绕组的电流幅值和频率增加,金属导体的温升和集肤效应越来越显著,定子电阻会增加,这时候电阻压降就会产生误差,由于总的端电压值也随着转速和负载的增大而增大,其中反电势E的复制远远大于电阻压降的误差,所以电阻压降这一项可以忽略,阻值不精确估计对估算结果基本没影响

1690dec51fd30f3877bd88d2c8107be0.png

在永磁同步电机的电磁能量转换过程中,磁场在定子、气隙和转子中形成闭合回路。磁路饱和是导体磁场常见的现象,电流产生的磁场会使磁路饱和程度增加,而磁路饱和效应影响定子的电感值,电感值随着电流的增大而相应减小。永磁体固有的磁场使得d轴方向的磁路比q轴方向更饱和,因此相同的电流施加到d轴和q轴上,q轴电感变化更大;但是常用的id=0矢量控制方式是使q轴转矩电流正比于负载转矩,而d轴激磁电流保持为零,因此实际运行中q轴电感受电流的影响更显著。特别是在大负载工况下,较大的转矩电流不仅使q轴电感减小,同时也会影响d轴电感,从而影响算法的估算结果。

反电动势无位置算法中影响转子位置估算角的主要因素是q轴电感Lq

21e1a0a52b882ee31ad339645a7575bb.png

衡量一台永磁同步电机的电感变化对反电势估算结果的影响,主要看电流变化对电感饱和程度的影响,以及永磁体磁链的幅值相对于电流激磁磁链的幅值,永磁体幅值越大,电感变化对估算结果的影响就越小。一般情况,永磁体同步电机电感的电压降误差远小于反电动势E,磁饱和造成的电感变化对算法的影响也相对有限

结论1: 电阻误差正相关于估算角度误差,电感误差反相关于估算角度误差,电流幅值误差反相关于估算角度误差,电压幅值误差正相关于估算角度误差。 结论2: 估算误差主要由以下因素引起:电阻的热效应、电感的磁饱和效应、电流传感器检测误差、逆变器死区时间对相电压幅值的影响以及DSP内部设置的PW M信号输出延迟 结论3: 估算误差主要包含以下谐波成分:0次(直流)、1次(基频)、2次(倍频)、6次(6倍频)。其中直流分量由电流传感器检测的幅值增益误差以及PWM信号输出延迟引起,基频分量由电流传感器检测的零漂误差引起,2倍频分量由电流传感器检测的幅值误差引起,6倍频分量由逆变器死区时间引起。 结论4: DSP的PWM信号输出延迟是最主要的误差,它可以根据DSP寄存器设置值、电机运行速度和开关频率的值进行精确补偿。其次是电感饱和效应引起的估算误差,电感变化引起的误差与q轴电感变化量和q轴转矩电流的乘积成反正切关系,并和永磁体磁链、d轴电流幅值等因素有关;在永磁电机高速运行段(大于10%额定速度),由电阻热效应、电流传感器检测误差、逆变器死区时间造成的误差较小,可以忽略不计,主要考虑电感对估算误差的贡献。

从系统角度看,矢量控制的永磁同步电机驱动系统可以分为两部分:一部分是以永磁同步电机为主体的电磁-物理系统,电磁方程和机械方程

构成了这个系统的基本规律,他是客观存在而且不能被改变的;另外一部分是具有速度-电流双闭环结构的控制系统,他具有两级参数可调的闭环PI控制器,其目的是为了使得控制那个的参数调整规律匹配电机系统的参数变换规律,从而实现人为控制电机运行状态的目的

afbf16a00bf82b1f701f2f8026ad2657.png

在实际调试矢量控制系统的速度-电流环的PI参数时,可以首先借鉴理论推导获得的结果,但是由于电机参数不一定准确,特别是转动惯量等参数无法准确测量,理论推导获得的PI参数还需要再次在实际运行中作为体哦啊,这个过程需要遵循 “先电流内环,后速度外环” 的顺序。比例参数Kp决定了系统的高频动态性能,积分参数Ki主要影响系统的低频性能,即稳态误差特性,因此先调整比例参数,再调整积分参数。

64f6ab0504912933c9fd1a42a76ae47b.png

SMO滑膜观测器,具体公式推导可以参考彻底吃透滑模观测器(PMSM无感算法)(理论精讲+推导+算法+调参+硬件运行) - 知乎 (zhihu.com)

77286a7291dafb6ebc15eb9fab28b50a.png

公式(4)是在定子α——β  坐标系下的电机方程

根据公式(5)可以将公式(4)简写成公式(6)

f790fa3a72ceb9effb4270b99cca55b2.png

公式(6)经过简单变化可以变换为另外一种形式,式(7)

c55d808a9e63b5788dca92d314479fa7.jpeg

将式(6)的矩阵拆开成两行公式然后分别计算,计算完将 iα  iβ的时间导数写在左边然后再重新写成矩阵形式就得到式(7)

ecb99d659c9f5a472d16a79a062a4b8d.png

44d6e3d33a7e3b0eb01ac62510f07c8c.png

20e2f1ecf9a276fd5041bdddb45c2c46.jpeg

52482f87a8879fa831b832912195f521.png

2428acdccd7df9f95e61e4dfe52e2a9e.png

式(8)减去式(7)我们得到式(10)。

45edec3a668227cf62bf960d78d947b2.jpeg

5a4f1efe9cc7d2a5825ca239b22211de.png

47978b32d9e3cbadfa6655fbd9187bd3.png

ca0dee0bf0d3cd33a20f6579af9857d8.png

4d3feda70b6299835f2f05f0c20ddbc5.png

b9b457321fe31124ba41fc872f79e044.png

请注意,很多文章附带的滑模面函数图示直接复制了讲解滑模理论的书上的范例。其中滑模面函数是画在观测电流误差(横轴)和观测电流误差的时间导数(纵轴)组成的平面坐标系上,实际上电机控制滑模观测器的滑模面函数应该画在实际电流(横轴)和观测电流(纵轴)所组成的平面坐标系,就像图6一样。另外在二维的情况下,滑模面是一根线。

5f79fb7516715c6de30c626aea2172b2.jpeg

5f86680ab4d3c83af74f72ff2d0960cd.png

32773ba234504e8163c4168ade20ea2a.png

cd47c4419a67bc2d1a8d519a0108bd92.png

33fab86a65eaec7d27f0511ee83751d5.png

cbf82768a5e233ae8310d635cb7a41b2.png

50afb39f061bd9bf71615dd269518d44.png

f86d17472cade9e727f34a4f1180a235.png

5379e5a5dffe4d44168b4508cec6df60.png

fbf5dfa7b330660beda2b692bf4c4bc4.png

4cc2b662e2d71e4d3a2316cbf736d4b2.png

42c061521da65f50556cb186d02c855c.jpeg

5291c6af45e55e262968c21b095a6a1a.png

e07f495c8d29b30ad271a79d29b8c338.png

87d1cb4e74c4218640ca8e9f5b61b6cd.png

2630f36536b7a492d672d6eb52d9b9ae.png

ea4fda265de0281dd0909ada89c6342c.jpeg

c3f3fda89da2e1b810b2f4f00ee2743d.png

1、反电动势计算

Rs为相电阻 ,Ls为相电感,Est_Ialpha,Est_Ibeta为观测电流,Ealpha和Ebeta为反电动势,将计算出来的反电动势带入到PLL计算,得出观测的电角度和角速度

//观测电流
    pv->Est_Ialpha += TS*(-Motor.Rs/Motor.Ls*pv->Est_Ialpha + 1/Motor.Ls*(pv->Ualpha - pv->Zalpha));
    pv->Est_Ibeta += TS*(-Motor.Rs/Motor.Ls*pv->Est_Ibeta + 1/Motor.Ls*(pv->Ubeta -pv->Zbeta));
    //反电动势,Zalpha与Zbeta是开关信号,需要经过滤波才能得到反电动势的等效值
//    pv->Zalpha = SMO_Kslide*Sign(pv->Est_Ialpha - pv->Ialpha);
//    pv->Zbeta = SMO_Kslide*Sign(pv->Est_Ibeta - pv->Ibeta);

    pv->Ialpha_Err = pv->Est_Ialpha - pv->Ialpha;
    pv->Ibeta_Err = pv->Est_Ibeta - pv->Ibeta;

    pv->Zalpha = SMO_Kslide*Sat(pv->Ialpha_Err, 0.5f);
    pv->Zbeta = SMO_Kslide*Sat(pv->Ibeta_Err, 0.5f);
    //低通滤波
    pv->Ealpha = pv->Ealpha*LPF_SMO_B + pv->Zalpha*LPF_SMO_A;
    pv->Ebeta = pv->Ebeta*LPF_SMO_B + pv->Zbeta*LPF_SMO_A;

2、PLL模块原理

9f78e69ff423394db651698f59c89070.png

3、PLL计算

pv->Theta_Err = -(alpha*arm_cos_f32(pv->Theta)) + (-beta)*arm_sin_f32(pv->Theta);                    //通过矢量叉乘或者角度误差
    pv->Omega += pv->Kp*(pv->Theta_Err - pv->Theta_Err_last) + TS*pv->Ki*pv->Theta_Err;                //经过PI调节器获得转速,转速为弧度制电角速度
     if(Sensorless.Observer == 3)//无感控制时的观测器选择,1——HFI;3——SMO
        pv->Omega_F = pv->Omega_F*LPF_PLL_B + pv->Omega*LPF_PLL_A; //低通滤波器
    else
        IIR_Butterworth(PLL_HFI_Para.Omega, &PLL_HFI_Para.Omega_F, &WE_IIR_LPF_Par);

    pv->Omega_hz = PLL_HFI_Para.Omega_F*ONE_OVER_TWOPI;

    pv->Theta_Err_last = pv->Theta_Err;
    pv->Theta += TS*pv->Omega;    //估算角度    
    //对电角度进行限幅
    if(pv->Theta >= PIX2)
        pv->Theta -= PIX2;
    else if(pv->Theta < 0)
        pv->Theta += PIX2;
    //在SMO中,反电动势需要进行LPF,因此存在滞后,通过观测电角速度对电角度进行补偿
    SMO_theta_compersation = my_atan(my_abs(pv->Omega_F)/(-wc));
    pv->Angle = pv->Theta - SMO_theta_compersation;

高频方波信号注入HFI

f02ee7df4d326f0ad1380e0c34dd160d.png

HFI角度计算

554c7aaa96798b3c88007d760a577a9c.png

void HFI_Angle_Cale(p_HFI pv)
{
    //提取高频电流分量
    pv->ialpha_h_last = pv->ialpha_h;
    pv->ibeta_h_last = pv->ibeta_h;

    pv->ialpha_h = (CLARKE_ICurr.Alpha - pv->Ialpha_last)*0.5f;
    pv->ibeta_h = (CLARKE_ICurr.Beta - pv->Ibeta_last)*0.5f;
    pv->ialpha_f = (CLARKE_ICurr.Alpha + pv->Ialpha_last)*0.5f;
    pv->ibeta_f = (CLARKE_ICurr.Beta + pv->Ibeta_last)*0.5f;

    pv->Ialpha_last = CLARKE_ICurr.Alpha;
    pv->Ibeta_last = CLARKE_ICurr.Beta;

    pv->SIGN = -Sign(pv->Uin);

    //包络检测
    pv->Ialpha_h = (pv->ialpha_h - pv->ialpha_h_last)*pv->SIGN;
    pv->Ibeta_h = (pv->ibeta_h - pv->ibeta_h_last)*pv->SIGN;

//    pv->theta = my_atan2(pv->Ialpha_h, pv->Ibeta_h);
    PLL_Cale((p_PLL)&PLL_HFI_Para, -pv->Ibeta_h, pv->Ialpha_h);
}

d2c9a49738185caac309d61777ca91fa.png

fdf078638e8e1b8d4e74f62c6b5762bc.png

547f28f830c2730587f8704002d17b4a.png

38afc60b9e61acac5fe94788edd5067b.png

//注入的高频方波电压
    if(HFI_Uin_flag)
        HFI.Uin = HFI_Uin_offset;
    else
        HFI.Uin = -HFI_Uin_offset;

    //IPARK计算
    IPARK_PVdq.Theta = PLL_HFI_Para.Theta;
    IPARK_PVdq.Qs = 0;
    IPARK_PVdq.Ds = pi_id.Out + HFI.Uin;                //d轴高频方波注入
    IPARK_Cale((M_IPARK)&IPARK_PVdq);
else if((HFI_Clock > 600) && (HFI_Clock<=610))    //累计 高频电流分量*Id_offset
    {
        pi_id.Ref = HFI_Id_offset;
        pi_id.Fbk = HFI.Idf;
        PI_Controller((M_PI_Control)&pi_id);
//        pi_id.OutF = pi_id.OutF*LPF_HFI_B + pi_id.Out*LPF_HFI_A;
        Sum_Id1 += my_abs(HFI.Idh*HFI_Id_offset);
    }
else if((HFI_Clock > 1000) && (HFI_Clock <= 1010))    //累计 高频电流分量*Id_offset
    {
        pi_id.Ref = -HFI_Id_offset;
        pi_id.Fbk = HFI.Idf;
        PI_Controller((M_PI_Control)&pi_id);
//        pi_id.OutF = pi_id.OutF*LPF_HFI_B + pi_id.Out*LPF_HFI_A;
        Sum_Id2 += my_abs(-HFI.Idh*HFI_Id_offset);
    }
if(Sum_Id1 < Sum_Id2)                                                                //收敛在S极,对角度进行补偿
     {
        HFI.theta_Init = PLL_HFI_Para.Theta + PI;
        if(HFI.theta_Init > PIX2) HFI.theta_Init -= PIX2;
//        HFI.theta = HFI.theta_Init;
        PLL_HFI_Para.Theta = HFI.theta_Init;
     }

I/F开环启动

19a100e15bdc9fc7e76bb1fd16a6c00a.png

174d344456412b5d0a3fb62b865ad787.png

VF开环启动

void IF_Vq_Control(void)    //控制模式:VF启动
{
    float PWM_Step_Angle_IF=0;
    float IF_Freq_EX=0;
    float Us_Limit=0;

    CAN_ControlPara.IF_Freq= (ADCSampPara.RP_speed_Voltage*0.6f-100)*0.06f;   //电转速指令转换
    if( CAN_ControlPara.IF_Freq>80)  //限制电最大频率
    CAN_ControlPara.IF_Freq = 80;
    IF_Freq_GXieLv.XieLv_X = CAN_ControlPara.IF_Freq;     // 电转速的目标值

    IF_Freq_GXieLv.XieLv_Grad = 0.163;  //0.1  //电机电角度斜坡处理的增量,即虚拟电机转速的加速度值。
    //0.162:最终转速指令800rpm,实测电机反馈也为800rpm
    IF_Freq_GXieLv.Grad_Timer = 60;    //60 //斜坡处理的周期,66.6667*60 = 4ms


    CAN_ControlPara.CANV_q = (ADCSampPara.RP_speed_Voltage*0.6f-100)*0.006f; //Vq给定值来源
    //CAN_ControlPara.CANV_q为加速阶段,速度给定的起始值,由后面斜坡处理后,供程序使用。
    if(CAN_ControlPara.CANV_q>9.0f)   
    CAN_ControlPara.CANV_q = 9.0f; 
    IF_Vq_GXieLv.XieLv_X = CAN_ControlPara.CANV_q;      // Vq的目标值

    IF_Vq_GXieLv.XieLv_Grad= 0.0092;    //0.1  //定义Vq的梯度值
    //实测800rpm,需要Vq=2.2V。0.0092:最终Vq输出为2.22.
    IF_Vq_GXieLv.Grad_Timer = 60;      //60   //定义Vq的梯度时间 


    IF_Freq_GXieLv.Timer_Count++;
    if(IF_Freq_GXieLv.Timer_Count>IF_Freq_GXieLv.Grad_Timer)   //每4ms规划一次频率和Vq
    {
        IF_Freq_GXieLv.Timer_Count=0;
        Grad_XieLv((p_GXieLv)&IF_Freq_GXieLv);   //频率:控制电机角度自++的变量
        Grad_XieLv((p_GXieLv)&IF_Vq_GXieLv);     //Vq开环指令的斜坡处理
    }

//    LS1=IF_Freq_GXieLv.XieLv_X ;
//    LS2=IF_Freq_GXieLv.XieLv_Y;

//    LS1=IF_Vq_GXieLv.XieLv_X ;
//    LS2=IF_Vq_GXieLv.XieLv_Y;



    IF_Freq_EX = Limit_Sat(IF_Freq_GXieLv.XieLv_Y,80,2);  //对频率限幅
    PWM_Step_Angle_IF =(float)(4096*IF_Freq_EX)/PWM_FREQ;
    VF_Angle+= PWM_Step_Angle_IF;
    if(VF_Angle>4095)
    VF_Angle-=4095; 
    VF_AngleJZ =(uint16_t)VF_Angle;   //虚拟的电机电角度

    pi_id.Ref = 0;   //id=0的d轴闭环启动,力矩较大
    pi_id.Fbk = PARK_PCurr.Ds ;
    PI_Controller((p_PI_Control)&pi_id);
    pi_id.OutF = pi_id.OutF*GM_Low_Lass_A+pi_id.Out*GM_Low_Lass_B;
    V_d = pi_id.OutF;    

    Us_Limit=(IQSqrt(Vdc_s*Vdc_s*100-V_d*V_d*100))*0.1;  
    V_q = IF_Vq_GXieLv.XieLv_Y;      //q轴开环给定Vq,电压较稳定
    V_q = Min(Us_Limit,V_q);

开环转闭环

void Speed_Vq_Control(void)    //控制模式:速度闭环
{
    float Us_Limit=0;

    SpeedRpm_GXieLv.XieLv_Grad = 1;   //2  // 速度输入的梯度计算
    SpeedRpm_GXieLv.Grad_Timer = 30;  //30 // 2ms速度环计算

    IF_Vq_GXieLv.XieLv_Grad = 0.01f ;    //0.1 //速度PI的积分量的梯度计算
    IF_Vq_GXieLv.Grad_Timer = 60 ;      //60   //速度PI的积分量的梯度时间

    CAN_ControlPara.CANV_q = (ADCSampPara.RP_speed_Voltage*0.6f-100)*0.0041f;
    CAN_ControlPara.SpeedRPM=(ADCSampPara.RP_speed_Voltage*0.8f-100); //3680*0.8-100  2844RPM;//速度控制模式速度输入

    if( CAN_ControlPara.SpeedRPM>Motor_MAX_Speed_RPM)
    CAN_ControlPara.SpeedRPM = Motor_MAX_Speed_RPM ;
    if(CAN_ControlPara.CANV_q>9.0f)
    CAN_ControlPara.CANV_q = 9.0f ;   

    SpeedRpm_GXieLv.XieLv_X = CAN_ControlPara.SpeedRPM;     // 

    IF_Vq_GXieLv.XieLv_X = CAN_ControlPara.CANV_q;         // 6.5v  

    SpeedRpm_GXieLv.Timer_Count++;
    if(SpeedRpm_GXieLv.Timer_Count>SpeedRpm_GXieLv.Grad_Timer)  //66.6667*30 = 2ms
    {
        SpeedRpm_GXieLv.Timer_Count=0;
        Grad_XieLv((p_GXieLv)&SpeedRpm_GXieLv);  //速度指令斜坡处理函数
        Grad_XieLv((p_GXieLv)&IF_Vq_GXieLv);     //Vq指令斜坡处理函数

        pi_spd.Ref = SpeedRpm_GXieLv.XieLv_Y;    // 速度环给定值
        pi_spd.Fbk = Speed_estPare.Speed_RPM ;   // 速度反馈值

        pi_spd.ui  = IF_Vq_GXieLv.XieLv_Y;      // ui为积分值,i1为上一刻积分值
        pi_spd.i1  = pi_spd.ui;
        pi_spd.v1  = pi_spd.ui;                 //v1为的比例+积分
        PI_Controller((p_PI_Control)&pi_spd);  // 速度环PI计算
        pi_spd.OutF = pi_spd.OutF*GM_Low_Lass_A+pi_spd.Out*GM_Low_Lass_B; //环路滤波后输出

//        pi_vf.Ref = SpeedRpm_GXieLv.XieLv_Y;                      // 加速最终的指令
//        pi_vf.Fbk = Speed_estPare.Speed_RPM ;   // 加速阶段速度反馈值
// 
//        PI_Controller((p_PI_Control)&pi_vf);  // 速度环PI计算
//        pi_vf.OutF = pi_vf.OutF*GM_Low_Lass_A+pi_vf.Out*GM_Low_Lass_B; //环路滤波后输出
//        pi_spd.OutF = pi_vf.OutF*0.05f+ pi_spd.OutF *0.95f;

    }

    pi_id.Ref = 0;    //id=0控制,d轴电流闭环
    pi_id.Fbk = PARK_PCurr.Ds ;
    PI_Controller((p_PI_Control)&pi_id);
    pi_id.OutF=pi_id.OutF*GM_Low_Lass_A+pi_id.Out*GM_Low_Lass_B;
    V_d = pi_id.OutF;   

    Us_Limit=(IQSqrt(Vdc_s*Vdc_s*100-V_d*V_d*100))*0.1;
    V_q = pi_spd.OutF;        //速度环输出直接为Vq,没有q轴电流环
    V_q = Min(Us_Limit,V_q);     
}

磁链观测器,可实现低速闭环启动

if (cnt < max_time) {
            float vq_avg = 0.0;
            float vd_avg = 0.0;
            float iq_avg = 0.0;
            float id_avg = 0.0;
            float samples2 = 0.0;

            for (int i = 0;i < 10000;i++) {
                vq_avg += mcpwm_foc_get_vq();
                vd_avg += mcpwm_foc_get_vd();
                iq_avg += mcpwm_foc_get_iq();
                id_avg += mcpwm_foc_get_id();
                samples2 += 1.0;
                chThdSleep(1);

                fault = mc_interface_get_fault();
                if (fault != FAULT_CODE_NONE) {
                    break;
                }
            }

            vq_avg /= samples2;
            vd_avg /= samples2;
            iq_avg /= samples2;
            id_avg /= samples2;

            float rad_s = RPM2RADPS_f(rpm_now); //角速度
            float v_mag = NORM2_f(vq_avg, vd_avg); //相电压
            float i_mag = NORM2_f(iq_avg, id_avg); //相电流
            *linkage = (v_mag - res * i_mag) / rad_s - i_mag * ind; //计算磁链
// See http://cas.ensmp.fr/~praly/Telechargement/Journaux/2010-IEEE_TPEL-Lee-Hong-Nam-Ortega-Praly-Astolfi.pdf
void foc_observer_update(float v_alpha, float v_beta, float i_alpha, float i_beta,
        float dt, observer_state *state, float *phase, motor_all_state_t *motor) {

    mc_configuration *conf_now = motor->m_conf;

    float R = conf_now->foc_motor_r;
    float L = conf_now->foc_motor_l;
    float lambda = conf_now->foc_motor_flux_linkage;

    // Saturation compensation
    switch(conf_now->foc_sat_comp_mode) {
    case SAT_COMP_LAMBDA: //根据磁链的估计值调整电感
        // Here we assume that the inductance drops by the same amount as the flux linkage. I have
        // no idea if this is a valid or even a reasonable assumption.
        if (conf_now->foc_observer_type >= FOC_OBSERVER_ORTEGA_LAMBDA_COMP) {
            L = L * (state->lambda_est / lambda);
        }
        break;

    case SAT_COMP_FACTOR: { //使用饱和补偿因子来调整电感和磁链。
        const float comp_fact = conf_now->foc_sat_comp * (motor->m_motor_state.i_abs_filter / conf_now->l_current_max);
        L -= L * comp_fact;
        lambda -= lambda * comp_fact;
    } break;

    case SAT_COMP_LAMBDA_AND_FACTOR: { //结合以上两种方式
        if (conf_now->foc_observer_type >= FOC_OBSERVER_ORTEGA_LAMBDA_COMP) {
            L = L * (state->lambda_est / lambda);
        }
        const float comp_fact = conf_now->foc_sat_comp * (motor->m_motor_state.i_abs_filter / conf_now->l_current_max);
        L -= L * comp_fact;
    } break;

    default:
        break;
    }

    // Temperature compensation  温度补偿
    if (conf_now->foc_temp_comp) {
        R = motor->m_res_temp_comp;
    }

    float ld_lq_diff = conf_now->foc_motor_ld_lq_diff;
    float id = motor->m_motor_state.id;
    float iq = motor->m_motor_state.iq;

    // Adjust inductance for saliency.
    if (fabsf(id) > 0.1 || fabsf(iq) > 0.1) {
        L = L - ld_lq_diff / 2.0 + ld_lq_diff * SQ(iq) / (SQ(id) + SQ(iq));
    }
    //之前都是准备工作,下面正式开始工作
    float L_ia = L * i_alpha;                 //电流产生的磁链
    float L_ib = L * i_beta;
    const float R_ia = R * i_alpha;            //电阻产生的电压
    const float R_ib = R * i_beta;
    const float gamma_half = motor->m_gamma_now * 0.5;   //gammashi 观测器增益

    switch (conf_now->foc_observer_type) {
    case FOC_OBSERVER_ORTEGA_ORIGINAL: {   //最基本的Ortega观测器,它基于电机模型和观测器增益更新磁链状态
    /*
        更新策略
        计算误差:
            计算磁链λ的平方与观测到的磁链x1和x2减去由电流产生的磁链L_ia和L_ib的平方之和之间的差值。
        强制误差负值:
            如果误差大于0,则将其设置为0。这是为了避免磁链幅值过早收敛到0,有助于收敛。
        状态更新:
            根据电压v_alpha和v_beta、电流在相电阻上产生的电压R_ia和R_ib以及观测器增益gamma_half和误差项来更新x1和x2。
    */
        float err = SQ(lambda) - (SQ(state->x1 - L_ia) + SQ(state->x2 - L_ib));

        // Forcing this term to stay negative helps convergence according to
        //
        // http://cas.ensmp.fr/Publications/Publications/Papers/ObserverPermanentMagnet.pdf
        // and
        // https://arxiv.org/pdf/1905.00833.pdf
        if (err > 0.0) {
            err = 0.0;
        }

        float x1_dot = v_alpha - R_ia + gamma_half * (state->x1 - L_ia) * err;
        float x2_dot = v_beta - R_ib + gamma_half * (state->x2 - L_ib) * err;

        state->x1 += x1_dot * dt;
        state->x2 += x2_dot * dt;
    } break;

    case FOC_OBSERVER_MXLEMMING:
    case FOC_OBSERVER_MXLEMMING_LAMBDA_COMP: //基于MxLemming的观测器,它们使用电机模型和观测器增益来更新磁链状态,并可能包括磁链连结的补偿
    /*
        更新策略
        磁链连结更新:
            计算磁链λ的平方与观测到的磁链x1和x2的平方之和之间的差值。
            根据观测器增益gamma_half和误差项更新磁链连结λ_est。
        限制磁链连结:
            将磁链λ_est的值限制在一定范围内,通常是λ的0.3倍至2.5倍之间。
        状态更新:
            使用电压v_alpha和v_beta、电流产生的电压R_ia和R_ib以及电感L和电流变化来更新x1和x2。
        限制磁链状态:
            将x1和x2的绝对值限制在λ_est的范围内。
    */
        state->x1 += (v_alpha - R_ia) * dt - L * (i_alpha - state->i_alpha_last);
        state->x2 += (v_beta - R_ib) * dt - L * (i_beta - state->i_beta_last);

        if (conf_now->foc_observer_type == FOC_OBSERVER_MXLEMMING_LAMBDA_COMP) {
            // This is essentially the flux linkage observer from
            // https://cas.mines-paristech.fr/~praly/Telechargement/Conferences/2017_IFAC_Bernard-Praly.pdf
            // with a slight modification. We use the same gain here as it is related to the Ortega-gain,
            // but we scale it down as it is not nearly as critical because the flux linkage is mostly DC.
            // When the motor starts to saturate we still want to be able to keep up though, so the gain is
            // still high enough to react with some "reasonable" speed.
            float err = SQ(state->lambda_est) - (SQ(state->x1) + SQ(state->x2));
            state->lambda_est += 0.1 * gamma_half * state->lambda_est * -err * dt;

            // Clamp the observed flux linkage (not sure if this is needed)
            utils_truncate_number(&(state->lambda_est), lambda * 0.3, lambda * 2.5);

            utils_truncate_number_abs(&(state->x1), state->lambda_est);
            utils_truncate_number_abs(&(state->x2), state->lambda_est);
        } else {
            utils_truncate_number_abs(&(state->x1), lambda);
            utils_truncate_number_abs(&(state->x2), lambda);
        }

        // Set these to 0 to allow using the same atan2-code as for Ortega
        L_ia = 0.0;
        L_ib = 0.0;
        break;

    case FOC_OBSERVER_ORTEGA_LAMBDA_COMP: { //观测器结合了Ortega观测器和磁链连结补偿
        /*
        更新策略
        磁链连结更新:
            计算磁链λ_est的平方与观测到的磁链x1和x2减去由电流产生的磁链L_ia和L_ib的平方之和之间的差值。
            根据观测器增益gamma_half和误差项更新磁链连结λ_est。
        限制磁链连结:
            将磁链λ_est的值限制在一定范围内,通常是λ的0.3倍至2.5倍之间。
        状态更新:
            使用电压v_alpha和v_beta、电流产生的电压R_ia和R_ib以及观测器增益gamma_half和误差项来更新x1和x2
        */
        float err = SQ(state->lambda_est) - (SQ(state->x1 - L_ia) + SQ(state->x2 - L_ib));

        // FLux linkage observer. See:
        // https://cas.mines-paristech.fr/~praly/Telechargement/Conferences/2017_IFAC_Bernard-Praly.pdf
        state->lambda_est += 0.2 * gamma_half * state->lambda_est * -err * dt;

        // Clamp the observed flux linkage (not sure if this is needed)
        utils_truncate_number(&(state->lambda_est), lambda * 0.3, lambda * 2.5);

        if (err > 0.0) {
            err = 0.0;
        }

        float x1_dot = v_alpha - R_ia + gamma_half * (state->x1 - L_ia) * err;
        float x2_dot = v_beta - R_ib + gamma_half * (state->x2 - L_ib) * err;

        state->x1 += x1_dot * dt;
        state->x2 += x2_dot * dt;
    } break;

    default:
        break;
    }

    state->i_alpha_last = i_alpha;
    state->i_beta_last = i_beta;

    UTILS_NAN_ZERO(state->x1);
    UTILS_NAN_ZERO(state->x2);

    // Prevent the magnitude from getting too low, as that makes the angle very unstable.
    float mag = NORM2_f(state->x1, state->x2);
    if (mag < (lambda * 0.5)) {
        state->x1 *= 1.1;
        state->x2 *= 1.1;
    }

    if (phase) {
        *phase = utils_fast_atan2(state->x2 - L_ib, state->x1 - L_ia);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值