categories: robotics, automation & robotics, embedded systems
Last post derived a clean continuous-time transfer function $$G(s)$$ from a real physical system. Problem: an ESP32, STM32, or PLC can't run $$G(s)$$ directly. Real hardware (motors, heaters, actuators) lives in continuous time, but a digital controller only ever sees the system at discrete sampling instants $$T_s$$ apart. This post is the bridge from the math you derived on paper to the code that actually runs in the control loop.
Step 1 — the continuous transfer function
A generic first-order object (a low-pass filter, or a DC motor's speed response — same shape either way) is described by: $$G(s)=\frac{Y(s)}{U(s)}=\frac{K}{Ts+1} \implies TsY(s)+Y(s)=KU(s)$$
Step 2 — backward Euler substitution (the actual discretization)
We approximate the differentiation operator with a difference between consecutive samples, $$s\approx\frac{1-z^{-1}}{T_s}$$, where $$T_s$$ is the microcontroller's fixed sampling period and $$z^{-1}$$ means "one sample ago." This is the step that actually turns continuous-time $$s$$ into something a loop running every $$T_s$$ seconds can compute: $$T\left(\frac{1-z^{-1}}{T_s}\right)Y(z)+Y(z)=KU(z)$$
Step 3 — derive the digital difference equation
Rearranging into a form indexed by sample number $$k$$ instead of the abstract variable $$z$$: $$Y(z)\left(\frac{T}{T_s}(1-z^{-1})+1\right)=KU(z)$$ $$Y(z)(T+T_s)-TY(z)z^{-1}=KT_sU(z)$$ $$y[k]=\frac{T}{T+T_s}\,y[k-1]+\frac{KT_s}{T+T_s}\,u[k]$$ That's the whole payoff: a single line that only needs the previous output $$y[k-1]$$ and the current input $$u[k]$$ — exactly the kind of state a microcontroller can keep in two variables and update every timer interrupt:
// runs once every T_s seconds, e.g. in a timer ISR
float y_prev = 0.0f;
const float T = /* time constant from your G(s) */;
const float Ts = /* your fixed sampling period */;
const float K = /* static gain from your G(s) */;
float update(float u) {
float alpha = T / (T + Ts);
float y = alpha * y_prev + (1.0f - alpha) * K * u;
y_prev = y;
return y;
}
Notice the whole series comes full circle here: post #6 got you a real $$G(s)$$ from Newton's second law, and this post turned that exact $$G(s)$$ into eight lines of C. That's the complete path from physical object to embedded code, and the same backward-Euler substitution generalizes to any order of transfer function, not just first-order. Thank you for reading, and thanks for following this whole series :)
The End