Skip to main content
Fix syntactic issues, do not use PI (it is a predefined macro).
Source Link
Edgar Bonet
  • 45.2k
  • 4
  • 42
  • 81

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 PIPIController(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 * error;sum;
}

You would use it like this:

void loop()
{
    static float sum_1, sum_2;  // State of the controllers.
    // ...
    vi_1 = PIPIController(&sum_1sum_1, r_o_1, y_o_1);
    vi_2 = PIPIController(&sum_2sum_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 PIPIController {
public:
    PIPIController() : sum(0) {}
    float operator()(float r, float y);
private:
    float sum;
};

PIfloat 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 * error;sum;
}

Which you would use like this:

void loop()
{
    static PIPIController 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.

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 PI(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 * error;
}

You would use it like this:

void loop()
{
    static float sum_1, sum_2;  // State of the controllers.
    // ...
    vi_1 = PI(&sum_1, r_o_1, y_o_1);
    vi_2 = PI(&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 PI {
public:
    PI() : sum(0) {}
    float operator()(float r, float y);
private:
    float sum;
};

PI::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 * error;
}

Which you would use like this:

void loop()
{
    static PI 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.

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.

Source Link
Edgar Bonet
  • 45.2k
  • 4
  • 42
  • 81

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 PI(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 * error;
}

You would use it like this:

void loop()
{
    static float sum_1, sum_2;  // State of the controllers.
    // ...
    vi_1 = PI(&sum_1, r_o_1, y_o_1);
    vi_2 = PI(&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 PI {
public:
    PI() : sum(0) {}
    float operator()(float r, float y);
private:
    float sum;
};

PI::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 * error;
}

Which you would use like this:

void loop()
{
    static PI 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.