PID control for pressure

Hi guys, I need some help with the PID algorithmn as I am having difficulties achieving the outcome that i want. I am trying to ultilise the PID library to control the pressure in the compression of my cylinder. So currently, my cylinder is connected to an air pump whereby it is pumping pressure into the cylinder via a valve.

But upon actuating the air pump, my cylinder is compressed fully and it doesn't reference to the setpoint hence, i think that the feedback might be having some issues. Sincerely hope to receive some help..

The output from serial:
image


#include <PID_v1.h>

int vacPin = 11; // connected to air pump
int amtPin = 6; //
int PressurePin = A0; //pressure sensor

//Define Variables we'll be connecting to
double Input, Output, Setpoint;
double PID_result = 0;
double PID_error = 0;
double Filtered = 0;
double previousFiltered = 0;
double pressureSetpoint =0;

//unsigned long currentTime, previousTime;

float sensorRating = 115; //-115kPa sensor
int vCal = 82; //vacuum cal
int zCal = 942; //zero cal
float pressure;

float alpha, tf = 0.01, ts = 0.001; // tf is the filter time constant & ts is the sampling time (or time in between executions of the equation)
int count =0;
//Specify the links and initial tuning parameters
//Define the aggressive and conservative Tuning Parameters

//double aggKp=4, aggKi=0.2, aggKd=1;
double consKp=15, consKi=15, consKd=0.25;

int sampleRate = 100;

PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, REVERSE);

void setup()
{
  Serial.begin(115200);
  //initialize the variables we're linked to
  Input = analogRead(PressurePin);
  //pressure= abs(analogRead(PressurePin) - zCal) * sensorRating / (zCal - vCal); //kPa
  Setpoint = 40;
 
  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(-255, 255); //min, max
  myPID.SetSampleTime(sampleRate); //This sets the amount of time that must pass before the Compute() function will execute the PID calculation again. If the set time has not passed when Compute() is called, the function returns back to the calling code without calculating the PID.
  
  //currentTime = millis();
}

void loop()
{
  
  PressureReadings();

  //For derivative we need real time to calculate speed change rate
  
 //previousTime = currentTime; //The previous time is stored before the actual time read
  //currentTime = millis(); // Actual time read
  
  
}

void PressureReadings()
{ 

// <--------------- read pressure in the cylinder --------------- >
  Input = analogRead(PressurePin);
  Setpoint = 40;
  //Serial.print("Pressure sensor value:");
  //Serial.print(Input);

  float volt = (Input*5.0)/1023.0;
  //Serial.print("  Volts: ");
  //Serial.print(volt);

  pressure = abs(((volt/5.0) - 0.92)/0.007652);

  //pressure= abs(analogRead(PressurePin) - zCal) * sensorRating / (zCal - vCal); //kPa
  

  //Primary goal is to present a filtered version of a noisy or crude sensor
  alpha = tf/(tf + ts);
  Filtered = (alpha*pressure + (1-alpha)*previousFiltered); //the low pass filter only allows low frequency signals
  
// <------------------ PID calculation and loop----------------->
  PID_error = Setpoint - Filtered; //error between setpoint and real value
  
  if (abs(PID_error) > 0.1)
  {
    
    PID_result = myPID.Compute(); //returns a true or false statement
   
    if (PID_result == 1) //if true
    {
    
      //Serial.print("  Output:");
      //Serial.print(Output);
      //Serial.println();
      
      if (Output >= 0)
      {

        
        analogWrite(vacPin, Output);
      //analogWrite(vacPin, 255*Output);
        //analogWrite(amtPin, 0);
        //analogWrite(vacPin, 200);
        count += 1;
        Serial.print("Counter: ");
        Serial.print(count);
        
      }
      else
      {
        
        analogWrite(amtPin, Output);
        analogWrite(vacPin, 0);
        Serial.print("IM HERE");
        //analogWrite(vacPin, Output);
        //Serial.print(test);
      }
  
    }
    else
    {
      Serial.print("HERE");
    }
  }
  else
  {
    //Serial.println("THERE");
    analogWrite(vacPin, 0);
    analogWrite(amtPin, 0);
  }


  previousFiltered = Filtered; //store previous error to the next loop
  //Serial.print("Input:");
  //Serial.print(Input);
  Serial.print("  Setpoint:");
  Serial.print(Setpoint);
  Serial.println();
  Serial.print("  Filtered pressure = ");
  Serial.print(Filtered);
  //Serial.println(" bars");
  delay(400);
  
}

It seems you are attempting to write a negative number to a PWM output.
That might be interpreted as a positive output (due to underflow of unsigned number).
It also seems that the program does never go there (as the message is not in your serial output).
I propose that you print the value of output to your serial to see what is happening.
You might also set I and D to zero as integral windup and an unsmooth pressure measurement might overrule your P action.
Floats should be set with a dot (float target = 40.0).
What unit is sample rate?
Is the delay(400) a problem? That might block your PID function.
Why a PID control and hysteresis? Why not simple on/off control with hysteresis?

Thank you for the reply.

The output will always be 255, and because its taking up too much space in the serial hence i comment it off. I added a counter in the PID loop, so i know that it does goes inside however, i am unsure why is the "Filtered pressure" not referencing the Setpoint.

Noted, i will alter the I and D parameters. Another issue was, at "PID myPID" the parameter REVERSE. Initially, it was DIRECT and my "SetOutputLimits" function i had place as (-255,255). However, i noticed that if my Setpoint value (e.g 900) is lower than my Input (e.g 910 ~ 950) the output will be always -255 and this result to no compression of my cylinder hence, i assumed that the Setpoint is comparing with analog Input from the pressure sensor. So I did some researched, apparently "If the Input is above the Setpoint the Output" it should be place as REVERSE.

The unit of the sample rate is in milli seconds.

The task requires the implementation of PID control instead of other methods.

If PID is required, then why the hysteresis?

Thank you for the reply.

The cylinder is connected to 2 valves, so firstly the air pump will compress the cylinder with reference to the setpoint however, if the "Filtered Pressure" exceeds the setpoint it will open/close the atmosphere valve to regulate the pressure in the cylinder but i have not implement it yet since I am stucked due to the "Filtered pressure" does not meet the setpoint assigned I have tried different setpoint but cylinder just compressed fully with the values stated on the image above.

I have also noticed that I included the wrong Input in the "myPID" but i'm still receiving the same result..

PID is applicable when the thing being controlled effectively has momentum: a car that can't be stopped on a dime.

can't you stop filling by simply closing a valve?

presumably you also want some hysteresis: there's an upper threshold to close the valve and a lower threshold to open it

Agree. Many times a bang-bang controller with some some hysteresis will do the job much more simply than a PID. Also, no tuning or worries about stability.

I have no knowledge of your PID library, but blocking code using delay(400) might not be compatible with the library...
Can you print the value of Input?
It seems your pressure is controlled around 70 instead of 40.
Even after filter it is still far from stable...
Could be instability due to too high I or D value.
Or could be that the delay should be removed first.

Your PID is using Input which is the analogRead() of a pin, but you are then computing pressure based on that value. If you want your PID to respond to pressure, your input should be your pressure value, not your analogRead() value. The way you have it now, the PID is driving towards making your analogRead() match your setpoint, which is 40.

You also should call .Compute() every time through loop, not just when the error is larger than 0.1

Thank you for the replies guys.

Yup, i noticed that i had used the wrong Input and the Setpoint wasn't in the same units from the Input. Noted, i have removed the delay and "error > 0.1", and replaced it with when there's an error it will just run .Compute().

Another query is I noticed that the "SetSampleTime(sampleRate)", the sampleRate have to be less than 5, otherwise it doesn't run the PID is there a reason why?

What is the unit of sample rate?
/s?

Based on the PID library README, the default unit is in milliseconds

So the only valid numbers for sampleRate are 0,1,2 3 and 4?

Your I and D settings also have a time unit.
Could it be that those values put a limit to your sample rate? If I is large, and the time interval is also large, the I action might be much larger than your P action (causing instability of your controller). Perhaps the PID algorithm makes you chose such a value for interval that this is less likely to happen... but: as said I am not familiar with this library... where did you get this library? Does it have documentation? Pleas share in a post. Also add the latest version of your code...

The default sample rate is 100 milliseconds if you don't change the default so that can't be a true statement. The only thing the sample rate does is determine if .Compute() will actually do anything or just return based on the elapsed time from the previous call. Since you have a 400 msec delay in your code, it is irrelevant anyway. (hint: remove the delay)

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.