The sampling period (Δt or Ts) is the time that elapses between two consecutive executions of the controller in a digital system.
When making the leap from paper to silicon, we leave the comfortable world of continuous mathematics (where time flows uninterrupted) and enter the discrete world. Here, our microcontroller (be it an Arduino, an ESP32, or an industrial PLC) is like a movie camera taking snapshots of reality.
The million-dollar question every programmer asks when implementing a PID is: how fast should my loop run? The instinctive answer is often: “As fast as the processor can go!” And that is one of the worst design decisions you could make.
The Myth of «Faster is Better»
If we shove the analog sensor reading and our digital control calculation directly into a wild loop() with no timing constraints, the execution will run at the CPU’s maximum clock speed (perhaps thousands of times per second).
This can cause three problems in the real world:
- CPU Starvation: If the microcontroller spends 99% of its time recalculating a PID for a motor that hasn’t even had the physical time to move a millimeter, we are wasting clock cycles. We run out of resources to read the serial port, update an OLED screen, or maintain a WiFi connection.
- Derivative Noise: As we briefly touched on in previous articles, the derivative action calculates the slope by dividing by
. If the sampling period is tiny ( s), any minuscule electrical noise from the sensor gets divided by that tiny number, creating a gigantic and unstable control spike. - Mathematical Inconsistency: If we leave the loop “free,” one iteration might take
ms, but if a packet suddenly arrives via Serial, the next iteration might take ms. This destroys the validity of our integrals and derivatives, which assume a constant time step to avoid distorting the areas under the curve.
Therefore, we need to establish a constant sampling time. But how fast does it need to be? If we go too fast, we waste resources; but if we go too slow, we become blind.
The Limit: The Nyquist-Shannon Theorem
To find the minimum speed at which we can read a sensor without losing important information, we turn to one of the most well-known theorems in information theory, formulated by Harry Nyquist and Claude Shannon.
The Nyquist-Shannon Theorem states that to reconstruct a continuous band-limited signal from ideal samples, the sampling frequency must be greater than twice the maximum frequency contained in the signal.
If we are trying to control a machine that vibrates at
Aliasing
Imagine a moving car wheel recorded by a video camera at 24 frames per second. Sometimes, the wheel appears to spin backward, even though the car is moving forward. That is Aliasing: the camera is taking pictures slower than the speed at which the wheel’s spokes turn, deceiving our brain.
Exactly the same thing happens in electronics. If the physical plant moves faster than the speed at which you read it, your microcontroller will connect the dots and “see” a low-frequency phantom signal that does not exist in reality. If your PID tries to correct this phantom signal, the system will collapse.
Interactive Aliasing Simulator
To see how dangerous it is to sample too slowly, I’ve prepared this Nyquist theorem simulator.
In the simulator, the blue wave is physical reality (our plant oscillating at 1Hz). Lower the sampling frequency to 1.1Hz and see how the reconstructed signal looks like a very long, very slow wave. Your Arduino is hallucinating.
Then raise it to 2.0Hz (the Nyquist limit) and finally to 10Hz. Now the points capture the real silhouette much better.
From Theory to Practice
The Nyquist theorem tells us the minimum speed to not lose information. But in control engineering, just reading the information is not enough; we must react in time and correct it.
If you sample right at the Nyquist limit (
For this reason, a fundamental rule of thumb exists in the automation industry:
Control Sampling Rule of Thumb:
The control loop execution frequency (
If you have a massive industrial oven that takes minutes to heat up (Slow dynamics, bandwidth of
If you have a racing drone that reacts to wind gusts in milliseconds (Fast dynamics, bandwidth of
Implementing Deterministic Sampling in C++
To achieve this in our code without blocking the execution of other processes, we use non-blocking timing structures.
The basic method for an Arduino or ESP32 involves evaluating the passage of time with millis() or micros(). It doesn’t completely eliminate jitter, but it prevents the frequency from depending directly on the duration of each loop() iteration.
// Define the desired sampling time (Ts = 10ms -> fs = 100Hz)
const unsigned long TIEMPO_MUESTREO_MS = 10;
unsigned long tiempo_anterior = 0;
void setup() {
// Initial configuration
tiempo_anterior = millis();
}
void loop() {
unsigned long tiempo_actual = millis();
// Check if at least the sampling time has passed
if (tiempo_actual - tiempo_anterior >= TIEMPO_MUESTREO_MS) {
// --- 1. Read sensors ---
float pv = leer_sensor();
// --- 2. Execute control algorithm (PID, discretization, etc.) ---
// Here we mathematically know that dt = 0.01 seconds
float dt = TIEMPO_MUESTREO_MS / 1000.0;
float salida = calcular_pid(pv, dt);
// --- 3. Act on the plant ---
escribir_actuador(salida);
// Advance the schedule without accumulating time drift
tiempo_anterior += TIEMPO_MUESTREO_MS;
}
// OUTSIDE THE IF!
// Here the microcontroller is free to do quick tasks
// without being affected by the PID's sluggishness.
atender_peticiones_wifi();
refrescar_pantalla();
}When jitter is not tolerable, we can use a hardware timer or a periodic real-time task. The ISR should be limited to recording the timestamp or waking a task; reading sensors, calculating the PID, or accessing communication within an interrupt can introduce new problems.