Chapter 26: State-Feedback with Integral Action
Lesson 4: Handling Step and Ramp Reference Inputs in State Space
This lesson studies how step and ramp reference signals are handled in a state-space servo system. Building on the augmented-state design of the previous lessons, we show why a constant reference requires one embedded integrator, why a ramp reference requires two embedded integrators, and how these requirements follow from the internal-model principle, regulator equations, and pole-placement controllability of the augmented plant.
1. Servo Tracking Problem in State Space
Consider the continuous-time LTI plant \( \dot{\mathbf{x} }=A\mathbf{x}+B\mathbf{u} \), \( \mathbf{y}=C\mathbf{x}+D\mathbf{u} \), where \( \mathbf{x}\in\mathbb{R}^n \), \( \mathbf{u}\in\mathbb{R}^m \), and \( \mathbf{y}\in\mathbb{R}^p \). A tracking problem asks for a feedback law that makes the tracking error \( \mathbf{e}(t)=\mathbf{r}(t)-\mathbf{y}(t) \) converge to zero for a prescribed reference class.
\[ \lim_{t\to\infty}\mathbf{e}(t)=\mathbf{0}. \]
State feedback alone can assign the closed-loop eigenvalues of \( A-BK \), but eigenvalue assignment by itself does not guarantee zero steady-state tracking error. Integral action supplies an internal dynamic model of the reference signal and forces the controller to accumulate tracking error until the steady-state error disappears.
flowchart TD
R["Reference r(t)"] --> SUM["Error e = r - y"]
SUM --> IM["Integrator chain / internal model"]
IM --> CTRL["State-feedback servo law"]
X["Measured or estimated state x"] --> CTRL
CTRL --> U["Control input u"]
U --> PLANT["Plant: xdot = A x + B u"]
PLANT --> Y["Output y = C x + D u"]
Y --> SUM
2. Step and Ramp References as Polynomial Signals
A step reference is a polynomial of degree zero; a ramp reference is a polynomial of degree one. More generally, if a scalar reference satisfies \( r^{(q)}(t)=0 \) for some integer \( q\ge 1 \), then it can be generated by a chain of \( q \) integrators. A step needs \( q=1 \), while a ramp needs \( q=2 \).
\[ \begin{array}{c|c|c|c} \text{reference} & r(t) & R(s) & \text{minimum internal model} \\ \hline \text{step} & r_0 & \dfrac{r_0}{s} & \dfrac{1}{s} \\ \text{ramp} & a t+r_0 & \dfrac{a}{s^2}+\dfrac{r_0}{s} & \dfrac{1}{s^2} \end{array} \]
This table is not a tuning rule; it is a structural requirement. If the controller contains only one integrator, it may reject constant tracking error but it generally cannot eliminate the steady-state error to a ramp. The controller must contain the reference generator as an internal model.
\[ \boxed{\text{polynomial degree }d\;\Longrightarrow\;\text{at least }d+1\text{ integrators per tracked output} }. \]
3. Step Tracking with One Error Integrator
For a step reference, define the integral state \( \boldsymbol{\eta}(t)=\int_0^t(\mathbf{r}(\sigma)-\mathbf{y}(\sigma))d\sigma \). If \( D=0 \), the augmented dynamics are
\[ \begin{bmatrix}\dot{\mathbf{x} }\\\dot{\boldsymbol{\eta} }\end{bmatrix} = \underbrace{\begin{bmatrix}A&0\\-C&0\end{bmatrix} }_{A_s} \begin{bmatrix}\mathbf{x}\\\boldsymbol{\eta}\end{bmatrix} + \underbrace{\begin{bmatrix}B\\0\end{bmatrix} }_{B_s}\mathbf{u} + \underbrace{\begin{bmatrix}0\\I_p\end{bmatrix} }_{E_s}\mathbf{r}. \]
The servo feedback has the form \( \mathbf{u}=-K_x\mathbf{x}-K_i\boldsymbol{\eta} \), or equivalently \( \mathbf{u}=-K_s\mathbf{z} \) with \( \mathbf{z}=[\mathbf{x}^T,\boldsymbol{\eta}^T]^T \). The closed-loop augmented matrix is
\[ A_{s,cl}=A_s-B_sK_s. \]
When \( (A_s,B_s) \) is controllable or at least stabilizable, the eigenvalues of \( A_{s,cl} \) can be assigned or stabilized. A useful design condition for square or nonsquare plants with \( D=0 \) is that the plant has no transmission zero at the origin in the tracked channels:
\[ \operatorname{rank}\begin{bmatrix}A&B\\C&0\end{bmatrix}=n+p. \]
This condition says that the plant can support a nonzero constant output without requiring an impossible steady-state state-input pair. If the rank is deficient, the integrator introduces an uncontrollable mode at the origin and pole placement on the augmented system fails.
4. Ramp Tracking with Two Error Integrators
A ramp reference requires a double integrator in the tracking-error channel. For a SISO system with \( D=0 \), one convenient realization is
\[ \dot{\eta}_1=r-y,\qquad \dot{\eta}_2=\eta_1. \]
With \( z=[x^T,\eta_1,\eta_2]^T \), the ramp-augmented plant becomes
\[ \begin{bmatrix}\dot{x}\\\dot{\eta}_1\\\dot{\eta}_2\end{bmatrix} = \underbrace{\begin{bmatrix}A&0&0\\-C&0&0\\0&1&0\end{bmatrix} }_{A_r} \begin{bmatrix}x\\\eta_1\\\eta_2\end{bmatrix} + \underbrace{\begin{bmatrix}B\\0\\0\end{bmatrix} }_{B_r}u + \underbrace{\begin{bmatrix}0\\1\\0\end{bmatrix} }_{E_r}r. \]
The feedback \( u=-K_xx-k_1\eta_1-k_2\eta_2 \) is then designed so that \( A_r-B_rK_r \) is Hurwitz. In a MIMO problem, two integrator vectors are used, each in \( \mathbb{R}^p \):
\[ \dot{\boldsymbol{\eta} }_1=\mathbf{r}-\mathbf{y},\qquad \dot{\boldsymbol{\eta} }_2=\boldsymbol{\eta}_1. \]
Thus ramp tracking augments the plant order by \( 2p \). This cost is structural: eliminating ramp error demands more controller memory than eliminating step error.
5. Why the Number of Integrators Determines the Tracking Class
For stable closed-loop systems, the final value theorem gives
\[ \lim_{t\to\infty}e(t)=\lim_{s\to 0}sE(s), \]
whenever the limit exists. Suppose the reference-to-error transfer function has a zero of order \( q \) at the origin:
\[ E(s)=S(s)R(s),\qquad S(s)=s^q\bar{S}(s),\qquad \bar{S}(0)\neq 0. \]
For a step, \( R(s)=r_0/s \). Therefore
\[ \lim_{s\to 0}sE(s)=\lim_{s\to 0}r_0s^q\bar{S}(s)=0\qquad \text{if }q\ge 1. \]
For a ramp, \( R(s)=a/s^2 \). Therefore
\[ \lim_{s\to 0}sE(s)=\lim_{s\to 0}a s^{q-1}\bar{S}(s)=0\qquad \text{if }q\ge 2. \]
Integral augmentation creates these zeros at the origin in the sensitivity from reference to error, provided that the augmented system is internally stable and has no incompatible invariant zero at the origin.
6. Regulator Equations: The State-Space View of Steady-State Tracking
A constant reference can be represented by an exosystem \( \dot{w}=0 \), \( r=Fw \). Perfect steady-state tracking requires matrices \( \Pi \) and \( \Gamma \) satisfying
\[ A\Pi+B\Gamma=\Pi S, \qquad C\Pi+D\Gamma=F, \]
where \( S=0 \) for a step and \( S=\begin{bmatrix}0&1\\0&0\end{bmatrix} \) for a ramp generator. These are the regulator equations. For a step, they reduce to finding a steady-state pair \( (x_{ss},u_{ss}) \) satisfying
\[ Ax_{ss}+Bu_{ss}=0, \qquad Cx_{ss}+Du_{ss}=r_0. \]
For a ramp, the steady-state trajectory is not constant. Instead, the plant state and input must contain polynomial terms compatible with the ramp generator. The double-integrator controller supplies the required polynomial memory, while feedback assigns stable error dynamics around that forced trajectory.
flowchart TD
A["Choose reference class"] --> B{"Polynomial degree"}
B -->|"degree 0: step"| C["Use one error integrator"]
B -->|"degree 1: ramp"| D["Use two error integrators"]
C --> E["Form augmented pair (As, Bs)"]
D --> F["Form augmented pair (Ar, Br)"]
E --> G["Check rank / stabilizability"]
F --> G
G --> H["Assign augmented poles"]
H --> I["Simulate tracking and control effort"]
7. MIMO Tracking and Dimensional Constraints
In MIMO systems, the number of integrators is tied to the number of independently tracked output channels. For step tracking of \( p \) outputs, one uses \( p \) integrator states. For ramp tracking, one uses \( 2p \) integrator states. The augmented feedback gain dimensions are therefore
\[ K_s\in\mathbb{R}^{m\times(n+p)}, \qquad K_r\in\mathbb{R}^{m\times(n+2p)}. \]
The tracked output dimension cannot be chosen independently of the actuator authority. Even if the original plant is controllable, adding integrators can create uncontrollable modes when the plant has invariant zeros at the origin or insufficient DC gain rank. For step tracking with \( D=0 \), the Rosenbrock matrix condition is
\[ \operatorname{rank}\begin{bmatrix}A&B\\C&0\end{bmatrix}=n+p. \]
For ramp tracking, the same origin-zero obstruction remains, but the closed-loop design is more demanding because the augmented system has a longer integrator chain and more low-frequency gain.
8. Computational Implementations
The example plant used in all implementations is \( A=\begin{bmatrix}0&1\\-2&-3\end{bmatrix} \), \( B=\begin{bmatrix}0\\1\end{bmatrix} \), and \( C=\begin{bmatrix}1&0\end{bmatrix} \). The step augmented poles are \( \{-4,-5,-6\} \); the ramp augmented poles are \( \{-3,-4,-5,-6\} \).
Chapter26_Lesson4.py
Python libraries commonly used for this topic are numpy,
scipy.signal, scipy.integrate,
matplotlib, and optionally python-control for
state-space models and control design utilities.
"""
Chapter26_Lesson4.py
State-feedback with integral action for step and ramp reference inputs.
Required libraries:
pip install numpy scipy matplotlib
Optional control library for independent verification:
pip install control
"""
import numpy as np
from scipy.integrate import solve_ivp
from scipy.signal import place_poles
import matplotlib.pyplot as plt
def controllability_matrix(A: np.ndarray, B: np.ndarray) -> np.ndarray:
"""Return C = [B, AB, ..., A^(n-1)B]."""
n = A.shape[0]
blocks = [B]
Apow = np.eye(n)
for _ in range(1, n):
Apow = Apow @ A
blocks.append(Apow @ B)
return np.hstack(blocks)
def rank_report(name: str, A: np.ndarray, B: np.ndarray) -> None:
Ctrb = controllability_matrix(A, B)
print(f"{name}: rank(C) = {np.linalg.matrix_rank(Ctrb)} / {A.shape[0]}")
# SISO plant: x_dot = A x + B u, y = C x
A = np.array([[0.0, 1.0], [-2.0, -3.0]])
B = np.array([[0.0], [1.0]])
C = np.array([[1.0, 0.0]])
# ---------------------------------------------------------------------------
# 1) Step-reference tracking: one error integrator
# eta_dot = r - y = r - C x
# z = [x1, x2, eta]^T
# z_dot = A_step z + B_step u + E_step r
# u = -K_step z
# ---------------------------------------------------------------------------
A_step = np.block([
[A, np.zeros((2, 1))],
[-C, np.zeros((1, 1))]
])
B_step = np.vstack([B, [[0.0]]])
E_step = np.array([[0.0], [0.0], [1.0]])
rank_report("Step-augmented system", A_step, B_step)
poles_step = np.array([-4.0, -5.0, -6.0])
K_step = place_poles(A_step, B_step, poles_step).gain_matrix
print("K_step =", K_step)
def step_closed_loop(t: float, z: np.ndarray) -> np.ndarray:
r = 1.0
u = (-K_step @ z.reshape(-1, 1)).item()
dz = A_step @ z.reshape(-1, 1) + B_step * u + E_step * r
return dz.ravel()
# ---------------------------------------------------------------------------
# 2) Ramp-reference tracking: two error-integrator states
# eta1_dot = r - y, eta2_dot = eta1
# z = [x1, x2, eta1, eta2]^T
# u = -K_ramp z
# ---------------------------------------------------------------------------
A_ramp = np.block([
[A, np.zeros((2, 2))],
[-C, np.zeros((1, 2))],
[np.zeros((1, 2)), np.array([[1.0, 0.0]])]
])
B_ramp = np.vstack([B, [[0.0], [0.0]]])
E_ramp = np.array([[0.0], [0.0], [1.0], [0.0]])
rank_report("Ramp-augmented system", A_ramp, B_ramp)
poles_ramp = np.array([-3.0, -4.0, -5.0, -6.0])
K_ramp = place_poles(A_ramp, B_ramp, poles_ramp).gain_matrix
print("K_ramp =", K_ramp)
def ramp_closed_loop(t: float, z: np.ndarray) -> np.ndarray:
r = t
u = (-K_ramp @ z.reshape(-1, 1)).item()
dz = A_ramp @ z.reshape(-1, 1) + B_ramp * u + E_ramp * r
return dz.ravel()
def simulate_and_plot() -> None:
t_eval = np.linspace(0.0, 8.0, 801)
sol_step = solve_ivp(
step_closed_loop,
(t_eval[0], t_eval[-1]),
y0=np.zeros(3),
t_eval=t_eval,
rtol=1e-9,
atol=1e-11,
)
y_step = (C @ sol_step.y[:2, :]).ravel()
r_step = np.ones_like(t_eval)
sol_ramp = solve_ivp(
ramp_closed_loop,
(t_eval[0], t_eval[-1]),
y0=np.zeros(4),
t_eval=t_eval,
rtol=1e-9,
atol=1e-11,
)
y_ramp = (C @ sol_ramp.y[:2, :]).ravel()
r_ramp = t_eval
print("Final step error:", r_step[-1] - y_step[-1])
print("Final ramp error:", r_ramp[-1] - y_ramp[-1])
plt.figure()
plt.plot(t_eval, r_step, "--", label="step reference")
plt.plot(t_eval, y_step, label="output")
plt.xlabel("time [s]")
plt.ylabel("y(t)")
plt.title("Step tracking with one error integrator")
plt.grid(True)
plt.legend()
plt.figure()
plt.plot(t_eval, r_ramp, "--", label="ramp reference")
plt.plot(t_eval, y_ramp, label="output")
plt.xlabel("time [s]")
plt.ylabel("y(t)")
plt.title("Ramp tracking with two error integrators")
plt.grid(True)
plt.legend()
plt.show()
if __name__ == "__main__":
simulate_and_plot()
Chapter26_Lesson4.cpp
The C++ implementation below avoids external libraries and simulates the
closed-loop augmented equations by fourth-order Runge-Kutta integration.
In larger projects, the Eigen library is recommended for
matrix computations.
/*
Chapter26_Lesson4.cpp
From-scratch RK4 simulation of step and ramp tracking with integral action.
Compile:
g++ -std=c++17 -O2 Chapter26_Lesson4.cpp -o Chapter26_Lesson4
Run:
./Chapter26_Lesson4
*/
#include <array>
#include <cmath>
#include <iomanip>
#include <iostream>
using Vec3 = std::array<double, 3>;
using Vec4 = std::array<double, 4>;
Vec3 add3(const Vec3& a, const Vec3& b, double scale = 1.0) {
return {a[0] + scale * b[0], a[1] + scale * b[1], a[2] + scale * b[2]};
}
Vec4 add4(const Vec4& a, const Vec4& b, double scale = 1.0) {
return {a[0] + scale * b[0], a[1] + scale * b[1], a[2] + scale * b[2], a[3] + scale * b[3]};
}
Vec3 stepDynamics(double /*t*/, const Vec3& z) {
// Plant: x1_dot = x2, x2_dot = -2 x1 -3 x2 + u, y = x1
// Step controller from pole placement at {-4,-5,-6}:
// K_step = [72, 12, -120], u = -K_step z.
const double r = 1.0;
const double u = -72.0 * z[0] - 12.0 * z[1] + 120.0 * z[2];
const double x1dot = z[1];
const double x2dot = -2.0 * z[0] - 3.0 * z[1] + u;
const double etadot = r - z[0];
return {x1dot, x2dot, etadot};
}
Vec4 rampDynamics(double t, const Vec4& z) {
// Ramp controller from pole placement at {-3,-4,-5,-6}:
// K_ramp = [117, 15, -342, -360], u = -K_ramp z.
const double r = t;
const double u = -117.0 * z[0] - 15.0 * z[1] + 342.0 * z[2] + 360.0 * z[3];
const double x1dot = z[1];
const double x2dot = -2.0 * z[0] - 3.0 * z[1] + u;
const double eta1dot = r - z[0];
const double eta2dot = z[2];
return {x1dot, x2dot, eta1dot, eta2dot};
}
Vec3 rk4Step3(double t, const Vec3& z, double h) {
Vec3 k1 = stepDynamics(t, z);
Vec3 k2 = stepDynamics(t + h / 2.0, add3(z, k1, h / 2.0));
Vec3 k3 = stepDynamics(t + h / 2.0, add3(z, k2, h / 2.0));
Vec3 k4 = stepDynamics(t + h, add3(z, k3, h));
return {
z[0] + h * (k1[0] + 2.0 * k2[0] + 2.0 * k3[0] + k4[0]) / 6.0,
z[1] + h * (k1[1] + 2.0 * k2[1] + 2.0 * k3[1] + k4[1]) / 6.0,
z[2] + h * (k1[2] + 2.0 * k2[2] + 2.0 * k3[2] + k4[2]) / 6.0
};
}
Vec4 rk4Step4(double t, const Vec4& z, double h) {
Vec4 k1 = rampDynamics(t, z);
Vec4 k2 = rampDynamics(t + h / 2.0, add4(z, k1, h / 2.0));
Vec4 k3 = rampDynamics(t + h / 2.0, add4(z, k2, h / 2.0));
Vec4 k4 = rampDynamics(t + h, add4(z, k3, h));
return {
z[0] + h * (k1[0] + 2.0 * k2[0] + 2.0 * k3[0] + k4[0]) / 6.0,
z[1] + h * (k1[1] + 2.0 * k2[1] + 2.0 * k3[1] + k4[1]) / 6.0,
z[2] + h * (k1[2] + 2.0 * k2[2] + 2.0 * k3[2] + k4[2]) / 6.0,
z[3] + h * (k1[3] + 2.0 * k2[3] + 2.0 * k3[3] + k4[3]) / 6.0
};
}
int main() {
const double h = 0.001;
const double tf = 8.0;
const int steps = static_cast<int>(tf / h);
Vec3 zStep = {0.0, 0.0, 0.0};
Vec4 zRamp = {0.0, 0.0, 0.0, 0.0};
for (int k = 0; k < steps; ++k) {
const double t = k * h;
zStep = rk4Step3(t, zStep, h);
zRamp = rk4Step4(t, zRamp, h);
}
const double yStep = zStep[0];
const double yRamp = zRamp[0];
const double stepError = 1.0 - yStep;
const double rampError = tf - yRamp;
std::cout << std::setprecision(10);
std::cout << "Final step output y(tf) = " << yStep << "\n";
std::cout << "Final step error = " << stepError << "\n";
std::cout << "Final ramp output y(tf) = " << yRamp << "\n";
std::cout << "Final ramp error = " << rampError << "\n";
return 0;
}
Chapter26_Lesson4.java
The Java implementation is also written from scratch. For larger modern control projects, useful Java numerical libraries include EJML, Apache Commons Math, and ojAlgo.
/*
Chapter26_Lesson4.java
From-scratch RK4 simulation for step and ramp tracking with integral action.
Compile:
javac Chapter26_Lesson4.java
Run:
java Chapter26_Lesson4
*/
public class Chapter26_Lesson4 {
static double[] add(double[] a, double[] b, double scale) {
double[] c = new double[a.length];
for (int i = 0; i < a.length; i++) {
c[i] = a[i] + scale * b[i];
}
return c;
}
static double[] stepDynamics(double t, double[] z) {
double r = 1.0;
// K_step = [72, 12, -120], u = -K_step z.
double u = -72.0 * z[0] - 12.0 * z[1] + 120.0 * z[2];
double x1dot = z[1];
double x2dot = -2.0 * z[0] - 3.0 * z[1] + u;
double etadot = r - z[0];
return new double[] {x1dot, x2dot, etadot};
}
static double[] rampDynamics(double t, double[] z) {
double r = t;
// K_ramp = [117, 15, -342, -360], u = -K_ramp z.
double u = -117.0 * z[0] - 15.0 * z[1] + 342.0 * z[2] + 360.0 * z[3];
double x1dot = z[1];
double x2dot = -2.0 * z[0] - 3.0 * z[1] + u;
double eta1dot = r - z[0];
double eta2dot = z[2];
return new double[] {x1dot, x2dot, eta1dot, eta2dot};
}
static double[] rk4(double t, double[] z, double h, boolean ramp) {
double[] k1 = ramp ? rampDynamics(t, z) : stepDynamics(t, z);
double[] k2 = ramp ? rampDynamics(t + h / 2.0, add(z, k1, h / 2.0))
: stepDynamics(t + h / 2.0, add(z, k1, h / 2.0));
double[] k3 = ramp ? rampDynamics(t + h / 2.0, add(z, k2, h / 2.0))
: stepDynamics(t + h / 2.0, add(z, k2, h / 2.0));
double[] k4 = ramp ? rampDynamics(t + h, add(z, k3, h))
: stepDynamics(t + h, add(z, k3, h));
double[] out = new double[z.length];
for (int i = 0; i < z.length; i++) {
out[i] = z[i] + h * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]) / 6.0;
}
return out;
}
public static void main(String[] args) {
double h = 0.001;
double tf = 8.0;
int steps = (int) (tf / h);
double[] zStep = {0.0, 0.0, 0.0};
double[] zRamp = {0.0, 0.0, 0.0, 0.0};
for (int k = 0; k < steps; k++) {
double t = k * h;
zStep = rk4(t, zStep, h, false);
zRamp = rk4(t, zRamp, h, true);
}
double stepError = 1.0 - zStep[0];
double rampError = tf - zRamp[0];
System.out.printf("Final step output y(tf) = %.10f%n", zStep[0]);
System.out.printf("Final step error = %.10f%n", stepError);
System.out.printf("Final ramp output y(tf) = %.10f%n", zRamp[0]);
System.out.printf("Final ramp error = %.10f%n", rampError);
}
}
Chapter26_Lesson4.m
MATLAB/Simulink designs typically use ss,
ctrb, rank, place,
acker, and lsim. In Simulink, the same
controller is implemented using State-Space, Sum, Integrator, Gain, and
Scope blocks.
% Chapter26_Lesson4.m
% State-feedback with integral action for step and ramp reference inputs.
% Required MATLAB toolboxes: Control System Toolbox for place(), ss(), lsim().
% The augmented matrices are also shown explicitly for implementation clarity.
clear; clc; close all;
A = [0 1; -2 -3];
B = [0; 1];
C = [1 0];
%% Step reference: one error integrator, eta_dot = r - y
A_step = [A zeros(2,1); -C 0];
B_step = [B; 0];
E_step = [0; 0; 1];
fprintf('rank ctrb step augmented = %d / %d\n', rank(ctrb(A_step, B_step)), size(A_step,1));
K_step = place(A_step, B_step, [-4 -5 -6]);
disp('K_step = '); disp(K_step);
Acl_step = A_step - B_step*K_step;
Bcl_step = E_step;
Ccl_step = [C 0];
Dcl_step = 0;
sys_step = ss(Acl_step, Bcl_step, Ccl_step, Dcl_step);
%% Ramp reference: two error-integrator states
% eta1_dot = r - y, eta2_dot = eta1
A_ramp = [A zeros(2,2); -C 0 0; 0 0 1 0];
B_ramp = [B; 0; 0];
E_ramp = [0; 0; 1; 0];
fprintf('rank ctrb ramp augmented = %d / %d\n', rank(ctrb(A_ramp, B_ramp)), size(A_ramp,1));
K_ramp = place(A_ramp, B_ramp, [-3 -4 -5 -6]);
disp('K_ramp = '); disp(K_ramp);
Acl_ramp = A_ramp - B_ramp*K_ramp;
Bcl_ramp = E_ramp;
Ccl_ramp = [C 0 0];
Dcl_ramp = 0;
sys_ramp = ss(Acl_ramp, Bcl_ramp, Ccl_ramp, Dcl_ramp);
%% Simulation
T = linspace(0, 8, 801).';
r_step = ones(size(T));
r_ramp = T;
y_step = lsim(sys_step, r_step, T);
y_ramp = lsim(sys_ramp, r_ramp, T);
fprintf('Final step error = %.10g\n', r_step(end) - y_step(end));
fprintf('Final ramp error = %.10g\n', r_ramp(end) - y_ramp(end));
figure;
plot(T, r_step, '--', T, y_step, 'LineWidth', 1.5);
grid on; xlabel('time [s]'); ylabel('y(t)');
title('Step tracking with one error integrator');
legend('reference', 'output', 'Location', 'best');
figure;
plot(T, r_ramp, '--', T, y_ramp, 'LineWidth', 1.5);
grid on; xlabel('time [s]'); ylabel('y(t)');
title('Ramp tracking with two error integrators');
legend('reference', 'output', 'Location', 'best');
%% Simulink construction notes
% 1. Put the plant in a State-Space block with matrices A, B, C, D = 0.
% 2. For step tracking, feed e = r - y into one Integrator block eta.
% 3. Implement u = -K_step*[x1; x2; eta] using a Gain block.
% 4. For ramp tracking, cascade two Integrator blocks driven by e:
% eta1_dot = e, eta2_dot = eta1.
% 5. Implement u = -K_ramp*[x1; x2; eta1; eta2].
Chapter26_Lesson4.nb
Wolfram Mathematica is useful for symbolic Ackermann calculations, controllability checks, and exact manipulation of augmented matrices.
Notebook[{
Cell["Chapter26 Lesson4: Step and Ramp Reference Inputs", "Title"],
Cell["This notebook constructs augmented state-space systems with one and two error integrators, places the closed-loop poles, and simulates step and ramp tracking.", "Text"],
Cell["ClearAll[\"Global`*\"];\n\nA = { {0, 1}, {-2, -3} };\nB = { {0}, {1} };\nCmat = { {1, 0} };\n\nControllabilityMatrix[A_, B_] := Module[{n = Length[A]},\n ArrayFlatten[{Table[MatrixPower[A, k].B, {k, 0, n - 1}]}]\n];\n\nAckermannGain[A_, B_, poles_] := Module[{n, coeff, phiA, Ctrb, en},\n n = Length[A];\n coeff = CoefficientList[Expand[Times @@ (s - # & /@ poles)], s];\n phiA = MatrixPower[A, n];\n Do[phiA = phiA + coeff[[k + 1]] MatrixPower[A, k], {k, 0, n - 1}];\n Ctrb = ControllabilityMatrix[A, B];\n en = UnitVector[n, n];\n {en}.Inverse[Ctrb].phiA\n];\n\n(* Step reference: etaDot = r - y *)\nAstep = ArrayFlatten[{ {A, ConstantArray[0, {2, 1}]}, {-Cmat, { {0} } } }];\nBstep = Join[B, { {0} }];\nEstep = { {0}, {0}, {1} };\nKstep = AckermannGain[Astep, Bstep, {-4, -5, -6}];\nEigenvalues[Astep - Bstep.Kstep]\n\n(* Ramp reference: eta1Dot = r - y, eta2Dot = eta1 *)\nAramp = ArrayFlatten[{ {A, ConstantArray[0, {2, 2}]}, {-Cmat, { {0, 0} } }, {ConstantArray[0, {1, 2}], { {1, 0} } } }];\nBramp = Join[B, { {0}, {0} }];\nEramp = { {0}, {0}, {1}, {0} };\nKramp = AckermannGain[Aramp, Bramp, {-3, -4, -5, -6}];\nEigenvalues[Aramp - Bramp.Kramp]\n\n(* Simulate by solving the augmented closed-loop equations. *)\nstepSol = NDSolveValue[{\n z1'[t] == z2[t],\n z2'[t] == -2 z1[t] - 3 z2[t] - (Kstep[[1, 1]] z1[t] + Kstep[[1, 2]] z2[t] + Kstep[[1, 3]] eta[t]),\n eta'[t] == 1 - z1[t],\n z1[0] == 0, z2[0] == 0, eta[0] == 0},\n {z1, z2, eta}, {t, 0, 8}];\n\nrampSol = NDSolveValue[{\n x1'[t] == x2[t],\n x2'[t] == -2 x1[t] - 3 x2[t] - (Kramp[[1, 1]] x1[t] + Kramp[[1, 2]] x2[t] + Kramp[[1, 3]] eta1[t] + Kramp[[1, 4]] eta2[t]),\n eta1'[t] == t - x1[t],\n eta2'[t] == eta1[t],\n x1[0] == 0, x2[0] == 0, eta1[0] == 0, eta2[0] == 0},\n {x1, x2, eta1, eta2}, {t, 0, 8}];\n\nPrint[\"Kstep = \", Kstep];\nPrint[\"Kramp = \", Kramp];\nPrint[\"Final step error = \", 1 - stepSol[[1]][8]];\nPrint[\"Final ramp error = \", 8 - rampSol[[1]][8]];\n\nPlot[{1, stepSol[[1]][t]}, {t, 0, 8}, PlotLegends -> {\"reference\", \"output\"}, PlotLabel -> \"Step tracking\"]\nPlot[{t, rampSol[[1]][t]}, {t, 0, 8}, PlotLegends -> {\"reference\", \"output\"}, PlotLabel -> \"Ramp tracking\"]", "Input"]
}]
9. Problems and Solutions
Problem 1 (Step Internal Model): A stable SISO plant is controlled by state feedback but has nonzero steady-state error to a unit step. Explain why adding one error integrator can remove this error, assuming the augmented system is stabilizable.
Solution: The unit step has Laplace transform \( 1/s \). One error integrator embeds a pole at the origin in the controller. After feedback stabilizes the augmented closed-loop system, the reference-to-error transfer function obtains a zero at the origin. Therefore
\[ \lim_{t\to\infty}e(t)=\lim_{s\to 0}sS(s)\dfrac{1}{s} =\lim_{s\to 0}S(s)=0, \]
because the augmented servo design makes \( S(0)=0 \).
Problem 2 (Ramp Tracking): Show that a controller with only one integrator generally cannot eliminate steady-state error to a ramp reference \( r(t)=at \).
Solution: If one integrator creates only a first-order zero in the reference-to-error transfer function, then \( S(s)=s\bar{S}(s) \). Since \( R(s)=a/s^2 \),
\[ \lim_{s\to 0}sE(s)=\lim_{s\to 0}s\,s\bar{S}(s)\dfrac{a}{s^2} =a\bar{S}(0), \]
which is generally nonzero. A second integrator gives \( S(s)=s^2\bar{S}(s) \), which makes the final value zero.
Problem 3 (Augmented Matrix Construction): For the plant \( \dot{x}=Ax+Bu \), \( y=Cx \), construct the augmented matrices for ramp tracking.
Solution: Use \( \dot{\eta}_1=r-Cx \) and \( \dot{\eta}_2=\eta_1 \). The augmented model is
\[ A_r=\begin{bmatrix}A&0&0\\-C&0&0\\0&1&0\end{bmatrix}, \qquad B_r=\begin{bmatrix}B\\0\\0\end{bmatrix}, \qquad E_r=\begin{bmatrix}0\\1\\0\end{bmatrix}. \]
Then choose \( u=-K_rz \) so that \( A_r-B_rK_r \) is Hurwitz.
Problem 4 (Origin Zero Obstruction): Explain the meaning of \( \operatorname{rank}\begin{bmatrix}A&B\\C&0\end{bmatrix}=n+p \) in step tracking.
Solution: The matrix equation \( Ax_{ss}+Bu_{ss}=0 \), \( Cx_{ss}=r_0 \) must be solvable for every constant reference in the tracked subspace. The rank condition states that the plant has no invariant zero at the origin in those output channels. If the condition fails, the integrator state introduces an uncontrollable origin mode, so the servo poles cannot all be moved into the left half plane.
Problem 5 (Controller Order): A two-output plant must track independent ramp references in both outputs. How many integrator states must be added, and what is the dimension of the augmented state?
Solution: A ramp requires two integrators per tracked output. With \( p=2 \), the controller needs \( 2p=4 \) integrator states. If the plant has order \( n \), the augmented state dimension is \( n+4 \).
10. Summary
Step tracking and ramp tracking differ not merely in reference amplitude but in reference dynamics. A step reference requires one error integrator; a ramp reference requires two. After augmentation, state feedback is applied to the enlarged system, and successful design requires the augmented pair to be stabilizable and free of incompatible origin-zero obstructions. The final value theorem and regulator equations provide complementary proofs of why the correct internal model eliminates steady-state tracking error.
11. References
- Francis, B.A., & Wonham, W.M. (1976). The internal model principle of control theory. Automatica, 12(5), 457–465.
- Davison, E.J. (1976). The robust control of a servomechanism problem for linear time-invariant multivariable systems. IEEE Transactions on Automatic Control, 21(1), 25–34.
- Francis, B.A. (1977). The linear multivariable regulator problem. SIAM Journal on Control and Optimization, 15(3), 486–505.
- Wonham, W.M. (1974). Linear multivariable control: a geometric approach. Lecture Notes in Economics and Mathematical Systems, 101.
- Hautus, M.L.J. (1983). Strong detectability and observers. Linear Algebra and its Applications, 50, 353–368.
- Desoer, C.A., & Wang, Y.T. (1980). On the generalized Nyquist stability criterion. IEEE Transactions on Automatic Control, 25(2), 187–196.
- Byrnes, C.I., & Isidori, A. (1991). Asymptotic stabilization of minimum phase nonlinear systems. IEEE Transactions on Automatic Control, 36(10), 1122–1137.