Chapter 21: Composite Adaptive Control
Lesson 1: Motivation: Faster Convergence and Better Robustness
This lesson explains why a conventional tracking-error adaptive law may stabilize an uncertain system while learning its parameters slowly or not at all. We introduce the central motivation of composite adaptive control: use both closed-loop tracking information and an auxiliary prediction error so that useful parameter learning can continue even after the tracking error has become small.
1. Learning Objectives and Prerequisites
After completing this lesson, students should be able to:
- explain why asymptotic tracking does not automatically imply parameter convergence;
- distinguish tracking error, prediction error, and parameter error;
- derive a derivative-free filtered regression for an uncertain plant;
- construct a basic composite adaptive law;
- prove Lyapunov stability for the composite closed loop;
- quantify how faster parameter learning improves practical tracking robustness;
- recognize the noise, excitation, filtering, and tuning limitations of the composite architecture.
The lesson assumes the material developed earlier in this course: linearly parameterized uncertainty, Lyapunov adaptive laws, parameter projection, normalization, persistent excitation, recursive estimation, robust modifications, and basic nonlinear tracking-error models.
2. Why Tracking-Error Adaptation Can Learn Slowly
Consider the scalar uncertain plant
\[ \dot{x}=-a x+u+\boldsymbol{\theta}^{T}\boldsymbol{\phi}(x,t), \qquad a>0, \]
where \(x\) is measured, \(a\) is known, \(\boldsymbol{\phi}\in\mathbb{R}^{p}\) is a known regressor, and \(\boldsymbol{\theta}\in\mathbb{R}^{p}\) is an unknown constant parameter vector. Let the desired reference model be
\[ \dot{x}_{m}=-a_{m}x_{m}+b_{m}r, \qquad a_{m}>0. \]
Define the tracking error and parameter error as
\[ e=x-x_{m}, \qquad \widetilde{\boldsymbol{\theta}} =\boldsymbol{\theta}-\widehat{\boldsymbol{\theta}}. \]
The certainty-equivalent control law
\[ u=a x+\dot{x}_{m}-k e -\widehat{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}, \qquad k>0, \]
produces the error dynamics
\[ \dot{e} =-k e+ \widetilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}. \]
A conventional tracking-error gradient law is
\[ \dot{\widehat{\boldsymbol{\theta}}} =\boldsymbol{\Gamma}\boldsymbol{\phi}e, \qquad \boldsymbol{\Gamma}=\boldsymbol{\Gamma}^{T}>0. \]
Choose the standard Lyapunov function
\[ V_{t} =\frac{1}{2}e^{2} +\frac{1}{2} \widetilde{\boldsymbol{\theta}}^{T} \boldsymbol{\Gamma}^{-1} \widetilde{\boldsymbol{\theta}}. \]
Because \(\dot{\widetilde{\boldsymbol{\theta}}} =-\dot{\widehat{\boldsymbol{\theta}}}\),
\[ \begin{aligned} \dot{V}_{t} &= e\left(-k e+ \widetilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}\right) + \widetilde{\boldsymbol{\theta}}^{T} \boldsymbol{\Gamma}^{-1} \left(-\boldsymbol{\Gamma}\boldsymbol{\phi}e\right)\\ &=-k e^{2}\le 0. \end{aligned} \]
This proves boundedness and usually supports \(e(t)\rightarrow 0\), but it does not make \(\dot V_t\) negative definite in \(\widetilde{\boldsymbol{\theta}}\). Once feedback has made \(e\) small, the update term \(\boldsymbol{\phi}e\) also becomes small. The controller can therefore track accurately with incorrect parameter estimates.
\[ e\approx 0 \quad\Longrightarrow\quad \dot{\widehat{\boldsymbol{\theta}}} \approx \mathbf{0}, \]
even when \(\widetilde{\boldsymbol{\theta}}\ne\mathbf{0}\). This is the central learning bottleneck addressed by composite adaptive control.
3. Two Independent Sources of Information
Parameter uncertainty influences two physically different residuals:
- Tracking residual: disagreement between the plant trajectory and the desired reference trajectory.
- Prediction residual: disagreement between measured plant dynamics and dynamics predicted by the current parameter estimate.
flowchart TD
R["Reference r"] --> RM["Reference model"]
RM --> E["Tracking error e"]
P["Uncertain plant"] --> E
E --> TA["Tracking-error adaptation"]
P --> FR["Filtered regression data"]
FR --> PE["Prediction error epsilon"]
PE --> PA["Prediction-error adaptation"]
TA --> SUM["Combined parameter update"]
PA --> SUM
SUM --> TH["Parameter estimate theta_hat"]
TH --> C["Adaptive controller"]
C --> P
Direct adaptive control primarily uses the first channel. Indirect identification primarily uses the second. Composite adaptation combines both channels in one stability-oriented update law.
4. Constructing the Prediction Error
Rearranging the plant equation gives a linear regression:
\[ y := \dot{x}+a x-u = \boldsymbol{\theta}^{T}\boldsymbol{\phi}. \]
The model prediction and prediction error are
\[ \widehat{y} = \widehat{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}, \qquad \varepsilon = y-\widehat{y} = \widetilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}. \]
Directly calculating \(\dot{x}\) is undesirable when measurements contain noise. Introduce the stable first-order filter
\[ F(s)=\frac{\alpha}{s+\alpha}, \qquad \alpha>0. \]
Define the filtered signals
\[ x_{f}=F(s)[x], \qquad u_{f}=F(s)[u], \qquad \boldsymbol{\phi}_{f}=F(s)[\boldsymbol{\phi}]. \]
Since \(F(s)[\dot{x}]=\alpha(x-x_f)\) for compatible zero filter initialization, a derivative-free filtered output is
\[ y_{f} = \alpha(x-x_{f})+a x_{f}-u_{f}. \]
Filtering the regression preserves linear parameterization:
\[ y_{f} = \boldsymbol{\theta}^{T}\boldsymbol{\phi}_{f}. \]
The filtered prediction error is therefore
\[ \varepsilon_{f} = y_{f} - \widehat{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}_{f} = \widetilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}_{f}. \]
The required filters can be implemented as ordinary differential equations:
\[ \begin{aligned} \dot{x}_{f}&=-\alpha x_{f}+\alpha x,\\ \dot{u}_{f}&=-\alpha u_{f}+\alpha u,\\ \dot{\boldsymbol{\phi}}_{f} &=-\alpha\boldsymbol{\phi}_{f} +\alpha\boldsymbol{\phi}. \end{aligned} \]
5. The Basic Composite Adaptive Law
Combine the tracking and prediction innovations:
\[ \boxed{ \dot{\widehat{\boldsymbol{\theta}}} = \boldsymbol{\Gamma} \left( \boldsymbol{\phi}e + \kappa \boldsymbol{\phi}_{f}\varepsilon_{f} \right)} \]
where \(\kappa>0\) weights the prediction channel. The two terms have complementary roles:
\[ \underbrace{\boldsymbol{\phi}e}_{\text{tracking correction}} \qquad+\qquad \underbrace{ \kappa\boldsymbol{\phi}_{f}\varepsilon_{f} }_{\text{model-prediction correction}}. \]
Setting \(\kappa=0\) recovers the conventional tracking-error law. Increasing \(\kappa\) places more emphasis on model learning, but also increases sensitivity to prediction noise and model mismatch.
6. Lyapunov Stability Proof
Use the same candidate as for tracking-only adaptation:
\[ V = \frac{1}{2}e^{2} + \frac{1}{2} \widetilde{\boldsymbol{\theta}}^{T} \boldsymbol{\Gamma}^{-1} \widetilde{\boldsymbol{\theta}}. \]
The composite parameter-error dynamics are
\[ \dot{\widetilde{\boldsymbol{\theta}}} = - \boldsymbol{\Gamma} \left( \boldsymbol{\phi}e + \kappa \boldsymbol{\phi}_{f}\varepsilon_{f} \right). \]
Differentiating \(V\) gives
\[ \begin{aligned} \dot{V} &= e\dot e+ \widetilde{\boldsymbol{\theta}}^{T} \boldsymbol{\Gamma}^{-1} \dot{\widetilde{\boldsymbol{\theta}}}\\ &= e\left( -k e+ \widetilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi} \right) - \widetilde{\boldsymbol{\theta}}^{T} \left( \boldsymbol{\phi}e+ \kappa\boldsymbol{\phi}_{f}\varepsilon_{f} \right)\\ &= -k e^{2} - \kappa \left( \widetilde{\boldsymbol{\theta}}^{T} \boldsymbol{\phi}_{f} \right)\varepsilon_{f}. \end{aligned} \]
Because \(\varepsilon_f= \widetilde{\boldsymbol{\theta}}^{T}\boldsymbol{\phi}_f\),
\[ \boxed{ \dot{V} = -k e^{2} - \kappa\varepsilon_{f}^{2} \le 0.} \]
Compared with tracking-only adaptation, the Lyapunov derivative now dissipates energy through two error channels. It follows that
\[ e,\; \widetilde{\boldsymbol{\theta}} \in\mathcal{L}_{\infty}, \qquad e,\;\varepsilon_f\in\mathcal{L}_{2}. \]
Under the usual boundedness and uniform-continuity conditions, Barbalat's lemma yields
\[ e(t)\rightarrow 0, \qquad \varepsilon_f(t)\rightarrow 0. \]
Parameter convergence still requires sufficient information in the filtered regressor. A standard persistent-excitation condition is
\[ \exists T>0,\;\mu>0: \quad \int_{t}^{t+T} \boldsymbol{\phi}_{f}(\tau) \boldsymbol{\phi}_{f}^{T}(\tau) \,d\tau \ge \mu\mathbf{I}, \qquad \forall t\ge 0. \]
Under this condition and the bounded closed-loop assumptions, \(\varepsilon_f\rightarrow 0\) forces \(\widetilde{\boldsymbol{\theta}}\rightarrow\mathbf{0}\). Later lessons will study alternatives based on finite stored information, interval excitation, and regressor transformations.
7. Why the Composite Channel Accelerates Learning
For a scalar parameter, suppose the tracking error has already become small. The prediction component approximately gives
\[ \dot{\widetilde{\theta}} \approx -\gamma\kappa\phi_f^{2}(t)\widetilde{\theta}. \]
The exact scalar solution is
\[ \widetilde{\theta}(t) = \widetilde{\theta}(t_0) \exp\left[ -\gamma\kappa \int_{t_0}^{t}\phi_f^{2}(\tau)\,d\tau \right]. \]
Thus every interval containing nonzero regressor energy shrinks the parameter error. The tracking-only law has no corresponding decay term after \(e\) is suppressed.
For vector parameters, define the accumulated information matrix
\[ \mathbf{R}_{f}(t_0,t) = \int_{t_0}^{t} \boldsymbol{\phi}_{f}(\tau) \boldsymbol{\phi}_{f}^{T}(\tau) \,d\tau. \]
Its eigenvalues quantify information in different parameter directions. A large minimum eigenvalue means that every direction is informed; rank deficiency means that some parameter combinations remain indistinguishable.
\[ \lambda_{\min}\!\left(\mathbf{R}_{f}\right)>0 \quad\Longrightarrow\quad \text{all parameter directions have been excited.} \]
flowchart TD
A["Start with uncertain parameters"] --> B["Feedback reduces tracking error"]
B --> C{"Is tracking error \nstill informative?"}
C -->|"yes"| D["Tracking channel \nupdates theta_hat"]
C -->|"no"| E{"Is prediction \nerror informative?"}
E -->|"yes"| F["Prediction channel \ncontinues learning"]
E -->|"no"| G["Parameter convergence \nneeds more excitation \nor stored data"]
D --> H["Smaller parameter mismatch"]
F --> H
H --> I["Smaller uncertainty \nentering error dynamics"]
I --> J["Faster recovery and \nimproved tracking robustness"]
8. How Faster Learning Improves Practical Robustness
The tracking dynamics can be interpreted as a stable linear system driven by an uncertainty residual:
\[ \dot e=-k e+d_{\theta}(t), \qquad d_{\theta}(t) = \widetilde{\boldsymbol{\theta}}^{T} \boldsymbol{\phi}(t). \]
The variation-of-constants formula gives
\[ e(t) = e^{-kt}e(0) + \int_{0}^{t} e^{-k(t-\tau)} d_{\theta}(\tau)\,d\tau. \]
If \(\|\boldsymbol{\phi}(t)\|\le\bar{\phi}\), then
\[ |e(t)| \le e^{-kt}|e(0)| + \bar{\phi} \int_{0}^{t} e^{-k(t-\tau)} \|\widetilde{\boldsymbol{\theta}}(\tau)\| \,d\tau. \]
Faster reduction of \(\|\widetilde{\boldsymbol{\theta}}\|\) directly tightens this tracking bound. The practical benefits include:
- shorter transients after startup or an operating-point change;
- less time spent with large uncertainty compensation error;
- smaller tracking degradation when feedback gain is limited;
- better recovery after a slowly varying parameter changes, provided the estimator remains sufficiently excited;
- a more accurate online model for monitoring, feedforward action, or fault detection.
This is practical robustness through improved learning. It is not a substitute for a robust-control guarantee against arbitrary unmodeled dynamics or disturbances. Prediction errors can be corrupted by noise, neglected dynamics, unknown disturbances, and filter transients.
9. Robust Composite Modifications and Design Trade-Offs
Earlier robustification tools can be applied to the composite update. For example, a leakage-modified law is
\[ \dot{\widehat{\boldsymbol{\theta}}} = \boldsymbol{\Gamma} \left( \boldsymbol{\phi}e + \kappa\boldsymbol{\phi}_{f}\varepsilon_{f} - \sigma\widehat{\boldsymbol{\theta}} \right), \qquad \sigma>0. \]
A projection version can enforce known parameter bounds:
\[ \dot{\widehat{\boldsymbol{\theta}}} = \operatorname{Proj}_{\Omega} \left[ \widehat{\boldsymbol{\theta}}, \boldsymbol{\Gamma} \left( \boldsymbol{\phi}e+ \kappa\boldsymbol{\phi}_{f}\varepsilon_f \right) \right]. \]
A normalized implementation can limit update magnitude:
\[ \dot{\widehat{\boldsymbol{\theta}}} = \boldsymbol{\Gamma} \frac{ \boldsymbol{\phi}e+ \kappa\boldsymbol{\phi}_{f}\varepsilon_f }{ 1+\|\boldsymbol{\phi}\|^{2} +\kappa\|\boldsymbol{\phi}_{f}\|^{2} }. \]
Important design trade-offs are summarized below.
| Design quantity | Increasing it tends to | Risk |
|---|---|---|
| \(\boldsymbol{\Gamma}\) | accelerate both update channels | noise amplification and numerical stiffness |
| \(\kappa\) | emphasize prediction-based learning | bias from unmodeled dynamics or disturbances |
| \(\alpha\) | increase filter bandwidth and reduce phase lag | pass more measurement noise |
| \(k\) | suppress tracking error rapidly | larger control effort and reduced tracking information |
| \(\sigma\) | limit drift under persistent disturbances | nonzero steady parameter bias |
10. Numerical Experiment Used in All Implementations
The programming labs use the same uncertain plant, regressor, and controller:
\[ \boldsymbol{\theta} = \begin{bmatrix} 0.8 & -0.6 \end{bmatrix}^{T}, \qquad \boldsymbol{\phi}(x,t) = \begin{bmatrix} x & \sin(1.3t) \end{bmatrix}^{T}. \]
\[ r(t)=1+0.5\sin(0.5t), \quad a=1.2, \quad a_m=b_m=1, \quad k=3, \quad \gamma=0.5, \quad \alpha=8. \]
Two simulations are compared:
\[ \kappa=0 \quad\text{(tracking-only)}, \qquad \kappa=5 \quad\text{(composite)}. \]
With a step size of \(10^{-3}\) seconds and a 20-second experiment, the supplied implementations produce approximately:
| Adaptive law | RMS tracking error | Peak tracking error | Final parameter-error norm |
|---|---|---|---|
| Tracking-only | 0.128264 | 0.432837 | 0.114304 |
| Composite | 0.044202 | 0.119172 | 0.001337 |
These values demonstrate the intended mechanism for this example, but they are not universal performance guarantees. Results depend on the excitation, gains, filter bandwidth, numerical method, disturbances, and model fidelity.
11. Python Implementation
Chapter21_Lesson1.py
This implementation uses NumPy for vector operations and Matplotlib for tracking- and parameter-error plots.
"""
Chapter21_Lesson1.py
Composite adaptive control demonstration.
Compares:
1. tracking-error-only gradient adaptation;
2. composite adaptation using tracking error plus filtered prediction error.
Dependencies:
pip install numpy matplotlib
"""
from dataclasses import dataclass
from typing import Dict
import matplotlib.pyplot as plt
import numpy as np
@dataclass(frozen=True)
class Config:
dt: float = 1.0e-3
final_time: float = 20.0
a: float = 1.2
am: float = 1.0
bm: float = 1.0
feedback_gain: float = 3.0
adaptation_gain: float = 0.5
filter_pole: float = 8.0
theta1: float = 0.8
theta2: float = -0.6
def simulate(composite_weight: float, cfg: Config) -> Dict[str, np.ndarray]:
"""Simulate one controller with explicit Euler integration."""
steps = int(round(cfg.final_time / cfg.dt)) + 1
time = np.linspace(0.0, cfg.final_time, steps)
x = 0.0
xm = 0.0
theta_hat = np.zeros(2)
# First-order filtered signals F(s) = alpha / (s + alpha).
x_f = 0.0
u_f = 0.0
phi_f = np.zeros(2)
theta = np.array([cfg.theta1, cfg.theta2], dtype=float)
x_log = np.zeros(steps)
xm_log = np.zeros(steps)
error_log = np.zeros(steps)
prediction_error_log = np.zeros(steps)
theta_hat_log = np.zeros((steps, 2))
parameter_error_norm_log = np.zeros(steps)
for index, t in enumerate(time):
reference = 1.0 + 0.5 * np.sin(0.5 * t)
xm_dot = -cfg.am * xm + cfg.bm * reference
tracking_error = x - xm
phi = np.array([x, np.sin(1.3 * t)], dtype=float)
control = (
cfg.a * x
+ xm_dot
- cfg.feedback_gain * tracking_error
- float(theta_hat @ phi)
)
x_dot = -cfg.a * x + control + float(theta @ phi)
# Derivative-free filtered regression:
# y_f = alpha(x - x_f) + a*x_f - u_f = theta^T phi_f.
y_f = (
cfg.filter_pole * (x - x_f)
+ cfg.a * x_f
- u_f
)
prediction_error = y_f - float(theta_hat @ phi_f)
theta_hat_dot = cfg.adaptation_gain * (
phi * tracking_error
+ composite_weight * phi_f * prediction_error
)
x_f_dot = cfg.filter_pole * (x - x_f)
u_f_dot = cfg.filter_pole * (control - u_f)
phi_f_dot = cfg.filter_pole * (phi - phi_f)
# Log before integration.
x_log[index] = x
xm_log[index] = xm
error_log[index] = tracking_error
prediction_error_log[index] = prediction_error
theta_hat_log[index] = theta_hat
parameter_error_norm_log[index] = np.linalg.norm(theta - theta_hat)
# Explicit Euler update.
x += cfg.dt * x_dot
xm += cfg.dt * xm_dot
theta_hat += cfg.dt * theta_hat_dot
x_f += cfg.dt * x_f_dot
u_f += cfg.dt * u_f_dot
phi_f += cfg.dt * phi_f_dot
return {
"time": time,
"x": x_log,
"xm": xm_log,
"error": error_log,
"prediction_error": prediction_error_log,
"theta_hat": theta_hat_log,
"parameter_error_norm": parameter_error_norm_log,
}
def report(name: str, result: Dict[str, np.ndarray]) -> None:
rms_error = float(np.sqrt(np.mean(result["error"] ** 2)))
peak_error = float(np.max(np.abs(result["error"])))
final_parameter_error = float(result["parameter_error_norm"][-1])
print(
f"{name:16s} | RMS tracking error = {rms_error:.6f} | "
f"peak error = {peak_error:.6f} | "
f"final parameter-error norm = {final_parameter_error:.6f}"
)
def main() -> None:
cfg = Config()
tracking_only = simulate(composite_weight=0.0, cfg=cfg)
composite = simulate(composite_weight=5.0, cfg=cfg)
report("Tracking only", tracking_only)
report("Composite", composite)
plt.figure()
plt.plot(tracking_only["time"], tracking_only["error"], label="tracking only")
plt.plot(composite["time"], composite["error"], label="composite")
plt.xlabel("Time (s)")
plt.ylabel("Tracking error")
plt.title("Tracking-error comparison")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.figure()
plt.semilogy(
tracking_only["time"],
tracking_only["parameter_error_norm"],
label="tracking only",
)
plt.semilogy(
composite["time"],
composite["parameter_error_norm"],
label="composite",
)
plt.xlabel("Time (s)")
plt.ylabel("Parameter-error norm")
plt.title("Parameter convergence")
plt.grid(True)
plt.legend()
plt.tight_layout()
np.savetxt(
"Chapter21_Lesson1_results.csv",
np.column_stack(
(
composite["time"],
tracking_only["error"],
composite["error"],
tracking_only["parameter_error_norm"],
composite["parameter_error_norm"],
)
),
delimiter=",",
header=(
"time,tracking_only_error,composite_error,"
"tracking_only_parameter_error,composite_parameter_error"
),
comments="",
)
plt.show()
if __name__ == "__main__":
main()
12. C++ Implementation
Chapter21_Lesson1.cpp
The C++17 implementation has no third-party numerical dependency and exports the comparison data to CSV.
// Chapter21_Lesson1.cpp
// Composite adaptive control demonstration.
//
// Build:
// g++ -std=c++17 -O2 Chapter21_Lesson1.cpp -o Chapter21_Lesson1
// Run:
// ./Chapter21_Lesson1
#include <array>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
struct Result {
std::vector<double> time;
std::vector<double> error;
std::vector<double> parameter_error;
double rms_error{};
double peak_error{};
double final_parameter_error{};
};
Result simulate(double composite_weight) {
constexpr double dt = 1.0e-3;
constexpr double final_time = 20.0;
constexpr double a = 1.2;
constexpr double am = 1.0;
constexpr double bm = 1.0;
constexpr double feedback_gain = 3.0;
constexpr double adaptation_gain = 0.5;
constexpr double filter_pole = 8.0;
constexpr std::array<double, 2> theta{0.8, -0.6};
const std::size_t steps =
static_cast<std::size_t>(std::llround(final_time / dt)) + 1U;
Result result;
result.time.resize(steps);
result.error.resize(steps);
result.parameter_error.resize(steps);
double x = 0.0;
double xm = 0.0;
std::array<double, 2> theta_hat{0.0, 0.0};
double x_f = 0.0;
double u_f = 0.0;
std::array<double, 2> phi_f{0.0, 0.0};
double squared_error_sum = 0.0;
double peak_error = 0.0;
for (std::size_t index = 0; index < steps; ++index) {
const double t = static_cast<double>(index) * dt;
const double reference = 1.0 + 0.5 * std::sin(0.5 * t);
const double xm_dot = -am * xm + bm * reference;
const double tracking_error = x - xm;
const std::array<double, 2> phi{x, std::sin(1.3 * t)};
const double estimated_uncertainty =
theta_hat[0] * phi[0] + theta_hat[1] * phi[1];
const double true_uncertainty =
theta[0] * phi[0] + theta[1] * phi[1];
const double control =
a * x + xm_dot - feedback_gain * tracking_error
- estimated_uncertainty;
const double x_dot = -a * x + control + true_uncertainty;
const double y_f = filter_pole * (x - x_f) + a * x_f - u_f;
const double prediction_error =
y_f - theta_hat[0] * phi_f[0] - theta_hat[1] * phi_f[1];
std::array<double, 2> theta_hat_dot{};
std::array<double, 2> phi_f_dot{};
for (std::size_t j = 0; j < 2; ++j) {
theta_hat_dot[j] = adaptation_gain * (
phi[j] * tracking_error
+ composite_weight * phi_f[j] * prediction_error
);
phi_f_dot[j] = filter_pole * (phi[j] - phi_f[j]);
}
const double x_f_dot = filter_pole * (x - x_f);
const double u_f_dot = filter_pole * (control - u_f);
const double parameter_error_0 = theta[0] - theta_hat[0];
const double parameter_error_1 = theta[1] - theta_hat[1];
const double parameter_error_norm = std::hypot(
parameter_error_0, parameter_error_1
);
result.time[index] = t;
result.error[index] = tracking_error;
result.parameter_error[index] = parameter_error_norm;
squared_error_sum += tracking_error * tracking_error;
peak_error = std::max(peak_error, std::abs(tracking_error));
x += dt * x_dot;
xm += dt * xm_dot;
for (std::size_t j = 0; j < 2; ++j) {
theta_hat[j] += dt * theta_hat_dot[j];
phi_f[j] += dt * phi_f_dot[j];
}
x_f += dt * x_f_dot;
u_f += dt * u_f_dot;
}
result.rms_error =
std::sqrt(squared_error_sum / static_cast<double>(steps));
result.peak_error = peak_error;
result.final_parameter_error = result.parameter_error.back();
return result;
}
void print_report(const std::string& name, const Result& result) {
std::cout << std::left << std::setw(16) << name
<< " | RMS tracking error = " << std::fixed << std::setprecision(6)
<< result.rms_error
<< " | peak error = " << result.peak_error
<< " | final parameter-error norm = "
<< result.final_parameter_error << '\n';
}
int main() {
try {
const Result tracking_only = simulate(0.0);
const Result composite = simulate(5.0);
print_report("Tracking only", tracking_only);
print_report("Composite", composite);
std::ofstream csv("Chapter21_Lesson1_results_cpp.csv");
if (!csv) {
throw std::runtime_error("Could not create CSV output.");
}
csv << "time,tracking_only_error,composite_error,"
"tracking_only_parameter_error,composite_parameter_error\n";
for (std::size_t i = 0; i < composite.time.size(); ++i) {
csv << composite.time[i] << ','
<< tracking_only.error[i] << ','
<< composite.error[i] << ','
<< tracking_only.parameter_error[i] << ','
<< composite.parameter_error[i] << '\n';
}
return 0;
} catch (const std::exception& exception) {
std::cerr << "Error: " << exception.what() << '\n';
return 1;
}
}
13. Java Implementation
Chapter21_Lesson1.java
The Java implementation uses only the standard library and writes the simulation trajectories to a CSV file.
// Chapter21_Lesson1.java
// Composite adaptive control demonstration.
//
// Build:
// javac Chapter21_Lesson1.java
// Run:
// java Chapter21_Lesson1
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Locale;
public final class Chapter21_Lesson1 {
private static final double DT = 1.0e-3;
private static final double FINAL_TIME = 20.0;
private static final double A = 1.2;
private static final double AM = 1.0;
private static final double BM = 1.0;
private static final double FEEDBACK_GAIN = 3.0;
private static final double ADAPTATION_GAIN = 0.5;
private static final double FILTER_POLE = 8.0;
private static final double[] THETA = {0.8, -0.6};
private static final class Result {
final double[] time;
final double[] error;
final double[] parameterError;
final double rmsError;
final double peakError;
final double finalParameterError;
Result(
double[] time,
double[] error,
double[] parameterError,
double rmsError,
double peakError,
double finalParameterError
) {
this.time = time;
this.error = error;
this.parameterError = parameterError;
this.rmsError = rmsError;
this.peakError = peakError;
this.finalParameterError = finalParameterError;
}
}
private static Result simulate(double compositeWeight) {
final int steps = (int) Math.round(FINAL_TIME / DT) + 1;
final double[] time = new double[steps];
final double[] error = new double[steps];
final double[] parameterError = new double[steps];
double x = 0.0;
double xm = 0.0;
final double[] thetaHat = {0.0, 0.0};
double xF = 0.0;
double uF = 0.0;
final double[] phiF = {0.0, 0.0};
double squaredErrorSum = 0.0;
double peakError = 0.0;
for (int index = 0; index < steps; index++) {
final double t = index * DT;
final double reference = 1.0 + 0.5 * Math.sin(0.5 * t);
final double xmDot = -AM * xm + BM * reference;
final double trackingError = x - xm;
final double[] phi = {x, Math.sin(1.3 * t)};
final double estimatedUncertainty =
thetaHat[0] * phi[0] + thetaHat[1] * phi[1];
final double trueUncertainty =
THETA[0] * phi[0] + THETA[1] * phi[1];
final double control =
A * x + xmDot - FEEDBACK_GAIN * trackingError
- estimatedUncertainty;
final double xDot = -A * x + control + trueUncertainty;
final double yF = FILTER_POLE * (x - xF) + A * xF - uF;
final double predictionError =
yF - thetaHat[0] * phiF[0] - thetaHat[1] * phiF[1];
final double[] thetaHatDot = new double[2];
final double[] phiFDot = new double[2];
for (int j = 0; j < 2; j++) {
thetaHatDot[j] = ADAPTATION_GAIN * (
phi[j] * trackingError
+ compositeWeight * phiF[j] * predictionError
);
phiFDot[j] = FILTER_POLE * (phi[j] - phiF[j]);
}
final double xFDot = FILTER_POLE * (x - xF);
final double uFDot = FILTER_POLE * (control - uF);
final double parameterError0 = THETA[0] - thetaHat[0];
final double parameterError1 = THETA[1] - thetaHat[1];
final double parameterErrorNorm =
Math.hypot(parameterError0, parameterError1);
time[index] = t;
error[index] = trackingError;
parameterError[index] = parameterErrorNorm;
squaredErrorSum += trackingError * trackingError;
peakError = Math.max(peakError, Math.abs(trackingError));
x += DT * xDot;
xm += DT * xmDot;
for (int j = 0; j < 2; j++) {
thetaHat[j] += DT * thetaHatDot[j];
phiF[j] += DT * phiFDot[j];
}
xF += DT * xFDot;
uF += DT * uFDot;
}
final double rmsError = Math.sqrt(squaredErrorSum / steps);
return new Result(
time,
error,
parameterError,
rmsError,
peakError,
parameterError[steps - 1]
);
}
private static void printReport(String name, Result result) {
System.out.printf(
Locale.US,
"%-16s | RMS tracking error = %.6f | peak error = %.6f "
+ "| final parameter-error norm = %.6f%n",
name,
result.rmsError,
result.peakError,
result.finalParameterError
);
}
private static void writeCsv(Result trackingOnly, Result composite)
throws IOException {
try (
BufferedWriter writer = new BufferedWriter(
new FileWriter("Chapter21_Lesson1_results_java.csv")
)
) {
writer.write(
"time,tracking_only_error,composite_error,"
+ "tracking_only_parameter_error,"
+ "composite_parameter_error\n"
);
for (int i = 0; i < composite.time.length; i++) {
writer.write(
String.format(
Locale.US,
"%.9f,%.9f,%.9f,%.9f,%.9f%n",
composite.time[i],
trackingOnly.error[i],
composite.error[i],
trackingOnly.parameterError[i],
composite.parameterError[i]
)
);
}
}
}
public static void main(String[] args) {
final Result trackingOnly = simulate(0.0);
final Result composite = simulate(5.0);
printReport("Tracking only", trackingOnly);
printReport("Composite", composite);
try {
writeCsv(trackingOnly, composite);
} catch (IOException exception) {
System.err.println("Could not write CSV: " + exception.getMessage());
System.exit(1);
}
}
}
14. MATLAB and Simulink-Oriented Implementation
Chapter21_Lesson1.m
The MATLAB script executes both adaptive laws and plots their tracking and parameter errors. In Simulink, the same equations map directly to: Integrator blocks for \(x\), \(x_m\), filter states, and parameter estimates; MATLAB Function or Product/Sum blocks for \(\boldsymbol{\phi}\), \(\varepsilon_f\), and the update law; and Scope blocks for error signals. Use a fixed-step solver substantially faster than the filter time constant \(1/\alpha\).
% Chapter21_Lesson1.m
% Composite adaptive control demonstration.
%
% Compares tracking-error-only gradient adaptation with a composite
% adaptive law using a derivative-free filtered prediction error.
clear; clc; close all;
cfg.dt = 1.0e-3;
cfg.finalTime = 20.0;
cfg.a = 1.2;
cfg.am = 1.0;
cfg.bm = 1.0;
cfg.feedbackGain = 3.0;
cfg.adaptationGain = 0.5;
cfg.filterPole = 8.0;
cfg.theta = [0.8; -0.6];
trackingOnly = simulateController(0.0, cfg);
composite = simulateController(5.0, cfg);
printReport("Tracking only", trackingOnly);
printReport("Composite", composite);
figure;
plot(trackingOnly.time, trackingOnly.error, 'LineWidth', 1.2);
hold on;
plot(composite.time, composite.error, 'LineWidth', 1.2);
grid on;
xlabel('Time (s)');
ylabel('Tracking error');
title('Tracking-error comparison');
legend('Tracking only', 'Composite', 'Location', 'best');
figure;
semilogy(trackingOnly.time, trackingOnly.parameterErrorNorm, 'LineWidth', 1.2);
hold on;
semilogy(composite.time, composite.parameterErrorNorm, 'LineWidth', 1.2);
grid on;
xlabel('Time (s)');
ylabel('Parameter-error norm');
title('Parameter convergence');
legend('Tracking only', 'Composite', 'Location', 'best');
resultsTable = table( ...
composite.time, ...
trackingOnly.error, ...
composite.error, ...
trackingOnly.parameterErrorNorm, ...
composite.parameterErrorNorm, ...
'VariableNames', { ...
'time', ...
'tracking_only_error', ...
'composite_error', ...
'tracking_only_parameter_error', ...
'composite_parameter_error' ...
} ...
);
writetable(resultsTable, 'Chapter21_Lesson1_results_matlab.csv');
function result = simulateController(compositeWeight, cfg)
time = (0:cfg.dt:cfg.finalTime).';
steps = numel(time);
x = 0.0;
xm = 0.0;
thetaHat = zeros(2, 1);
xFiltered = 0.0;
uFiltered = 0.0;
phiFiltered = zeros(2, 1);
xLog = zeros(steps, 1);
xmLog = zeros(steps, 1);
errorLog = zeros(steps, 1);
predictionErrorLog = zeros(steps, 1);
thetaHatLog = zeros(steps, 2);
parameterErrorNormLog = zeros(steps, 1);
for index = 1:steps
t = time(index);
reference = 1.0 + 0.5 * sin(0.5 * t);
xmDot = -cfg.am * xm + cfg.bm * reference;
trackingError = x - xm;
phi = [x; sin(1.3 * t)];
control = cfg.a * x + xmDot ...
- cfg.feedbackGain * trackingError ...
- thetaHat.' * phi;
xDot = -cfg.a * x + control + cfg.theta.' * phi;
yFiltered = cfg.filterPole * (x - xFiltered) ...
+ cfg.a * xFiltered - uFiltered;
predictionError = yFiltered - thetaHat.' * phiFiltered;
thetaHatDot = cfg.adaptationGain * ( ...
phi * trackingError ...
+ compositeWeight * phiFiltered * predictionError ...
);
xFilteredDot = cfg.filterPole * (x - xFiltered);
uFilteredDot = cfg.filterPole * (control - uFiltered);
phiFilteredDot = cfg.filterPole * (phi - phiFiltered);
xLog(index) = x;
xmLog(index) = xm;
errorLog(index) = trackingError;
predictionErrorLog(index) = predictionError;
thetaHatLog(index, :) = thetaHat.';
parameterErrorNormLog(index) = norm(cfg.theta - thetaHat);
x = x + cfg.dt * xDot;
xm = xm + cfg.dt * xmDot;
thetaHat = thetaHat + cfg.dt * thetaHatDot;
xFiltered = xFiltered + cfg.dt * xFilteredDot;
uFiltered = uFiltered + cfg.dt * uFilteredDot;
phiFiltered = phiFiltered + cfg.dt * phiFilteredDot;
end
result.time = time;
result.x = xLog;
result.xm = xmLog;
result.error = errorLog;
result.predictionError = predictionErrorLog;
result.thetaHat = thetaHatLog;
result.parameterErrorNorm = parameterErrorNormLog;
result.rmsError = sqrt(mean(errorLog.^2));
result.peakError = max(abs(errorLog));
result.finalParameterError = parameterErrorNormLog(end);
end
function printReport(name, result)
fprintf( ...
'%-16s | RMS tracking error = %.6f | peak error = %.6f | final parameter-error norm = %.6f\n', ...
name, ...
result.rmsError, ...
result.peakError, ...
result.finalParameterError ...
);
end
15. Wolfram Mathematica Notebook
Chapter21_Lesson1.nb
The downloadable file is a text-form Wolfram Notebook expression containing the complete simulation in an executable input cell.
Notebook[{
Cell["Chapter 21, Lesson 1: Composite Adaptive Control", "Title"],
Cell[BoxData[
"ClearAll[\"Global`*\"]; dt = 1.0*^-3; finalTime = 20.0;
a = 1.2; am = 1.0; bm = 1.0; feedbackGain = 3.0;
adaptationGain = 0.5; filterPole = 8.0; theta = {0.8, -0.6};
simulate[compositeWeight_] := Module[
{steps, time, x = 0.0, xm = 0.0, thetaHat = {0.0, 0.0},
xFiltered = 0.0, uFiltered = 0.0, phiFiltered = {0.0, 0.0},
xLog, xmLog, errorLog, predictionErrorLog, thetaHatLog, parameterErrorLog,
t, reference, xmDot, trackingError, phi, control, xDot, yFiltered,
predictionError, thetaHatDot, xFilteredDot, uFilteredDot, phiFilteredDot, index},
steps = Round[finalTime/dt] + 1; time = N[Range[0, steps - 1] dt];
xLog = ConstantArray[0.0, steps]; xmLog = ConstantArray[0.0, steps];
errorLog = ConstantArray[0.0, steps]; predictionErrorLog = ConstantArray[0.0, steps];
thetaHatLog = ConstantArray[{0.0, 0.0}, steps];
parameterErrorLog = ConstantArray[0.0, steps];
Do[
t = time[[index]]; reference = 1.0 + 0.5 Sin[0.5 t];
xmDot = -am xm + bm reference; trackingError = x - xm;
phi = {x, Sin[1.3 t]};
control = a x + xmDot - feedbackGain trackingError - thetaHat.phi;
xDot = -a x + control + theta.phi;
yFiltered = filterPole (x - xFiltered) + a xFiltered - uFiltered;
predictionError = yFiltered - thetaHat.phiFiltered;
thetaHatDot = adaptationGain (phi trackingError + compositeWeight phiFiltered predictionError);
xFilteredDot = filterPole (x - xFiltered); uFilteredDot = filterPole (control - uFiltered);
phiFilteredDot = filterPole (phi - phiFiltered);
xLog[[index]] = x; xmLog[[index]] = xm; errorLog[[index]] = trackingError;
predictionErrorLog[[index]] = predictionError; thetaHatLog[[index]] = thetaHat;
parameterErrorLog[[index]] = Norm[theta - thetaHat];
x = x + dt xDot; xm = xm + dt xmDot; thetaHat = thetaHat + dt thetaHatDot;
xFiltered = xFiltered + dt xFilteredDot; uFiltered = uFiltered + dt uFilteredDot;
phiFiltered = phiFiltered + dt phiFilteredDot;, {index, 1, steps}];
<|\"Time\" -> time, \"Error\" -> errorLog, \"ParameterError\" -> parameterErrorLog,
\"RMS\" -> Sqrt[Mean[errorLog^2]], \"Peak\" -> Max[Abs[errorLog]],
\"FinalParameterError\" -> Last[parameterErrorLog]|>];
trackingOnly = simulate[0.0]; composite = simulate[5.0];
Print[Row[{\"Tracking only: RMS = \", NumberForm[trackingOnly[\"RMS\"], {8, 6}],
\", peak = \", NumberForm[trackingOnly[\"Peak\"], {8, 6}],
\", final parameter-error norm = \", NumberForm[trackingOnly[\"FinalParameterError\"], {8, 6}]}]];
Print[Row[{\"Composite: RMS = \", NumberForm[composite[\"RMS\"], {8, 6}],
\", peak = \", NumberForm[composite[\"Peak\"], {8, 6}],
\", final parameter-error norm = \", NumberForm[composite[\"FinalParameterError\"], {8, 6}]}]];
trackingPlot = ListLinePlot[{Transpose[{trackingOnly[\"Time\"], trackingOnly[\"Error\"]}],
Transpose[{composite[\"Time\"], composite[\"Error\"]}]},
PlotLegends -> {\"Tracking only\", \"Composite\"}, AxesLabel -> {\"Time (s)\", \"Tracking error\"},
PlotLabel -> \"Tracking-error comparison\", GridLines -> Automatic, ImageSize -> Large];
parameterPlot = ListLogPlot[{Transpose[{trackingOnly[\"Time\"], trackingOnly[\"ParameterError\"]}],
Transpose[{composite[\"Time\"], composite[\"ParameterError\"]}]}, Joined -> True,
PlotLegends -> {\"Tracking only\", \"Composite\"}, AxesLabel -> {\"Time (s)\", \"Parameter-error norm\"},
PlotLabel -> \"Parameter convergence\", GridLines -> Automatic, ImageSize -> Large];
Print[trackingPlot]; Print[parameterPlot];
Export[\"Chapter21_Lesson1_results_mathematica.csv\", Prepend[Transpose[{composite[\"Time\"],
trackingOnly[\"Error\"], composite[\"Error\"], trackingOnly[\"ParameterError\"],
composite[\"ParameterError\"]}], {\"time\", \"tracking_only_error\", \"composite_error\",
\"tracking_only_parameter_error\", \"composite_parameter_error\"}]];
"], "Input"]
}];
16. Implementation Verification and Interpretation
Verify the experiment using the following sequence:
- Run the tracking-only case and confirm that the closed loop remains stable and the tracking error decreases.
- Observe that the parameter-error norm decreases much more slowly than the tracking error.
- Enable the prediction term and confirm that \(\varepsilon_f\) drives continued learning.
- Compare RMS error, peak error, and final parameter-error norm.
- Reduce reference excitation and observe that parameter convergence degrades even though tracking may remain acceptable.
- Add measurement noise and vary \(\alpha\) and \(\kappa\) to expose the noise-speed trade-off.
The numerical integrator is explicit Euler to keep all five implementations structurally identical. For demanding systems, replace it with Runge-Kutta integration or an appropriate variable-step solver.
17. Problems and Solutions
Problem 1 — Tracking stability without parameter convergence. For the tracking-only law, prove that \(\dot V_t=-ke^2\). Explain mathematically why this result does not imply \(\widetilde{\boldsymbol{\theta}}\rightarrow\mathbf{0}\).
Solution. Substitution of \(\dot e=-ke+ \widetilde{\boldsymbol{\theta}}^T\boldsymbol{\phi}\) and \(\dot{\widetilde{\boldsymbol{\theta}}} =-\boldsymbol{\Gamma}\boldsymbol{\phi}e\) into \(\dot V_t\) cancels the cross term:
\[ \dot V_t = -ke^2+ e\widetilde{\boldsymbol{\theta}}^T\boldsymbol{\phi} - \widetilde{\boldsymbol{\theta}}^T\boldsymbol{\phi}e = -ke^2. \]
The derivative is negative semidefinite, not negative definite, in the joint state \((e,\widetilde{\boldsymbol{\theta}})\). The invariant set characterized only by \(e=0\) can contain nonzero parameter errors unless regressor excitation supplies additional identifiability.
Problem 2 — Derivative-free regression. Starting from \(\dot x+a x-u= \boldsymbol{\theta}^T\boldsymbol{\phi}\), derive \(y_f=\alpha(x-x_f)+a x_f-u_f\).
Solution. Apply \(F(s)=\alpha/(s+\alpha)\) to both sides:
\[ F(s)[\dot x]+aF(s)[x]-F(s)[u] = \boldsymbol{\theta}^T F(s)[\boldsymbol{\phi}]. \]
For zero compatible filter initialization, \(F(s)[\dot x]=\alpha(x-x_f)\). Substituting \(x_f=F(s)[x]\), \(u_f=F(s)[u]\), and \(\boldsymbol{\phi}_f=F(s)[\boldsymbol{\phi}]\) gives
\[ \alpha(x-x_f)+a x_f-u_f = \boldsymbol{\theta}^T\boldsymbol{\phi}_f. \]
Problem 3 — Composite Lyapunov derivative. Prove that the composite update gives \(\dot V=-ke^2-\kappa\varepsilon_f^2\).
Solution. The tracking cross term is canceled by \(-\boldsymbol{\Gamma}\boldsymbol{\phi}e\) in the parameter-error dynamics. The remaining prediction term is
\[ -\kappa \widetilde{\boldsymbol{\theta}}^T \boldsymbol{\phi}_f\varepsilon_f = -\kappa\varepsilon_f^2, \]
because \(\varepsilon_f= \widetilde{\boldsymbol{\theta}}^T\boldsymbol{\phi}_f\). Therefore both tracking and prediction errors dissipate Lyapunov energy.
Problem 4 — Scalar learning rate. Assume \(e\approx0\), \(\phi_f(t)=\bar\phi\ne0\), and scalar \(\theta\). Determine the time required to reduce \(|\widetilde\theta|\) by a factor \(\rho\), where \(0<\rho<1\).
Solution. The reduced error equation and solution are
\[ \dot{\widetilde\theta} = -\gamma\kappa\bar\phi^2\widetilde\theta, \qquad |\widetilde\theta(t)| = |\widetilde\theta(0)| e^{-\gamma\kappa\bar\phi^2t}. \]
Setting \(|\widetilde\theta(t)|= \rho|\widetilde\theta(0)|\) yields
\[ t_{\rho} = \frac{\ln(1/\rho)} {\gamma\kappa\bar\phi^2}. \]
Problem 5 — Tracking bound. Suppose \(\|\boldsymbol{\phi}(t)\|\le\bar\phi\) and \(\|\widetilde{\boldsymbol{\theta}}(t)\| \le\bar\theta e^{-\lambda t}\). Derive a bound for \(|e(t)|\).
Solution. Substitute the parameter bound into the convolution inequality:
\[ |e(t)| \le e^{-kt}|e(0)| + \bar\phi\bar\theta \int_0^t e^{-k(t-\tau)}e^{-\lambda\tau}\,d\tau. \]
For \(k\ne\lambda\),
\[ |e(t)| \le e^{-kt}|e(0)| + \bar\phi\bar\theta \frac{e^{-\lambda t}-e^{-kt}} {k-\lambda}. \]
For \(k=\lambda\), the integral becomes \(t e^{-kt}\). Faster parameter convergence increases \(\lambda\) and reduces the uncertainty-driven tracking transient.
Problem 6 — Filter selection. Explain the consequences of choosing \(\alpha\) extremely small or extremely large.
Solution. A very small \(\alpha\) strongly attenuates noise but introduces substantial lag, weakens the instantaneous prediction channel, and can slow adaptation. A very large \(\alpha\) approximates an unfiltered regression, reduces lag, and passes more high-frequency noise. The selected bandwidth should be above the dominant informative plant frequencies but below frequencies dominated by sensor noise and unmodeled dynamics.
18. Summary
Tracking-error adaptation can stabilize an uncertain plant without identifying its parameters because feedback may drive the tracking residual to zero before the parameter residual vanishes. Composite adaptive control adds a prediction-error channel derived from the plant model. For the basic filtered architecture, the combined law produces the Lyapunov derivative \(\dot V=-ke^2-\kappa\varepsilon_f^2\). The additional dissipation permits continued learning after tracking becomes accurate, improves convergence when the regressor is informative, and reduces the duration for which parameter mismatch acts as a disturbance. These benefits must be balanced against measurement noise, filter transients, model mismatch, and excitation limitations.
19. References
- Slotine, J.-J. E., & Li, W. (1989). Composite adaptive control of robot manipulators. Automatica, 25(4), 509–519. doi:10.1016/0005-1098(89)90094-0.
- Yuan, J., & Stepanenko, Y. (1993). Composite adaptive control of flexible joint robots. Automatica, 29(3), 609–619. doi:10.1016/0005-1098(93)90058-2.
- Chowdhary, G. V., & Johnson, E. N. (2011). Theory and flight-test validation of a concurrent-learning adaptive controller. Journal of Guidance, Control, and Dynamics, 34(2), 592–607. doi:10.2514/1.46866.
- Chowdhary, G., Yucelen, T., Mühlegg, M., & Johnson, E. N. (2013). Concurrent learning adaptive control of linear systems with exponentially convergent bounds. International Journal of Adaptive Control and Signal Processing, 27(4), 280–301. doi:10.1002/acs.2297.
- Pan, Y., & Yu, H. (2016). Composite learning from adaptive dynamic surface control. IEEE Transactions on Automatic Control, 61(9), 2603–2609. doi:10.1109/TAC.2015.2495232.
- Pan, Y., Pan, L., Darouach, M., & Yu, H. (2016). Composite learning: An efficient way of parameter estimation in adaptive control. Proceedings of the 35th Chinese Control Conference, 3280–3285. doi:10.1109/ChiCC.2016.7553862.
- Aranovskiy, S., Bobtsov, A., Ortega, R., & Pyrkin, A. (2017). Performance enhancement of parameter estimators via dynamic regressor extension and mixing. IEEE Transactions on Automatic Control, 62(7), 3546–3550. doi:10.1109/TAC.2016.2614889.
- Ortega, R., Praly, L., Aranovskiy, S., Yi, B., & Zhang, W. (2018). On dynamic regressor extension and mixing parameter estimators: Two Luenberger observers interpretations. Automatica, 95, 548–551. doi:10.1016/j.automatica.2018.06.011.
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.