Chapter 1: Introduction to Adaptive Control
Lesson 4: Adaptive vs Robust vs Optimal Control – Conceptual Comparison
This lesson compares three major control philosophies: adaptive control, robust control, and optimal control. Students already familiar with Linear Control will recognize state feedback, stability margins, and quadratic performance indices. The new contribution here is to clarify when a controller should learn online, when it should tolerate uncertainty without learning, and when it should optimize performance for a trusted model.
1. Unified Problem Statement
Consider an uncertain continuous-time plant \( \dot{\mathbf{x}}(t) \) with state \( \mathbf{x}(t)\in\mathbb{R}^n \), input \( \mathbf{u}(t)\in\mathbb{R}^m \), and unknown parameter vector \( \boldsymbol{\theta} \):
\[ \dot{\mathbf{x}}(t) = \mathbf{A}(\boldsymbol{\theta})\mathbf{x}(t) + \mathbf{B}(\boldsymbol{\theta})\mathbf{u}(t) + \mathbf{E}\mathbf{d}(t), \qquad \mathbf{y}(t)=\mathbf{C}\mathbf{x}(t) \]
The three control philosophies differ mainly in how they treat \( \boldsymbol{\theta} \) and disturbance \( \mathbf{d}(t) \).
- Optimal control: assumes a sufficiently reliable model and chooses \( \mathbf{u}(t) \) to minimize a cost functional.
- Robust control: assumes uncertainty belongs to a known set and designs one fixed controller that works for all admissible plants.
- Adaptive control: adjusts controller parameters online using measured signals, so the controller changes as the plant behavior is learned.
flowchart TD
P["Uncertain plant: x_dot = A(theta) x + B(theta) u"] --> Q["Control objective"]
Q --> O["Optimal control: \noptimize known model"]
Q --> R["Robust control: \ntolerate uncertainty set"]
Q --> A["Adaptive control: \nupdate controller online"]
O --> O1["Main question: \nwhat input minimizes cost?"]
R --> R1["Main question: \nwhat fixed controller works \nfor all allowed plants?"]
A --> A1["Main question: \nhow should controller parameters \nlearn from data?"]
2. Optimal Control Viewpoint
In standard infinite-horizon Linear Quadratic Regulator design, the plant model is assumed known:
\[ \dot{\mathbf{x}}=\mathbf{A}\mathbf{x}+\mathbf{B}\mathbf{u} \]
The objective is to minimize the quadratic cost
\[ J(\mathbf{u}) = \int_0^\infty \left( \mathbf{x}^{T}\mathbf{Q}\mathbf{x} + \mathbf{u}^{T}\mathbf{R}\mathbf{u} \right)\,dt, \qquad \mathbf{Q}=\mathbf{Q}^{T}\succeq \mathbf{0}, \quad \mathbf{R}=\mathbf{R}^{T}\succ \mathbf{0} \]
The optimal state feedback law is
\[ \mathbf{u}^{\star}(t)=-\mathbf{K}\mathbf{x}(t), \qquad \mathbf{K}=\mathbf{R}^{-1}\mathbf{B}^{T}\mathbf{P} \]
where \( \mathbf{P}=\mathbf{P}^{T}\succ\mathbf{0} \) solves the algebraic Riccati equation
\[ \mathbf{A}^{T}\mathbf{P} + \mathbf{P}\mathbf{A} - \mathbf{P}\mathbf{B}\mathbf{R}^{-1}\mathbf{B}^{T}\mathbf{P} + \mathbf{Q} = \mathbf{0}. \]
The optimal viewpoint is therefore performance-centered. If the model is accurate, optimal control is systematic and powerful. If the model is wrong, the controller may no longer be optimal and may even lose stability.
2.1 Lyapunov Verification of LQR Stability
Let \( \mathbf{A}_c=\mathbf{A}-\mathbf{B}\mathbf{K} \). Choose the Lyapunov function
\[ V(\mathbf{x})=\mathbf{x}^{T}\mathbf{P}\mathbf{x}. \]
Along the closed-loop dynamics \( \dot{\mathbf{x}}=\mathbf{A}_c\mathbf{x} \),
\[ \dot{V} = \mathbf{x}^{T} \left( \mathbf{A}_c^{T}\mathbf{P} + \mathbf{P}\mathbf{A}_c \right) \mathbf{x}. \]
Using the Riccati equation and \( \mathbf{K}=\mathbf{R}^{-1}\mathbf{B}^{T}\mathbf{P} \),
\[ \dot{V} = - \mathbf{x}^{T} \left( \mathbf{Q} + \mathbf{K}^{T}\mathbf{R}\mathbf{K} \right) \mathbf{x}. \]
Thus the LQR closed loop is stable under the usual stabilizability and detectability assumptions. However, this proof is for the model used in the Riccati equation; it does not automatically cover large plant mismatch.
3. Robust Control Viewpoint
Robust control starts from a family of possible plants rather than one exact plant. In a simple parametric form,
\[ \boldsymbol{\theta}\in\Theta, \qquad \Theta = \left\{ \boldsymbol{\theta}: \theta_i^{-}\le \theta_i \le \theta_i^{+}, \quad i=1,\dots,p \right\}. \]
A robust controller is usually fixed after design:
\[ \mathbf{u}(t)=\mathcal{K}\big(\mathbf{x}(t),\mathbf{r}(t)\big), \qquad \dot{\mathcal{K}}=\mathbf{0}. \]
The design goal is not necessarily to be optimal for one model, but to maintain stability and acceptable performance for every plant in \( \Theta \):
\[ \text{Find } \mathcal{K} \text{ such that } \mathbf{A}_{cl}(\boldsymbol{\theta}) \text{ is stable for all } \boldsymbol{\theta}\in\Theta. \]
3.1 Scalar Robust Gain Selection
Consider the uncertain scalar plant
\[ \dot{x}=a x+b u, \qquad a\in[a_{\min},a_{\max}], \qquad b\in[b_{\min},b_{\max}], \qquad b_{\min}>0. \]
Let the controller be \( u=-kx \). Then
\[ \dot{x}=(a-bk)x. \]
If we want a decay rate at least \( \alpha>0 \), it is sufficient to choose
\[ k>\frac{a_{\max}+\alpha}{b_{\min}}. \]
Indeed,
\[ a-bk \le a_{\max}-b_{\min}k < -\alpha. \]
Hence
\[ |x(t)|\le |x(0)|e^{-\alpha t}. \]
This proof shows the essence of robust control: stability is guaranteed for the entire uncertainty set, but the gain may be conservative because it is designed for the worst admissible case.
4. Adaptive Control Viewpoint
Adaptive control uses online data to update controller parameters. A typical adaptive controller has the form
\[ \mathbf{u}(t) = \mathcal{K} \big( \mathbf{x}(t),\mathbf{r}(t),\hat{\boldsymbol{\theta}}(t) \big), \qquad \dot{\hat{\boldsymbol{\theta}}}(t) = \mathcal{F} \big( \mathbf{x}(t),\mathbf{r}(t),\mathbf{e}(t) \big). \]
Here \( \hat{\boldsymbol{\theta}}(t) \) is not a fixed design value; it evolves during operation. This is the defining feature that separates adaptive control from robust and optimal control.
4.1 Scalar Tracking Error Model
Consider
\[ \dot{x}=a x+b u, \qquad b\neq 0, \]
and a desired reference model
\[ \dot{x}_m=-\lambda x_m+\lambda r, \qquad \lambda>0. \]
Use the adaptive controller
\[ u=\hat{\theta}_x x+\hat{\theta}_r r, \qquad \boldsymbol{\phi} = \begin{bmatrix} x\\ r \end{bmatrix}, \qquad \hat{\boldsymbol{\theta}} = \begin{bmatrix} \hat{\theta}_x\\ \hat{\theta}_r \end{bmatrix}. \]
Suppose there exist ideal parameters \( \boldsymbol{\theta}^{\star} \) such that the plant exactly matches the reference model under \( u={\boldsymbol{\theta}^{\star}}^{T}\boldsymbol{\phi} \). Define the tracking error and parameter error:
\[ e=x-x_m, \qquad \tilde{\boldsymbol{\theta}} = \hat{\boldsymbol{\theta}} - \boldsymbol{\theta}^{\star}. \]
The error dynamics can be written as
\[ \dot{e} = -\lambda e + b\tilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}. \]
4.2 Lyapunov Adaptive Law
Choose
\[ V(e,\tilde{\boldsymbol{\theta}}) = \frac{1}{2}e^2 + \frac{|b|}{2\gamma} \tilde{\boldsymbol{\theta}}^{T}\tilde{\boldsymbol{\theta}}, \qquad \gamma>0. \]
Since \( \dot{\tilde{\boldsymbol{\theta}}}=\dot{\hat{\boldsymbol{\theta}}} \), choose the update law
\[ \dot{\hat{\boldsymbol{\theta}}} = -\gamma\,\operatorname{sgn}(b)\,e\,\boldsymbol{\phi}. \]
Then
\[ \begin{aligned} \dot{V} &= e\dot{e} + \frac{|b|}{\gamma} \tilde{\boldsymbol{\theta}}^{T} \dot{\hat{\boldsymbol{\theta}}} \\ &= e \left( -\lambda e + b\tilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi} \right) - |b|\operatorname{sgn}(b) e \tilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi} \\ &= -\lambda e^2. \end{aligned} \]
Therefore \( V(t) \) is nonincreasing and \( e(t) \) remains bounded. This basic calculation is the mathematical reason adaptive laws are usually designed together with a Lyapunov function rather than by intuition alone.
5. Direct Comparison
| Aspect | Optimal Control | Robust Control | Adaptive Control |
|---|---|---|---|
| Main assumption | A reliable nominal model is available. | Uncertainty belongs to a known admissible set. | Unknown parameters can be learned from signals. |
| Main goal | Minimize a cost functional. | Guarantee stability/performance despite uncertainty. | Improve behavior online by changing controller parameters. |
| Controller parameters | Fixed after solving optimization problem. | Fixed after worst-case design. | Time-varying through an update law. |
| Typical mathematical object | Riccati equation, value function, Hamiltonian. | Uncertainty set, margins, worst-case inequalities. | Tracking error, parameter estimate, Lyapunov function. |
| Strength | Excellent performance when model is accurate. | Strong guarantees against bounded uncertainty. | Can handle unknown or slowly changing parameters. |
| Weakness | Model mismatch can damage stability/performance. | May be conservative. | Can be sensitive to noise, unmodeled dynamics, and poor excitation. |
flowchart TD
S["Start: plant model and \nuncertainty information"] --> M["Is model accurate enough \nfor performance optimization?"]
M -->|yes| O["Use optimal control"]
M -->|no| U["Is uncertainty bounded \nby a credible set?"]
U -->|yes| R["Use robust control"]
U -->|no| D["Can online measurements reveal \nuseful parameter information?"]
D -->|yes| A["Use adaptive control"]
D -->|no| C["Improve modeling, sensing, \nor use conservative fixed design"]
6. Mathematical Comparison on One Scalar Plant
Use one plant to compare all three viewpoints:
\[ \dot{x}=a x+b u, \qquad b>0. \]
6.1 Optimal Nominal Design
If \( a=a_0 \) and \( b=b_0 \) are treated as known, scalar LQR with cost
\[ J=\int_0^\infty \left(qx^2+ru^2\right)\,dt, \qquad q\ge 0, \quad r>0, \]
gives
\[ u=-k_{\mathrm{lqr}}x, \qquad k_{\mathrm{lqr}} = \frac{b_0P}{r}, \]
where the positive Riccati solution is
\[ P = \frac{r}{b_0^2} \left( a_0+ \sqrt{ a_0^2+\frac{b_0^2q}{r} } \right). \]
6.2 Robust Fixed Design
If \( a\in[a_{\min},a_{\max}] \) and \( b\in[b_{\min},b_{\max}] \), choose
\[ u=-k_{\mathrm{rob}}x, \qquad k_{\mathrm{rob}} > \frac{a_{\max}+\alpha}{b_{\min}}, \qquad \alpha>0. \]
Then every admissible plant satisfies the exponential bound \( |x(t)|\le |x(0)|e^{-\alpha t} \).
6.3 Adaptive Design
If \( a \) is unknown and the sign of \( b \) is known, use
\[ u=\hat{\theta}x, \qquad \dot{\hat{\theta}} = -\gamma\operatorname{sgn}(b)ex, \]
where \( e=x-x_m \) and \( \dot{x}_m=-\lambda x_m \). The adaptive gain changes during operation instead of being fixed.
The conceptual distinction is now visible: optimal control trusts a model, robust control protects against a set, and adaptive control modifies its parameters online.
7. Python Implementation
The following script compares nominal LQR, robust fixed feedback, and a simple adaptive feedback law on the same uncertain scalar plant.
Chapter1_Lesson4.py
"""
Chapter1_Lesson4.py
Adaptive vs Robust vs Optimal Control - scalar comparison lab.
"""
import csv
import math
from dataclasses import dataclass
from typing import List
try:
import matplotlib.pyplot as plt
except Exception:
plt = None
@dataclass
class SimulationResult:
name: str
t: List[float]
x: List[float]
u: List[float]
extra: List[float]
def cost(self, q: float = 1.0, r: float = 0.1) -> float:
total = 0.0
for k in range(len(self.t) - 1):
dt = self.t[k + 1] - self.t[k]
total += dt * (q * self.x[k] ** 2 + r * self.u[k] ** 2)
return total
def max_abs_state(self) -> float:
return max(abs(v) for v in self.x)
def scalar_lqr_gain(a: float, b: float, q: float, r: float) -> float:
if abs(b) < 1e-12:
raise ValueError("b must be nonzero")
p = (r / (b * b)) * (a + math.sqrt(a * a + (b * b * q) / r))
return (b * p) / r
def simulate_fixed_gain(name, a_true, b_true, k, x0, dt, tf):
n = int(tf / dt) + 1
t = [i * dt for i in range(n)]
x = [x0]
u = [-k * x0]
extra = [k]
for i in range(n - 1):
ui = -k * x[-1]
dx = a_true * x[-1] + b_true * ui
x.append(x[-1] + dt * dx)
u.append(-k * x[-1])
extra.append(k)
return SimulationResult(name, t, x, u, extra)
def simulate_adaptive(a_true, b_true, lam, gamma, x0, dt, tf):
n = int(tf / dt) + 1
t = [i * dt for i in range(n)]
x = [x0]
xm = x0
theta_hat = 0.0
theta_trace = [theta_hat]
u = [theta_hat * x0]
sgn_b = 1.0 if b_true >= 0.0 else -1.0
for _ in range(n - 1):
e = x[-1] - xm
theta_dot = -gamma * sgn_b * e * x[-1]
theta_hat += dt * theta_dot
ui = theta_hat * x[-1]
dx = a_true * x[-1] + b_true * ui
dxm = -lam * xm
x.append(x[-1] + dt * dx)
xm += dt * dxm
u.append(theta_hat * x[-1])
theta_trace.append(theta_hat)
return SimulationResult("Adaptive MRAC-like", t, x, u, theta_trace)
def save_csv(results, filename):
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
header = ["t"]
for res in results:
tag = res.name.replace(" ", "_").replace("-", "_")
header += [f"x_{tag}", f"u_{tag}", f"extra_{tag}"]
writer.writerow(header)
for i in range(len(results[0].t)):
row = [results[0].t[i]]
for res in results:
row += [res.x[i], res.u[i], res.extra[i]]
writer.writerow(row)
def main():
a_true = 1.20
b_true = 1.00
x0 = 1.0
dt = 0.001
tf = 8.0
k_lqr = scalar_lqr_gain(a=0.20, b=1.00, q=1.0, r=0.1)
a_max = 1.50
b_min = 0.80
alpha = 0.50
k_robust = (a_max + alpha) / b_min + 0.20
optimal_nominal = simulate_fixed_gain(
"Optimal nominal LQR", a_true, b_true, k_lqr, x0, dt, tf
)
robust_fixed = simulate_fixed_gain(
"Robust fixed", a_true, b_true, k_robust, x0, dt, tf
)
adaptive = simulate_adaptive(
a_true=a_true, b_true=b_true, lam=1.00, gamma=4.00, x0=x0, dt=dt, tf=tf
)
results = [optimal_nominal, robust_fixed, adaptive]
save_csv(results, "Chapter1_Lesson4_results.csv")
print("Controller comparison for x_dot = a x + b u")
print(f"Actual plant: a={a_true}, b={b_true}")
print(f"Nominal LQR gain: {k_lqr:.4f}")
print(f"Robust gain: {k_robust:.4f}")
for res in results:
print(
f"{res.name:22s} | cost={res.cost():8.4f} | "
f"max|x|={res.max_abs_state():8.4f} | final x={res.x[-1]: .5f}"
)
if plt is not None:
plt.figure()
for res in results:
plt.plot(res.t, res.x, label=res.name)
plt.xlabel("time [s]")
plt.ylabel("state x(t)")
plt.title("Adaptive vs Robust vs Optimal Control")
plt.legend()
plt.grid(True)
plt.savefig("Chapter1_Lesson4_comparison.png", dpi=180)
plt.show()
if __name__ == "__main__":
main()
8. C++ Implementation
Chapter1_Lesson4.cpp
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
struct SimulationResult {
std::string name;
std::vector<double> t;
std::vector<double> x;
std::vector<double> u;
std::vector<double> extra;
};
double scalar_lqr_gain(double a, double b, double q, double r) {
double p = (r / (b * b)) * (a + std::sqrt(a * a + (b * b * q) / r));
return (b * p) / r;
}
SimulationResult simulate_fixed_gain(
const std::string& name,
double a_true,
double b_true,
double k,
double x0,
double dt,
double tf
) {
int n = static_cast<int>(tf / dt) + 1;
SimulationResult res;
res.name = name;
res.t.resize(n);
res.x.resize(n);
res.u.resize(n);
res.extra.resize(n);
res.x[0] = x0;
for (int i = 0; i < n; ++i) {
res.t[i] = i * dt;
}
for (int i = 0; i < n - 1; ++i) {
double ui = -k * res.x[i];
double dx = a_true * res.x[i] + b_true * ui;
res.u[i] = ui;
res.extra[i] = k;
res.x[i + 1] = res.x[i] + dt * dx;
}
res.u[n - 1] = -k * res.x[n - 1];
res.extra[n - 1] = k;
return res;
}
SimulationResult simulate_adaptive(
double a_true,
double b_true,
double lambda,
double gamma,
double x0,
double dt,
double tf
) {
int n = static_cast<int>(tf / dt) + 1;
SimulationResult res;
res.name = "Adaptive MRAC-like";
res.t.resize(n);
res.x.resize(n);
res.u.resize(n);
res.extra.resize(n);
res.x[0] = x0;
double xm = x0;
double theta_hat = 0.0;
double sign_b = (b_true >= 0.0) ? 1.0 : -1.0;
for (int i = 0; i < n; ++i) {
res.t[i] = i * dt;
}
for (int i = 0; i < n - 1; ++i) {
double e = res.x[i] - xm;
double theta_dot = -gamma * sign_b * e * res.x[i];
theta_hat += dt * theta_dot;
double ui = theta_hat * res.x[i];
double dx = a_true * res.x[i] + b_true * ui;
double dxm = -lambda * xm;
res.u[i] = ui;
res.extra[i] = theta_hat;
res.x[i + 1] = res.x[i] + dt * dx;
xm += dt * dxm;
}
res.u[n - 1] = theta_hat * res.x[n - 1];
res.extra[n - 1] = theta_hat;
return res;
}
double cost(const SimulationResult& res, double q = 1.0, double r = 0.1) {
double total = 0.0;
for (std::size_t i = 0; i + 1 < res.t.size(); ++i) {
double dt = res.t[i + 1] - res.t[i];
total += dt * (q * res.x[i] * res.x[i] + r * res.u[i] * res.u[i]);
}
return total;
}
double max_abs_state(const SimulationResult& res) {
double m = 0.0;
for (double v : res.x) {
m = std::max(m, std::abs(v));
}
return m;
}
int main() {
double a_true = 1.20;
double b_true = 1.00;
double x0 = 1.0;
double dt = 0.001;
double tf = 8.0;
double k_lqr = scalar_lqr_gain(0.20, 1.00, 1.0, 0.1);
double k_robust = (1.50 + 0.50) / 0.80 + 0.20;
SimulationResult optimal = simulate_fixed_gain(
"Optimal nominal LQR", a_true, b_true, k_lqr, x0, dt, tf
);
SimulationResult robust = simulate_fixed_gain(
"Robust fixed", a_true, b_true, k_robust, x0, dt, tf
);
SimulationResult adaptive = simulate_adaptive(
a_true, b_true, 1.00, 4.00, x0, dt, tf
);
std::vector<SimulationResult> results = {optimal, robust, adaptive};
std::cout << "Controller comparison for x_dot = a x + b u\n";
for (const auto& res : results) {
std::cout << std::setw(22) << std::left << res.name
<< " | cost=" << cost(res)
<< " | max|x|=" << max_abs_state(res)
<< " | final x=" << res.x.back() << "\n";
}
return 0;
}
9. Java Implementation
Chapter1_Lesson4.java
public class Chapter1_Lesson4 {
static class SimulationResult {
String name;
double[] t;
double[] x;
double[] u;
double[] extra;
SimulationResult(String name, int n) {
this.name = name;
t = new double[n];
x = new double[n];
u = new double[n];
extra = new double[n];
}
}
static double scalarLqrGain(double a, double b, double q, double r) {
double p = (r / (b * b)) * (a + Math.sqrt(a * a + (b * b * q) / r));
return (b * p) / r;
}
static SimulationResult simulateFixedGain(
String name,
double aTrue,
double bTrue,
double k,
double x0,
double dt,
double tf) {
int n = (int)(tf / dt) + 1;
SimulationResult res = new SimulationResult(name, n);
res.x[0] = x0;
for (int i = 0; i < n; i++) {
res.t[i] = i * dt;
}
for (int i = 0; i < n - 1; i++) {
double ui = -k * res.x[i];
double dx = aTrue * res.x[i] + bTrue * ui;
res.u[i] = ui;
res.extra[i] = k;
res.x[i + 1] = res.x[i] + dt * dx;
}
res.u[n - 1] = -k * res.x[n - 1];
res.extra[n - 1] = k;
return res;
}
static SimulationResult simulateAdaptive(
double aTrue,
double bTrue,
double lambda,
double gamma,
double x0,
double dt,
double tf) {
int n = (int)(tf / dt) + 1;
SimulationResult res = new SimulationResult("Adaptive MRAC-like", n);
res.x[0] = x0;
double xm = x0;
double thetaHat = 0.0;
double signB = bTrue >= 0.0 ? 1.0 : -1.0;
for (int i = 0; i < n; i++) {
res.t[i] = i * dt;
}
for (int i = 0; i < n - 1; i++) {
double e = res.x[i] - xm;
double thetaDot = -gamma * signB * e * res.x[i];
thetaHat += dt * thetaDot;
double ui = thetaHat * res.x[i];
double dx = aTrue * res.x[i] + bTrue * ui;
double dxm = -lambda * xm;
res.u[i] = ui;
res.extra[i] = thetaHat;
res.x[i + 1] = res.x[i] + dt * dx;
xm += dt * dxm;
}
res.u[n - 1] = thetaHat * res.x[n - 1];
res.extra[n - 1] = thetaHat;
return res;
}
static double cost(SimulationResult res, double q, double r) {
double total = 0.0;
for (int i = 0; i + 1 < res.t.length; i++) {
double dt = res.t[i + 1] - res.t[i];
total += dt * (q * res.x[i] * res.x[i] + r * res.u[i] * res.u[i]);
}
return total;
}
static double maxAbsState(SimulationResult res) {
double m = 0.0;
for (double v : res.x) {
m = Math.max(m, Math.abs(v));
}
return m;
}
public static void main(String[] args) {
double aTrue = 1.20;
double bTrue = 1.00;
double x0 = 1.0;
double dt = 0.001;
double tf = 8.0;
double kLqr = scalarLqrGain(0.20, 1.00, 1.0, 0.1);
double kRobust = (1.50 + 0.50) / 0.80 + 0.20;
SimulationResult optimal = simulateFixedGain(
"Optimal nominal LQR", aTrue, bTrue, kLqr, x0, dt, tf);
SimulationResult robust = simulateFixedGain(
"Robust fixed", aTrue, bTrue, kRobust, x0, dt, tf);
SimulationResult adaptive = simulateAdaptive(
aTrue, bTrue, 1.00, 4.00, x0, dt, tf);
SimulationResult[] results = {optimal, robust, adaptive};
System.out.println("Controller comparison for x_dot = a x + b u");
for (SimulationResult res : results) {
System.out.printf(
"%-22s | cost=%10.4f | max|x|=%10.4f | final x=% .5f%n",
res.name,
cost(res, 1.0, 0.1),
maxAbsState(res),
res.x[res.x.length - 1]
);
}
}
}
10. MATLAB and Simulink Implementation
This MATLAB script performs the numerical comparison and optionally builds a simple Simulink model for the robust fixed-gain closed loop.
Chapter1_Lesson4.m
% Chapter1_Lesson4.m
% Adaptive vs Robust vs Optimal Control - scalar comparison lab.
clear; clc;
a_true = 1.20;
b_true = 1.00;
x0 = 1.0;
dt = 0.001;
tf = 8.0;
t = 0:dt:tf;
% Optimal nominal LQR
a_nom = 0.20;
b_nom = 1.00;
q = 1.0;
r = 0.1;
P = (r/(b_nom^2))*(a_nom + sqrt(a_nom^2 + (b_nom^2*q)/r));
k_lqr = (b_nom*P)/r;
% Robust fixed gain
a_max = 1.50;
b_min = 0.80;
alpha = 0.50;
k_robust = (a_max + alpha)/b_min + 0.20;
optimal = simulate_fixed_gain('Optimal nominal LQR', a_true, b_true, k_lqr, x0, t);
robust = simulate_fixed_gain('Robust fixed', a_true, b_true, k_robust, x0, t);
adaptive = simulate_adaptive(a_true, b_true, 1.00, 4.00, x0, t);
fprintf('Controller comparison for x_dot = a x + b u\n');
print_metrics(optimal);
print_metrics(robust);
print_metrics(adaptive);
figure;
plot(t, optimal.x, 'LineWidth', 1.2); hold on;
plot(t, robust.x, 'LineWidth', 1.2);
plot(t, adaptive.x, 'LineWidth', 1.2);
grid on;
xlabel('time [s]');
ylabel('state x(t)');
title('Adaptive vs Robust vs Optimal Control');
legend('Optimal nominal LQR','Robust fixed','Adaptive MRAC-like');
% Optional Simulink model generation for robust closed loop.
if exist('simulink', 'file') == 4
mdl = 'Chapter1_Lesson4_Simulink_RobustLoop';
if bdIsLoaded(mdl), close_system(mdl, 0); end
new_system(mdl);
open_system(mdl);
add_block('simulink/Continuous/Integrator', ...
[mdl '/Integrator_x'], ...
'InitialCondition', num2str(x0), ...
'Position', [400 110 430 140]);
add_block('simulink/Math Operations/Gain', ...
[mdl '/minus_k'], ...
'Gain', ['-' num2str(k_robust)], ...
'Position', [180 170 230 210]);
add_block('simulink/Math Operations/Gain', ...
[mdl '/a_times_x'], ...
'Gain', num2str(a_true), ...
'Position', [180 60 230 100]);
add_block('simulink/Math Operations/Gain', ...
[mdl '/b_times_u'], ...
'Gain', num2str(b_true), ...
'Position', [280 160 330 200]);
add_block('simulink/Math Operations/Sum', ...
[mdl '/sum_dx'], ...
'Inputs', '++', ...
'Position', [350 105 380 145]);
add_block('simulink/Sinks/Scope', ...
[mdl '/Scope_x'], ...
'Position', [500 110 540 140]);
add_line(mdl, 'Integrator_x/1', 'a_times_x/1');
add_line(mdl, 'Integrator_x/1', 'minus_k/1');
add_line(mdl, 'minus_k/1', 'b_times_u/1');
add_line(mdl, 'a_times_x/1', 'sum_dx/1');
add_line(mdl, 'b_times_u/1', 'sum_dx/2');
add_line(mdl, 'sum_dx/1', 'Integrator_x/1');
add_line(mdl, 'Integrator_x/1', 'Scope_x/1');
set_param(mdl, 'StopTime', num2str(tf));
save_system(mdl);
end
function res = simulate_fixed_gain(name, a_true, b_true, k, x0, t)
dt = t(2)-t(1);
x = zeros(size(t));
u = zeros(size(t));
x(1) = x0;
for i = 1:(numel(t)-1)
u(i) = -k*x(i);
dx = a_true*x(i) + b_true*u(i);
x(i+1) = x(i) + dt*dx;
end
u(end) = -k*x(end);
res.name = name;
res.x = x;
res.u = u;
end
function res = simulate_adaptive(a_true, b_true, lambda, gamma, x0, t)
dt = t(2)-t(1);
x = zeros(size(t));
u = zeros(size(t));
theta = zeros(size(t));
x(1) = x0;
xm = x0;
theta_hat = 0.0;
sign_b = sign(b_true);
for i = 1:(numel(t)-1)
e = x(i) - xm;
theta_dot = -gamma*sign_b*e*x(i);
theta_hat = theta_hat + dt*theta_dot;
u(i) = theta_hat*x(i);
dx = a_true*x(i) + b_true*u(i);
dxm = -lambda*xm;
x(i+1) = x(i) + dt*dx;
xm = xm + dt*dxm;
theta(i+1) = theta_hat;
end
u(end) = theta_hat*x(end);
res.name = 'Adaptive MRAC-like';
res.x = x;
res.u = u;
res.extra = theta;
end
function print_metrics(res)
q = 1.0;
r = 0.1;
dt = 0.001;
J = sum(dt*(q*res.x.^2 + r*res.u.^2));
fprintf('%-22s | cost=%10.4f | max|x|=%10.4f | final x=% .5f\n', ...
res.name, J, max(abs(res.x)), res.x(end));
end
11. Wolfram Mathematica Implementation
Chapter1_Lesson4.nb
ClearAll["Global`*"]
aTrue = 1.20;
bTrue = 1.00;
x0 = 1.0;
tf = 8;
scalarLQRGain[a_, b_, q_, r_] :=
Module[{p},
p = (r/b^2) (a + Sqrt[a^2 + b^2 q/r]);
b p/r
]
kLQR = scalarLQRGain[0.20, 1.00, 1.0, 0.1];
kRobust = (1.50 + 0.50)/0.80 + 0.20;
fixedSolution[k_] :=
NDSolveValue[
{
x'[t] == aTrue x[t] + bTrue (-k x[t]),
x[0] == x0
},
x,
{t, 0, tf}
]
adaptiveSolution =
NDSolveValue[
{
x'[t] == aTrue x[t] + bTrue theta[t] x[t],
xm'[t] == -1.0 xm[t],
theta'[t] == -4.0 Sign[bTrue] (x[t] - xm[t]) x[t],
x[0] == x0,
xm[0] == x0,
theta[0] == 0
},
{x, xm, theta},
{t, 0, tf}
]
Plot[
Evaluate[
{
fixedSolution[kLQR][t],
fixedSolution[kRobust][t],
adaptiveSolution[[1]][t]
}
],
{t, 0, tf},
PlotLegends -> {
"Optimal nominal LQR",
"Robust fixed",
"Adaptive MRAC-like"
},
AxesLabel -> {"t", "x(t)"},
PlotLabel -> "Adaptive vs Robust vs Optimal Control"
]
12. Problems and Solutions
Problem 1: Consider \( \dot{x}=a x+b u \), \( b>0 \), and \( u=-kx \). Suppose \( a\in[a_{\min},a_{\max}] \) and \( b\in[b_{\min},b_{\max}] \) with \( b_{\min}>0 \). Find a sufficient condition on \( k \) such that \( |x(t)|\le |x(0)|e^{-\alpha t} \).
Solution:
The closed-loop system is \( \dot{x}=(a-bk)x \). To guarantee decay rate \( \alpha \), require \( a-bk<-\alpha \) for all admissible \( a,b \). Since the worst case is \( a=a_{\max} \) and \( b=b_{\min} \),
\[ a-bk \le a_{\max}-b_{\min}k. \]
Therefore it is sufficient that
\[ a_{\max}-b_{\min}k<-\alpha, \qquad k>\frac{a_{\max}+\alpha}{b_{\min}}. \]
Then \( |x(t)|\le |x(0)|e^{-\alpha t} \).
Problem 2: For the scalar LQR system \( \dot{x}=a x+b u \) with cost \( J=\int_0^\infty(qx^2+ru^2)dt \), derive the positive Riccati solution.
Solution:
The scalar algebraic Riccati equation is
\[ 2aP-\frac{b^2}{r}P^2+q=0. \]
Rearranging gives
\[ \frac{b^2}{r}P^2-2aP-q=0. \]
The positive solution is
\[ P = \frac{r}{b^2} \left( a+\sqrt{a^2+\frac{b^2q}{r}} \right). \]
Hence \( k_{\mathrm{lqr}}=\frac{bP}{r} \).
Problem 3: In adaptive control, suppose the tracking error satisfies
\[ \dot{e} = -\lambda e + b\tilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}, \qquad \lambda>0. \]
Show that the adaptive law \( \dot{\hat{\boldsymbol{\theta}}} = -\gamma\operatorname{sgn}(b)e\boldsymbol{\phi} \) makes a suitable Lyapunov derivative nonpositive.
Solution:
Use
\[ V = \frac{1}{2}e^2 + \frac{|b|}{2\gamma} \tilde{\boldsymbol{\theta}}^{T} \tilde{\boldsymbol{\theta}}. \]
Since \( \dot{\tilde{\boldsymbol{\theta}}}=\dot{\hat{\boldsymbol{\theta}}} \),
\[ \begin{aligned} \dot{V} &= e \left( -\lambda e + b\tilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi} \right) + \frac{|b|}{\gamma} \tilde{\boldsymbol{\theta}}^{T} \dot{\hat{\boldsymbol{\theta}}} \\ &= -\lambda e^2 + b e\tilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi} - |b|\operatorname{sgn}(b) e\tilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi} \\ &= -\lambda e^2. \end{aligned} \]
Therefore \( \dot{V}\le 0 \), so the error and parameter estimate remain bounded.
Problem 4: Explain why a robust controller may be more conservative than an adaptive controller for the same uncertain plant.
Solution:
A robust controller is selected to satisfy guarantees for the worst admissible plant in an uncertainty set. Therefore, even when the actual plant is mild, the gain may still be large because it was designed for the most difficult allowed case. An adaptive controller can change its parameter estimates using measured data, so it may move toward a controller better matched to the actual plant. However, this advantage depends on the quality of measurements, excitation, and the validity of the adaptive model structure.
Problem 5: A plant has a very accurate model, but the design objective is to reduce actuator energy while maintaining state regulation. Which philosophy is the most natural first choice?
Solution:
Optimal control is the natural first choice because the model is trusted and the design objective is explicitly expressed as a trade-off between state regulation and control effort. For a linear plant, LQR is the standard candidate:
\[ J = \int_0^\infty \left( \mathbf{x}^{T}\mathbf{Q}\mathbf{x} + \mathbf{u}^{T}\mathbf{R}\mathbf{u} \right)\,dt. \]
13. Summary
Optimal control minimizes a performance index for a trusted model. Robust control designs a fixed controller that works for a prescribed uncertainty set. Adaptive control changes controller parameters online using measured signals. These viewpoints are complementary, not mutually exclusive. Later lessons will develop adaptive control in more detail, especially how parameter update laws are constructed and why Lyapunov analysis is central to their stability guarantees.
14. References
- Kalman, R.E. (1960). Contributions to the theory of optimal control. Boletin de la Sociedad Matematica Mexicana, 5, 102–119.
- Bellman, R. (1957). Dynamic programming and a new formalism in the calculus of variations. Proceedings of the National Academy of Sciences, 40(4), 231–235.
- Zames, G. (1966). On the input-output stability of time-varying nonlinear feedback systems. IEEE Transactions on Automatic Control, 11(2), 228–238.
- Doyle, J.C. (1978). Guaranteed margins for LQG regulators. IEEE Transactions on Automatic Control, 23(4), 756–757.
- Narendra, K.S., & Annaswamy, A.M. (1987). A new adaptive law for robust adaptation without persistent excitation. IEEE Transactions on Automatic Control, 32(2), 134–145.
- Ioannou, P.A., & Kokotovic, P.V. (1984). Instability analysis and improvement of robustness of adaptive control. Automatica, 20(5), 583–594.
- Åström, K.J., & Wittenmark, B. (1973). On self tuning regulators. Automatica, 9(2), 185–199.
- Monopoli, R.V. (1974). Model reference adaptive control with an augmented error signal. IEEE Transactions on Automatic Control, 19(5), 474–484.
- Narendra, K.S., & Valavani, L.S. (1978). Stable adaptive controller design—direct control. IEEE Transactions on Automatic Control, 23(4), 570–583.
- Safonov, M.G., & Athans, M. (1977). Gain and phase margin for multiloop LQG regulators. IEEE Transactions on Automatic Control, 22(2), 173–179.
Help keep these engineering tutorials free and growing
If these lessons, examples, and project pages help you, a small donation supports the continued creation and improvement of free control, robotics, software, and engineering education resources.
Created and maintained by Abolfazl Mohammadijoo.