In fact, the quantification error produced by both approximations (the one implemented by @Brett and the change @jojo proposed) is nearly the same.

When solving in the Laplace domain, the transfer function of the integral term is Ki/s. The Laplace domain is meant for continuous time, though, so any digital implementation (discrete) will produce not exact measurements. There is another domain, called Z, which is meant for discrete time. So, when implementing transfer functions in digital systems, we should take this as a reference.

When changing from the Laplace domain to the Z domain, we can use three different transformations: Euler’s Fordward Difference (@jojo), Backward Difference (@Brett) and Tustin’s or Bilinear. Attending to the meaning of integration, which is calculating the

area below a function, we can analyze those three different approaches graphically:

https://ia601207.us.archive.org/28/items/IntegralTermComparison/intcomp.png

As you can see, both the Fordward and Backward approaches take the area as a rectangle. The first one, takes the previous sample and pushes it fordward, assuming the signal is constant from (k-1)Ts to kTs (Ts=sampling period). The second one makes the same, but taking the last sample as constant value, so pushing it backward. In fact, the performance of both of them should be the same. Although when changing the setpoint to a greater value the Backward method is faster (because it quantifies more than the ideal integration), when setting it to a lower one they sweep. So, depending on the application, especially in bipolar ones (i.e. controlling a motor in two directions), we’ll get no better performance with none of them.

The third approach, Tustin’s, takes the area as a trapezoid, considerably reducing the quantification error and getting a value just in the middle between the previous ones. So attending to theory, this would be the best one (there are also mathematical reasons I don’t get to understand which say so).

When talking about implementation, you can see that this approach needs adding the last and the previous error samples. The division can be done to Ki, the same way you did with Ts, so its value will be 1 bit smaller, which will compensate that the other operand is 1 bit larger. As you don’t save lastError value, this (my) suggestion has two instructions more than your actual design, and changes just other two lines:

double iInput = (error + lastError);

ITerm+= (ki * iInput);

lastError = error;

…

ki = Ki * SampleTimeInSec / 2;

—————————–

Apart from that suggestion, when you performed the comparison with Ts=5s, the systems were probably not meeting the plant timing requirements, which means the controller’s work speed was not enough for a good performance. Although it can be used, depending on the application, not having into account the plant dynamics can lead to unstable responses. For an optimal control system we should try to meet the Real Time requirements (which typically means to set the sample time at least 10 times lower than the minimum change time in the plant).

In the setpoint response comparison, when starting the ramp down, the existing system has reached its steady-state value (nearly the setpoint value), while the system suggested by @jojo is still transitory. For a better analysis I think it would be good to wait until both systems have reached their steady-state value, before changing the setpoint.

In the disturbance rejection comparison we can see that the existing one (@Brett, Backward) performs better with positive disturbances, but the suggested one (@jojo, Fordward) is quite faster with negatives (besides initial states not being the same), es exposed previously.

Finally, I hope you can excuse me for my bad english, and I wish this information/explanation will help you improve this great library. I would also like to congratulate you not only for doing it, but for writing all these explanations to help beginners understand easily control.

Opositivo

]]>