Skip to content

Commit 12697fc

Browse files
committed
New Version 2.2.1
- Even faster AutoTune function - AutoTune now determines the controllability of the process - Added AMIGO_PID tuning rule - Added `GetTd()` function to display dead time
1 parent ab8da72 commit 12697fc

File tree

7 files changed

+101
-78
lines changed

7 files changed

+101
-78
lines changed

QuickPID.cpp

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**********************************************************************************
2-
QuickPID Library for Arduino - Version 2.2.0
2+
QuickPID Library for Arduino - Version 2.2.1
33
by dlloydev https://github.com/Dlloydev/QuickPID
44
Based on the Arduino PID Library by Brett Beauregard
55
@@ -84,8 +84,8 @@ bool QuickPID::Compute()
8484
******************************************************************************************/
8585
void QuickPID::AutoTune(int inputPin, int outputPin, int tuningRule, int Print = 0, uint32_t timeout = 120) {
8686

87-
uint32_t stepPrevTime, stepTime;
88-
float Ku, Tu;
87+
uint32_t t0, t1, t2, t3;
88+
float Ku, Tu, td;
8989

9090
const int Rule[10][3] =
9191
{ //ckp, cki, ckd x 1000
@@ -95,7 +95,7 @@ void QuickPID::AutoTune(int inputPin, int outputPin, int tuningRule, int Print =
9595
{ 454, 206, 72 }, // TYREUS_LUYBEN_PID
9696
{ 303, 1212, 0 }, // CIANCONE_MARLIN_PI
9797
{ 303, 1333, 37 }, // CIANCONE_MARLIN_PID
98-
{ 0, 0, 0 }, // AMIGOF_PI (reserved)
98+
{ 0, 0, 0 }, // AMIGOF_PID
9999
{ 700, 1750, 105 }, // PESSEN_INTEGRAL_PID
100100
{ 333, 667, 111 }, // SOME_OVERSHOOT_PID
101101
{ 200, 400, 67 }, // NO_OVERSHOOT_PID
@@ -104,46 +104,53 @@ void QuickPID::AutoTune(int inputPin, int outputPin, int tuningRule, int Print =
104104
peakHigh = atSetpoint;
105105
peakLow = atSetpoint;
106106
timeout *= 1000;
107-
if (Print == 1) Serial.println();
108107
if (Print == 1) Serial.print("Stabilizing (33%) →");
109108
QuickPID::Stabilize(inputPin, outputPin, timeout);
110-
if (Print == 1) Serial.print(" Running AutoTune");
111-
QuickPID::StepUp(inputPin, outputPin, timeout);
112-
stepPrevTime = micros();
113-
if (Print == 1) Serial.print("");
114-
QuickPID::StepDown(inputPin, outputPin, timeout);
115-
if (Print == 1) Serial.print("");
116-
QuickPID::StepUp(inputPin, outputPin, timeout);
117-
stepTime = micros();
118-
if (Print == 1) Serial.print("");
119-
QuickPID::StepDown(inputPin, outputPin, timeout);
109+
if (Print == 1) Serial.print(" Running AutoTune");
110+
t0 = micros();
111+
112+
analogWrite (outputPin, (atOutput + outputStep)); // step up output, wait for input to reach +'ve hysteresis
113+
while ((analogReadAvg(inputPin) < (atSetpoint + 0.2)) && (millis() <= timeout)) QuickPID::CheckPeak(inputPin);
114+
t1 = micros();
115+
116+
while ((analogReadAvg(inputPin) < (atSetpoint + hysteresis)) && (millis() <= timeout)) QuickPID::CheckPeak(inputPin);
117+
t2 = micros();
118+
120119
if (Print == 1) Serial.print("");
121-
QuickPID::StepUp(inputPin, outputPin, timeout);
120+
analogWrite (outputPin, (atOutput - outputStep)); // step down output, wait for input to reach -'ve hysteresis
121+
while ((analogReadAvg(inputPin) > (atSetpoint - hysteresis)) && (millis() <= timeout)) QuickPID::CheckPeak(inputPin);
122+
122123
if (Print == 1) Serial.print("");
123-
stepTime = micros();
124-
Tu = (float)(stepTime - stepPrevTime) / 2000000.0; // ultimate period based on 2 cycles
124+
analogWrite (outputPin, (atOutput + outputStep)); // step up output, wait for input to reach +'ve hysteresis
125+
while ((analogReadAvg(inputPin) < (atSetpoint + hysteresis)) && (millis() <= timeout)) QuickPID::CheckPeak(inputPin);
126+
t3 = micros();
127+
if (Print == 1) Serial.println(" Done.");
128+
129+
td = (float)(t1 - t0) / 1000000.0; // dead time (seconds)
130+
dispTd = td;
131+
132+
Tu = (float)(t3 - t2) / 1000000.0; // ultimate period (seconds)
125133
dispTu = Tu;
134+
126135
Ku = (float)(4 * outputStep * 2) / (float)(3.14159 * sqrt (sq (peakHigh - peakLow) - sq (hysteresis))); // ultimate gain
127136
dispKu = Ku;
128137

129-
if (Print == 1) {
130-
Serial.println(); Serial.print("Ultimate Period Tu: "); Serial.print(Tu, 3); Serial.println("s");
131-
Serial.print("Ultimate Gain Ku: "); Serial.println(Ku, 3);
132-
133-
Serial.print("peakHigh: "); Serial.println(peakHigh);
134-
Serial.print("peakLow: "); Serial.println(peakLow);
138+
if (tuningRule == 6) { //AMIGO_PID
139+
(td < readPeriod) ? td = readPeriod : td = td;
140+
kp = (0.2 + 0.45 * (Tu / td)) / Ku;
141+
float Ti = (((0.4 * td) + (0.8 * Tu)) / (td + (0.1 * Tu)) * td);
142+
float Td = (0.5 * td * Tu) / ((0.3 * td) + Tu);
143+
ki = kp / Ti;
144+
kd = Td * kp;
145+
} else { //other rules
146+
kp = Rule[tuningRule][ckp] / 1000.0 * Ku;
147+
ki = Rule[tuningRule][cki] / 1000.0 * Ku / Tu;
148+
kd = Rule[tuningRule][ckd] / 1000.0 * Ku * Tu;
135149
}
136150

137-
kp = Rule[tuningRule][ckp] / 1000.0 * Ku;
138-
ki = Rule[tuningRule][cki] / 1000.0 * Ku / Tu;
139-
kd = Rule[tuningRule][ckd] / 1000.0 * Ku * Tu;
140-
141-
if (Print == 1) {
142-
Serial.print("Kp: "); Serial.print(kp, 3);
143-
Serial.print(" Ki: "); Serial.print(ki, 3);
144-
Serial.print(" Kd: "); Serial.println(kd, 3);
145-
}
146-
SetTunings(kp, ki, kd);
151+
dispKp = kp;
152+
dispKi = ki;
153+
dispKd = kd;
147154
}
148155

149156
/* SetTunings(....)************************************************************
@@ -273,6 +280,9 @@ float QuickPID::GetKu() {
273280
float QuickPID::GetTu() {
274281
return dispTu;
275282
}
283+
float QuickPID::GetTd() {
284+
return dispTd;
285+
}
276286
bool QuickPID::GetMode() {
277287
return inAuto ? AUTOMATIC : MANUAL;
278288
}
@@ -325,24 +335,11 @@ int16_t QuickPID::Saturate(int16_t Out) {
325335
return Out;
326336
}
327337

328-
void QuickPID::StepUp(int inputPin, int outputPin, uint32_t timeout) {
329-
analogWrite (outputPin, (atOutput + outputStep)); // step up output, wait for input to reach +'ve hysteresis
330-
while ((analogReadAvg(inputPin) < (atSetpoint + hysteresis)) && (millis() <= timeout)) {
331-
float rdAvg = analogReadAvg(inputPin);
332-
if (rdAvg > peakHigh) peakHigh = rdAvg;
333-
if ((rdAvg < peakLow) && (peakHigh >= (atSetpoint + hysteresis))) peakLow = rdAvg;
334-
delayMicroseconds(readPeriod);
335-
}
336-
}
337-
338-
void QuickPID::StepDown(int inputPin, int outputPin, uint32_t timeout) {
339-
analogWrite (outputPin, (atOutput - outputStep)); // step down output, wait for input to reach -'ve hysteresis
340-
while ((analogReadAvg(inputPin) > (atSetpoint - hysteresis)) && (millis() <= timeout)) {
341-
float rdAvg = analogReadAvg(inputPin);
342-
if (rdAvg > peakHigh) peakHigh = rdAvg;
343-
if ((rdAvg < peakLow) && (peakHigh >= (atSetpoint + hysteresis))) peakLow = rdAvg;
344-
delayMicroseconds(readPeriod);
345-
}
338+
void QuickPID::CheckPeak(int inputPin) {
339+
float rdAvg = analogReadAvg(inputPin);
340+
if (rdAvg > peakHigh) peakHigh = rdAvg;
341+
if ((rdAvg < peakLow) && (peakHigh >= (atSetpoint + hysteresis))) peakLow = rdAvg;
342+
delayMicroseconds(readPeriod);
346343
}
347344

348345
void QuickPID::Stabilize(int inputPin, int outputPin, uint32_t timeout) {
@@ -352,14 +349,14 @@ void QuickPID::Stabilize(int inputPin, int outputPin, uint32_t timeout) {
352349
}
353350
// coarse adjust
354351
analogWrite (outputPin, 0);
355-
while ((analogReadAvg(inputPin) > atSetpoint) && (millis() <= timeout));
356-
analogWrite(outputPin, atOutput * 2);
352+
while ((analogReadAvg(inputPin) > (atSetpoint - hysteresis)) && (millis() <= timeout));
353+
analogWrite(outputPin, atOutput + 20);
357354
while ((analogReadAvg(inputPin) < atSetpoint) && (millis() <= timeout));
358355

359356
// fine adjust
360-
analogWrite (outputPin, atOutput - outputStep - 1);
357+
analogWrite (outputPin, atOutput - outputStep);
361358
while ((analogReadAvg(inputPin) > atSetpoint) && (millis() <= timeout));
362-
analogWrite(outputPin, atOutput + outputStep + 1);
359+
analogWrite(outputPin, atOutput + outputStep);
363360
while ((analogReadAvg(inputPin) < atSetpoint) && (millis() <= timeout));
364361
analogWrite(outputPin, atOutput);
365362
}

QuickPID.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ class QuickPID
5656
float GetKp(); // These functions query the pid for interal values. They were created mainly for
5757
float GetKi(); // the pid front-end, where it's important to know what is actually inside the PID.
5858
float GetKd();
59-
float GetKu();
60-
float GetTu();
59+
float GetKu(); // Ultimate Gain
60+
float GetTu(); // Ultimate Period
61+
float GetTd(); // Dead Time
6162
bool GetMode();
6263
bool GetDirection();
6364

@@ -68,6 +69,7 @@ class QuickPID
6869
private:
6970
void Initialize();
7071
int16_t Saturate(int16_t);
72+
void CheckPeak(int);
7173
void StepUp(int, int, uint32_t);
7274
void StepDown(int, int, uint32_t);
7375
void Stabilize(int, int, uint32_t);
@@ -77,6 +79,7 @@ class QuickPID
7779
float dispKd;
7880
float dispKu;
7981
float dispTu;
82+
float dispTd;
8083

8184
float pOn; // proportional mode (0-1) default = 1, 100% Proportional on Error
8285
float kp; // (P)roportional Tuning Parameter
@@ -98,7 +101,7 @@ class QuickPID
98101

99102
// AutoTune
100103
float peakHigh, peakLow;
101-
const word readPeriod = 250;
104+
const word readPeriod = 1667;
102105
const byte outputStep = 1;
103106
const byte hysteresis = 1;
104107
const int atSetpoint = 341; // 1/3 of 10-bit ADC matches 8-bit PWM value of 85 for symetrical waveform

README.md

Lines changed: 13 additions & 5 deletions
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) and additionally determine the ultimate gain `Ku` and ultimate period `Tu`. There are 8 tuning rules available to choose from. Also, there 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

@@ -11,9 +11,9 @@ This PID controller provides a faster *read-compute-write* cycle than alternativ
1111
Development began with a fork of the Arduino PID Library. Modifications and new features have been added as described in the change log and below:
1212

1313
- Quicker hybrid fixed/floating point math in compute function
14-
- Built-in `AutoTune()` function automatically determines and sets `Kp`, `Ki` and `Kd`. and also ultimate gain `Ku` and ultimate period `Tu` of the control system. There are 8 tuning rules to choose from
15-
- [AutoTune](https://github.com/Dlloydev/QuickPID/wiki/AutoTune) uses a precise and low control effort sequence that gets results quickly
16-
- `POn` parameter now controls the setpoint weighting and mix of Proportional on Error to Proportional on Measurement
14+
- Built-in `AutoTune()` function automatically determines and sets `Kp`, `Ki` and `Kd`. and also ultimate gain `Ku` and ultimate period `Tu` of the control system. There are 9 tuning rules to choose from
15+
- [AutoTune](https://github.com/Dlloydev/QuickPID/wiki/AutoTune) uses a precise and low control effort sequence that gets results quickly. It also determines how difficult the process is to control.
16+
- `POn` parameter controls the setpoint weighting and mix of Proportional on Error to Proportional on Measurement
1717
- Reorganized and more efficient PID algorithm, faster analog read function, micros() timing resolution
1818
- Runs a complete PID cycle (*read-compute-write*) faster than just an `analogRead()` command in Arduino
1919

@@ -29,7 +29,7 @@ Development began with a fork of the Arduino PID Library. Modifications and new
2929

3030
### [AutoTune RC Filter](https://github.com/Dlloydev/QuickPID/wiki/AutoTune_RC_Filter)
3131

32-
This example allows you to experiment with the AutoTune, various tuning rules and the POn control on an RC filter.
32+
This example allows you to experiment with the AutoTune, various tuning rules and the POn control on an RC filter. It automatically determines and sets the tuning parameters.
3333

3434
#### [QuickPID WiKi ...](https://github.com/Dlloydev/QuickPID/wiki)
3535

@@ -156,6 +156,7 @@ float QuickPID::GetKi()
156156
float QuickPID::GetKd()
157157
float QuickPID::GetKu()
158158
float QuickPID::GetTu()
159+
float QuickPID::GetTd()
159160
bool QuickPID::GetMode()
160161
bool QuickPID::GetDirection()
161162
```
@@ -174,6 +175,13 @@ A faster configuration of `analogRead()`where a preset of 32 is used. If the ar
174175
175176
#### [![arduino-library-badge](https://www.ardu-badge.com/badge/QuickPID.svg?)](https://www.ardu-badge.com/QuickPID)
176177
178+
- Even faster AutoTune function
179+
- AutoTune now determines the controllability of the process
180+
- Added AMIGO_PID tuning rule
181+
- Added `GetTd()` function to display dead time
182+
183+
#### Version 2.2.0
184+
177185
- Improved AutoTune function
178186
- Added 8 tuning rules
179187
- Added `GetKu()` function to display ultimate gain

examples/AutoTune_RC_Filter/AutoTune_RC_Filter.ino

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
3 TYREUS_LUYBEN_PID Time-constant (lag) dominant processes (conservative)
1212
4 CIANCONE_MARLIN_PI Delay (dead-time) dominant processes
1313
5 CIANCONE_MARLIN_PID Delay (dead-time) dominant processes
14-
6 AMIGOF_PI TURNS CONTROLLER OFF (reserved for future version)
14+
6 AMIGO_PID More universal than ZN_PID (uses a dead time dependancy)
1515
7 PESSEN_INTEGRAL_PID Similar to ZN_PID but with better dynamic response
1616
8 SOME_OVERSHOOT_PID ZN_PID with lower proportional and integral gain
1717
9 NO_OVERSHOOT_PID ZN_PID with much lower P,I,D gain settings
@@ -22,11 +22,10 @@
2222
const byte inputPin = 0;
2323
const byte outputPin = 3;
2424

25-
// Specify the initial tuning parameters
26-
int Print = 1; // on(1), off(0)
27-
int tuningRule = 2; // see above table
28-
float POn = 1.0; // Mix of PonE to PonM (0.0-1.0)
29-
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+
unsigned long timeout = 120; // AutoTune timeout (sec)
3029

3130
int Input, Output, Setpoint;
3231
float Kp = 0, Ki = 0, Kd = 0;
@@ -36,14 +35,29 @@ QuickPID myQuickPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, POn, DIRECT);
3635
void setup()
3736
{
3837
Serial.begin(115200);
39-
myQuickPID.SetSampleTimeUs(5000); // 5ms (ideally 10x faster than shortest RC time constant under test)
4038
myQuickPID.AutoTune(inputPin, outputPin, tuningRule, Print, timeout);
39+
myQuickPID.SetTunings(myQuickPID.GetKp(), myQuickPID.GetKi(), myQuickPID.GetKd());
40+
myQuickPID.SetSampleTimeUs(5000); // recommend 5000µs (5ms) minimum
4141
myQuickPID.SetMode(AUTOMATIC);
4242
Setpoint = 700;
43+
44+
if (Print == 1) {
45+
// Controllability https://blog.opticontrols.com/wp-content/uploads/2011/06/td-versus-tau.png
46+
if (float(myQuickPID.GetTu() / myQuickPID.GetTd() + 0.0001) > 0.75) Serial.println("This process is easy to control.");
47+
else if (float(myQuickPID.GetTu() / myQuickPID.GetTd() + 0.0001) > 0.25) Serial.println("This process has average controllability.");
48+
else Serial.println("This process is difficult to control.");
49+
Serial.print("Tu: "); Serial.print(myQuickPID.GetTu()); // Ultimate Period (sec)
50+
Serial.print(" td: "); Serial.print(myQuickPID.GetTd()); // Dead Time (sec)
51+
Serial.print(" Ku: "); Serial.print(myQuickPID.GetKu()); // Ultimate Gain
52+
Serial.print(" Kp: "); Serial.print(myQuickPID.GetKp());
53+
Serial.print(" Ki: "); Serial.print(myQuickPID.GetKi());
54+
Serial.print(" Kd: "); Serial.println(myQuickPID.GetKd());
55+
delay(6000);
56+
}
4357
}
4458

4559
void loop()
46-
{
60+
{ // plotter
4761
Serial.print("Setpoint:"); Serial.print(Setpoint); Serial.print(",");
4862
Serial.print("Input:"); Serial.print(Input); Serial.print(",");
4963
Serial.print("Output:"); Serial.print(Output); Serial.print(",");

keywords.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ GetKi KEYWORD2
2424
GetKd KEYWORD2
2525
GetKu KEYWORD2
2626
GetTu KEYWORD2
27+
GetTd KEYWORD2
2728
GetMode KEYWORD2
2829
GetDirection KEYWORD2
2930
analogReadFast KEYWORD2

library.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "QuickPID",
33
"keywords": "PID, controller, signal",
4-
"description": "QuickPID is a fast fixed/floating point implementation of the Arduino PID library with built-in AutoTune function with 8 tuning rules to choose from. This controller can automatically determine and set parameters (P,I,D). The POn setting controls the mix of Proportional on Error to Proportional on Measurement.",
4+
"description": "A fast fixed/floating point PID controller with AutoTune and 8 tuning rules to choose from. This controller can automatically determine and set tuning parameters. An added feature is contolling the mix of Proportional on Error to Proportional on Measurement.",
55
"url": "https://github.com/Dlloydev/QuickPID",
66
"include": "QuickPID",
77
"authors":
88
[
99
{
10-
"name": "dlloydev"
10+
"name": "David Lloyd"
1111
}
1212
],
1313
"repository":

library.properties

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name=QuickPID
2-
version=2.2.0
3-
author=dlloydev
2+
version=2.2.1
3+
author=David Lloyd
44
maintainer=David Lloyd <[email protected]>
5-
sentence=QuickPID is a fast fixed/floating point implementation of the Arduino PID library with built-in AutoTune function with 8 tuning rules to choose from.
6-
paragraph=This controller can automatically determine and set parameters (P,I,D). The POn setting controls the mix of Proportional on Error to Proportional on Measurement.
5+
sentence=A fast fixed/floating point PID controller with AutoTune and 8 tuning rules to choose from.
6+
paragraph=This controller can automatically determine and set tuning parameters. An added feature is contolling the mix of Proportional on Error to Proportional on Measurement.
77
category=Signal Input/Output
88
url=https://github.com/Dlloydev/QuickPID
99
architectures=*

0 commit comments

Comments
 (0)