brewco/pid.c

changeset 487
d5bc44183aa4
parent 486
5a237a99a793
child 488
bee1f70fb42b
equal deleted inserted replaced
486:5a237a99a793 487:d5bc44183aa4
1 /*****************************************************************************
2 * Copyright (C) 2015
3 *
4 * Michiel Broek <mbroek at mbse dot eu>
5 *
6 * This file is part of the mbsePi-apps
7 *
8 * This is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * mbsePi-apps is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with ThermFerm; see the file COPYING. If not, write to the Free
20 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * Based on the Arduino PID Library 1.1.1 by Brett Beauregard <br3ttb@gmail.com>
23 * This Library is licensed under a GPLv3 License
24 *****************************************************************************/
25
26 #include "brewco.h"
27 #include "pid.h"
28 #include "util.h"
29
30
31 /*
32 * The parameters specified here are those for for which we can't set up
33 * reliable defaults, so we need to have the user set them.
34 */
35 void PID_init(pid_var *pid, double *Input, double *Output, double *Setpoint, double Kp, double Ki, double Kd, int ControllerDirection)
36 {
37 pid->myOutput = Output;
38 pid->myInput = Input;
39 pid->mySetpoint = Setpoint;
40 pid->inAuto = FALSE;
41 PID_setOutputLimits(pid, 0, 255);
42 pid->SampleTime = 100;
43 PID_setDirection(pid, ControllerDirection);
44 PID_setTunings(pid, Kp, Ki, Kd);
45 pid->lastTime = millis() - pid->SampleTime;
46 }
47
48
49
50 /*
51 * Allows the controller Mode to be set to manual (0) or Automatic (non-zero)
52 * when the transition from manual to auto occurs, the controller is
53 * automatically initialized
54 */
55 void PID_setMode(pid_var *pid, int Mode)
56 {
57 int newAuto = (Mode == P_AUTOMATIC);
58
59 if (newAuto != pid->inAuto) {
60 /*
61 * we just went from manual to auto
62 */
63 pid->ITerm = *pid->myOutput;
64 pid->lastInput = *pid->myInput;
65 if (pid->ITerm > pid->outMax)
66 pid->ITerm = pid->outMax;
67 else if (pid->ITerm < pid->outMin)
68 pid->ITerm = pid->outMin;
69 /*
70 * If turned to manual, turn output off.
71 */
72 if (Mode == P_MANUAL)
73 *pid->myOutput = pid->outMin;
74 }
75 pid->inAuto = newAuto;
76 }
77
78
79
80 /*
81 * This function will be used far more often than SetInputLimits. while
82 * the input to the controller will generally be in the 0-1023 range (which is
83 * the default already,) the output will be a little different. maybe they'll
84 * be doing a time window and will need 0-8000 or something. or maybe they'll
85 * want to clamp it from 0-125. who knows. at any rate, that can all be done
86 * here.
87 */
88 void PID_setOutputLimits(pid_var *pid, double Min, double Max)
89 {
90 if (Min >= Max)
91 return;
92
93 pid->outMin = Min;
94 pid->outMax = Max;
95
96 if (pid->inAuto == P_AUTOMATIC) {
97 if (*pid->myOutput > pid->outMax)
98 *pid->myOutput = pid->outMax;
99 else if (*pid->myOutput < pid->outMin)
100 *pid->myOutput = pid->outMin;
101
102 if (pid->ITerm > pid->outMax)
103 pid->ITerm = pid->outMax;
104 else if (pid->ITerm < pid->outMin)
105 pid->ITerm = pid->outMin;
106 }
107 }
108
109
110
111 /*
112 * This function allows the controller's dynamic performance to be adjusted.
113 * it's called automatically from the constructor, but tunings can also
114 * be adjusted on the fly during normal operation.
115 */
116 void PID_setTunings(pid_var *pid, double Kp, double Ki, double Kd)
117 {
118 if (Kp < 0 || Ki < 0 || Kd < 0)
119 return;
120 pid->dispKp = Kp;
121 pid->dispKi = Ki;
122 pid->dispKd = Kd;
123
124 double SampleTimeInSec = ((double)pid->SampleTime) / 1000;
125 pid->Kp = Kp;
126 pid->Ki = Ki * SampleTimeInSec;
127 pid->Kd = Kd / SampleTimeInSec;
128
129 if (pid->Direction == P_REVERSE) {
130 pid->Kp = (0 - pid->Kp);
131 pid->Ki = (0 - pid->Ki);
132 pid->Kd = (0 - pid->Kd);
133 }
134 }
135
136
137
138 /*
139 * The PID will either be connected to a DIRECT acting process (+Output leads
140 * to +Input) or a REVERSE acting process(+Output leads to -Input.) we need to
141 * know which one, because otherwise we may increase the output when we should
142 * be decreasing. This is called from PID_init().
143 */
144 void PID_setDirection(pid_var *pid, int Direction)
145 {
146 if (pid->inAuto && Direction != pid->Direction) {
147 pid->Kp = (0 - pid->Kp);
148 pid->Ki = (0 - pid->Ki);
149 pid->Kd = (0 - pid->Kd);
150 }
151 pid->Direction = Direction;
152 }
153
154
155
156 /*
157 * sets the period, in Milliseconds, at which the calculation is performed.
158 */
159 void PID_setSampleTime(pid_var *pid, int NewSampleTime)
160 {
161 if (NewSampleTime > 0) {
162 double ratio = (double)NewSampleTime / (double)pid->SampleTime;
163
164 pid->Ki *= ratio;
165 pid->Kd /= ratio;
166 pid->SampleTime = NewSampleTime;
167 }
168 }
169
170
171
172 /*
173 * Just because you set the Kp=-1 doesn't mean it actually happened. these
174 * functions query the internal state of the PID. they're here for display
175 * purposes. this are the functions the PID Front-end uses for example
176 */
177 double PID_getKp(pid_var *pid)
178 {
179 return pid->dispKp;
180 }
181
182
183
184 double PID_getKi(pid_var *pid)
185 {
186 return pid->dispKi;
187 }
188
189
190
191 double PID_getKd(pid_var *pid)
192 {
193 return pid->dispKd;
194 }
195
196
197
198 int PID_getMode(pid_var *pid)
199 {
200 return pid->inAuto ? P_AUTOMATIC : P_MANUAL;
201 }
202
203
204
205 int PID_getDirection(pid_var *pid)
206 {
207 return pid->Direction;
208 }
209
210
211
212 int PID_getSampleTime(pid_var *pid)
213 {
214 return pid->SampleTime;
215 }
216
217
218
219 /*
220 * This, as they say, is where the magic happens. this function should be called
221 * every time "void loop()" executes. the function will decide for itself whether a new
222 * pid Output needs to be computed. returns true when the output is computed,
223 * false when nothing has been done.
224 */
225 int PID_compute(pid_var *pid)
226 {
227 if (! pid->inAuto)
228 return FALSE;
229
230 long now = millis();
231 long timeChange = (now - pid->lastTime);
232
233 if (timeChange >= pid->SampleTime) {
234 /*
235 * Compute all the working error variables
236 */
237 double input = *pid->myInput;
238 double error = *pid->mySetpoint - input;
239 pid->ITerm += (pid->Ki * error);
240 if (pid->ITerm > pid->outMax)
241 pid->ITerm = pid->outMax;
242 else if (pid->ITerm < pid->outMin)
243 pid->ITerm = pid->outMin;
244 double dInput = input - pid->lastInput;
245
246 /*
247 * Compute PID output
248 */
249 double output = pid->Kp * error + pid->ITerm - pid->Kd * dInput;
250 if (output > pid->outMax)
251 output = pid->outMax;
252 else if (output < pid->outMin)
253 output = pid->outMin;
254 *pid->myOutput = output;
255
256 /*
257 * Remember some variables for the next time
258 */
259 pid->lastInput = input;
260 pid->lastTime = now;
261 return TRUE;
262 }
263
264 return FALSE;
265 }
266
267

mercurial