Robotics

Practical Robotics for Engineers #7: Discretizing a Transfer Function for a Microcontroller


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
Administrator

This post was written by the administrator

Recent Posts

Proof Problems in Mathematics The Beginning Integrals, Course Practical Physics for Engineers IT Matura Course | Algorithms IT Matura Course | Databases IT Matura Course | Theory IT Matura Course | Spreadsheet

Archive

Year 2022

Comments