PID

从第一次训练开始我们就接触到了一个新的名词——PID控制理论。接触这个理论时间还是挺早的。一个应用的场合就是在智能小车两个轮子速度的控制上,通过pid使得两个轮子的速度尽可能接近相等。那个时候就感觉很纠结,速度控制直接左边慢一点就给左边加速,反之给右边加速。但真正当第一个题目(恒温水壶)做出来的时候就发现,如果用传统的方法去做控制(即温度高则不加温,温度低则加温),控制的提升速度与控制值的超调范围无法达到两者兼顾的效果(这也是为什么最后我们做出来的结果都不怎么好的原因之一,因为我网上百度的pid算法是个伪的。)。理论上(PID到现在为止没有调试成功过),通过PID算法能够使被控制量兼顾效率与精度这两个我们所关注的量。

PID理论说白了就是三个量。Kp,Ki,Kd。

先说下Kp,K是常量的意思,p代表的比例(proportion),Kp可以说是PID算法中必不可少的一个量。没有它控制就不可能实现。它的作用是根据当前量与设定量的差值按照一定比例放大后得到控制量。

比如一个温控系统,很自然的我们能够想到当温度越接近我们设定值时我们就越要放慢速度加热,kp就是起一个这样的作用。比如kp我设为10,我设定恒温的温度为60度,当前温度为49度,那么我输出的控制量(使用占空比对热得快实现控温)为10*(60-49)=110;如果当前温度为20度我输出为10*(60-20)=400,但由于400太大了,比最大控制值0xff(255)还大,我就直接255了。如果当前温度为61度我输出为10*(60-61)=-10<0我输出就为0。即不加热让其自然冷却。讲了这么多归纳一个公式就是UKp=Kp*(Uset-Unow),其中UKp是通过Kp这个参数计算所输出的控制量,Uset为设定温度,Unow为当前温度。

接下来就是Ki这个量,i代表的是integration,积分。空洞的说没意思,直接举栗子,依旧是温控系统,当我温度上升到59度时(设定温度是60度)这个时候我通过上面Kp得到的控制量是10,但此时环境温度很低。你10的控制量所提供的能量刚刚好能够抵消掉散失到空气中的热量,此时温度就恒定在59度了,当然这是与60度我们设定的值有了差距。这个时候KI就要开始起作用了,KI基本公式是从0到当前时刻(Uset-Unow)的积分值乘以Ki,但在单片机(计算机中也是)中只有数字量,于是分就变成了相乘,公式就成了UKi=UKi+KI*(Uset-Unow)*dt,dt是系统的采样周期,即Unow隔dt采样一次。这个值比较小,大多都不为整数,而众所周知的51单片机中浮点数运算较慢,于是我一般会将公式改为UKi=UKi+KI*(Uset-Unow)/F,F为频率,一般为比一大的整数,即F=1/dt。

回到刚才的问题,积分的作用就非常明显了。系统恒定在59度这KI*(60-59)/F随着时间不断在积分在增加,当控制量超过一定值时系统温度就会开始上升了,由于在接近我们设定的点之前(Uset-Unow)较大,而这一部分的值依旧被积分到了Uk之中,所以Ki的作用会导致最终的控制量有一定的超调,也就是说Ki设置好之后可以稍微减少Kp的值。

最后就是Kd,d代表对时间微分(differential),即速度,是对未来趋势的一种预测,比如还是恒温系统,我停止加热之后温度并不会立即停止上升(热得快有余温还有就是水受热不均所致),此时通过微分量预测上升的速度对输出的控制量进行适当缩小,可以尽量避免“冲过头”这种事情发生。公式UKd=UKd+Kd*((Uset-Unow)now-(Uset-Unow)before)/dt。(Uset-Unow)now是现在的(Uset-Unow)值,(Uset-Unow)before是在前一刻(Uset-Unow)的值。把dt依旧转化成F即UKd=UKd+Kd*((Uset-Unow)now-(Uset-Unow)before)*F,UKd即为微分所对应的控制量。

好啦。。三个量都讲完了,最终的输出控制量Uk就是这三个量之和,即UK=UKp+UKi+UKd。但在51单片机中,浮点数运算会较慢,于是除了0以外我们最小只能设定PID的三个参数为1,大多数时候这个1都还是很大的一个值。所以在得到最终的输出值时我们一般都会对这个值做移位处理(右移)即移动n位相当于除以2的n次方。调整这个n可以使得输出在一个合适的值之内。

推介阅读英文版的维基百科关于PID的页面。那里面三个图把这三个变量起的作用描述的很清楚了。。

在实际的使用中,究竟应该如何调节(或者用更加专业的话说是整定)PID控制算法的三个。

首先可以将KP,KI,KD三个常量全部设为一,观察一下系统的调节情况是不是过快。大致确定输出控制量的结果需要右移多少位来做最终的控制量。

然后将KI,KD设为0,KP从0一直逐渐增加试探,直到被控制量有一定超调,且有一定的小震荡。此时kp算调节到差不多了。我们可以继续调节Ki,通过增大Ki使被控制量最终平稳下来的值尽可能是我们设定的值。积分量ki的调节与Kp的调节相似,从小到大调整。但要注意ki的增加会使得超调量变大,所以ki增大时kp应当相应减小一点。调节完KP与Ki之后的效果就是有一定超调,但最终还是能基本稳定在设定值,最后我们就开始调节Kd。依旧是从小到大调整,通过kd的调节能使超调尽量减小。

PID控制算法还有一种调节方式——齐格勒-尼科尔斯方法。

其调试方式为,首先将积分和微分增益设置为0,然后比例增益从零开始逐渐增加,直到到达极限增益KU,此时控制器输出值以恒定值振荡。KU振荡周期TU根据不同的类型,按下表中的方式来设置比例、积分和微分增益。

Ziegler–Nichols方法[2]
控制类型 K_p K_i K_d
比例 K_u/2 - -
比例-积分 K_u/2.2 1.2K_p/T_u -
经典比例-积分-微分(PID)[3] 0.60 K_u 2K_p/T_u K_pT_u/8
Pessen Integral Rule[3] 0.7 K_u 2.5K_p/T_u 0.15K_p T_u
some overshoot[3] 0.33 K_u 2K_p/T_u K_pT_u/3K_pT_u/3

 

--维基百科 http://zh.wikipedia.org/wiki/Ziegler%E2%80%93Nichols%E6%96%B9%E6%B3%95

看上去 会比第一种会简单的多。但现在有个问题就是Ku究竟是该调整到一个什么样的值。

自己不负责任的理解:Ku应该是设定ki与kd全为0时调整kp的值,使得被控制量出现等幅震荡时此时的kp即ku。

wiki上还有提到其他两种方法,没怎么仔细看,有时间再去研究研究。


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
//pid.h
#ifndef __PID__
#define __PID__
/*PID = Uk + KP*[E(k)-E(k-1)]+KI*E(k)+KD*[E(k)-2E(k-1)+E(k-2)];(增量型PID算式)
函数入口: RK(设定值),CK(实际值),KP,KI,KD
函数出口: U(K)*/
typedef  struct  PIDValue
{
     int8 KP;
     int8 KI;
     int8 KD;
     int8 F;
     int8 BITMOV;
     int  EK[3];
     
     int  UK;
     int  RK;
     int  CK;
     int  UK_REAL;
 
}pid_str;
//PIDValueStr  PID;
void     pid_exe(pid_str *PID)  ;
#endif
 
//pid.c
/*PID = PID->UK_REAL + PID->KP*[E(k)-E(k-1)]+PID->KI*E(k)+PID->KD*[E(k)-2E(k-1)+E(k-2)];(增量型PID算式)
函数入口: PID->RK(设定值),PID->CK(实际值),PID->KP,PID->KI,PID->KD
函数出口: U(K)*/
#include"defines.h"
#include"pid.h"
#define MAXOUT 0xff
//#define MAXGAP 100
 
void  pid_exe(pid_str*PID)
{
     PID->EK[2]=PID->EK[1];
     PID->EK[1]=PID->EK[0];
     PID->EK[0]=PID->RK-PID->CK;
     PID->UK_REAL=PID->UK_REAL
         +PID->KP*(PID->EK[0]-PID->EK[1]) //微分一次后积分即原数
         +( float )PID->KI*PID->EK[0]/PID->F //直接积分
         +( float )PID->KD*(PID->EK[0]-2*PID->EK[1]+PID->EK[2])*PID->F; //二阶微分后积分即一阶微分
     if ((PID->UK_REAL>>PID->BITMOV)>=MAXOUT)
     {
         PID->UK=MAXOUT;
     } else  if (PID->UK_REAL>>PID->BITMOV<=-MAXOUT)
     {
         PID->UK=-MAXOUT;
     } else
     {
         PID->UK=PID->UK_REAL>>PID->BITMOV;
     }
         
}

这里的代码用到的是增量型的PID(即UK_REAL + PID->KP*[E(k)-E(k-1)]+PID->KI*E(k)+PID->KD*[E(k)-2E(k-1)+E(k-2)];这句话所对应的是pid控制量在之前pid控制量的基础上增加的值,相当于求了一次导)。最终输出的结果将每一次运算的值累加输出就行了。

附上摘抄的位置型pid与增量型pid的区别来。。

(1)位置式PID控制的输出与整个过去的状态有关,用到了误差的累加值;而增量式PID的输出只与当前拍和前两拍的误差有关,因此位置式PID控制的累积误差相对更大;

(2)增量式PID控制输出的是控制量增量,并无积分作用,因此该方法适用于执行机构带积分部件的对象,如步进电机等,而位置式PID适用于执行机构不带积分部件的对象,如电液伺服阀。

(3)由于增量式PID输出的是控制量增量,如果计算机出现故障,误动作影响较小,而执行机构本身有记忆功能,可仍保持原位,不会严重影响系统的工作,而位置式的输出直接对应对象的输出,因此对系统影响较大。


  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值