tttapa / Control-Surface-Motor-Fader

Arduino motorized fader controller, and example code for integration with the Control Surface library.
https://tttapa.github.io/Pages/Arduino/Control-Theory/Motor-Fader/
GNU General Public License v3.0
105 stars 10 forks source link

PID controller pre-calculated Ki_Ts not possible ? #12

Open lole-elol opened 2 years ago

lole-elol commented 2 years ago

Hi Pieter,

first, thanks for your great article and deep introduction to this topic.

However, I cannot quite follow you in one point, in the provided Implementation for the PID controller you are using Ki already multiplied by Ts.

For the integral, you provided the following expression: $$e_i[k] = e_i[k-1] +T_s e[k-1] $$

In the code, the integral is computed as int32_t newIntegral = integral + error and then added to the PID algorithm as

bool backward = false;
int32_t calcIntegral = backward ? newIntegral : integral; // will always evaluate to integral

int_32_t output = kp* error + ki_ts * integral,
integral = newIntegral;

I don't understand how this implementation corresponds to the mathematical expression.

From my point of view, it should be implemented as follows

int32_t newIntegral = integral + error * Ts

int_32_t output = kp* error + ki * integral,
integral = newIntegral;

Maybe I'm overlooking something, could someone help me out here ?

aspdigital commented 2 months ago

I am looking at the code (https://github.com/tttapa/Control-Surface-Motor-Fader/blob/master/Motor-Controller/Controller.hpp#L91) and have the same question. Too bad it hasn't been addressed in a couple of years.

I agree with @lole-elol's statements. I think this code might be a vestige of some other PID loop project/implementation. There is no mention of backward in any of the discussion such as at https://tttapa.github.io/Pages/Arduino/Control-Theory/Motor-Fader/PID-Cpp-Implementation.html. @tttapa, can you clarify?

Edit to add: backward refers to the backward Euler discretization method as discussed here: https://tttapa.github.io/Pages/Arduino/Control-Theory/Motor-Fader/PID-Controllers.html#backward-euler but the code as written will always use the forward method.

tttapa commented 2 months ago

Since the initial integral is zero, the recursion is homogeneous, which means that we can scale it arbitrarily without affecting the result. For example, using the substitution $e_i^\prime[k] = T_s^{-1}\, e_i[k]$:

\begin{aligned}
e_i[k] &= e_i[k-1] +T_s\, e[k-1] \\
u_i[k] &= K_i e_i[k-1] \\
\text{is }&\text{equivalent to} \\
e_i^\prime[k] &= e_i^\prime[k-1] + e[k-1] \\
u_i[k] &= K_i T_s\, e_i^\prime[k-1] \\
\end{aligned}

You can verify this using the following code: https://godbolt.org/z/s4qr785bG

#include <iostream>
#include <ranges>

int main() {
    const float Ts = 0.123;
    int32_t errors[] {3, 2, 6, 2, 5, 5, 2, -1, 6, 3};
    float integral = 0;
    int32_t integralʹ = 0;
    for (auto [k, error] : std::views::enumerate(errors)) {
        std::cout << "    eᵢ[" << k << "] = " << integral << "\n";
        std::cout << "Ts eᵢʹ[" << k << "] = " << Ts * integralʹ << "\n";
        integral = integral + Ts * error;
        integralʹ = integralʹ + error;
    }
}

Backward indeed refers to backward Euler integration. It is disabled by default: you could enable it if you wanted to, but the same effect can be achieved by changing Ki and Kp.