Board logo

标题: 用整型变量(非浮点运算)的PID算法及源程序 [打印本页]

作者: 苹果也疯狂    时间: 2015-3-15 21:39     标题: 用整型变量(非浮点运算)的PID算法及源程序

真正要用PID算法的时候,发现书上的代码在我们51上来实现还不是那么容易的事情。简单的说来,就是不能直接调用。仔细分析你可以发现,教材上的、网上现行的PID实现的C语言代码几乎都是用浮点型的数据来做的,可以想象,如果我们的计算使用浮点数据,那我们的51单片机来运行的话会有多痛苦。所以,本人自己琢磨着弄了一个整型变量来实现了PID算法,由于是用整型数来做的,所以也不是很精确,但是对于很多的使用场合,这个精度也够了。关于系数和采样电压全部是放大10倍处理的。所以精度不是很高,但是也不是那么低,大部分的场合都够用了。实在觉得精度不够,可以再放大10倍或者100倍处理,但是要注意不超出整个数据类型的范围就可以了。




本人做的是带死区控制的PID算法。




具体的参考代码参见下面:

Typedef Struct PIDValue

{

    Uint32 Ek_Uint32[3];         //差值保存,给定和反馈的差值

    Uint8  EkFlag_Uint8[3];     //符号,1则对应的Ek[I]为负数,0为对应的Ek[I]为正数

    Uint8   KP_Uint8;

Uint8   KI_Uint8;

Uint8   KD_Uint8;

Uint8   B_Uint8;     //死区电压




Uint8   KP;      //显示修改的时候用

Uint8   KI;      //

Uint8   KD;      //

Uint8   B;       //

Uint16  Uk_Uint16;    //上一时刻的控制电压

}PIDValueStr;




PIDValueStr Xdata PID;

/*******************************

**PID = Uk + (KP*E(K) - KI*E(K-1) + KD*E(K-2));

********************************/

Void    PIDProcess(Void)

{

Uint32 Idata Temp[3];  //

Uint32 Idata PostSum;  //正数和

Uint32 Idata NegSum;   //负数和

Temp[0] = 0;

    Temp[1] = 0;

    Temp[2] = 0;

PostSum = 0;

NegSum = 0;

If( ADPool.Value_Uint16[UINADCH] > ADPool.Value_Uint16[UFADCH] )  //给定大于反馈,则EK为正数

{

     Temp[0] = ADPool.Value_Uint16[UINADCH] - ADPool.Value_Uint16[UFADCH];   //计算Ek[0]

        If( Temp[0] > PID.B_Uint8 )

        {

      //数值移位

            PID.Ek_Uint32[2] = PID.Ek_Uint32[1];

            PID.Ek_Uint32[1] = PID.Ek_Uint32[0];

            PID.Ek_Uint32[0] = Temp[0];

            //符号移位

   PID.EkFlag_Uint8[2] = PID.EkFlag_Uint8[1];

   PID.EkFlag_Uint8[1] = PID.EkFlag_Uint8[0];

   PID.EkFlag_Uint8[0] = 0;                       //当前EK为正数

            Temp[0] = (Uint32)PID.KP_Uint8 * PID.Ek_Uint32[0];    // KP*EK0

            Temp[1] = (Uint32)PID.KI_Uint8 * PID.Ek_Uint32[1];    // KI*EK1

            Temp[2] = (Uint32)PID.KD_Uint8 * PID.Ek_Uint32[2];    // KD*EK2

        }

}

Else   //反馈大于给定

{

     Temp[0] = ADPool.Value_Uint16[UFADCH] - ADPool.Value_Uint16[UINADCH];   //计算Ek[0]

        If( Temp[0] > PID.B_Uint8 )

        {

      //数值移位

            PID.Ek_Uint32[2] = PID.Ek_Uint32[1];

            PID.Ek_Uint32[1] = PID.Ek_Uint32[0];

            PID.Ek_Uint32[0] = Temp[0];

            //符号移位

   PID.EkFlag_Uint8[2] = PID.EkFlag_Uint8[1];

   PID.EkFlag_Uint8[1] = PID.EkFlag_Uint8[0];

   PID.EkFlag_Uint8[0] = 1;                       //当前EK为负数

            Temp[0] = (Uint32)PID.KP_Uint8 * PID.Ek_Uint32[0];    // KP*EK0

            Temp[1] = (Uint32)PID.KI_Uint8 * PID.Ek_Uint32[1];    // KI*EK1

            Temp[2] = (Uint32)PID.KD_Uint8 * PID.Ek_Uint32[2];    // KD*EK2

        }

}




/*以下部分代码是讲所有的正数项叠加,负数项叠加*/

    If(PID.EkFlag_Uint8[0]==0)

    {

        PostSum += Temp[0];   //正数和

}

    Else

{

        NegSum += Temp[0];    //负数和

}                         // KP*EK0

    If(PID.EkFlag_Uint8[1]!=0)      

    {

        PostSum += Temp[1];   //正数和

}

Else

{

        NegSum += Temp[1];    //负数和

}                         // - KI * EK1

    If(PID.EkFlag_Uint8[2]==0)

    {

        PostSum += Temp[2];   //正数和

    }

Else

{

        NegSum += Temp[2];    //负数和

}                         // KD * EK2

    PostSum += (Uint32)PID.Uk_Uint16;        //

    If( PostSum > NegSum )             // 是否控制量为正数

    {

        Temp[0] = PostSum - NegSum;

        If( Temp[0] < (Uint32)ADPool.Value_Uint16[UMAXADCH] )   //小于限幅值则为计算值输出

  {

            PID.Uk_Uint16 = (Uint16)Temp[0];

  }

  Else

  {

            PID.Uk_Uint16 = ADPool.Value_Uint16[UMAXADCH];    //否则为限幅值输出

     }

    }

    Else               //控制量输出为负数,则输出0

    {

        PID.Uk_Uint16 = 0;

    }

}




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0