1

Trying to implement PI controller in function. Already worked for one controller. However, for more than one controller, the results are shifting. Suppose to get same values in the following code. Any help I appreciate it.

float r_in_1 = 10.00;
float y_out_1 = 5.00;

float r_in_2 = 10.00;
float y_out_2 = 5.00;

float r_o_1;
float y_o_1;

float r_o_2;
float y_o_2;

float vi_1 = 0;
float vi_2 = 0;

void setup() {

 Serial.begin(9600);

  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

r_o_1 = pow_2(r_in_1);

r_o_2 = pow_2(r_in_2);

y_o_1 = pow_2(y_out_1);

y_o_2 = pow_2(y_out_2);


vi_1 = PI_1(r_o_1, y_o_1);
vi_2 = PI_1(r_o_2, y_o_2);

Serial.print(vi_1);
Serial.print(',');
Serial.println(vi_2);
delay(1000);

}


float pow_2(float x){

float z;

z = x*x;

return z;

}

float PI_1(float r, float y){

float e;
static float sum_e=0;
float vi;
float kp=2.00;
float ki=10.00;

e = r - y;
sum_e =  sum_e + e;
vi = kp * e + ki * sum_e;

return vi;
}
  • What do you mean by 'more than one controller'? Please be detailed about what you are trying to achieve, and what is wrong the current result – dhimaspw Dec 6 '16 at 7:40
  • The current result should become same value for vi_1 and vi_2. But it is shifting. – zalt Dec 6 '16 at 7:41
  • I mean when I comment out one of those vi_1 = PI_1(r_o_1, y_o_1); or vi_2 = PI_1(r_o_2, y_o_2); it is working fine. But when both of them are executing the result are shifting. – zalt Dec 6 '16 at 7:45
  • what is your current result? I am a bit confused by "shifting". Edit your question, add the result you got. – dhimaspw Dec 6 '16 at 7:45
  • Ok, what I suppose to get is this (vi_1,vi_2) is what I get in serial monitor. (900,900), (1650,1650), (2400,2400) ,(3150,3150), (3900,3900) – zalt Dec 6 '16 at 7:47
2

A PI controller has “state”: it has to remember the sum of all the previous errors. If you want several controllers you have to independently store the state of each one.

The traditional solution used by C programmers is to write a controller function that takes an extra parameter which is a pointer to the state. In C++ you can use a reference instead:

// PI controller.
float PIController(float &sum, float r, float y)
{
    const float kp = 2.00;
    const float ki = 10.00;

    float e = r - y;  // error
    sum += e;         // integral of error
    return kp * e + ki * sum;
}

You would use it like this:

void loop()
{
    static float sum_1, sum_2;  // State of the controllers.
    // ...
    vi_1 = PIController(sum_1, r_o_1, y_o_1);
    vi_2 = PIController(sum_2, r_o_1, y_o_1);
}

The C++ language provides a nicer way of writing the same thing: you can use a class to wrap the state and the controlling method together under the same name:

// PI controller.
class PIController {
public:
    PIController() : sum(0) {}
    float operator()(float r, float y);
private:
    float sum;
};

float PIController::operator()(float r, float y)
{
    const float kp = 2.00;
    const float ki = 10.00;

    float e = r - y;  // error
    sum += e;         // integral of error
    return kp * e + ki * sum;
}

Which you would use like this:

void loop()
{
    static PIController pi_1, pi_2;  // Controller instances.
    // ...
    vi_1 = pi_1(r_o_1, y_o_1);
    vi_2 = pi_2(r_o_1, y_o_1);
}

The generated assembly should not be much different. Note that in both cases the static variables of loop() could be globals if you prefer.

1

The problem is you declare static float sum_e in PI_1 function. The value of will increase everytime PI_1 function is called. In 1 loop, you call PI_1 two times, resulting first call always smaller than second call because of the value of sum_e. Sum of error should be declared as global variable, and each controller has its own "sum" variable.
Declare as global :

float sum_e[100]; //for 100 controller.

Your function

float PI_1(float r, float y,int number){

float e;
float vi;
float kp=2.00;
float ki=10.00;

e = r - y;
sum_e[number] =  sum_e[number] + e;    //integrator for controller number
vi = kp * e + ki * sum_e[number];

return vi;
    } 

function call

vi_1 = PI_1(r_o_1, y_o_1,1);
vi_2 = PI_1(r_o_2, y_o_2,2);

Here is the rough example. There are lot more efficient ways than this, but this should gives you some idea. You can reduce the code yourself since the values should be correct already.

  • If I remove static then the integrator term will not work. I mean it just prints same values constantly. – zalt Dec 6 '16 at 8:02
  • I edit my answer – dhimaspw Dec 6 '16 at 8:05
  • what I want to do is to reduce the code. – zalt Dec 6 '16 at 8:05
  • I would like to have only one PI function. As the matter of fact i have more controllers. – zalt Dec 6 '16 at 8:08
  • use array. you can also use it for other variables like r_o y_o vi_ etc. – dhimaspw Dec 6 '16 at 8:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.