Skip to content

Commit 42873bb

Browse files
committed
Update AutoTune
AutoTune function is now non-blocking, no timeouts are required, exists in a sketch and uses Input and Output variables directly.
1 parent f326bbb commit 42873bb

File tree

7 files changed

+196
-207
lines changed

7 files changed

+196
-207
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# QuickPID [![arduino-library-badge](https://www.ardu-badge.com/badge/QuickPID.svg?)](https://www.ardu-badge.com/QuickPID)
22

3-
QuickPID is a fast fixed/floating point implementation of the Arduino PID library with built-in [AutoTune](https://github.com/Dlloydev/QuickPID/wiki/AutoTune) function. This controller can automatically determine and set parameters (Kp, Ki, Kd). Additionally the Ultimate Gain `Ku`, Ultimate Period `Tu`, Dead Time `td` and controllability of the process are determined. There are 9 tuning rules available to choose from. Also available is a POn setting that controls the mix of Proportional on Error to Proportional on Measurement.
3+
QuickPID is a fast fixed/floating point implementation of the Arduino PID library with built-in [AutoTune](https://github.com/Dlloydev/QuickPID/wiki/AutoTune) function. This controller can automatically determine and set parameters (Kp, Ki, Kd). Additionally the Ultimate Gain `Ku`, Ultimate Period `Tu`, Dead Time `td` and controllability of the process are determined. There are 9 tuning rules available to choose from. Also available is a POn setting that controls the mix of Proportional on Error to Proportional on Measurement.
44

55
### About
66

@@ -184,6 +184,10 @@ Use this link for reference. Note that if you're using QuickPID, there's no need
184184
185185
#### [![arduino-library-badge](https://www.ardu-badge.com/badge/QuickPID.svg?)](https://www.ardu-badge.com/QuickPID)
186186
187+
#### Version 2.2.8
188+
189+
- AutoTune is now independent of the QuickPID library and can be run from a sketch. AutoTune is now non-blocking, no timeouts are required and it uses Input and Output variables directly. See the example [AutoTune_RC_Filter.ino](https://github.com/Dlloydev/QuickPID/blob/master/examples/AutoTune_RC_Filter/AutoTune_RC_Filter.ino) for details.
190+
187191
#### Version 2.2.7
188192
189193
- Fixed REVERSE acting controller mode.

examples/AutoTune_RC_Filter/AutoTune_RC_Filter.ino

Lines changed: 166 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,59 +22,184 @@
2222
const byte inputPin = 0;
2323
const byte outputPin = 3;
2424

25-
int Print = 0; // on(1) monitor, off(0) plotter
26-
int tuningRule = 1; // see above table
27-
float POn = 1.0; // mix of PonE to PonM (0.0-1.0)
28-
unsigned long timeout = 120; // AutoTune timeout (sec)
25+
int Print = 0; // on(1) monitor, off(0) plotter
26+
int tuningRule = 1; // see above table
27+
float POn = 1.0; // mix of PonE to PonM (0.0-1.0)
28+
byte aTune = 0; // autoTune status, done = 10
2929

3030
float Input, Output, Setpoint;
3131
float Kp = 0, Ki = 0, Kd = 0;
3232

33-
// choose controller direction:
34-
// DIRECT: Input increases when the output is increased or the error is positive (heating).
35-
// REVERSE: Input decreases when the output is increased or when the error is positive (cooling).
3633
QuickPID myQuickPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, POn, QuickPID::DIRECT);
37-
//QuickPID myQuickPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, POn, QuickPID::REVERSE);
3834

39-
void setup()
40-
{
35+
void setup() {
4136
Serial.begin(115200);
4237
Serial.println();
38+
}
4339

44-
myQuickPID.AutoTune(inputPin, outputPin, tuningRule, Print, timeout);
45-
myQuickPID.SetTunings(myQuickPID.GetKp(), myQuickPID.GetKi(), myQuickPID.GetKd());
46-
myQuickPID.SetSampleTimeUs(4000); // 4ms
47-
myQuickPID.SetMode(QuickPID::AUTOMATIC);
48-
Setpoint = 700;
49-
50-
if (Print == 1) {
51-
// Controllability https://blog.opticontrols.com/wp-content/uploads/2011/06/td-versus-tau.png
52-
if (float(myQuickPID.GetTu() / myQuickPID.GetTd() + 0.0001) > 0.75) Serial.println("This process is easy to control.");
53-
else if (float(myQuickPID.GetTu() / myQuickPID.GetTd() + 0.0001) > 0.25) Serial.println("This process has average controllability.");
54-
else Serial.println("This process is difficult to control.");
55-
Serial.print("Tu: "); Serial.print(myQuickPID.GetTu()); // Ultimate Period (sec)
56-
Serial.print(" td: "); Serial.print(myQuickPID.GetTd()); // Dead Time (sec)
57-
Serial.print(" Ku: "); Serial.print(myQuickPID.GetKu()); // Ultimate Gain
58-
Serial.print(" Kp: "); Serial.print(myQuickPID.GetKp());
59-
Serial.print(" Ki: "); Serial.print(myQuickPID.GetKi());
60-
Serial.print(" Kd: "); Serial.println(myQuickPID.GetKd());
61-
Serial.println();
62-
delay(5000); //view results
40+
void loop() {
41+
Input = avg(myQuickPID.analogReadFast(inputPin));
42+
if (aTune < 10) autoTune();
43+
if (aTune == 9) { // apply new tunings
44+
myQuickPID.SetTunings(Kp, Ki, Kd);
45+
myQuickPID.SetMode(QuickPID::AUTOMATIC);
46+
Setpoint = 700;
47+
}
48+
analogWrite(outputPin, Output);
49+
if (aTune == 10) { // compute
50+
if (Print == 0) { // serial plotter
51+
Serial.print("Setpoint:"); Serial.print(Setpoint); Serial.print(",");
52+
Serial.print("Input:"); Serial.print(Input); Serial.print(",");
53+
Serial.print("Output:"); Serial.print(Output); Serial.print(",");
54+
Serial.println(",");
55+
}
56+
Input = avg(myQuickPID.analogReadFast(inputPin));
57+
myQuickPID.Compute();;
58+
analogWrite(outputPin, Output);
6359
}
6460
}
6561

66-
void loop()
67-
{ // plotter
68-
Serial.print("Setpoint:"); Serial.print(Setpoint); Serial.print(",");
69-
Serial.print("Input:"); Serial.print(Input); Serial.print(",");
70-
Serial.print("Output:"); Serial.print(Output); Serial.print(",");
71-
Serial.println(",");
62+
void autoTune() {
63+
const int Rule[10][3] =
64+
{ //ckp, cki, ckd x 1000
65+
{ 450, 540, 0 }, // ZIEGLER_NICHOLS_PI
66+
{ 600, 176, 75 }, // ZIEGLER_NICHOLS_PID
67+
{ 313, 142, 0 }, // TYREUS_LUYBEN_PI
68+
{ 454, 206, 72 }, // TYREUS_LUYBEN_PID
69+
{ 303, 1212, 0 }, // CIANCONE_MARLIN_PI
70+
{ 303, 1333, 37 }, // CIANCONE_MARLIN_PID
71+
{ 0, 0, 0 }, // AMIGOF_PID
72+
{ 700, 1750, 105 }, // PESSEN_INTEGRAL_PID
73+
{ 333, 667, 111 }, // SOME_OVERSHOOT_PID
74+
{ 200, 400, 67 }, // NO_OVERSHOOT_PID
75+
};
76+
const byte ckp = 0, cki = 1, ckd = 2; //c = column
77+
78+
const byte outputStep = 1;
79+
const byte hysteresis = 1;
80+
const int atSetpoint = 341; // 1/3 of 10-bit ADC matches 8-bit PWM value of 85 for symetrical waveform
81+
const int atOutput = 85;
7282

73-
if (myQuickPID.GetDirection() == QuickPID::REVERSE) {
74-
Input = (1023 - myQuickPID.analogReadFast(inputPin)); // simulate reverse acting controller (cooling)
75-
} else {
76-
Input = (myQuickPID.analogReadFast(inputPin));
83+
static uint32_t t0, t1, t2, t3;
84+
static float Ku, Tu, td, kp, ki, kd, rdAvg, peakHigh, peakLow;
85+
86+
switch (aTune) {
87+
case 0:
88+
peakHigh = atSetpoint;
89+
peakLow = atSetpoint;
90+
if (Print == 1) Serial.print("Stabilizing (33%) →");
91+
for (int i = 0; i < 16; i++) avg(Input); // initialize
92+
Output = 0;
93+
aTune++;
94+
break;
95+
case 1: // start coarse adjust
96+
if (avg(Input) < (atSetpoint - hysteresis)) {
97+
Output = atOutput + 20;
98+
aTune++;
99+
}
100+
break;
101+
case 2: // start fine adjust
102+
if (avg(Input) > atSetpoint) {
103+
Output = atOutput - outputStep;
104+
aTune++;
105+
}
106+
break;
107+
case 3: // run AutoTune
108+
if (avg(Input) < atSetpoint) {
109+
if (Print == 1) Serial.print(" Running AutoTune");
110+
Output = atOutput + outputStep;
111+
aTune++;
112+
}
113+
break;
114+
case 4: // get t0
115+
if (avg(Input) > atSetpoint) {
116+
t0 = micros();
117+
if (Print == 1) Serial.print("");
118+
aTune++;
119+
}
120+
break;
121+
case 5: // get t1
122+
if (avg(Input) > atSetpoint + 0.2) {
123+
t1 = micros();
124+
aTune++;
125+
}
126+
break;
127+
case 6: // get t2
128+
rdAvg = avg(Input);
129+
if (rdAvg > peakHigh) peakHigh = rdAvg;
130+
if ((rdAvg < peakLow) && (peakHigh >= (atSetpoint + hysteresis))) peakLow = rdAvg;
131+
132+
if (rdAvg > atSetpoint + hysteresis) {
133+
t2 = micros();
134+
if (Print == 1) Serial.print("");
135+
Output = atOutput - outputStep;
136+
aTune++;
137+
}
138+
break;
139+
case 7: // begin t3
140+
rdAvg = avg(Input);
141+
if (rdAvg > peakHigh) peakHigh = rdAvg;
142+
if ((rdAvg < peakLow) && (peakHigh >= (atSetpoint + hysteresis))) peakLow = rdAvg;
143+
if (rdAvg < atSetpoint - hysteresis) {
144+
if (Print == 1) Serial.print("");
145+
Output = atOutput + outputStep;
146+
aTune++;
147+
}
148+
break;
149+
case 8: // get t3
150+
rdAvg = avg(Input);
151+
if (rdAvg > peakHigh) peakHigh = rdAvg;
152+
if ((rdAvg < peakLow) && (peakHigh >= (atSetpoint + hysteresis))) peakLow = rdAvg;
153+
if (rdAvg > atSetpoint + hysteresis) {
154+
t3 = micros();
155+
if (Print == 1) Serial.println(" Done.");
156+
aTune++;
157+
td = (float)(t1 - t0) / 1000000.0; // dead time (seconds)
158+
Tu = (float)(t3 - t2) / 1000000.0; // ultimate period (seconds)
159+
Ku = (float)(4 * outputStep * 2) / (float)(3.14159 * sqrt (sq (peakHigh - peakLow) - sq (hysteresis))); // ultimate gain
160+
if (tuningRule == 6) { //AMIGO_PID
161+
(td < 10) ? td = 10 : td = td; // 10µs minimum
162+
kp = (0.2 + 0.45 * (Tu / td)) / Ku;
163+
float Ti = (((0.4 * td) + (0.8 * Tu)) / (td + (0.1 * Tu)) * td);
164+
float Td = (0.5 * td * Tu) / ((0.3 * td) + Tu);
165+
ki = kp / Ti;
166+
kd = Td * kp;
167+
} else { //other rules
168+
kp = Rule[tuningRule][ckp] / 1000.0 * Ku;
169+
ki = Rule[tuningRule][cki] / 1000.0 * Ku / Tu;
170+
kd = Rule[tuningRule][ckd] / 1000.0 * Ku * Tu;
171+
}
172+
Kp = kp;
173+
Ki = ki;
174+
Kd = kd;
175+
if (Print == 1) {
176+
// Controllability https://blog.opticontrols.com/wp-content/uploads/2011/06/td-versus-tau.png
177+
if ((Tu / td + 0.0001) > 0.75) Serial.println("This process is easy to control.");
178+
else if ((Tu / td + 0.0001) > 0.25) Serial.println("This process has average controllability.");
179+
else Serial.println("This process is difficult to control.");
180+
Serial.print("Tu: "); Serial.print(Tu); // Ultimate Period (sec)
181+
Serial.print(" td: "); Serial.print(td); // Dead Time (sec)
182+
Serial.print(" Ku: "); Serial.print(Ku); // Ultimate Gain
183+
Serial.print(" Kp: "); Serial.print(Kp);
184+
Serial.print(" Ki: "); Serial.print(Ki);
185+
Serial.print(" Kd: "); Serial.println(Kd);
186+
Serial.println();
187+
}
188+
}
189+
break;
190+
case 9: // ready to set tunings
191+
aTune++;
192+
break;
77193
}
78-
myQuickPID.Compute();
79-
analogWrite(outputPin, Output);
194+
}
195+
196+
float avg(int inputVal) {
197+
static int arrDat[16];
198+
static int pos;
199+
static long sum;
200+
pos++;
201+
if (pos >= 16) pos = 0;
202+
sum = sum - arrDat[pos] + inputVal;
203+
arrDat[pos] = inputVal;
204+
return (float)sum / 16.0;
80205
}

keywords.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
##########################################
88

99
QuickPID KEYWORD1
10-
mode_t KEYWORD1
11-
direction_t KEYWORD1
12-
analog_write_channel_t KEYWORD1
10+
myQuickPID KEYWORD1
1311

1412
##########################################
1513
# Methods and Functions (KEYWORD2)
@@ -44,3 +42,6 @@ AUTOMATIC LITERAL1
4442
MANUAL LITERAL1
4543
DIRECT LITERAL1
4644
REVERSE LITERAL1
45+
mode_t LITERAL1
46+
direction_t LITERAL1
47+
analog_write_channel_t LITERAL1

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"keywords": "PID, controller, signal",
44
"description": "A fast fixed/floating point PID controller with AutoTune and 9 tuning rules to choose from. This controller can automatically determine and set tuning parameters. Compatible with most Arduino and ESP32 boards.",
55
"license": "MIT",
6-
"version": "2.2.7",
6+
"version": "2.2.8",
77
"url": "https://github.com/Dlloydev/QuickPID",
88
"include": "QuickPID",
99
"authors":

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=QuickPID
2-
version=2.2.7
2+
version=2.2.8
33
author=David Lloyd
44
maintainer=David Lloyd <[email protected]>
55
sentence=A fast fixed/floating point PID controller with AutoTune and 9 tuning rules to choose from.

0 commit comments

Comments
 (0)