Arduino PID Library by Brett Beauregard

I am trying to grasp the reason for why the compute() function is not loading data into the output variable. The output is always 0 when it seems it should be 100. The below code is a copy of the "basic" example from the PID examples. I have modified it by setting the KI and KD to 0 and forcing Input to be 50 instead of reading a pin. I have added serial to be able to print the value of Input to the serial monitor.

Thanks for helping.

/********************************************************
 * PID Basic Example
 * Reading analog input 0 to control analog PWM output 3
 ********************************************************/

#include <PID_v1.h>

#define PIN_INPUT 0
#define PIN_OUTPUT 3

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
double Kp=2, Ki=0, Kd=0;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

void setup()
{
  //initialize the variables we're linked to
  Input = 50;
  Setpoint = 100;

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  Serial.begin(115200);
}

void loop()
{
  Input = analogRead(PIN_INPUT);
  myPID.Compute();
  Serial.println(Output);
  analogWrite(PIN_OUTPUT, Output);
}

Try

 Serial.println(Output, DEC);

You are not "forcing Input to be 50", you are reading Input from the analog input and probably getting out of range values. The output values are constrained to (0,255) by default.

Try this:

/********************************************************
 * PID Basic Example
 * Reading analog input 0 to control analog PWM output 3
 ********************************************************/

#include <PID_v1.h>

#define PIN_INPUT A0
#define PIN_OUTPUT 3

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
double Kp=2, Ki=0, Kd=0;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

void setup()
{
  //initialize the variables we're linked to
  Input = 50;
  Setpoint = 100;

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  Serial.begin(9600);
}

void loop()
{
  Input = 50.; //analogRead(PIN_INPUT);
  myPID.Compute();
  Serial.println(Output);
  analogWrite(PIN_OUTPUT, Output);
}

Thanks jremington! That works. Somehow didn't notice the analogRead in the loop. I hope this isn't asking too much but given the conclusion that The PID library is working correctly on my computer, Is there any kind soul out there that might see a reason for the following code to be returning 0 when running the compute() function in the loop?

#include <PID_v1.h>

//Serial input processing variables/constants
const byte Input_size = 20;
char Received_data[Input_size];
boolean Data_finnished = false;

//Motor control variable/constants
const byte MotorA_pwr_pin = 12;
const byte MotorA_dir_pin = 11;
double MotorA_pwr = 0;
boolean MotorA_dir;
int MotorA_spd = 0; //Desired amount of angular degrees of rotation per/second
long MotorA_pos_cur = 0; //For tracking motor position in encoder pulse counts
int MotorA_dir_cur = 0; //For tracking direction motor is actually turning and used to change motor position variable
const int MotorA_res = 720; //Encoder pulse counts per one full motor shaft revolution

//Encoder processing variables/constants
const byte EncoderA_a_pin = 2;
const byte EncoderA_b_pin = 3;
double EncoderA_trig_dur = 0;
unsigned long EncoderA_trig_past = 0;
double EncoderA_trig_tar = 0;
boolean EncoderA_trig = false;

//PID Variables
double PID_a_kp = 4;
double PID_a_ki = 0;
double PID_a_kd = 0;
int PID_a_Sample_freq = 20;
int PID_a_Sample_dur;
int PID_scaler = 50; //Used to scale PID k values smaller than entered (ie. gets tiring entering long decimal numbers over and over)

//Variable for timing the display of diagnastic serial data
unsigned long Past_millis;

PID PID_a(&EncoderA_trig_dur, &MotorA_pwr, &EncoderA_trig_tar, PID_a_kp / PID_scaler, PID_a_ki / PID_scaler, PID_a_kd / PID_scaler, DIRECT);

void setup()
{
  Serial.begin(115200);
 
  pinMode(MotorA_pwr_pin, OUTPUT);
  pinMode(MotorA_dir_pin, OUTPUT);
  pinMode(EncoderA_a_pin, INPUT_PULLUP);
  pinMode(EncoderA_b_pin, INPUT_PULLUP);

  MotorA_spd_tar(MotorA_spd);

  attachInterrupt(digitalPinToInterrupt(2), EncoderA_a, CHANGE);
  attachInterrupt(digitalPinToInterrupt(3), EncoderA_b, CHANGE);

  PID_a.SetOutputLimits(-255.0, 255.0);
}

void loop()
{  
  //Gather and process serial data if availible
  if(Serial.available() > 0) Load_serial_data();
  if(Data_finnished == true) Process_data();

  //Update PID settings with new variable data that may have been changed via serial input
  PID_a.SetTunings(PID_a_kp / PID_scaler, PID_a_ki / PID_scaler, PID_a_kd / PID_scaler);
  PID_a.SetSampleTime(PID_a_Sample_freq);

  //Compute PID and display data
  //Serial.println(PID_a.Compute());
  Serial.println(MotorA_pwr);
  //Serial.println((EncoderA_trig_dur - EncoderA_trig_tar) * (PID_a_kp / PID_scaler)); //Using this to validate variables are generating correctly when spinning motor by hand
  //Serial.println(EncoderA_trig_dur);

  //Change motor direction if PID value is negative and invert PID value
  if(MotorA_pwr < 0) {MotorA_dir = true; abs(MotorA_pwr);}

  //Update motor control
  analogWrite(MotorA_pwr_pin, (int)MotorA_pwr);
  digitalWrite(MotorA_dir_pin, MotorA_dir);

  //Reset motor direction back to target
  MotorA_dir = false;
}

/*  Motor has two encoder signals 90 out of phase.  One intterupt
    routine is used per signal for easy tracking of motor direction
*/

void EncoderA_a()
{
  //Process micro seconds since last pulse count
  unsigned long Trig_cur = micros();
  EncoderA_trig_dur = Trig_cur - EncoderA_trig_past;
  EncoderA_trig_past = Trig_cur;

  //Update motor positioning variables
  if(digitalRead(EncoderA_a_pin) == digitalRead(EncoderA_b_pin)) MotorA_dir_cur = -1; else MotorA_dir_cur = 1;
  MotorA_pos_cur += MotorA_dir_cur;
  EncoderA_trig = true;
}

void EncoderA_b()
{
  //Process micro seconds since last pulse count
  unsigned long Trig_cur = micros();
  EncoderA_trig_dur = Trig_cur - EncoderA_trig_past;
  EncoderA_trig_past = Trig_cur;

  //Update motor positioning variables
  if(digitalRead(EncoderA_a_pin) == digitalRead(EncoderA_b_pin)) MotorA_dir_cur = 1; else MotorA_dir_cur = -1;
  MotorA_pos_cur += MotorA_dir_cur;
  EncoderA_trig = true;
}

//Used for recalculating target amount of micro seconds between pulse counts after target motor speed has been changed
void MotorA_spd_tar(int Speed)
{
  if(Speed == 0) EncoderA_trig_tar = 0; //Makes 0 is not attempted to be divided
  else EncoderA_trig_tar = 1000000 / (MotorA_res / 360 * Speed);
}

//For recieving serial data sent from serial monitor without blocking
void Load_serial_data()
{
  char New_data;
  static byte Char_pos;
  char Data_end = '\n';
  
  New_data = Serial.read();
    
  if(New_data == Data_end) {Data_finnished = true; Char_pos = 0;}
  else {Received_data[Char_pos] = New_data; Char_pos++;}
}

//For processing sent serial data and updating appropriate variable values
void Process_data()
{
  char Menu;
  String Value;

  //Assigns first character of array to be used to decide which variable should be set
  Menu = Received_data[0];

  //Dumps remaining array charactyers into a string to be used to update variables
  for(byte i = 1; Received_data[i] != 0;) {Value += Received_data[i]; i++;}

  switch(Menu)
  {
    case 'p':
      PID_a_kp = Value.toDouble();
      Serial.print("PID_a_kp, ");
      Serial.println(PID_a_kp);
      Serial.println();
    break;

    case 'i':
      PID_a_ki = Value.toDouble();
      Serial.print("PID_a_ki, ");
      Serial.println(PID_a_ki);
      Serial.println();
    break;

    case 'd':
      PID_a_kd = Value.toDouble();
      Serial.print("PID_a_kd, ");
      Serial.println(PID_a_kd);
      Serial.println();
    break;

    case 's':
      MotorA_spd = Value.toInt();
      Serial.print("MotorA speed, ");
      Serial.println(MotorA_spd);
      Serial.println();
      MotorA_spd_tar(MotorA_spd);
    break;

    case 'r':
      MotorA_dir = Value.toInt();
      Serial.print("MotorA direction, ");
      Serial.println(MotorA_dir);
      Serial.println();
    break;

    case 'f':
      PID_a_Sample_freq = Value.toInt();
      Serial.print("PID_a_Sample_freq, ");
      Serial.println(PID_a_Sample_freq);
      Serial.println();
    break;

    case 'c':
      PID_scaler = Value.toInt();
      Serial.print("PID_scaler, ");
      Serial.println(PID_scaler);
      Serial.println();
    break;

    case 'm':
      Serial.println("Current settings");
      Serial.print("PID_a_kp, ");
      Serial.println(PID_a_kp);
      Serial.print("PID_a_ki, ");
      Serial.println(PID_a_ki);
      Serial.print("PID_a_kd, ");
      Serial.println(PID_a_kd);
      Serial.print("Motor speed, ");
      Serial.println(MotorA_spd);
      Serial.println();
    break;
  }

  //Sets all array values back to 0
  for(byte i; i <= Input_size;) {Received_data[i] = 0; i++;}
  Data_finnished = false;
}

The best way to debug problems like this is to put in lots of Serial.print() statements, so you can see what all the input, intermediate and output numbers are, and if they make sense.

Forum member can't do that for you, because they don't have your hardware.

I understand. Its the invisible nature of the compute() function that's getting me. I don't know how to validate what is going on inside of it other than seeing that it keeps returning 0.

NOTHING is stopping you from modifying the PID library to use serial.print.

But the library is extremely unlikely to be the problem.

Aha! I have found the problem. I didn't realize this function call "PID_a.SetMode(AUTOMATIC);" was required in the setup().

Thanks for helping