Chapter 2: Uncertainty and Parametric Modeling for Adaptation
Lesson 4: Matching Conditions and “Ideal Parameters” Concept
This lesson explains the algebraic foundation behind many adaptive controllers: the existence of constant controller parameters that would make the uncertain plant behave exactly like a chosen reference model. These parameters are called ideal parameters. We derive matching equations for scalar and state-space systems, distinguish matched and unmatched uncertainty, prove ideal tracking for the matched case, and implement numerical checks in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica.
1. Conceptual Overview
In the previous lessons, we introduced linearly parameterized models and regressor vectors. The next conceptual step is to ask whether the chosen adaptive-controller structure is sufficiently expressive. In adaptive control, this question is formalized through matching conditions.
A matching condition asks whether there exists a constant vector \( \theta^* \) such that, if the controller used \( \theta^* \), the plant would reproduce the desired model dynamics. The vector \( \theta^* \) is called an ideal parameter vector. It is usually unknown because it depends on unknown plant coefficients, but its assumed existence is central to many stability proofs.
\[ \text{Plant} + \text{Controller}(\theta^*) = \text{Reference Model} \]
The goal of adaptation is therefore not initially to find a physically meaningful mass, damping, or gain. Instead, it is often to adjust the controller parameter estimate \( \hat{\theta}(t) \) so that it behaves as if it were close to the ideal value \( \theta^* \).
flowchart TD
A["Uncertain plant model"] --> B["Choose desired reference model"]
B --> C["Choose controller structure"]
C --> D["Write closed-loop algebraic equations"]
D --> E["Check if constant \nideal parameters exist"]
E -->|yes| F["Matched uncertainty case"]
E -->|no| G["Unmatched or under-parameterized case"]
F --> H["Adaptive law can estimate \nuseful controller parameters"]
G --> I["Need redesign, robustification, \nor accept residual error"]
2. Scalar First-Order Matching Conditions
Consider a first-order uncertain plant
\[ \dot{x}(t) = a x(t) + b u(t), \qquad b \neq 0 \]
and a desired first-order reference model
\[ \dot{x}_m(t) = a_m x_m(t) + b_m r(t), \qquad a_m < 0. \]
Suppose we select the control law
\[ u(t) = \theta_x x(t) + \theta_r r(t). \]
Substituting this controller into the plant gives
\[ \dot{x}(t) = \left(a+b\theta_x\right)x(t)+b\theta_r r(t). \]
Exact matching with the reference model requires the coefficients of \( x(t) \) and \( r(t) \) to match:
\[ a+b\theta_x^* = a_m, \qquad b\theta_r^* = b_m. \]
Therefore, if \( b\neq 0 \), the ideal parameters are
\[ \theta_x^* = \frac{a_m-a}{b}, \qquad \theta_r^* = \frac{b_m}{b}. \]
These equations are not yet an adaptive law. They only say that the selected controller structure is capable of achieving the desired model if the correct constant parameters were known.
3. State-Space Matching Conditions for Full-State Feedback
Now consider a single-input state-space plant
\[ \dot{\mathbf{x} }(t) = \mathbf{A}\mathbf{x}(t)+\mathbf{b}u(t), \qquad \mathbf{x}\in\mathbb{R}^n. \]
Let the desired reference model be
\[ \dot{\mathbf{x} }_m(t) = \mathbf{A}_m\mathbf{x}_m(t)+\mathbf{b}_m r(t), \qquad \mathbf{A}_m \text{ is Hurwitz}. \]
Choose the full-state controller
\[ u(t)=\mathbf{k}_x^{T}\mathbf{x}(t)+k_r r(t), \]
where \( \mathbf{k}_x\in\mathbb{R}^n \) and \( k_r\in\mathbb{R} \). The closed-loop plant becomes
\[ \dot{\mathbf{x} } = \left(\mathbf{A}+\mathbf{b}\mathbf{k}_x^T\right)\mathbf{x} + \mathbf{b}k_r r. \]
The exact model matching conditions are
\[ \mathbf{A}+\mathbf{b}\mathbf{k}_x^{*T}=\mathbf{A}_m, \qquad \mathbf{b}k_r^*=\mathbf{b}_m. \]
These equations are restrictive. Since \( \mathbf{b}\mathbf{k}_x^{*T} \) is a rank-one matrix, not every desired matrix \( \mathbf{A}_m \) can be obtained from an arbitrary \( \mathbf{A} \) and \( \mathbf{b} \). In canonical SISO MRAC designs, the plant and model are often placed in compatible forms precisely so that these equations can hold.
4. Matched vs Unmatched Uncertainty
In adaptive control, uncertainty is called matched when it enters through the same channel as the control input. For the system
\[ \dot{\mathbf{x} } = \mathbf{A}_0\mathbf{x} + \mathbf{b}\left(u+\theta^{*T}\boldsymbol{\phi}(\mathbf{x})\right), \]
the uncertain term \( \mathbf{b}\theta^{*T}\boldsymbol{\phi}(\mathbf{x}) \) is matched because it is multiplied by the same input vector \( \mathbf{b} \) as the control. In contrast,
\[ \dot{\mathbf{x} } = \mathbf{A}_0\mathbf{x} + \mathbf{b}u + \mathbf{d}\theta^{*T}\boldsymbol{\phi}(\mathbf{x}), \qquad \mathbf{d}\notin\operatorname{span}\{\mathbf{b}\}, \]
contains unmatched uncertainty because the uncertain term acts through a direction not directly controlled by \( u \). Standard direct MRAC is naturally suited to matched uncertainty. Unmatched uncertainty usually requires different design tools, additional states, robust terms, or accepting a nonzero residual.
flowchart LR
U["Control input u"] --> B["Input channel b"]
M["Uncertainty phi(x)^T theta*"] --> B
B --> X["State dynamics"]
M2["Uncertainty phi(x)^T theta*"] --> D["Different channel d"]
D --> X
B --> C1["Matched case: same channel"]
D --> C2["Unmatched case: different channel"]
5. Ideal Parameters and Parameter Error
Once an ideal vector \( \theta^* \) is assumed to exist, adaptive-control analysis usually introduces the parameter error
\[ \tilde{\theta}(t)=\hat{\theta}(t)-\theta^*. \]
The vector \( \hat{\theta}(t) \) is the online estimate used by the controller. The vector \( \theta^* \) is a mathematical reference value, not a known quantity. This distinction is crucial:
\[ \theta^* \text{ exists for analysis, while } \hat{\theta}(t) \text{ is implemented in the controller.} \]
In later MRAC lessons, the tracking error dynamics often take the form
\[ \dot{\mathbf{e} } = \mathbf{A}_m\mathbf{e} + \mathbf{b}\tilde{\theta}^{T}\boldsymbol{\phi}(\mathbf{x},r), \]
where \( \mathbf{e}=\mathbf{x}-\mathbf{x}_m \). This expression shows why ideal parameters are useful: they isolate all mismatch into the term involving \( \tilde{\theta} \).
6. Proof of Ideal Tracking in the Matched Scalar Case
Consider again the scalar plant and reference model:
\[ \dot{x}=a x+b u, \qquad \dot{x}_m=a_m x_m+b_m r, \qquad a_m < 0. \]
Suppose the controller uses the ideal parameters:
\[ u=\theta_x^*x+\theta_r^*r, \qquad a+b\theta_x^*=a_m, \qquad b\theta_r^*=b_m. \]
Then the plant satisfies
\[ \dot{x} = a_m x+b_m r. \]
Define the tracking error \( e=x-x_m \). Subtracting the reference-model equation from the plant equation yields
\[ \dot{e} = \dot{x}-\dot{x}_m = a_m(x-x_m) = a_m e. \]
Hence,
\[ e(t)=e(0)\exp(a_m t). \]
Since \( a_m < 0 \), the tracking error converges exponentially to zero:
\[ \lim_{t\to\infty}e(t)=0. \]
This proof is simple but fundamental. Adaptive control attempts to recover the behavior of this ideal closed loop without knowing \( a \) and \( b \) exactly.
7. Matching Residuals and Least-Squares Interpretation
In realistic modeling, exact matching may fail. One way to test matching algebraically is to write the matching equations as a linear system:
\[ \mathbf{M}\theta^*=\mathbf{q}. \]
Exact matching exists if and only if \( \mathbf{q}\in\operatorname{Range}(\mathbf{M}) \). Equivalently, the minimum matching residual is zero:
\[ \rho_{\min} = \min_{\theta}\|\mathbf{M}\theta-\mathbf{q}\|_2. \]
If \( \rho_{\min}=0 \), an ideal parameter vector exists. If \( \rho_{\min}>0 \), the selected controller structure cannot exactly reproduce the desired model, even with the best constant parameter vector.
For a full-column-rank matrix \( \mathbf{M} \), the least-squares solution is
\[ \theta_{\mathrm{LS} } = \left(\mathbf{M}^{T}\mathbf{M}\right)^{-1}\mathbf{M}^{T}\mathbf{q}. \]
This is not an adaptive law. It is an offline algebraic diagnostic for whether the controller parameterization is structurally capable of exact matching.
8. Python Implementation
The following program computes ideal scalar matching parameters, simulates the ideal matched closed loop, compares it with a mismatched controller, and optionally saves a plot.
Chapter2_Lesson4.py
"""
Chapter2_Lesson4.py
Adaptive Control - Chapter 2, Lesson 4
Matching Conditions and Ideal Parameters Concept
This script demonstrates:
1) Algebraic computation of ideal controller parameters for a first-order plant.
2) Exact matching test for a scalar MRAC-style controller.
3) Numerical comparison of an ideal matched controller and a deliberately mismatched controller.
Plant:
x_dot = a*x + b*u
Reference model:
xm_dot = am*xm + bm*r
Control structure:
u = theta_x*x + theta_r*r
Exact matching requires:
a + b*theta_x = am
b*theta_r = bm
"""
import math
import csv
from dataclasses import dataclass
from typing import Callable, Tuple, List
@dataclass
class MatchingResult:
theta_x_star: float
theta_r_star: float
matched: bool
residual_A: float
residual_B: float
def ideal_parameters_scalar(a: float, b: float, am: float, bm: float,
tolerance: float = 1e-10) -> MatchingResult:
"""Compute ideal parameters for scalar plant/reference-model matching."""
if abs(b) < tolerance:
raise ValueError("Matching is impossible with b approximately zero: input cannot affect the plant.")
theta_x_star = (am - a) / b
theta_r_star = bm / b
residual_A = (a + b * theta_x_star) - am
residual_B = (b * theta_r_star) - bm
matched = abs(residual_A) <= tolerance and abs(residual_B) <= tolerance
return MatchingResult(theta_x_star, theta_r_star, matched, residual_A, residual_B)
def rk4_step(f: Callable[[float, float], float], t: float, x: float, dt: float) -> float:
"""One Runge-Kutta 4 step for a scalar ODE x_dot = f(t, x)."""
k1 = f(t, x)
k2 = f(t + 0.5 * dt, x + 0.5 * dt * k1)
k3 = f(t + 0.5 * dt, x + 0.5 * dt * k2)
k4 = f(t + dt, x + dt * k3)
return x + (dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
def simulate(a: float, b: float, am: float, bm: float,
theta_x: float, theta_r: float,
r_func: Callable[[float], float],
x0: float = 0.0,
xm0: float = 0.0,
dt: float = 0.001,
tf: float = 8.0) -> List[Tuple[float, float, float, float, float]]:
"""Simulate plant and reference model under fixed controller parameters."""
steps = int(tf / dt)
x = x0
xm = xm0
rows = []
for k in range(steps + 1):
t = k * dt
r = r_func(t)
u = theta_x * x + theta_r * r
rows.append((t, x, xm, x - xm, u))
def f_plant(time: float, state: float) -> float:
ref = r_func(time)
control = theta_x * state + theta_r * ref
return a * state + b * control
def f_model(time: float, state: float) -> float:
ref = r_func(time)
return am * state + bm * ref
x = rk4_step(f_plant, t, x, dt)
xm = rk4_step(f_model, t, xm, dt)
return rows
def write_csv(filename: str, rows: List[Tuple[float, float, float, float, float]]) -> None:
with open(filename, "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["t", "x", "xm", "e", "u"])
writer.writerows(rows)
def main() -> None:
# Unknown plant coefficients in real adaptive control; known here only for demonstration.
a = -0.7
b = 2.0
# Desired model chosen by the designer.
am = -2.0
bm = 2.0
result = ideal_parameters_scalar(a, b, am, bm)
print("Ideal parameter computation")
print("---------------------------")
print(f"theta_x_star = {result.theta_x_star:.6f}")
print(f"theta_r_star = {result.theta_r_star:.6f}")
print(f"matched = {result.matched}")
print(f"residual_A = {result.residual_A:.3e}")
print(f"residual_B = {result.residual_B:.3e}")
# Piecewise reference: step followed by a sinusoidal segment.
def r_func(t: float) -> float:
return 1.0 if t < 4.0 else 0.8 + 0.2 * math.sin(2.0 * math.pi * 0.5 * (t - 4.0))
rows_ideal = simulate(a, b, am, bm,
result.theta_x_star, result.theta_r_star,
r_func, x0=-0.4, xm0=0.2)
# Deliberately mismatched controller: same structure, wrong parameters.
rows_bad = simulate(a, b, am, bm,
0.35 * result.theta_x_star, 1.25 * result.theta_r_star,
r_func, x0=-0.4, xm0=0.2)
write_csv("Chapter2_Lesson4_ideal.csv", rows_ideal)
write_csv("Chapter2_Lesson4_mismatched.csv", rows_bad)
rms_ideal = math.sqrt(sum(row[3] ** 2 for row in rows_ideal) / len(rows_ideal))
rms_bad = math.sqrt(sum(row[3] ** 2 for row in rows_bad) / len(rows_bad))
print(f"RMS tracking error with ideal parameters = {rms_ideal:.6f}")
print(f"RMS tracking error with mismatched parameters = {rms_bad:.6f}")
# Optional plot if matplotlib is installed.
try:
import matplotlib.pyplot as plt
t = [row[0] for row in rows_ideal]
e_ideal = [row[3] for row in rows_ideal]
e_bad = [row[3] for row in rows_bad]
plt.figure()
plt.plot(t, e_ideal, label="ideal matched")
plt.plot(t, e_bad, label="mismatched")
plt.xlabel("time [s]")
plt.ylabel("tracking error e = x - xm")
plt.title("Matching Conditions and Ideal Parameters")
plt.grid(True)
plt.legend()
plt.savefig("Chapter2_Lesson4_tracking_error.png", dpi=150, bbox_inches="tight")
print("Saved plot: Chapter2_Lesson4_tracking_error.png")
except Exception as exc:
print("Plot skipped:", exc)
if __name__ == "__main__":
main()
9. C++ Implementation
This C++ implementation uses only the standard library and writes CSV files for external plotting.
Chapter2_Lesson4.cpp
/*
Chapter2_Lesson4.cpp
Adaptive Control - Chapter 2, Lesson 4
Matching Conditions and Ideal Parameters Concept
Compile:
g++ -std=c++17 Chapter2_Lesson4.cpp -o Chapter2_Lesson4
Run:
./Chapter2_Lesson4
*/
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
struct MatchingResult {
double theta_x_star;
double theta_r_star;
bool matched;
double residual_A;
double residual_B;
};
MatchingResult idealParametersScalar(double a, double b, double am, double bm, double tolerance = 1e-10) {
if (std::abs(b) < tolerance) {
throw std::runtime_error("Matching is impossible: b is approximately zero.");
}
MatchingResult result{};
result.theta_x_star = (am - a) / b;
result.theta_r_star = bm / b;
result.residual_A = (a + b * result.theta_x_star) - am;
result.residual_B = (b * result.theta_r_star) - bm;
result.matched = std::abs(result.residual_A) <= tolerance && std::abs(result.residual_B) <= tolerance;
return result;
}
double referenceSignal(double t) {
if (t < 4.0) {
return 1.0;
}
return 0.8 + 0.2 * std::sin(2.0 * M_PI * 0.5 * (t - 4.0));
}
template <typename Function>
double rk4Step(Function f, double t, double x, double dt) {
const double k1 = f(t, x);
const double k2 = f(t + 0.5 * dt, x + 0.5 * dt * k1);
const double k3 = f(t + 0.5 * dt, x + 0.5 * dt * k2);
const double k4 = f(t + dt, x + dt * k3);
return x + (dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4);
}
double simulateAndWriteCsv(const std::string& filename,
double a, double b, double am, double bm,
double theta_x, double theta_r,
double x0, double xm0,
double dt, double tf) {
std::ofstream file(filename);
file << "t,x,xm,e,u\n";
double x = x0;
double xm = xm0;
const int steps = static_cast<int>(tf / dt);
double sum_e2 = 0.0;
for (int k = 0; k <= steps; ++k) {
const double t = k * dt;
const double r = referenceSignal(t);
const double u = theta_x * x + theta_r * r;
const double e = x - xm;
sum_e2 += e * e;
file << std::setprecision(12) << t << "," << x << "," << xm << "," << e << "," << u << "\n";
auto plant = [&](double time, double state) {
const double ref = referenceSignal(time);
const double control = theta_x * state + theta_r * ref;
return a * state + b * control;
};
auto model = [&](double time, double state) {
const double ref = referenceSignal(time);
return am * state + bm * ref;
};
x = rk4Step(plant, t, x, dt);
xm = rk4Step(model, t, xm, dt);
}
return std::sqrt(sum_e2 / static_cast<double>(steps + 1));
}
int main() {
const double a = -0.7;
const double b = 2.0;
const double am = -2.0;
const double bm = 2.0;
const auto result = idealParametersScalar(a, b, am, bm);
std::cout << "Ideal parameter computation\n";
std::cout << "theta_x_star = " << result.theta_x_star << "\n";
std::cout << "theta_r_star = " << result.theta_r_star << "\n";
std::cout << "matched = " << std::boolalpha << result.matched << "\n";
std::cout << "residual_A = " << result.residual_A << "\n";
std::cout << "residual_B = " << result.residual_B << "\n";
const double rmsIdeal = simulateAndWriteCsv(
"Chapter2_Lesson4_ideal_cpp.csv",
a, b, am, bm,
result.theta_x_star, result.theta_r_star,
-0.4, 0.2, 0.001, 8.0
);
const double rmsBad = simulateAndWriteCsv(
"Chapter2_Lesson4_mismatched_cpp.csv",
a, b, am, bm,
0.35 * result.theta_x_star, 1.25 * result.theta_r_star,
-0.4, 0.2, 0.001, 8.0
);
std::cout << "RMS tracking error with ideal parameters = " << rmsIdeal << "\n";
std::cout << "RMS tracking error with mismatched parameters = " << rmsBad << "\n";
return 0;
}
10. Java Implementation
The Java version mirrors the C++ implementation and writes CSV outputs.
Chapter2_Lesson4.java
/*
Chapter2_Lesson4.java
Adaptive Control - Chapter 2, Lesson 4
Matching Conditions and Ideal Parameters Concept
Compile:
javac Chapter2_Lesson4.java
Run:
java Chapter2_Lesson4
*/
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
public class Chapter2_Lesson4 {
static class MatchingResult {
double thetaXStar;
double thetaRStar;
boolean matched;
double residualA;
double residualB;
}
interface ScalarOde {
double eval(double t, double x);
}
static MatchingResult idealParametersScalar(double a, double b, double am, double bm, double tolerance) {
if (Math.abs(b) < tolerance) {
throw new IllegalArgumentException("Matching is impossible: b is approximately zero.");
}
MatchingResult result = new MatchingResult();
result.thetaXStar = (am - a) / b;
result.thetaRStar = bm / b;
result.residualA = (a + b * result.thetaXStar) - am;
result.residualB = (b * result.thetaRStar) - bm;
result.matched = Math.abs(result.residualA) <= tolerance && Math.abs(result.residualB) <= tolerance;
return result;
}
static double referenceSignal(double t) {
if (t < 4.0) {
return 1.0;
}
return 0.8 + 0.2 * Math.sin(2.0 * Math.PI * 0.5 * (t - 4.0));
}
static double rk4Step(ScalarOde f, double t, double x, double dt) {
double k1 = f.eval(t, x);
double k2 = f.eval(t + 0.5 * dt, x + 0.5 * dt * k1);
double k3 = f.eval(t + 0.5 * dt, x + 0.5 * dt * k2);
double k4 = f.eval(t + dt, x + dt * k3);
return x + (dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4);
}
static double simulateAndWriteCsv(String filename,
double a, double b, double am, double bm,
double thetaX, double thetaR,
double x0, double xm0,
double dt, double tf) throws IOException {
int steps = (int) (tf / dt);
double x = x0;
double xm = xm0;
double sumE2 = 0.0;
try (PrintWriter out = new PrintWriter(new FileWriter(filename))) {
out.println("t,x,xm,e,u");
for (int k = 0; k <= steps; k++) {
double t = k * dt;
double r = referenceSignal(t);
double u = thetaX * x + thetaR * r;
double e = x - xm;
sumE2 += e * e;
out.printf(Locale.US, "%.12f,%.12f,%.12f,%.12f,%.12f%n", t, x, xm, e, u);
ScalarOde plant = (time, state) -> {
double ref = referenceSignal(time);
double control = thetaX * state + thetaR * ref;
return a * state + b * control;
};
ScalarOde model = (time, state) -> {
double ref = referenceSignal(time);
return am * state + bm * ref;
};
x = rk4Step(plant, t, x, dt);
xm = rk4Step(model, t, xm, dt);
}
}
return Math.sqrt(sumE2 / (double) (steps + 1));
}
public static void main(String[] args) throws IOException {
double a = -0.7;
double b = 2.0;
double am = -2.0;
double bm = 2.0;
MatchingResult result = idealParametersScalar(a, b, am, bm, 1e-10);
System.out.println("Ideal parameter computation");
System.out.printf(Locale.US, "theta_x_star = %.6f%n", result.thetaXStar);
System.out.printf(Locale.US, "theta_r_star = %.6f%n", result.thetaRStar);
System.out.println("matched = " + result.matched);
System.out.printf(Locale.US, "residual_A = %.3e%n", result.residualA);
System.out.printf(Locale.US, "residual_B = %.3e%n", result.residualB);
double rmsIdeal = simulateAndWriteCsv(
"Chapter2_Lesson4_ideal_java.csv",
a, b, am, bm,
result.thetaXStar, result.thetaRStar,
-0.4, 0.2, 0.001, 8.0
);
double rmsBad = simulateAndWriteCsv(
"Chapter2_Lesson4_mismatched_java.csv",
a, b, am, bm,
0.35 * result.thetaXStar, 1.25 * result.thetaRStar,
-0.4, 0.2, 0.001, 8.0
);
System.out.printf(Locale.US, "RMS tracking error with ideal parameters = %.6f%n", rmsIdeal);
System.out.printf(Locale.US, "RMS tracking error with mismatched parameters = %.6f%n", rmsBad);
}
}
11. MATLAB/Simulink Implementation
This MATLAB script computes ideal parameters, simulates the matched and mismatched controllers, writes CSV data, plots tracking error, and optionally builds a simple Simulink model if Simulink is installed.
Chapter2_Lesson4.m
% Chapter2_Lesson4.m
% Adaptive Control - Chapter 2, Lesson 4
% Matching Conditions and Ideal Parameters Concept
%
% This script computes ideal matching parameters for a first-order plant,
% simulates the ideal matched closed loop, compares it with a mismatched
% controller, and optionally creates a simple Simulink-style block diagram
% if Simulink is available.
clear; clc; close all;
% Plant: x_dot = a*x + b*u
a = -0.7;
b = 2.0;
% Reference model: xm_dot = am*xm + bm*r
am = -2.0;
bm = 2.0;
tol = 1e-10;
if abs(b) < tol
error('Matching is impossible because b is approximately zero.');
end
theta_x_star = (am - a)/b;
theta_r_star = bm/b;
residual_A = (a + b*theta_x_star) - am;
residual_B = b*theta_r_star - bm;
matched = abs(residual_A) <= tol && abs(residual_B) <= tol;
fprintf('Ideal parameter computation\n');
fprintf('theta_x_star = %.6f\n', theta_x_star);
fprintf('theta_r_star = %.6f\n', theta_r_star);
fprintf('matched = %d\n', matched);
fprintf('residual_A = %.3e\n', residual_A);
fprintf('residual_B = %.3e\n', residual_B);
rfun = @(t) (t < 4).*1.0 + (t >= 4).*(0.8 + 0.2*sin(2*pi*0.5*(t - 4)));
% Combined ODE state z = [x; xm]
odeIdeal = @(t,z) [
a*z(1) + b*(theta_x_star*z(1) + theta_r_star*rfun(t));
am*z(2) + bm*rfun(t)
];
odeBad = @(t,z) [
a*z(1) + b*(0.35*theta_x_star*z(1) + 1.25*theta_r_star*rfun(t));
am*z(2) + bm*rfun(t)
];
tspan = [0 8];
z0 = [-0.4; 0.2];
[t1,z1] = ode45(odeIdeal, tspan, z0);
[t2,z2] = ode45(odeBad, tspan, z0);
eIdeal = z1(:,1) - z1(:,2);
eBad = z2(:,1) - z2(:,2);
fprintf('RMS tracking error with ideal parameters = %.6f\n', rms(eIdeal));
fprintf('RMS tracking error with mismatched parameters = %.6f\n', rms(eBad));
Tideal = table(t1, z1(:,1), z1(:,2), eIdeal, ...
'VariableNames', {'t','x','xm','e'});
Tbad = table(t2, z2(:,1), z2(:,2), eBad, ...
'VariableNames', {'t','x','xm','e'});
writetable(Tideal, 'Chapter2_Lesson4_ideal_matlab.csv');
writetable(Tbad, 'Chapter2_Lesson4_mismatched_matlab.csv');
figure;
plot(t1, eIdeal, 'LineWidth', 1.5); hold on;
plot(t2, eBad, 'LineWidth', 1.5);
grid on;
xlabel('time [s]');
ylabel('tracking error e = x - x_m');
title('Matching Conditions and Ideal Parameters');
legend('ideal matched', 'mismatched');
% Optional Simulink construction: creates a documentation-level block model.
% The script still runs even when Simulink is unavailable.
if exist('new_system', 'file') == 2
model = 'Chapter2_Lesson4_Simulink_Matching';
if bdIsLoaded(model)
close_system(model, 0);
end
new_system(model);
open_system(model);
add_block('simulink/Sources/Step', [model '/Reference r'], ...
'Position', [50 60 100 90], 'Time', '0', 'Before', '0', 'After', '1');
add_block('simulink/Math Operations/Gain', [model '/theta_r_star'], ...
'Position', [150 50 220 90], 'Gain', num2str(theta_r_star));
add_block('simulink/Continuous/Transfer Fcn', [model '/Matched Closed Loop Plant'], ...
'Position', [290 50 430 90], ...
'Numerator', ['[' num2str(b*theta_r_star) ']'], ...
'Denominator', ['[1 ' num2str(-(a + b*theta_x_star)) ']']);
add_block('simulink/Continuous/Transfer Fcn', [model '/Reference Model'], ...
'Position', [290 145 430 185], ...
'Numerator', ['[' num2str(bm) ']'], ...
'Denominator', ['[1 ' num2str(-am) ']']);
add_block('simulink/Math Operations/Sum', [model '/Error x - xm'], ...
'Position', [485 85 515 135], 'Inputs', '+-');
add_block('simulink/Sinks/Scope', [model '/Scope'], ...
'Position', [570 90 620 130]);
add_line(model, 'Reference r/1', 'theta_r_star/1');
add_line(model, 'theta_r_star/1', 'Matched Closed Loop Plant/1');
add_line(model, 'Reference r/1', 'Reference Model/1');
add_line(model, 'Matched Closed Loop Plant/1', 'Error x - xm/1');
add_line(model, 'Reference Model/1', 'Error x - xm/2');
add_line(model, 'Error x - xm/1', 'Scope/1');
save_system(model);
fprintf('Created optional Simulink model: %s.slx\n', model);
end
12. Wolfram Mathematica Implementation
The Mathematica notebook code symbolically verifies the matching equations and numerically plots the ideal and mismatched tracking error.
Chapter2_Lesson4.nb
(* Chapter2_Lesson4.nb
Adaptive Control - Chapter 2, Lesson 4
Matching Conditions and Ideal Parameters Concept
Paste this script into a Mathematica notebook or evaluate it as Wolfram Language code.
*)
ClearAll["Global`*"];
(* Plant and reference model coefficients *)
a = -0.7;
b = 2.0;
am = -2.0;
bm = 2.0;
If[Abs[b] < 10^-10, Print["Matching is impossible because b is approximately zero."]; Abort[]];
thetaXStar = (am - a)/b;
thetaRStar = bm/b;
residualA = (a + b thetaXStar) - am;
residualB = b thetaRStar - bm;
matched = Abs[residualA] <= 10^-10 && Abs[residualB] <= 10^-10;
Print["Ideal parameter computation"];
Print["theta_x_star = ", NumberForm[thetaXStar, {8, 6}]];
Print["theta_r_star = ", NumberForm[thetaRStar, {8, 6}]];
Print["matched = ", matched];
Print["residual_A = ", ScientificForm[residualA]];
Print["residual_B = ", ScientificForm[residualB]];
r[t_] := Piecewise[{ {1.0, t < 4.0} }, 0.8 + 0.2 Sin[2 Pi 0.5 (t - 4.0)]];
idealSol = NDSolve[
{
x'[t] == a x[t] + b (thetaXStar x[t] + thetaRStar r[t]),
xm'[t] == am xm[t] + bm r[t],
x[0] == -0.4,
xm[0] == 0.2
},
{x, xm},
{t, 0, 8}
][[1]];
badSol = NDSolve[
{
xb'[t] == a xb[t] + b (0.35 thetaXStar xb[t] + 1.25 thetaRStar r[t]),
xmb'[t] == am xmb[t] + bm r[t],
xb[0] == -0.4,
xmb[0] == 0.2
},
{xb, xmb},
{t, 0, 8}
][[1]];
idealError[t_] := Evaluate[x[t] - xm[t] /. idealSol];
badError[t_] := Evaluate[xb[t] - xmb[t] /. badSol];
Plot[
{idealError[t], badError[t]},
{t, 0, 8},
PlotLegends -> {"ideal matched", "mismatched"},
AxesLabel -> {"time [s]", "e = x - xm"},
PlotLabel -> "Matching Conditions and Ideal Parameters",
GridLines -> Automatic
]
(* Algebraic verification of the matching equations *)
FullSimplify[
{
a + b thetaXStar == am,
b thetaRStar == bm
}
]
13. Problems and Solutions
Problem 1: Scalar ideal parameters. Consider \( \dot{x}=a x+b u \) with \( a=-1 \), \( b=2 \). The desired reference model is \( \dot{x}_m=-4x_m+3r \). For the controller \( u=\theta_x x+\theta_r r \), compute \( \theta_x^* \) and \( \theta_r^* \).
Solution: The matching equations are \( a+b\theta_x^*=a_m \) and \( b\theta_r^*=b_m \). Therefore,
\[ \theta_x^*=\frac{a_m-a}{b} = \frac{-4-(-1)}{2} = -\frac{3}{2}, \qquad \theta_r^*=\frac{b_m}{b} = \frac{3}{2}. \]
Problem 2: Error dynamics under ideal matching. For the same scalar system, prove that if the ideal parameters are used, then \( e(t)=x(t)-x_m(t) \) converges to zero when \( a_m < 0 \).
Solution: Under ideal matching, \( \dot{x}=a_m x+b_m r \). Since \( \dot{x}_m=a_m x_m+b_m r \),
\[ \dot{e} = \dot{x}-\dot{x}_m = a_m(x-x_m) = a_m e. \]
Thus \( e(t)=e(0)\exp(a_m t) \). Because \( a_m < 0 \), the error converges exponentially to zero.
Problem 3: Nonexistence of matching when input gain is zero. Consider \( \dot{x}=a x \) but suppose the desired model is \( \dot{x}_m=a_m x_m+b_m r \) with \( b_m\neq 0 \). Can the controller \( u=\theta_x x+\theta_r r \) match the model?
Solution: No. Since the plant has no input channel, the control signal does not affect \( \dot{x} \). Algebraically, this corresponds to \( b=0 \). The matching equation \( b\theta_r^*=b_m \) becomes \( 0=b_m \), which contradicts \( b_m\neq 0 \). Therefore exact matching is impossible.
Problem 4: State-space matching test. Let
\[ \mathbf{A} = \begin{bmatrix} 0 & 1\\ -2 & -1 \end{bmatrix}, \qquad \mathbf{b} = \begin{bmatrix} 0\\ 1 \end{bmatrix}, \qquad \mathbf{A}_m = \begin{bmatrix} 0 & 1\\ -6 & -5 \end{bmatrix}. \]
Determine whether a row vector \( \mathbf{k}_x^{*T}=[k_1\;k_2] \) exists such that \( \mathbf{A}+\mathbf{b}\mathbf{k}_x^{*T}=\mathbf{A}_m \).
Solution: Since
\[ \mathbf{b}\mathbf{k}_x^{*T} = \begin{bmatrix} 0\\ 1 \end{bmatrix} \begin{bmatrix} k_1 & k_2 \end{bmatrix} = \begin{bmatrix} 0 & 0\\ k_1 & k_2 \end{bmatrix}, \]
the first row of \( \mathbf{A} \) cannot be changed. The desired model has the same first row, so matching is possible. The second row gives
\[ -2+k_1=-6, \qquad -1+k_2=-5. \]
Hence \( k_1=-4 \) and \( k_2=-4 \).
Problem 5: Matched vs unmatched uncertainty. For
\[ \dot{\mathbf{x} } = \mathbf{A}_0\mathbf{x} + \mathbf{b}u + \mathbf{d}\theta^{*T}\boldsymbol{\phi}(\mathbf{x}), \]
explain the condition under which the uncertainty is matched.
Solution: The uncertainty is matched if \( \mathbf{d} \) lies in the span of \( \mathbf{b} \). That is, there must exist a scalar \( \lambda \) such that \( \mathbf{d}=\lambda\mathbf{b} \). If no such scalar exists, the uncertainty acts through a direction not directly controlled by \( u \), and the uncertainty is unmatched.
14. Summary
Matching conditions are algebraic existence statements. They determine whether a selected controller structure can, in principle, reproduce a desired reference model for some constant ideal parameter vector \( \theta^* \). In scalar systems, matching often reduces to simple coefficient equations. In state-space systems, the controller input channel imposes structural restrictions. Matched uncertainty enters through the same channel as the control input, while unmatched uncertainty does not. The ideal parameter vector is usually unknown and is introduced for analysis; adaptive laws later attempt to adjust \( \hat{\theta}(t) \) so that the closed-loop system behaves like the ideal matched system.
15. References
- Parks, P.C. (1966). Lyapunov redesign of model reference adaptive control systems. IEEE Transactions on Automatic Control, 11(3), 362–367.
- 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.
- Morse, A.S. (1980). Global stability of parameter-adaptive control systems. IEEE Transactions on Automatic Control, 25(3), 433–439.
- 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.
- Kreisselmeier, G., & Narendra, K.S. (1982). Stable model reference adaptive control in the presence of bounded disturbances. IEEE Transactions on Automatic Control, 27(6), 1169–1175.
- Rohrs, C.E., Valavani, L., Athans, M., & Stein, G. (1985). Robustness of continuous-time adaptive control algorithms in the presence of unmodeled dynamics. IEEE Transactions on Automatic Control, 30(9), 881–889.
- Goodwin, G.C., Ramadge, P.J., & Caines, P.E. (1980). Discrete-time multivariable adaptive control. IEEE Transactions on Automatic Control, 25(3), 449–456.
- Sastry, S., & Bodson, M. (1989). Adaptive control: stability, convergence, and robustness. Prentice Hall Advanced Reference Series.
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.