说到PID算法,想必大部人并不陌生,PID算法在很多方面都有重要应用,比如电机的速度控制,恒温槽的温度控制,四轴飞行器的平衡控制等等,作为闭环控制系统中的一种重要算法,其优点和可实现性都成为人们的首选。下面简单来讲解一下PID算法:

首先PID算法是有比例,积分,微分三部分组成,先说下比例部分,所谓比例部分,就是呈线性关系,举个例子,一个电热丝加热水,开始的时候温度很低,离50℃很大,这时应该加大功率,离目标温度越大,其功率应该越大,反之越小,这就是比例部分。

乍一看,既然比例部分已经可以控制温度了为啥还需要积分和微分部分呢,难道是多此一举么?其实不然,在实际中会出现这种情况,当加热到50℃时,系统很难停止下来,而是会持续一段时间,这样就会超过预设值,所以仅有比例控制并不完美,这是就需要积分部分和微分部分。积分部分就是把之前的误差全部累加起来,这样起始时由于误差很大加热功率就大,随着接近预设值后功率开始减少,微分部分就是起始时温度增加很快,表示此时需要很大的功率,随着温度接近预设值,其斜率开始减小最后为零,意味着功率也减少,当然很难为零,一般在一定的范围内波动。

现在开始用C语言来实现PID算法:

位置式:

比例部分

    Kp:比例系数  SetValue:预设值  FactValue:当前实际值  Error_1:当前误差

则比例部分为:

    Sp  =   Kp*(SetValue – FactValue)

或者

    Sp  =  Kp*Error_1

注解:Sp大小反应需要控制的量大小,比如Sp越大,功率越大。当Sp为负值时,表示要超过预设值,如果是电机,则需要反转

积分部分

    Ki:积分系数  Error_1:当前误差  Error_2:上一次误差  Error_3:上上一次误差  ……..Error_n:开始时的误差

则积分部分为:

    Si  =  Ki*(Error_1+Error_2+Error_3+……+Error_n)

注解:因为整个是一个过程,所以上一次误差其实就是上一次的当前误差

微分部分

    Kd:微分系数  Error_1:当前误差  Error_2:上一次误差 

则微分部分为:

    Sd  =  Kd*(Error_1-Error_2)

综上部分的PID得:

    PID=Sp + Si + Sd = Kp*Error_1 + Ki*(Error_1+Error_2+Error_3+……+Error_n) + Kd*(Error_1-Error_2)

增量式

 将上述推导的PID记作时间为k时刻的PID控制量,则

    PID(k) =Sp + Si + Sd = Kp*Error_1(k) + Ki*(Error_1(k)+Error_2(k-1)+Error_3(k-2)+……+Error_n(0)) + Kd*(Error_1(k)-Error_2(k-1))        1

将上式k=k-1代入得:

    PID(k-1) =Sp + Si + Sd = Kp*Error_1(k-1) + Ki*(Error_1(k-1)+Error_2(k-2)+Error_3(k-3)+……+Error_n(0)) + Kd*(Error_1(k-1)-Error_2(k-2))               2

1-2得:

    PID(k) – PID(k-1) =  Kp*(Error_1(k)-Error_1(k-1)) + Ki*(Error_1(k)) + Kd*(Error_1(k)-2*Error_2(k-1)+Error_2(k-2))

PID(k) – PID(k-1)记作detPID

    detPID = Kp*(Error_1(k)-Error_1(k-1)) + Ki*(Error_1(k)) + Kd*(Error_1(k)-2*Error_2(k-1)+Error_2(k-2))

这样就得到了增量式的PID算法,其计算的结果为增加的控制量

增量式的PID有个好处就是只与当前三个误差量有关系,与其他无关,这样就简化的处理过程,而且提高了精度,下面是PID源码:

/*文件名:PID.h*/

#ifndef  _PID_H_
#define  _PID_H_

extern float Kp,Ki,Kd;       //系数(全局变量)
extern float AclValue; //实际值
extern float SetValue;

int PID(void);
    

#endif

 

/*########################################################################
文件名:PID.c
时间: 2018.9.7
备注:无
#########################################################################*/


#include "PID.h"

float Kp=10,Ki=0.8,Kd=0.5;  //系数

float SetValue=2000;  //设定值

float AclValue=0; //实际

float Error1=0,Error2=0,Error3=0;     //误差

/*  下面为增量式PID算法  */

/**********************************************************************************
函数名:PID
返回值:输出增量
参数:无
备注:当输出大于0表示小于预设值,当输出小于0表示大于预设值
***********************************************************************************/
int PID(void)
{
    float OutValue =0;
    Error3 = SetValue - AclValue;

    OutValue = Kp*(Error3-Error2)+Ki*(Error3)+Kd*(Error3-2*Error2+Error1);
    
    Error1=Error2;         //这部分是迭代,因为上次的误差就是上次的当前误差
    Error2=Error3;
    
    if(OutValue>3000)        //这部分是规定最大输出增量
        OutValue=3000;
    if(OutValue<-3000)
        OutValue=-3000;
    
    return OutValue;
}

下面给出计算机模拟代码;

#include "stdio.h"

float Kp=10,Ki=2,Kd=0.5;  //系数

float SetValue=1256;  //设定值

float AclValue=0; //实际

float Error1=0,Error2=0,Error3=0;     //误差

/*  下面为增量式PID算法  */

/**********************************************************************************
函数名:PID
返回值:输出增量
参数:无
备注:当输出大于0表示小于预设值,当输出小于0表示大于预设值
***********************************************************************************/
int PID(void)
{
    float OutValue =0;
    Error3 = SetValue - AclValue;

    OutValue = Kp*(Error3-Error2)+Ki*(Error3)+Kd*(Error3-2*Error2+Error1);
    
    Error1=Error2;
    Error2=Error3;
    
    return OutValue;
}


int main(void)
{
    unsigned int i=1000;
    while(i)
    {
        
        PID();  //特别注意这里:必须要运行,因为需要执行这一步:Error1=Error2; Error2=Error3;

printf("当前实际值为:%f \n",AclValue); AclValue += PID(); i--);
    } 

return 0;
}

运行结果:

 

    

 

版权声明:本文为listenscience原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/listenscience/p/9648374.html