Chapter 12: Digital Control and Real-Time Implementation
Lesson 5: Lab: Implement Controller with Real Loop Timing
This lab connects the theory of sampled-data robot control to an actual real-time loop implementation. You will implement a discrete-time joint-space controller, measure real loop timing (sampling, computation delay, jitter), and interpret its effect on stability and tracking performance. We combine state-space sampled-data models with practical timing primitives in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica, using robotics-related libraries where appropriate.
1. Lab Objectives and Setup
We assume you already designed a stabilizing continuous-time joint-space controller in previous chapters (e.g., PD or computed-torque) and discretized it with a zero-order hold in Lessons 12.1–12.2. In this lab, we:
- Implement a discrete-time joint controller with sampling period \( T_s \).
- Measure the actual inter-sample intervals \( T_k \) and computation time \( T_c(k) \).
- Model sampling jitter as a perturbation of the nominal discrete-time closed loop and analyze robustness.
-
Use language-specific real-time constructs (threads, timers,
executors) and robotics frameworks:
-
Python:
rclpy(ROS 2),numpy, simple timing withtime.perf_counter(). -
C++:
rclcpp,Eigen,<chrono>/<thread>for timing. -
Java:
ScheduledExecutorService, optionallyrosjavafor integration. - MATLAB/Simulink: discrete-time blocks, Robotics System Toolbox.
- Wolfram Mathematica: numerical integration and timed loops.
-
Python:
We focus on a single revolute joint modeled as a linearized second-order system around a given operating point. Let \( q(t) \) denote joint position, \( \dot{q}(t) \) velocity, and control input \( \tau(t) \) the actuator torque command.
\[ \mathbf{x}(t) = \begin{bmatrix} q(t) \\ \dot{q}(t) \end{bmatrix}, \quad \dot{\mathbf{x}}(t) = \mathbf{A}\,\mathbf{x}(t) + \mathbf{B}\,u(t), \quad y(t) = \mathbf{C}\,\mathbf{x}(t), \quad u(t) = \tau(t) \]
Here \( \mathbf{A} \in \mathbb{R}^{2\times 2} \) and \( \mathbf{B} \in \mathbb{R}^{2\times 1} \) come from the linearization of the robot joint dynamics around the operating point, which you already know from kinematics and dynamics.
2. Discrete-Time Controller with Nominal Sampling
Using a zero-order hold (ZOH) with nominal sample time \( T_s \), the equivalent discrete-time model is:
\[ \mathbf{x}[k+1] = \mathbf{\Phi}\,\mathbf{x}[k] + \mathbf{\Gamma}\,u[k], \quad y[k] = \mathbf{C}\,\mathbf{x}[k], \]
\[ \mathbf{\Phi} = \exp(\mathbf{A} T_s), \quad \mathbf{\Gamma} = \int_{0}^{T_s} \exp\!\big(\mathbf{A}\sigma\big)\,\mathrm{d}\sigma\, \mathbf{B}. \]
A joint-space PD controller in discrete time for tracking a desired trajectory \( q_d[k] \) is:
\[ e[k] = q_d[k] - q[k], \quad \dot{e}[k] \approx \frac{e[k] - e[k-1]}{T_s}, \]
\[ \tau[k] = K_p\,e[k] + K_d\,\dot{e}[k] = K_p\,e[k] + K_d\frac{e[k]-e[k-1]}{T_s}. \]
In state-space form, if we augment the state with the previous error \( e[k-1] \), this becomes a linear dynamic controller. When combined with the discrete-time plant, we obtain a closed-loop system \( \mathbf{x}_c[k+1] = \mathbf{A}_c \mathbf{x}_c[k] \) whose eigenvalues must satisfy \( |\lambda_i(\mathbf{A}_c)| < 1 \) for stability.
3. Modeling Real Loop Timing and Jitter
In a real implementation, the loop does not execute at perfectly periodic instants. Let \( t_k \) be the time at which the control input \( u[k] \) is applied (after sensing and computation). We model:
\[ t_k = k T_s + \delta_k, \]
where \( \delta_k \) is the (unknown) timing error. The actual inter-sample interval is
\[ T_k = t_{k+1} - t_k = T_s + \Delta T_k, \quad \Delta T_k = \delta_{k+1} - \delta_k. \]
We call \( \Delta T_k \) the sampling jitter. In practice, the OS scheduler, cache misses, communication delay, and other tasks cause \( \Delta T_k \neq 0 \). Assume \( |\Delta T_k| \leq J_{\max} \) (bounded jitter).
The sampled-data model with aperiodic sampling is
\[ \mathbf{x}[k+1] = \mathbf{\Phi}_k\,\mathbf{x}[k] + \mathbf{\Gamma}_k\,u[k], \]
\[ \mathbf{\Phi}_k = \exp\big(\mathbf{A}(T_s+\Delta T_k)\big), \quad \mathbf{\Gamma}_k = \int_{0}^{T_s+\Delta T_k} \exp(\mathbf{A}\sigma)\,\mathrm{d}\sigma\, \mathbf{B}. \]
For small jitter \( |\Delta T_k| \ll T_s \), a first-order expansion yields
\[ \mathbf{\Phi}_k \approx \mathbf{\Phi} + \Delta T_k\,\mathbf{A}\mathbf{\Phi}, \quad \mathbf{\Gamma}_k \approx \mathbf{\Gamma} + \Delta T_k\,\mathbf{B}. \]
With state feedback \( u[k] = -\mathbf{K}\mathbf{x}[k] \), the closed-loop dynamics become
\[ \mathbf{x}[k+1] = \big(\mathbf{\Phi}_k - \mathbf{\Gamma}_k\mathbf{K}\big)\mathbf{x}[k] = \big(\mathbf{A}_d + \Delta\mathbf{A}_k\big)\mathbf{x}[k], \]
\[ \mathbf{A}_d = \mathbf{\Phi} - \mathbf{\Gamma}\mathbf{K}, \quad \Delta\mathbf{A}_k \approx \Delta T_k\left(\mathbf{A}\mathbf{\Phi} - \mathbf{B}\mathbf{K}\right). \]
Thus jitter appears as a multiplicative perturbation of the nominal discrete-time closed-loop matrix \( \mathbf{A}_d \). We can analyze robustness using matrix norms.
Lemma (robust stability with small jitter).
Suppose there exists a matrix norm \( \|\cdot\| \) and a number \( \alpha \) with \( \|\mathbf{A}_d\| \leq \alpha < 1 \). If
\[ \|\Delta\mathbf{A}_k\| \leq \varepsilon \quad \text{for all } k \]
and \( \alpha + \varepsilon < 1 \), then the origin of the perturbed system \( \mathbf{x}[k+1]=( \mathbf{A}_d + \Delta\mathbf{A}_k )\mathbf{x}[k] \) is exponentially stable.
Proof sketch.
From submultiplicativity: \( \|\mathbf{x}[k+1]\| \leq \|\mathbf{A}_d+\Delta\mathbf{A}_k\|\;\|\mathbf{x}[k]\| \leq (\alpha+\varepsilon)\|\mathbf{x}[k]\| \). Induction gives
\[ \|\mathbf{x}[k]\| \leq (\alpha+\varepsilon)^k \|\mathbf{x}[0]\|. \]
Because \( \alpha+\varepsilon < 1 \), the bound decays geometrically, implying exponential stability.
In the lab, you will estimate jitter from timing measurements and reason whether it threatens the stability margin implied by your discrete-time design.
4. Real-Time Control Loop Flow
The following flow describes the structure of a real-time control task executing at nominal period \( T_s \). This is the pattern you will implement in each language.
flowchart TD
START["Start lab program"] --> INIT["Initialize robot interface, gains, Ts"]
INIT --> TIMESET["Set 'next_time' = 'now' + Ts"]
TIMESET --> LOOP{"Loop while 't' < 't_final'"}
LOOP --> SENSE["Read joint states q, dq from sensors or simulator"]
SENSE --> REF["Compute reference q_d, dq_d at current time"]
REF --> CTRL["Compute error e, de and control torque tau_k"]
CTRL --> SEND["Send tau_k to robot or simulator"]
SEND --> LOG["Log timestamps, measured dt, jitter, and state"]
LOG --> SLEEP["Sleep until 'next_time' (if time remains)"]
SLEEP --> UPDATE["Update 'next_time' = 'next_time' + Ts"]
UPDATE --> LOOP
Critical measurements for analysis:
- Measured period: \( T_k = t_{k} - t_{k-1} \).
- Computation time: wall-clock between sensing and end of control computation.
- Jitter: \( \Delta T_k = T_k - T_s \).
For each experiment, you will plot \( T_k \), \( \Delta T_k \), and tracking error \( e[k] \) against time to relate timing and control quality.
5. Python Lab Implementation (ROS 2 / Simulation Friendly)
Python is not a hard real-time language, but it is convenient for rapid prototyping and simulation. We assume either:
-
A ROS 2 node using
rclpysubscribing to joint states and publishing torque commands, or - A simulation backend (e.g., PyBullet or a custom ODE integrator) that advances in real time.
The code below illustrates a 1-DOF PD controller with timing
measurement. Replace the read_joint() and
write_torque() functions with your robot or simulator APIs.
import time
import math
import numpy as np
# Controller parameters
Ts = 0.002 # nominal sampling time [s]
Kp = 50.0
Kd = 2.0
# Reference trajectory: simple sinusoid
def q_ref(t):
qd = 0.5 * math.sin(2.0 * math.pi * 0.5 * t) # 0.5 rad amplitude, 0.5 Hz
dqd = 0.5 * 2.0 * math.pi * 0.5 * math.cos(2.0 * math.pi * 0.5 * t)
return qd, dqd
# Robot I/O (to be implemented for your platform)
def read_joint():
"""
Return (q, dq) for the controlled joint.
Connect this to your low-level interface or simulator.
"""
# Example dummy implementation:
# return sim.get_joint_state()
raise NotImplementedError
def write_torque(tau):
"""
Send torque tau to the actuator.
"""
# Example dummy implementation:
# sim.set_joint_torque(tau)
raise NotImplementedError
# Logging arrays
log_t = []
log_Tk = []
log_jitter = []
log_q = []
log_qd = []
log_e = []
t_final = 5.0 # run 5 seconds
t0 = time.perf_counter()
last_time = t0
next_time = t0 + Ts
e_prev = 0.0
while True:
now = time.perf_counter()
t = now - t0
if t >= t_final:
break
# Measured period and jitter
Tk = now - last_time
jitter = Tk - Ts
last_time = now
# Read current joint state
q, dq = read_joint()
# Reference at current time
qd, dqd = q_ref(t)
# PD control in discrete time
e = qd - q
de = (e - e_prev) / max(Tk, 1e-6) # use measured Tk in derivative
e_prev = e
tau = Kp * e + Kd * de
# Send control
write_torque(tau)
# Log signals
log_t.append(t)
log_Tk.append(Tk)
log_jitter.append(jitter)
log_q.append(q)
log_qd.append(qd)
log_e.append(e)
# Sleep until next nominal sample time
next_time += Ts
sleep_time = next_time - time.perf_counter()
if sleep_time > 0.0:
time.sleep(sleep_time)
# After the loop, analyze logs:
# - Plot Tk vs t
# - Plot jitter vs t
# - Plot tracking error vs t
Remark. In this implementation, the derivative term uses the measured \( T_k \) instead of nominal \( T_s \), partially compensating for jitter in the derivative estimate. For small jitter and sufficiently low closed-loop bandwidth, this improves robustness compared to a fixed \( T_s \)-based derivative.
6. C++ Implementation with High-Resolution Timing
C++ is common for low-level control, particularly in ROS 2 with
rclcpp and ros2_control. Here we show a bare
loop using std::chrono. In a full system, the same logic
would be inside a controller update method called by the
real-time manager.
#include <chrono>
#include <thread>
#include <vector>
#include <cmath>
// Replace with your own robot interface
struct JointInterface {
double readPosition();
double readVelocity();
void writeTorque(double tau);
};
int main() {
using clock = std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::microseconds;
JointInterface joint;
const double Ts = 0.002; // 2 ms
const double Kp = 50.0;
const double Kd = 2.0;
auto t0 = clock::now();
auto last = t0;
auto next = t0 + std::chrono::duration<double>(Ts);
double e_prev = 0.0;
double t_final = 5.0;
std::vector<double> log_t, log_Tk, log_jitter, log_q, log_qd, log_e;
while (true) {
auto now = clock::now();
double t = std::chrono::duration<double>(now - t0).count();
if (t >= t_final) break;
double Tk = std::chrono::duration<double>(now - last).count();
last = now;
double jitter = Tk - Ts;
// Joint state
double q = joint.readPosition();
double dq = joint.readVelocity();
// Reference
double qd = 0.5 * std::sin(2.0 * M_PI * 0.5 * t);
double dqd = 0.5 * 2.0 * M_PI * 0.5 * std::cos(2.0 * M_PI * 0.5 * t);
double e = qd - q;
double de = (e - e_prev) / std::max(Tk, 1e-6);
e_prev = e;
double tau = Kp * e + Kd * de;
joint.writeTorque(tau);
// Log signals
log_t.push_back(t);
log_Tk.push_back(Tk);
log_jitter.push_back(jitter);
log_q.push_back(q);
log_qd.push_back(qd);
log_e.push_back(e);
// Sleep until next nominal instant
next += std::chrono::duration<double>(Ts);
auto now2 = clock::now();
if (next > now2) {
std::this_thread::sleep_until(next);
}
}
// Post-process logs (e.g., save to file) for plotting.
return 0;
}
On POSIX systems you can further improve determinism by assigning a
real-time scheduling policy (SCHED_FIFO) and core affinity
to the control thread. These OS-level details are beyond the scope of
this lab, but are essential for hard real-time guarantees.
7. Java Implementation with ScheduledExecutorService
Java is less common in low-level robotics, but illustrates periodic
tasks with ScheduledExecutorService. Integrations with ROS
are possible via rosjava. Here, the emphasis is measuring
timing and logging jitter.
import java.util.concurrent.*;
import java.util.*;
import java.lang.Math;
class JointInterface {
public double readPosition() { /* ... */ return 0.0; }
public double readVelocity() { /* ... */ return 0.0; }
public void writeTorque(double tau) { /* ... */ }
}
public class RealTimeController {
public static void main(String[] args) {
final double Ts = 0.002; // 2 ms
final double Kp = 50.0;
final double Kd = 2.0;
final double tFinal = 5.0;
JointInterface joint = new JointInterface();
List<Double> log_t = new ArrayList<>();
List<Double> log_Tk = new ArrayList<>();
List<Double> log_jitter = new ArrayList<>();
List<Double> log_q = new ArrayList<>();
List<Double> log_qd = new ArrayList<>();
List<Double> log_e = new ArrayList<>();
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
final long startNano = System.nanoTime();
final double[] ePrev = new double[]{0.0};
final long periodNano = (long)(Ts * 1e9);
Runnable task = new Runnable() {
long lastNano = startNano;
@Override
public void run() {
long nowNano = System.nanoTime();
double t = (nowNano - startNano) / 1e9;
if (t >= tFinal) {
exec.shutdown();
return;
}
double Tk = (nowNano - lastNano) / 1e9;
lastNano = nowNano;
double jitter = Tk - Ts;
double q = joint.readPosition();
double dq = joint.readVelocity();
double qd = 0.5 * Math.sin(2.0 * Math.PI * 0.5 * t);
double dqd = 0.5 * 2.0 * Math.PI * 0.5 * Math.cos(2.0 * Math.PI * 0.5 * t);
double e = qd - q;
double de = (e - ePrev[0]) / Math.max(Tk, 1e-6);
ePrev[0] = e;
double tau = Kp * e + Kd * de;
joint.writeTorque(tau);
log_t.add(t);
log_Tk.add(Tk);
log_jitter.add(jitter);
log_q.add(q);
log_qd.add(qd);
log_e.add(e);
}
};
exec.scheduleAtFixedRate(task, 0, periodNano, TimeUnit.NANOSECONDS);
}
}
The JVM introduces nontrivial jitter compared to C++. The experiment of comparing \( \Delta T_k \) distributions between Java and C++ is instructive for appreciating language/runtime overhead in control.
8. MATLAB/Simulink Implementation
MATLAB and Simulink are widely used for prototyping and hardware-in-the-loop experiments. We outline both a script-based discrete-time loop and a Simulink model configuration.
8.1 Script-Based Discrete Loop in MATLAB
Ts = 0.002;
Kp = 50;
Kd = 2;
tFinal = 5.0;
% Robot interface: implement these as needed
readJoint = @() deal(0.0, 0.0); % [q, dq]
writeTorque = @(tau) []; % send torque
t0 = tic;
lastTime = 0.0;
nextTime = Ts;
e_prev = 0.0;
log_t = [];
log_Tk = [];
log_jitter = [];
log_q = [];
log_qd = [];
log_e = [];
while true
now = toc(t0);
if now >= tFinal
break;
end
Tk = now - lastTime;
lastTime = now;
jitter = Tk - Ts;
[q, dq] = readJoint();
qd = 0.5 * sin(2*pi*0.5*now);
dqd = 0.5 * 2*pi*0.5 * cos(2*pi*0.5*now);
e = qd - q;
de = (e - e_prev) / max(Tk, 1e-6);
e_prev = e;
tau = Kp * e + Kd * de;
writeTorque(tau);
log_t(end+1,1) = now;
log_Tk(end+1,1) = Tk;
log_jitter(end+1,1) = jitter;
log_q(end+1,1) = q;
log_qd(end+1,1) = qd;
log_e(end+1,1) = e;
% Sleep to enforce nominal Ts
pause(max(0.0, nextTime - now));
nextTime = nextTime + Ts;
end
% Plot log_Tk, log_jitter, and log_e vs log_t
8.2 Simulink Model
In Simulink, create a model with:
- A discrete-time controller subsystem (sample time \( T_s \)) implementing the PD law.
- A continuous-time joint plant (from prior chapters) connected through a zero-order hold.
- A Rate Transition block if you simulate multi-rate loops.
-
Scope blocks logging sample times via
Clockand difference blocks to measure \( T_k \).
For hardware deployment (e.g., Simulink Real-Time), the same model can execute on a real-time target, giving near-ideal sampling with minimal jitter, against which you can compare PC-based implementations.
9. Wolfram Mathematica Implementation
Mathematica is most convenient for symbolic analysis and offline simulation, but we can still emulate a real-time loop by sampling \( \mathbf{A} \), \( \mathbf{B} \) and iterating a discrete-time model while measuring wall-clock time.
(* Parameters *)
Ts = 0.002;
Kp = 50.;
Kd = 2.;
tFinal = 5.0;
(* Simple linearized joint model: x = {q, dq} *)
A = { {0., 1.}, {-100., -5.} };
B = { {0.}, {100.} };
Phi = MatrixExp[A Ts];
Gamma = Integrate[MatrixExp[A s], {s, 0, Ts}].B;
(* Initial state *)
x = {0., 0.};
ePrev = 0.;
(* Logging *)
log = {};
t0 = AbsoluteTime[];
last = t0;
next = t0 + Ts;
While[True,
now = AbsoluteTime[];
t = now - t0;
If[t >= tFinal, Break[]];
Tk = now - last;
last = now;
jitter = Tk - Ts;
(* Reference *)
qd = 0.5*Sin[2.*Pi*0.5*t];
dqd = 0.5*2.*Pi*0.5*Cos[2.*Pi*0.5*t];
q = x[[1]];
dq = x[[2]];
e = qd - q;
de = (e - ePrev)/Max[Tk, 10.^-6];
ePrev = e;
tau = Kp*e + Kd*de;
(* Discrete-time update using nominal Phi, Gamma *)
x = Phi.x + Gamma*tau;
AppendTo[log, {t, Tk, jitter, q, qd, e}];
pause = next - AbsoluteTime[];
If[pause > 0., Pause[pause]];
next = next + Ts;
]
(* 'log' contains {t, Tk, jitter, q, qd, e} for plotting and analysis *)
Symbolic capabilities can further be used to derive \( \mathbf{\Phi}(T) \) and \( \mathbf{\Gamma}(T) \) exactly as functions of sample time, enabling analytic sensitivity analysis of closed-loop eigenvalues to timing variations.
10. Problems and Solutions
Problem 1 (Discrete-Time Model from Continuous-Time Joint Dynamics). Consider the linearized joint model \( \dot{\mathbf{x}}(t) = \mathbf{A}\mathbf{x}(t) + \mathbf{B}u(t) \) with constant matrices \( \mathbf{A} \), \( \mathbf{B} \). Show that under a zero-order hold with sampling period \( T_s \), the discrete-time model is \( \mathbf{x}[k+1] = \mathbf{\Phi}\,\mathbf{x}[k] + \mathbf{\Gamma}\,u[k] \) with \( \mathbf{\Phi} = \exp(\mathbf{A} T_s) \) and \( \mathbf{\Gamma} = \int_0^{T_s} \exp(\mathbf{A}\sigma)\,\mathrm{d}\sigma\,\mathbf{B} \).
Solution.
Assume \( u(t) = u[k] \) for \( t \in [kT_s,(k+1)T_s) \). The solution of the linear ODE from \( t_k = kT_s \) to \( t_{k+1} = (k+1)T_s \) is
\[ \mathbf{x}(t_{k+1}) = \exp(\mathbf{A} T_s)\,\mathbf{x}(t_k) + \int_{0}^{T_s} \exp\big(\mathbf{A}(T_s-\sigma)\big)\,\mathbf{B}u[k]\;\mathrm{d}\sigma. \]
Using the substitution \( r = T_s-\sigma \), \( \mathrm{d}r = -\mathrm{d}\sigma \), the limits reverse and we get
\[ \int_{0}^{T_s} \exp\big(\mathbf{A}(T_s-\sigma)\big)\,\mathrm{d}\sigma = \int_0^{T_s} \exp(\mathbf{A}r)\,\mathrm{d}r. \]
Identifying \( \mathbf{x}[k] = \mathbf{x}(t_k) \) and \( \mathbf{x}[k+1]=\mathbf{x}(t_{k+1}) \) yields the ZOH formulas for \( \mathbf{\Phi} \) and \( \mathbf{\Gamma} \).
Problem 2 (Jitter-Induced Perturbation Bound). Let \( \mathbf{A}_d = \mathbf{\Phi} - \mathbf{\Gamma}\mathbf{K} \) be the nominal closed-loop matrix for a state-feedback design with sampling period \( T_s \). Suppose jitter produces time-varying matrices \( \mathbf{A}_d + \Delta\mathbf{A}_k \) with \( \|\Delta\mathbf{A}_k\| \leq c J_{\max} \) for some known constant \( c > 0 \). Give a sufficient inequality on \( J_{\max} \) to guarantee robust stability in the sense of the lemma from Section 3.
Solution.
From the lemma, a sufficient condition for exponential stability is the existence of a norm with \( \|\mathbf{A}_d\| \leq \alpha < 1 \) and \( \alpha + \varepsilon < 1 \), where \( \varepsilon \geq \|\Delta\mathbf{A}_k\| \) for all \( k \). If \( \|\Delta\mathbf{A}_k\| \leq c J_{\max} \), we can choose \( \varepsilon = c J_{\max} \) and require
\[ \alpha + c J_{\max} < 1 \quad \Longrightarrow \quad J_{\max} < \frac{1-\alpha}{c}. \]
Hence any jitter bound \( J_{\max} \) satisfying this inequality preserves stability.
Problem 3 (Real-Time Feasibility of the Control Task). Consider a single control task with worst-case computation time \( C \) and sampling period \( T_s \). Under a simple rate-monotonic analysis for a single task, show that if \( C < T_s \), the task can meet its deadline. Then, for your implementation, estimate \( C \) from timing logs and check feasibility.
Solution.
For a single periodic task with relative deadline equal to period, the schedulability condition under a uniprocessor scheduler is that its processor utilization \( U = C/T_s \) is less than or equal to 1. If \( C < T_s \), then \( U < 1 \), meaning the CPU has enough time within each period to execute the task before the next release. In your implementation, the measured computation time per iteration can be approximated as
\[ C \approx \max_k \big(t_k^{\text{end}} - t_k^{\text{start}}\big), \]
where \( t_k^{\text{start}} \) is the timestamp just before sensing and \( t_k^{\text{end}} \) just after control output is sent. If the empirical \( C \) satisfies \( C < T_s \), your controller is schedulable under ideal conditions. If not, you must reduce computation (e.g., simplify computations, move heavy tasks to slower threads) or increase \( T_s \).
Problem 4 (Effect of Using \( T_k \) vs \( T_s \) in the Derivative Term). In the PD controller, consider two implementations of the derivative:
\[ \text{(a)}\quad \dot{e}[k] = \frac{e[k]-e[k-1]}{T_s}, \qquad \text{(b)}\quad \dot{e}[k] = \frac{e[k]-e[k-1]}{T_k}. \]
Qualitatively analyze which implementation is more robust to jitter and explain the trade-off in terms of noise amplification.
Solution.
Implementation (a) ignores jitter and uses constant \( T_s \). When \( T_k \) deviates from \( T_s \), the effective derivative gain becomes
\[ K_d^{\text{eff}}(k) = K_d \frac{T_k}{T_s}, \]
i.e., timing variation translates into multiplicative variation of the derivative gain, which can destabilize the loop for large jitter. Implementation (b) uses the actual period \( T_k \), which keeps the derivative estimate closer to the true derivative \( \frac{\mathrm{d}e}{\mathrm{d}t} \) and thus is more robust to jitter. However, \( T_k \) fluctuates sample to sample, and noise in the measurement is divided by a random quantity. If \( T_k \) occasionally becomes very small, the derivative can amplify sensor noise dramatically. In practice, a hybrid approach with saturation
\[ \dot{e}[k] = \frac{e[k]-e[k-1]}{\max(T_k,T_{\min})} \]
with a lower bound \( T_{\min} > 0 \) balances jitter compensation and noise robustness.
Problem 5 (Conceptual Timing Analysis Flow). Sketch a conceptual flow for evaluating whether a proposed control period \( T_s \) is feasible on given hardware, taking into account computation time, scheduling, and robustness margin. Use the flow to justify either decreasing or increasing \( T_s \) in your lab implementation.
Solution (flow).
flowchart TD
S["Choose nominal Ts and design discrete controller"] --> M["Measure worst case computation time C"]
M --> F{"Is C < Ts with margin?"}
F -->|yes| J["Measure jitter and estimate J_max"]
F -->|no| ADJ["Adjust: optimize code or increase Ts"]
J --> R{"Is jitter bound small \nvs stability margin?"}
R -->|yes| OK["Accept Ts and deploy controller"]
R -->|no| RED["Reduce bandwidth or increase Ts"]
This flow emphasizes that timing analysis and control design must be performed together: an aggressive \( T_s \) with too heavy computations can violate both schedulability and stability.
11. Summary
In this lab, you bridged the gap between discrete-time control theory and actual real-time implementation. Starting from a linearized joint model, you implemented a sampled PD controller, measured real sampling periods, computation times, and jitter, and interpreted their influence on stability and performance via a perturbation view of the closed-loop matrix. Through implementations in Python, C++, Java, MATLAB/Simulink, and Mathematica, you observed language- and runtime-dependent jitter characteristics and learned how to structure a deterministic control loop and check basic schedulability conditions. These skills underpin advanced real-time control architectures and safety-critical controllers in later chapters.
12. References
- Åström, K. J., & Wittenmark, B. (1984). Computer-controlled systems: Theory and design. Prentice Hall.
- Henningsson, T., Cervin, A., & Årzén, K.-E. (2008). Sampling jitter effects in computer control systems. European Journal of Control, 14(3), 241–254.
- Fujioka, H. (2009). Stability analysis of systems with aperiodic sample-and-hold devices. Automatica, 45(3), 771–775.
- Zhang, W., Branicky, M. S., & Phillips, S. M. (2001). Stability of networked control systems. IEEE Control Systems Magazine, 21(1), 84–99.
- Cervin, A., Henriksson, D., Lincoln, B., Eker, J., & Årzén, K.-E. (2003). How does control timing affect performance? Analysis and simulation of timing using Jitterbug and TrueTime. IEEE Control Systems Magazine, 23(3), 16–30.
- Buttazzo, G. C., Lipari, G., & Abeni, L. (1998). Elastic task model for adaptive rate control. IEEE Real-Time Systems Symposium, 286–295.
- Goodwin, G. C., Salgado, M. E., Middleton, R. H., & McInnis, B. C. (1995). Frequency domain tools for designing fast sampling rate controllers. Automatica, 31(4), 659–669.
- Nesic, D., Teel, A. R., & Carnevale, D. (2009). Explicit computation of the sampling period in emulation of controllers for nonlinear sampled-data systems. IEEE Transactions on Automatic Control, 54(3), 619–624.