How to express a PI compensator in my program [Water Level Control]

Hello every one,
I’m doing a water level control system with Ulrasonic sensor.
After determining Gzas I designed a compensator to achieve 0 steady state error, as well as decrease the settling time and rise time.
I found Gzas to be first order as follows:
G_zas= (0.00538 Z)/(Z-0.9803)
My PI compensator is:
G_c=20*(Z-0.9803)/(Z-1)

To be honest, I am lost in how to enter this compensator in my program so that my Arduino (Infiduino R3) performs the job of the PI calculated.

My program is the following:


#include <NewPing.h>

#define TRIGGER_PIN 12
#define ECHO_PIN 11
#define pwm 9
#define MAX_DISTANCE 1000

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
void setup(){
  pinMode (9, OUTPUT);
  Serial.begin(9600);
  }

void loop() {
  delay(100);
  unsigned int uS = sonar.ping();
  int Water_level = map((uS / US_ROUNDTRIP_CM)  ,5,15,30,120); //mapping the water level to the pwm
  if((uS / US_ROUNDTRIP_CM)>5){
    analogWrite(pwm , Water_level ) ;
  }
  else analogWrite(pwm , 0) ;
  Serial.print("\nPing:");
  Serial.print(uS / US_ROUNDTRIP_CM);
  Serial.print("cm");
}

I would appreciate it very much if someone helps me out.

Kind regards,
Abdessalam

I didn't understand this:

I found Gzas to be first order as follows:
G_zas= (0.00538 Z)/(Z-0.9803)
My PI compensator is:
G_c=20*(Z-0.9803)/(Z-1)

But if you're trying to do PI control, check out Brett Beauregard's PID library - you can always set the kd tuning parameter to zero.

wildbill:
I didn't understand this:
But if you're trying to do PI control, check out Brett Beauregard's PID library - you can always set the kd tuning parameter to zero.

Thank you for your quick reply and guidance.
Gzas is the forward path transfer function of my control loop without a compensator. I found it using a Matlab tool, after gathering enough input/output data (my input is pwm / my output was the water flow; measured by a water flow sensor)
Gc is the transfer function of the controller (which is the arduino) I have chosen a PI to eliminate steady state error and to decrease rise and settling time as stated.
Now I need to make my arduino act as a PI ! That's my problem basically.
Someone told me that they did it by transforming my controller's transfer function back in time domain (Difference equation) and then enter it in the program. But unfortunately I did not manage to make it work.

Can you please link me to Brett Beauregard's PID library.

Thanks again.

Greensprings:
what is a pi compensator?

Proportional- Integral (PI) Control

The combination of proportional and integral terms is important to increase the speed of the response and also to eliminate the steady state error.

Greensprings:
thanks.... i still dont know what that means, but anyway

Really sorry that I could not develop more.

Can someone help me ?

Perhaps you could talk with your (presumed) classmate, who is working on exactly the same problem! http://forum.arduino.cc/index.php?topic=288349.0

jremington:
Perhaps you could talk with your (presumed) classmate, who is working on exactly the same problem! http://forum.arduino.cc/index.php?topic=288349.0

Thanks for the heads up ! He's in my team.
I was assigned by the team to search for a solution to this problem.

If this is an assignment for an engineering class, I suspect you won't find much help satisfying the professor's expectations on this forum.

However, if all you want to do is solve a simple water level control problem, you certainly don't need the theoretical transfer function approach.

If you already have a water level sensor, a pump controller and an Arduino capable of interacting with both, about 10 lines of code is all you will need. Even the PI(D) library is unnecessary. Lots of people will be happy to help you with that approach.

jremington:
If this is an assignment for an engineering class, I suspect you won’t find much help satisfying the professor’s expectations on this forum.

However, if all you want to do is solve a simple water level control problem, you certainly don’t need the theoretical transfer function approach.

If you already have a water level sensor, a pump controller and an Arduino capable of interacting with both, about 10 lines of code is all you will need. Even the PI(D) library is unnecessary. Lots of people will be happy to help you with that approach.

Thank you for your reply.
Am not trying to satisfy the professor’s expectations because from the first place, it’s my benefit to learn.
I’m a Control Engineer who has just started his studies and who has just discovered Arduino.

Technically, we’ve managed to solve a simple water level control problem. However because of the inaccuracy of the Ultrasonic Sensor led to disturbing the work of the pump; (Mapping the water level to the pwm that controls the pump through a TIP120 transistor operating as a switch).

The only thing that could cover up such a disturbance, is of course a compensator, which we selected to be a PI for achieving theoretical goals (zero steady state for example).
As you can see here sir, we derived the expression of that PI, theoretically, but did not manage to build it into our main program (stated in my first post).
I personally don’t know much of programming, and I’ve been oriented to this forum, and am asking for help to acquire some knowledge that will help me and others in our future projects.

Thank you again for your kind reply, and your offer to help.

Best regards.

I don't know much about control theory. However, your current arduino code is essentially "analog write a value based on the instantaneous value of the distance from the pin sensor."

void loop() {
  delay(100);
  unsigned int uS = sonar.ping();
  int Water_level = map((uS / US_ROUNDTRIP_CM)...); //mapping the water level to the pwm
  if (Water_level != target)
    analogWrite(pwm , Water_level ) ;
  :
}

Whereas a reasonable PID algorithm needs to be more like "change the PWM output based on how quickly the ping value is approaching the target position." I would have hoped you had learned about that before getting the assignment.

Minimally, this means you need to take into account previous values of the ping sensor as well as the current value:

void loop() {
   :
  uS = sonar.ping();
  Water_level = map((uS / US_ROUNDTRIP_CM)  ,5,15,30,120); //mapping the water level to the pwm
  /**********************************/
  Water_level = adjust_for_history(Water_level);
  /**********************************/
  if((uS / US_ROUNDTRIP_CM)>5){
    analogWrite(pwm , Water_level ) ;
  }

Here, "adjust_for_history()" does the time-based processing. You could think of it as storing the last N points, fitting a curve to them, and adjusting the value of the parameter to change the shape of the curve so that it approaches the target value "smoothly." But I suspect that this "PI compensator" stuff is a mathematical simplification of that that works for certain types of behavior, and you only have to keep a single running calculation going...
You WILL want to convert things to floating point in order to use that sort of calculation...

PID Library

The only thing that could cover up such a disturbance, is of course a compensator, which we selected to be a PI for achieving theoretical goals (zero steady state for example).

I'm also interested in learning. Can you explain how the compensator will correct for the inaccuracies of the ultrasonic measurement?

westfw:

void loop() {

:
  uS = sonar.ping();
  Water_level = map((uS / US_ROUNDTRIP_CM)  ,5,15,30,120); //mapping the water level to the pwm
  //
  Water_level = adjust_for_history(Water_level);
  /
/
  if((uS / US_ROUNDTRIP_CM)>5){
    analogWrite(pwm , Water_level ) ;
  }





Here, "adjust_for_history()" does the time-based processing. You could think of it as storing the last N points, fitting a curve to them, and adjusting the value of the parameter to change the shape of the curve so that it approaches the target value "smoothly." But I suspect that this "PI compensator" stuff is a mathematical simplification of that that works for certain types of behavior, and you only have to keep a single running calculation going...
You WILL want to convert things to floating point in order to use that sort of calculation...

I tried to write it this way but I got an error saying that I could not use (adjust_for_history()) as a function.


jremington:
I'm also interested in learning. Can you explain how the compensator will correct for the inaccuracies of the ultrasonic measurement?

Sure mate !
My controller (compensator) circuit actively controls the system so as to hold it at the set point by generating a signal that is the difference between the desired output (in my case water level to stop at a certain level) and the current value (which is detected by my Ultrasonic sensor).

The difference between my design without and with a compensator is very simple; consists of not mapping the distance measured by the sensor to the pwm signal that controls my pump (because obviously any small disturbance will act upon the control signal that drives the pump.)
My controller, in this case, has as input the error signal ( difference between desired level and actual one) and as output the control signal (pwm) that will ensure smooth work of the pump.

Assuming that I was accurate in designing my compensator, and describing it in my code correctly, I will cover up the error generated in the control signal that was due to the inaccuracy of my Ultrasonic sensor.

I got an error saying that I could not use (adjust_for_history()) as a function.

You would have to WRITE that function, of course.
To implement a simple exponentially weighted moving average of the distance (http://en.wikipedia.org/wiki/Moving_average ) you would use code that looked something like:

float St = -1;  // the current average; start at "uninitialized"
static const float alpha = 0.1;
int adjust_for_history (int Yt)
{
   float fYt = Yt;   // convert to float
   if (St == -1) { // Check for initialization
      St = fYt;
   } else {
      St = alpha * fYt + (1.0 - alpha)*St;
   }
   return (int) St;
}