To answer the “how” first:
Here’s a java PID implementation we created a number of years ago that does what you describe, along with its underlying integration and derivative calculation classes.
To answer the “why”:
This question hits at two related topics:
- What’s the “best” way to take integrals and derivatives in discrete time?
- How much does (or should) periodic loop jitter matter?
The answer to both lies in a tradeoff of computational and implementation complexity, and how bad making assumptions about what the underlying analog signal makes the error.
Most academic theory on this work tends to assume fixed timestep, and most industry controllers I’ve seen assume the same. I attribute this to a few facts:
- Given a certain fixed sample rate, it’s hard to make generally-true assumptions about what the underlying analog signal is, in a way that is helpful to computation.
- Given any underlying analog signal, and are having issues with your math on the signal, increasing the sample rate will almost always make the results “better”.
Sometimes marginally better, sometimes only a bit better, but in very very few cases worse.
Academically speaking, there’s this thing called the Nyquist rate that defines what sample rate is needed for a particular underlying analog signal. In a horribly over-simplified nutshell, Nyquist says “if you meet this sample rate, all this other math will just work at a fixed sample rate”.
As a result: most embedded design patters would say that if jitter is causing you to miss the Nyquist rate for your signal processing, you fix the jitter problem before you get “creative” with your math.
Side reason, that’s important to the software-y folks: single-step debugging through code that has lots of TImer.getFPGATimestamp() calls means the code’s behavior will change based on whether you’re debugging or not. For this reason alone, those libraries linked above haven’t seen use on a robot in multiple years.