改进初学者的PID-正反作用
最近看到了Brett Beauregard发表的有关PID的系列文章,感觉对于理解PID算法很有帮助,于是将系列文章翻译过来!在自我提高的过程中,也希望对同道中人有所帮助。作者Brett Beauregard的原文网址:http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-direction/
1、问题所在
将PID连接过程分为两组:直接作用和反向作用。到目前为止,我所展示的所有例子都是直接行动。也就是说,输出的增加会导致输入的增加。对于反向作用过程,情况正好相反。例如,在冰箱中,冷却水的增加会导致温度下降。要使初学者 PID 使用反向过程,kp、ki 和 kp 的符号都必须为负数。
这本身不是问题,但用户必须选择正确的符号,并确保所有参数都具有相同的符号。
2、解决方案
为了让这个过程简单一点,我要求 kp、ki 和 kp 都是大于等于0的。如果用户连接到反向进程,则使用SetControllerDirection函数指定反向进程。这可以确保所有参数都具有相同的符号,并使事情操作起来更直观。
3、代码
1 /*working variables*/
2 unsigned long lastTime;
3 double Input,Output,Setpoint;
4 double ITerm,lastInput;
5 double kp,ki,kd;
6 int SampleTime = 1000; //1 sec
7 double outMin,outMax;
8 bool inAuto = false;
9
10 #define MANUAL 0
11 #define AUTOMATIC 1
12
13 #define DIRECT 0
14 #define REVERSE 1
15 int controllerDirection = DIRECT;
16
17 void Compute()
18 {
19 if(!inAuto) return;
20 unsigned long now = millis();
21 int timeChange = (now - lastTime);
22 if(timeChange>=SampleTime)
23 {
24 /*Compute all the working error variables*/
25 double error = Setpoint - Input;
26 ITerm+= (ki * error);
27 if(ITerm > outMax) ITerm= outMax;
28 else if(ITerm < outMin) ITerm= outMin;
29 double dInput = (Input - lastInput);
30
31 /*Compute PID Output*/
32 Output = kp * error + ITerm- kd * dInput;
33 if(Output > outMax) Output = outMax;
34 else if(Output < outMin) Output = outMin;
35
36 /*Remember some variables for next time*/
37 lastInput = Input;
38 lastTime = now;
39 }
40 }
41
42 void SetTunings(double Kp,double Ki,double Kd)
43 {
44 if (Kp<0 || Ki<0|| Kd<0) return;
45
46 double SampleTimeInSec = ((double)SampleTime)/1000;
47 kp = Kp;
48 ki = Ki * SampleTimeInSec;
49 kd = Kd / SampleTimeInSec;
50
51 if(controllerDirection ==REVERSE)
52 {
53 kp = (0 - kp);
54 ki = (0 - ki);
55 kd = (0 - kd);
56 }
57 }
58
59 void SetSampleTime(int NewSampleTime)
60 {
61 if (NewSampleTime > 0)
62 {
63 double ratio = (double)NewSampleTime
64 / (double)SampleTime;
65 ki *= ratio;
66 kd /= ratio;
67 SampleTime = (unsigned long)NewSampleTime;
68 }
69 }
70
71 void SetOutputLimits(double Min,double Max)
72 {
73 if(Min > Max) return;
74 outMin = Min;
75 outMax = Max;
76
77 if(Output > outMax) Output = outMax;
78 else if(Output < outMin) Output = outMin;
79
80 if(ITerm > outMax) ITerm= outMax;
81 else if(ITerm < outMin) ITerm= outMin;
82 }
83
84 void SetMode(int Mode)
85 {
86 bool newAuto = (Mode == AUTOMATIC);
87 if(newAuto == !inAuto)
88 { /*we just went from manual to auto*/
89 Initialize();
90 }
91 inAuto = newAuto;
92 }
93
94 void Initialize()
95 {
96 lastInput = Input;
97 ITerm = Output;
98 if(ITerm > outMax) ITerm= outMax;
99 else if(ITerm < outMin) ITerm= outMin;
100 }
101
102 void SetControllerDirection(int Direction)
103 {
104 controllerDirection = Direction;
105 }
4、PID 完成
差不多结束了。我们已经把“初学者的PID”变成了我目前知道的最健壮的控制器。对于那些正在寻找PID库的详细解释的读者,我希望您得到了您想要的。对于那些正在编写自己的PID的人,我希望您能够收集到一些想法,这些想法可以为您节省一些时间。
最后说明两点:
- 如果这个系列中的东西看起来不对,请告诉我。我可能错过了什么,或者可能只需要在我的解释中更清楚。无论哪种方式,我都想知道。
- 这只是一个基本的PID。为了简单起见,我有意省略了许多其他问题。在我的脑海中:前馈,重置平铺,整数数学,不同的PID形式,使用速度而不是位置。如果有兴趣让我探讨这些话题,请让我知道。
欢迎关注:
如果阅读这篇文章让您略有所得,还请点击下方的【好文要顶】按钮。
当然,如果您想及时了解我的博客更新,不妨点击下方的【关注我】按钮。
如果您希望更方便且及时的阅读相关文章,也可以扫描上方二维码关注我的微信公众号【木南创智】