I am having some rather weird outputs from my Arduino. I am using PID and everything seems to be going fine but once in a while the system will output either max-output (255) or min-output (0) i dont know why! Has anyone else had problems of that sort with the PID library? i am trying to control the position of one motor with a Pot, the motor also has a pot connected to it to get a servo-like application.
The output to serial will give it off like this:
If i run my feedback loop (serial connection) faster i see that the output of max/min will almost always come several times at a time. I cant seem to find any consistency in the wrong output.
I am taking the average of several readings to minimize jitter, dont know what else to do. Maybe i am using the PID library wrong?
Any advice would be great.
#include <PID_v1.h>
// Define input/output pins
const int steerPot = A3;
const int wheelPot = A1;
const int outputPin = 3;
const int directionPin = 2;
const int KpPin = A5; //PID-reg*****************
const int KiPin = A4 ;
const int KdPin = A2;
// Define variables
int steerPos; // position of steeringwheel
int wheelPos; // position of wheels
double error; //error between wheelPot and steerPot
double avgInput; //Calculated average input
double avgSetpoint; //Calculated average setpoint
double Setpoint, Input, Output; // PID variables
float Kp; //PID-reg***************
float Ki;
float Kd;
// Define changeble values
double readings = 5; //Amount of readings the averege setpoint,input is calculated on
byte outputMax = 100; //Max output to motor (PWM)
const byte errorMax = 2; // Max error between input/setpoint before action is taken
const int steerUL = (1023-175); //Upper limit for pot
const int steerLL = 175; //Lower limit for pot
// Major timer, lets user adjust how often the major loop (read pots and take action) should run
unsigned long previousMillis = 0UL;
unsigned long inputinterval = 25UL; // Time between main-loop run
// Serial output to console
unsigned long serialMillis = 0UL; // store timer current value, and start offset
unsigned long serialFeedbackMillis = 20UL; //Time between sending feedback to serial
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, 1 ,0 ,0, DIRECT);
void setup() {
TCCR2B = TCCR2B & 0b11111000 | 0x02; // Timer 2: PWM 3 & 11 @ 3906,25 Hz
Serial.begin(115200);
pinMode(steerPot, INPUT); //Not needed if analogRead is used.
pinMode(wheelPot, INPUT);
pinMode(outputPin, OUTPUT);
pinMode(directionPin, OUTPUT);
pinMode(Kp, INPUT); //PID-reg******************
pinMode(Ki, INPUT);
pinMode(Kd, INPUT);
//initialize the variables we're linked to
Input = analogRead(steerPot);
Setpoint = analogRead(wheelPot);
//turn the PID on
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(0, outputMax); //Limits the output
myPID.SetSampleTime(200); // Determines how often the PID algorithm evaluates (default = 200)
}
void loop() {
// current times since startup
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > inputinterval) { //Checks if its time to run the master loop
previousMillis += inputinterval; // if its time, it ads one interuptinterval for the next interval
// reads the 3 pots and updates the Kp,Ki,Kd values
Kp = analogRead(KpPin) / 100.0; // Values from 0.0 to 10.23
Ki = analogRead(KiPin) / 1000.0; // Values from 0.0 to 1.023
Kd = analogRead(KdPin) / 1000.0; // Values from 0.0 to 1.023
myPID.SetTunings(Kp, Ki, Kd);
/* Kp = analogRead(KpPin); // reads the 3 pots and updates the Kp,Ki,Kd values
Ki = analogRead(KiPin);
Kd = analogRead(KdPin);
myPID.SetTunings(analogRead(KpPin)/100, analogRead(KiPin)/1000, analogRead(KdPin)/1000);
*/
error = getError();
if(error >= errorMax ) {
TurnR();
}
else if (error <= -errorMax) {
TurnL();
}
else {
digitalWrite(3, LOW);
}
}
if(currentMillis - serialMillis > serialFeedbackMillis) { //Checks if its time to run the master loop
serialMillis += serialFeedbackMillis; // if its time to print, it ads one interuptinterval for the next interval
Cprint();
}
}
void TurnR() {
if (analogRead(steerPot) < steerLL) { // To stop the pot from overturning
analogWrite(outputPin, 0);
}
else {
Setpoint = getSetpoint()*-1;
Input = getInput()*-1;
myPID.Compute();
//Output = abs(Output);
digitalWrite(directionPin, HIGH);
analogWrite(outputPin, Output);
}
}
void TurnL() {
if (analogRead(steerPot) > steerUL) { // To stop the pot from overturning
analogWrite(outputPin, 0);
}
else {
Setpoint = getSetpoint(); // If the pot is within the limits, the output is generated and sent to the motorboard
Input = getInput();
myPID.Compute();
Output = abs(Output);
digitalWrite(directionPin, LOW);
analogWrite(outputPin, Output);
}
}
double getError(){
double wheelPos = getSetpoint();
double steerPos = getInput();
error = (steerPos-wheelPos);
return error;
}
double getSetpoint() {
long sum;
long avg;
for (int i = 0; i <= readings; i++) {
sum += analogRead(wheelPot);
//delay(1);
}
avgSetpoint = sum/readings;
sum = 0;
return avgSetpoint;
}
double getInput() {
long sum;
long avg;
for (int i = 0; i <= readings; i++) {
sum += analogRead(steerPot);
delay(1);
}
avgInput = sum/readings;
sum = 0;
return avgInput;
}
void Cprint() {
Serial.write("SteerPot: ");
Serial.print(analogRead(steerPot));
Serial.write(" wheelPot: ");
Serial.print(analogRead(wheelPot));
Serial.write(" error: ");
Serial.print(error);
Serial.write(" Output: ");
Serial.println(Output);
Serial.print(" P: ");
Serial.print(Kp);
Serial.print(" I: ");
Serial.print(Ki );
Serial.print(" D: ");
Serial.println(Kd);
}
It might help to print the value of your input variable as well in your cprint routine. As it is, I can't make sense of the numbers. Since you have the posts set to make Ki and Kd zero, it might also help for debugging purposes to set them that way in the code, just in case there's some small fractional part in either of them.