I had to learn Laplace transform and Z-Transform during my engineering study. After many years I have been revisiting these mathematical tools. I wondered where these could be applied in robotics with Arduino or similar microcontrollers like ESP32 boards. This note is about this, where and when to use Laplace and Z transforms.
So first of all, Laplace transform is for continuous systems (analog systems) and Z-transform is for discrete systems (digital systems), meaning that Laplace is used when modeling physical systems like DC motors or mechanical arms using differential equations, while Z-transform is applied when implementing these models in microcontrollers such as Arduino using sampled or digitized signals. For example, when controlling the speed of a DC motor, you first model the motor using Laplace transform to get a transfer function like , where is motor speed and is input voltage. Then, you design a continuous-time PID controller in Laplace domain to achieve desired performance (such as faster response or reduced overshoot), convert the controller into a discrete-time form using the Z-transform or Tustin approximation, and finally implement the difference equations in Arduino code that reads motor speed using an encoder and adjusts the PWM signal accordingly to control the motor in real time.
Step 1: Model the DC Motor in Laplace Domain
Use the physical parameters of a DC motor to derive the transfer function :
🧮 DC Motor Model
Where:
-
: moment of inertia (kg·m²)
-
: damping coefficient (N·m·s)
-
: armature resistance (Ω)
-
: inductance (H)
-
: motor torque/back-emf constant
You can simplify this into a first-order system for simulation:
⚠️ Note: is fixed, based on the physical motor.
Step 2: Design the Controller
Design a PID controller in the Laplace domain to improve motor response:
Tune using:
-
Ziegler–Nichols method
-
Trial-and-error
-
Simulation feedback
🎯 The controller is designed based on the motor model .
Step 3: Discretize the Controller to C(z)
To implement the PID controller on Arduino (discrete system), convert to C(z) using Z-transform.
Use Tustin (bilinear) approximation:
The PID controller in discrete form becomes:
Where:
-
: PWM output
-
: current error (desired - actual speed)
-
: sampling time (e.g., 0.1s)
Step 4: Arduino PID Implementation (Code)
// PID constants
float Kp = 1.2, Ki = 0.5, Kd = 0.05;
float T = 0.1; // Sampling time = 100 ms
float e[3] = {0}; // PID error buffer
float u = 0; // PWM output
int setSpeed = 10; // target speed in counts per 100 ms
const int pwmPin = 5; // MOSFET gate for motor control
const int Q1Pin = 9; // Encoder Q1
const int Q2Pin = 10; // Encoder Q2
const int IDXPin = 11; // Encoder Index pulse (optional)
volatile long encoderCount = 0;
long lastEncoderCount = 0;
unsigned long lastTime = 0;
// Track last Q1/Q2 to detect direction
int lastQ1 = 0;
int lastQ2 = 0;
void setup() {
pinMode(pwmPin, OUTPUT);
pinMode(Q1Pin, INPUT);
pinMode(Q2Pin, INPUT);
pinMode(IDXPin, INPUT); // Optional
Serial.begin(9600);
Serial.println("Proteus DC Motor Encoder PID Control");
}
void loop() {
// Quadrature decoding (called frequently to detect movement)
updateEncoder();
// PID loop every 100 ms
if (millis() - lastTime >= T * 1000) {
lastTime = millis();
e[2] = e[1];
e[1] = e[0];
// Speed = encoder ticks since last sample
long currentCount = encoderCount;
int feedback = currentCount - lastEncoderCount;
lastEncoderCount = currentCount;
e[0] = setSpeed - feedback;
float du = Kp * (e[0] - e[1]) + Ki * T * e[0] + Kd / T * (e[0] - 2 * e[1] + e[2]);
u += du;
u = constrain(u, 0, 255);
analogWrite(pwmPin, (int)u);
// Display information
Serial.print("Position: ");
Serial.print(encoderCount);
Serial.print(" | Speed: ");
Serial.print(feedback);
Serial.print(" | PWM: ");
Serial.print((int)u);
// Optional: display Q1, Q2, IDX state
Serial.print(" | Q1Q2IDX: ");
Serial.print(digitalRead(Q1Pin));
Serial.print(digitalRead(Q2Pin));
Serial.println(digitalRead(IDXPin));
}
}
// Quadrature decoder
void updateEncoder() {
int q1 = digitalRead(Q1Pin);
int q2 = digitalRead(Q2Pin);
// Detect rising edge on Q1 and determine direction from Q2
if (q1 != lastQ1) {
if (q1 == 1) {
if (q2 == 0) encoderCount++; // CW
else encoderCount--; // CCW
}
}
// Save last states
lastQ1 = q1;
lastQ2 = q2;
}
This Arduino code implements a discrete PID (Proportional-Integral-Derivative) controller to regulate the speed of a DC motor. It reads the actual speed (or a proxy like a potentiometer or analog sensor) from analog pin A0 every 100 milliseconds, compares it with a setpoint (setSpeed = 512), and calculates the error. Using PID logic, it computes the necessary adjustment (du) and updates the PWM control signal (u) sent to the motor via pin 9. The PWM value is constrained between 0 and 255 to control the motor speed. The feedback value is also printed to the Serial Monitor for observation and debugging.
This code does not use any arduino PID library like PID_v1.h as used in the Arduino PID Controller - Temperature PID Controller tutorial. This pid code uses manual discrete PID logic, giving you complete control over how errors are handled, how tuning is applied, and how each PID component behaves. This makes it more flexible and educational, but also prone to bugs and harder to maintain if your project grows.
The circuit diagram for this implementation is below.