PID with Timing and Stopping Criterion
Modified from Yoshio's PID
#include <superSerial.h>
// Boolean values
#define TRUE 1
#define FALSE 0
// Hardware Pin Information
#define POT_PIN 3
#define M1_PWM 3
#define M1_DIR 12
// Output Bounds
#define OUT_MAX 255
#define OUT_MIN -255
// Timer Variables
unsigned long startTime, stopTime, now, lastTime, duration;
// PID Variables
double Setpoint, Input, Output, sampleTime, potVal, Last;
double P, I, D;
double Error, vError, Integral;
double timeChange, runTime;
double Kp, Ki, Kd;
// Setpoint achivement variables
#define P_ERR_MAX 10
#define V_ERR_MAX 2
int n, nReq = 30;
// Program Setup
void setup() {
// Set serial communication baud rate
Serial.begin(115200);
// Initialize the Motor pins as output and turn them off
pinMode (M1_PWM,OUTPUT); digitalWrite (M1_PWM,LOW);
pinMode (M1_DIR,OUTPUT); digitalWrite (M1_DIR,LOW);
analogWrite (M1_PWM, 0);
// Initialize and set default time length for the code
duration = 5000; // How long to PID for in milli-seconds
}
// Main portion of the code
void loop() {
// Ask for the Set Point value
Serial.println();
Serial.println("What would you like the Set Point to be?");
Setpoint = sSerial.readFloat();
Serial.print("Setpoint: ");
Serial.println(Setpoint,0);
Serial.println();
// Ask for the PID gain values
Serial.print("Kp? ");
Kp = sSerial.readFloat();
Serial.println(Kp,3);
Serial.print("Ki? ");
Ki = sSerial.readFloat();
Serial.println(Ki,3);
Serial.print("Kd? ");
Kd = sSerial.readFloat();
Serial.println(Kd,3);
Serial.println();
// Report back the PID value
Serial.print("Kp: ");
Serial.print(Kp,3);
Serial.print(" Ki: ");
Serial.print(Ki,3);
Serial.print(" Kd: ");
Serial.println(Kd,3);
Serial.println();
// Start of the PID code
startTime = millis();
lastTime = millis();
stopTime = startTime;
timeChange = 0;
Integral = 0;
n = 0;
Last = analogRead (POT_PIN);
// Runs the PID code for a set duration
while ( n < nReq && (stopTime-startTime) < duration) {
Input = analogRead (POT_PIN);
Output = ComputePID();
/*
// Since output can be between -255 to 255, set the motor direction.
// You may need to change LOW <-> HIGH depending on your pot/motor setup.
// Do so if you see the turntable running the wrong direction.
*/
if (Output > 0) {
digitalWrite(M1_DIR,HIGH);
}
else {
digitalWrite(M1_DIR,LOW);
}
analogWrite(M1_PWM,abs(Output));
Serial.print("Pot Value: ");
Serial.print(Input,3);
Serial.print(" Drive: ");
Serial.print(Output,3);
Serial.print(" dt: ");
Serial.println((timeChange)/1000,3);
// Check the ending Criterion
if ( abs(Error) < P_ERR_MAX && abs(vError) < V_ERR_MAX) { n++; }
stopTime = millis();
}
runTime = (stopTime - startTime);
runTime = runTime/1000;
if ( n == nReq ) {
Serial.print ("Setpoint has been REACHED! Time (sec): ");
Serial.println (runTime,3);
}
else {
Serial.print ("Setpoint was NEVER reached! :( Time (sec): ");
Serial.println (runTime,3);
}
analogWrite(M1_PWM,0);
}
// Meat and Bone of the PID alogorithm computation - Look at Lecture notes
double ComputePID() {
now = micros();
timeChange = now-lastTime;
Error = Setpoint-Input;
vError = Error-Last;
Integral = Integral + Error;
P = Error * Kp;
I = Integral * Ki * (timeChange/1000000);
D = vError * Kd / (timeChange/1000000);
Output = P + I + D;
if (Output > OUT_MAX) Output = OUT_MAX;
else if (Output < OUT_MIN) Output = OUT_MIN;
lastTime = now;
Last = Error;
return Output;
}