首先,要知道,PID拿来干什么?我说说我的理解。
好像一个漏水的水桶,高2米,我们要加水到1米。现在我们要往里面加水,那怎么加呢,每次加多少呢?
又好比温室大棚,我们要控制这个温度到20度,大棚会导热散热,环境的影响,都会使温度发生变化,加热器怎么加热呢?
这就用PID了。就拿漏水水桶加水来说吧。
先看看PID系统长什么样。
PID(proportion integration differentiation)其实就是指比例,积分,微分控制
理解一下这个图,
一个实际测量到的值output(即上次你执行命令后产生的),一个设定值setpoint。
Error=Setpoint-Output
进入PID系统,求和之后,输出到执行器Process。
再次测量Output,回馈给系统,再与Setpoint对比,直到Error为0。
PID系统理想方程
kp 比例系数
err(r) 理想和实际的偏差
T1 积分系数
TD 微分系数
系数都是调节作用,固定值。
err(r) 是一个变化的值。它是我们想要水桶加到1米,与实际中你每次加水总要漏掉0.2米的水桶,实际测的高度的值。
第一次加水,加进去1米,它又漏掉0.2,剩着0.8。所以实际值为0.8。
err(r) =1-0.8=0.2
静差、稳态误差
再往里加水,加kp*err(r) ,但总不能加到整整1米,液位离我们想要的高度总是差那么一点,这也就是所谓的稳态误差,或者叫静差
具体 P、I、D各是什么作用自己了解。我给出两篇不错的文章连接。
http://www.sohu.com/a/209432412_488169
https://blog.csdn.net/qq_25352981/article/details/81007075
最简单的位置型PID 代码实现:
struct _pid
{
float SetSpeed; //定义设定值
float ActualSpeed; //定义实际值
float err; //定义偏差值
float err_last; //定义上一个偏差值
float Kp,Ki,Kd; //定义比例、积分、微分系数
float voltage; //定义电压值(控制执行器的变量)
float integral; //定义积分值
}pid;
void PID_init()
{
printf("PID_init begin \n"); //开始初始化PID,其实就设定 PID系数
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");
}
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;
}
增量式PID 代码实现
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;
}