分享

PID调节C语言解析

 咖啡屋2016 2019-01-04

PID调节C语言解析

2018年03月15日 22:32:34 彼时思默 阅读数:456
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37258787/article/details/79575215

前言

老师布置了个任务,用编程实现PID调节,鉴于我们专业都学过C语言和VB,于是我就想拿Kotlin练练手.
上网搜索一番别人怎么用C实现的,找到个比较详细的,但是展示了太多数据,我这里整理一下基础知识.
Kotlin的实现在这里

简要解析

参考资料,来自这里
通过误差信号控制被控量,而控制器本身就是比例、积分、微分三个环节的加和。这里我们规定(在t时刻):

  • 输入量为rin(t);
  • 输出量为rout(t);
  • 偏差量为err(t)=rin(t)-rout(t);
  • 如果偏差为0,则比例环节不起作用,只有存在偏差时,比例环节才起作用。
  • 而微分信号则反应了偏差信号的变化规律,或者说是变化趋势,根据偏差信号的变化趋势来进行超前调节,从而增加了系统的快速性。。

位置型PID的C语言实现

第一步:定义PID变量结构体,代码如下:

struct _pid{
    float SetSpeed;            //定义设定值
    float ActualSpeed;        //定义实际值
    float err;                //定义偏差值
    float err_last;            //定义上一个偏差值
    float Kp,Ki,Kd;            //定义比例、积分、微分系数
    float voltage;          //定义电压值(控制执行器的变量)
    float integral;            //定义积分值
}pid;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

控制算法中所需要用到的参数在一个结构体中统一定义,方便后面的使用。

第二部:初始化变量,代码如下:

void PID_init(){
    printf("PID_init begin \n");
    pid.SetSpeed=0.0;
    pid.ActualSpeed=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.voltage=0.0;
    pid.integral=0.0;
    pid.Kp=0.2;
    pid.Ki=0.015;
    pid.Kd=0.2;
    printf("PID_init end \n");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

统一初始化变量,尤其是Kp,Ki,Kd三个参数,调试过程当中,对于要求的控制效果,可以通过调节这三个量直接进行调节。

第三步:编写控制算法,代码如下:

float PID_realize(float speed){
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    pid.integral+=pid.err;
    pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
    pid.err_last=pid.err;
    pid.ActualSpeed=pid.voltage*1.0;
    return pid.ActualSpeed;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注意:这里用了最基本的算法实现形式,没有考虑死区问题,没有设定上下限,只是对公式的一种直接的实现,后面的介绍当中还会逐渐的对此改进。

到此为止,PID的基本实现部分就初步完成了。下面是测试代码:

int main(){
    printf("System begin \n");
    PID_init();
    int count=0;
    while(count<1000)
    {
        float speed=PID_realize(200.0);
        printf("%f\n",speed);
        count++;
    }
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

增量型PID的C语言实现

这就是离散化PID的增量式表示方式,由公式可以看出,增量式的表达结果和最近三次的偏差有关,这样就大大提高了系统的稳定性。需要注意的是最终的输出结果应该为

   u(K)+增量调节值;
  • 1
include<stdio.h>
struct _pid{
    float SetSpeed;           //定义设定值
    float ActualSpeed;        //定义实际值
    float err;                //定义偏差值
    float err_next;           //定义上一个偏差值
    float err_last;           //定义最上前的偏差值
    float Kp,Ki,Kd;           //定义比例、积分、微分系数
}pid;

void PID_init(){
    pid.SetSpeed=0.0;
    pid.ActualSpeed=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.err_next=0.0;
    pid.Kp=0.2;
    pid.Ki=0.015;
    pid.Kd=0.2;
}

float PID_realize(float speed){
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    float incrementSpeed=pid.Kp*(pid.err-pid.err_next)+pid.Ki*pid.err+pid.Kd*(pid.err-2*pid.err_next+pid.err_last);
    pid.ActualSpeed+=incrementSpeed;
    pid.err_last=pid.err_next;
    pid.err_next=pid.err;
    return pid.ActualSpeed;
}

int main(){
    PID_init();
    int count=0;
    while(count<1000)
    {
        float speed=PID_realize(200.0);
        printf("%f\n",speed);
        count++;
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

总结:系统的稳定性大大提高

积分分离的PID控制算法C语言实现

在普通PID控制中,引入积分环节的目的,主要是为了消除静差,提高控制精度。但是在启动、结束或大幅度增减设定时,短时间内系统输出有很大的偏差,会造成PID运算的积分积累,导致控制量超过执行机构可能允许的最大动作范围对应极限控制量,从而引起较大的超调,甚至是震荡,这是绝对不允许的。

为了克服这一问题,引入了积分分离的概念,其基本思路是 当被控量与设定值偏差较大时,取消积分作用; 当被控量接近给定值时,引入积分控制,以消除静差,提高精度。其具体实现代码如下:

pid.Kp=0.2;
pid.Ki=0.04;
pid.Kd=0.2;  //初始化过程
if(abs(pid.err)>200)
{
    index=0;
    }else{
    index=1;
    pid.integral+=pid.err;
}
    pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);    //算法具体实现过程
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

总结:系统的快速性得到了提高。

抗积分饱和的PID控制算法C语言实现

所谓的积分饱和现象是指如果系统存在一个方向的偏差,PID控制器的输出由于积分作用的不断累加而加大,从而导致执行机构达到极限位置,若控制器输出U(k)继续增大,执行器开度不可能再增大,此时计算机输出控制量超出了正常运行范围而进入饱和区。一旦系统出现反向偏差,u(k)逐渐从饱和区退出。进入饱和区越深则退出饱和区时间越长。在这段时间里,执行机构仍然停留在极限位置而不随偏差反向而立即做出相应的改变,这时系统就像失控一样,造成控制性能恶化,这种现象称为积分饱和现象或积分失控现象。
防止积分饱和的方法之一就是抗积分饱和法,该方法的思路是在计算u(k)时,首先判断上一时刻的控制量u(k-1)是否已经超出了极限范围: 如果u(k-1)>umax,则只累加负偏差; 如果u(k-1)<umin,则只累加正偏差。从而避免控制量长时间停留在饱和区。

struct _pid{
    float SetSpeed;           //定义设定值
    float ActualSpeed;        //定义实际值
    float err;                //定义偏差值
    float err_last;           //定义上一个偏差值
    float Kp,Ki,Kd;           //定义比例、积分、微分系数
    float voltage;            //定义电压值(控制执行器的变量)
    float integral;           //定义积分值
    float umax;
    float umin;
}pid;

void PID_init(){
    printf("PID_init begin \n");
    pid.SetSpeed=0.0;
    pid.ActualSpeed=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.voltage=0.0;
    pid.integral=0.0;
    pid.Kp=0.2;
    pid.Ki=0.1;       //注意,和上几次相比,这里加大了积分环节的值
    pid.Kd=0.2;
    pid.umax=400;
    pid.umin=-200;
    printf("PID_init end \n");
}
float PID_realize(float speed){
    int index;
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;

   if(pid.ActualSpeed>pid.umax)  //灰色底色表示抗积分饱和的实现
    {

       if(abs(pid.err)>200)      //蓝色标注为积分分离过程
        {
            index=0;
        }else{
            index=1;
            if(pid.err<0)
            {
              pid.integral+=pid.err;
            }
        }
    }else if(pid.ActualSpeed<pid.umin){
        if(abs(pid.err)>200)      //积分分离过程
        {
            index=0;
        }else{
            index=1;
            if(pid.err>0)
            {
	            pid.integral+=pid.err;
            }
        }
    }else{
        if(abs(pid.err)>200)                    //积分分离过程
        {
            index=0;
        }else{
            index=1;
            pid.integral+=pid.err;
        }
    }

    pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);

    pid.err_last=pid.err;
    pid.ActualSpeed=pid.voltage*1.0;
    return pid.ActualSpeed;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

总结:系统的稳定时间相对前几次来讲缩短了不少。

梯形积分的PID控制算法C语言实现

先看一下梯形算法的积分环节公式
作为PID控制律的积分项,其作用是消除余差,为了尽量减小余差,应提高积分项运算精度,为此可以将矩形积分改为梯形积分,具体实现的语句为:

pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral/2+pid.Kd*(pid.err-pid.err_last);  //梯形积分
  • 1

总结:精度进一步提高。

变积分的PID控制算法C语言实现

变积分PID可以看成是积分分离的PID算法的更一般的形式。在普通的PID控制算法中,由于积分系数ki是常数,所以在整个控制过程中,积分增量是不变的。但是,系统对于积分项的要求是,系统偏差大时,积分作用应该减弱甚至是全无,而在偏差小时,则应该加强。积分系数取大了会产生超调,甚至积分饱和,取小了又不能短时间内消除静差。因此,根据系统的偏差大小改变积分速度是有必要的。
变积分PID的基本思想是设法改变积分项的累加速度,使其与偏差大小相对应:偏差越大,积分越慢; 偏差越小,积分越快。

这里给积分系数前加上一个比例值index:

当abs(err)<180时,index=1;

当180<abs(err)<200时,index=(200-abs(err))/20;

当abs(err)>200时,index=0;
  • 1
  • 2
  • 3
  • 4
  • 5

最终的比例环节的比例系数值为ki*index;
具体PID实现代码如下:

pid.Kp=0.4;
pid.Ki=0.2;    //增加了积分系数
pid.Kd=0.2;

float PID_realize(float speed){
	float index;
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;

    if(abs(pid.err)>200)           //变积分过程
    {
	    index=0.0;
    }else if(abs(pid.err)<180){
	    index=1.0;
	    pid.integral+=pid.err;
    }else{
	    index=(200-abs(pid.err))/20;
	    pid.integral+=pid.err;
    }
    pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);

    pid.err_last=pid.err;
    pid.ActualSpeed=pid.voltage*1.0;
    return pid.ActualSpeed;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

总结:系统的稳定速度非常快
我在另一篇博客https://blog.csdn.net/qq_37258787/article/details/79603352使用Kotlin语言实现上述功能,并进行了简单的数据可视化展示.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多