I've been trying to simulate a PID using Brett Beauregard's PID library in order to understand it better. Here is my code.
#include <PID_v1.h>
const unsigned int numReadings = 500;
double analogVals[numReadings];
unsigned int i = 0;
int angle = 0;
//Define Variables we'll be connecting to
double Setpoint, Input, Output;
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);
void setup()
{
//turn the PID on
myPID.SetMode(AUTOMATIC);
Serial.begin(115200);
Setpoint = 300;
myPID.SetOutputLimits(-2,2);
myPID.SetSampleTime(100);
Input = random(292, 302) ;
}
void loop()
{
if (i % 2 == 0) {
Input += random(-2,3);
}
angle += 1 % 360;
Input += sin(angle*355/(113*180));
myPID.Compute();
Input += Output;
analogVals[i] = Input;
i++;
if (i>= numReadings)
{
for (int j = 0; j < i; j++) {
Serial.println(analogVals[j]);
}
i = 0; //reset to beginning of array, so you don't try to save readings outside of the bounds of the array
}
}
Even if I restrict the output of the PID to a very small value, I get huge nonsense results of this code. It oscillates like -1000 to 1000. I've read this https://robotics.stackexchange.com/questions/167/what-are-good-strategies-for-tuning-pid-loops and tried to adjust my parameters, but they're just shots in the dark really, I can't even damp these massive oscillations. How would I tune a PID to cancel out a sin error ?
Thanks in advance
-
please post the link to the PID library?Jose Enrique Calderon– Jose Enrique Calderon2019年04月01日 03:31:11 +00:00Commented Apr 1, 2019 at 3:31
1 Answer 1
I do not have your PID_v1.h, but I made a simple experiment trying to control your example with a simple P-Controller instead of the PID lib. I made the following changes to your code:
...
double K_P = 1;
...
//myPID.Compute();
Output = K_P*(Setpoint - Input);
Input += Output;
This is working for very small amounts of K_P
, but as soon as K_P
gets higher than 2.0 it gets instable very fast. I guess this is what you are observing. The reason for me is, that you have implemented a ideal control path. In real control tasks you will always have some transfer behavior
Therefore I added a simple PT1 element (first-order lag element) to your code, now your system is much more stable and you can experiment with the K, I and D factors. Here it works with K_P even higher than 400:
// #include <PID_v1.h>
const unsigned int numReadings = 500;
double analogVals[numReadings];
unsigned int i = 0;
int angle = 0;
//Define Variables we'll be connecting to
double Setpoint, Input, Output;
double Input_PT1;
double K_P = 20;
//Specify the links and initial tuning parameters
//PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);
void setup()
{
//turn the PID on
// myPID.SetMode(AUTOMATIC);
Serial.begin(115200);
Setpoint = 300;
// myPID.SetOutputLimits(-2,2);
//myPID.SetSampleTime(100);
Input = random(292, 302) ;
}
void loop()
{
if (i % 2 == 0) {
Input += random(-2, 3);
}
angle += 1 % 360;
Input += sin(angle * 355 / (113 * 180));
//myPID.Compute();
Output = K_P * (Setpoint - Input_PT1);
Input += Output;
// start PT1
Input_PT1 -= Input_PT1 / 25;
Input_PT1 += Input / 25;
// end PT1
analogVals[i] = Input_PT1;
i++;
if (i >= numReadings)
{
for (int j = 0; j < i; j++) {
Serial.println(analogVals[j]);
}
i = 0; //reset to beginning of array, so you don't try to save readings outside of the bounds of the array
}
}
Some hint:
- The sketch above does not initialize
Input_PT1
, so it takes some secondes until you reach a steady value of 300, if you do not want to wait, just initializeInput_PT1
with 300. But this is exactly where you can start increasing first I, then D factors to try to increase speed for reaching a steady value of 300 and experiencing what influence I and D part will have. Input_PT1
is not a good naming, since this is the output of the PT1 element, but since you are using it as the PID Input, I did not change.
Here a drawing of what I implemented in contrast to your sketch (at least I hope so ;-))