Hi again,
I'm tried messing arround with PID loops. I started with an LED, LDR and a potentiometer for the setpoint.
I study engineering and already had some lessons on control engeneering. I started writing my own program without the libraries but I had some fluctuating outputs. Next I tried the PID_v1.h library but I have the same problem.
I tried decreasing the Ki value but al it seems to do is lower the value where the fluctuations stop. And trying to speed the rise time up with Kp seems to make it even worse.
Legend for the graph:
-blue= setpoint (potentiometer)
-yellow=LDR reading
-red=LED output
#include <PID_v1.h>
int led = 10;
int pot = A2;
int ldr = A1;
int outputs[] = {led};
int inputs[] = {pot, ldr};
double setpoint = 0;
double sensor = 0;
double Kp = 0;
double Ki = 010;
double Kd = 0;
double output = 0;
unsigned long last_loop = 0;
unsigned long loop_time = 100;
int previous_error = 0;
int number_of_readings = 100;
PID myPID(&sensor, &output, &setpoint, Kp, Ki, Kd, DIRECT);
void setup() {
for (int i = 0; i < sizeof(outputs); i++) {
pinMode(outputs[i], OUTPUT);
}
for (int i = 0; i < sizeof(inputs); i++) {
pinMode(inputs[i], INPUT);
}
myPID.SetOutputLimits(0, 255); //set outputs to min 0 & max 255
myPID.SetMode(AUTOMATIC); //ON
Serial.begin(9600); //debug
}
void loop() {
if (millis() - last_loop > loop_time) {
setpoint = map(analogRead(pot), 0, 1023, 0, 255);
sensor = 0;
for (int i = 0; i < number_of_readings; i++) {
sensor += analogRead(ldr);
}
sensor = sensor / number_of_readings; //average reading
sensor = map(sensor, 0, 1023, 0, 255);
myPID.Compute();
analogWrite(led, output);
//Debugging/monitoring
Serial.print(setpoint);
Serial.print(",\t");
Serial.print(sensor);
Serial.print(",\t");
Serial.print(output);
Serial.print(",\t");
Serial.print(0);
Serial.print(",\t");
Serial.print(255);
Serial.println();
last_loop = millis();
}
}
There are a number of tutorials on the web that explain how to rationally set the K values for the PID algorithm. Start by adjusting Kp, with Ki and Kd = 0; then compensate with the others.
Google "pid tuning" to find the tutorials.
Problems:
- You have NO proportional control. Set Kp to 1.0 to start. Set Ki and Kd to 0.0 to start. Increase Kp until it oscillates, turn it down 30%, add Ki and Kd to make it responsive without overshoot.
- You are using 'sizeof' on an int array and not dividing by 2 so you are going off the end of your array. The sizeof operator returns the size IN BYTES.
- You don't need to set pinMode when using analogRead().
- Since the LDR reading and the Pot readings only have to be in the same units so there is no reason to map them to the smaller PWM range.
#include <PID_v1.h>
const byte LED_PWMPin = 10;
const byte Pot_AIPin = A2;
const byte LDR_AIPin = A1;
double PID_Input = 0.0; // Same units as PID_Setpoint
double PID_Output = 0.0; // Arbitrary units. DIRECT means Higher PID_Output -> Higher PID_Input
double PID_Setpoint = 0.0; // Same units as PID_Input
double Kp = 1.0;
double Ki = 0.0;
double Kd = 0.0;
unsigned long last_loop = 0;
const unsigned long loop_time = 100;
const int ReadingsToAverage = 100;
PID myPID(&PID_Input, &PID_Output, &PID_Setpoint, Kp, Ki, Kd, DIRECT);
void setup()
{
Serial.begin(115200); //debug
pinMode(LED_PWMPin, OUTPUT);
myPID.SetOutputLimits(0, 255); // set outputs to min 0 & max 255
myPID.SetMode(AUTOMATIC); // ON
}
void loop()
{
if (millis() - last_loop > loop_time)
{
last_loop += loop_time;
PID_Setpoint = analogRead(Pot_AIPin);
PID_Input = 0;
for (int i = 0; i < ReadingsToAverage; i++)
{
PID_Input += analogRead(LDR_AIPin);
}
PID_Input = PID_Input / ReadingsToAverage;
myPID.Compute();
analogWrite(LED_PWMPin, PID_Output);
// Debugging/monitoring
Serial.print(PID_Setpoint);
Serial.print(",\t");
Serial.print(PID_Input);
Serial.print(",\t");
Serial.print(PID_Output);
Serial.print(",\t");
Serial.print(0);
Serial.print(",\t");
Serial.print(255);
Serial.println();
}
}
johnwasser:
You are using 'sizeof' on an int array and not dividing by 2 so you are going off the end of your array. The sizeof operator returns the size IN BYTES.
For this reason, the idiom is (sizeof(array)/sizeof(array[0])) to get the number of elements in an array.
johnwasser:
Problems:
You have NO proportional control. Set Kp to 1.0 to start. Set Ki and Kd to 0.0 to start. Increase Kp until it oscillates, turn it down 30%, add Ki and Kd to make it responsive without overshoot.
I can turn Kp down to a level where the led doesn't even turn on anymore and it still flickers. It always does this on the low range of the setpoint. If I use Ki=1 it works but its very slow so I try improving the rise time with a higher Kp but then it starts to flicker on the lower range.
Since the LDR reading and the Pot readings only have to be in the same units so there is no reason to map them to the smaller PWM range.
This was mostly to visualize it, I have now put it in the Serial commands only.