Chapter 26: State-Feedback with Integral Action
Lesson 3: Design of State-Feedback Gains for the Augmented System
This lesson develops the gain-design problem for state feedback with integral action. Starting from an augmented state containing both the plant state and the integral of tracking error, we derive the augmented closed-loop matrix, controllability conditions, pole-placement design equations, and steady-state tracking proof. Implementation examples are given in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica.
1. Servo Objective and Augmented State
Consider the continuous-time LTI plant \( \dot{x}=Ax+Bu \), \( y=Cx+Du \), where \( x\in\mathbb{R}^{n} \), \( u\in\mathbb{R}^{m} \), and \( y\in\mathbb{R}^{p} \). The tracking objective is to make selected outputs follow a constant reference \( r\in\mathbb{R}^{p} \) despite plant initial conditions and constant matched disturbances.
Integral action introduces an additional state \( z\in\mathbb{R}^{p} \) defined by the accumulated tracking error:
\[ \dot{z}=r-y=r-Cx-Du. \]
Define the augmented state \( x_a=\begin{bmatrix}x^{\top}&z^{\top}\end{bmatrix}^{\top} \). Then the augmented dynamics can be written as
\[ \dot{x}_a=A_a x_a+B_a u+E_a r, \]
\[ A_a= \begin{bmatrix} A & 0\\ -C & 0 \end{bmatrix}, \qquad B_a= \begin{bmatrix} B\\ -D \end{bmatrix}, \qquad E_a= \begin{bmatrix} 0\\ I_p \end{bmatrix}. \]
In many introductory servo-design problems, \( D=0 \), so \( B_a=\begin{bmatrix}B^{\top}&0\end{bmatrix}^{\top} \). The integral state is not an artificial tuning variable; it is a dynamic memory of output error. Therefore the controller has one additional dynamic mode for each integrated output.
flowchart TD
R["Reference r"] --> SUM["Tracking error e = r - y"]
Y["Output y"] --> SUM
SUM --> INT["Integrator: z_dot = e"]
X["Plant state x"] --> CTRL["Control law: u = -Kx - Ki z"]
INT --> CTRL
CTRL --> PLANT["Plant: x_dot = Ax + Bu"]
PLANT --> Y
PLANT --> X
2. Feedback Structure and Closed-Loop Matrix
The augmented state-feedback law is chosen as
\[ u=-Kx-K_i z=-K_a x_a, \qquad K_a=\begin{bmatrix}K&K_i\end{bmatrix}. \]
Substitution into the augmented model gives
\[ \dot{x}_a=(A_a-B_aK_a)x_a+E_a r. \]
Therefore the gain-design problem is to select \( K_a \) such that the eigenvalues of \( A_a-B_aK_a \) satisfy the desired transient and stability specifications:
\[ \operatorname{spec}(A_a-B_aK_a)= \{\lambda_1,\lambda_2,\ldots,\lambda_{n+p}\}, \qquad \operatorname{Re}(\lambda_i)<0. \]
The integral gain \( K_i \) should not be treated as independent from \( K \). Both gains must be designed together because the augmented closed-loop characteristic polynomial is determined by the full matrix \( A_a-B_aK_a \).
3. Controllability Conditions for the Augmented Pair
Complete arbitrary pole assignment for the augmented system requires controllability of \( (A_a,B_a) \). The Kalman controllability matrix is
\[ \mathcal{C}_a= \begin{bmatrix} B_a&A_aB_a&A_a^2B_a&\cdots&A_a^{n+p-1}B_a \end{bmatrix}. \]
The rank test is
\[ \operatorname{rank}(\mathcal{C}_a)=n+p. \]
Equivalently, by the PBH test, the augmented pair is controllable if and only if
\[ \operatorname{rank} \begin{bmatrix} \lambda I_n-A & 0 & B\\ C & \lambda I_p & D \end{bmatrix} =n+p, \qquad \forall \lambda\in\mathbb{C}. \]
The most important special case is the condition at \( \lambda=0 \). For \( D=0 \), this becomes
\[ \operatorname{rank} \begin{bmatrix} -A & B\\ C & 0 \end{bmatrix} =n+p. \]
This condition says that the plant must not have a transmission zero at the origin in the channels being integrated. If the plant has an invariant zero at zero, then the integrator introduces an internal mode that cannot be assigned by state feedback.
Proof sketch. Apply the PBH test to \( (A_a,B_a) \):
\[ \operatorname{rank} \begin{bmatrix} \lambda I_{n+p}-A_a&B_a \end{bmatrix} =n+p. \]
Substituting the definitions of \( A_a \) and \( B_a \) gives the block matrix above. At \( \lambda=0 \), the lower block loses the direct integrator dynamics, so solvability depends on the plant equations and output map. The resulting rank condition is exactly the Rosenbrock system-matrix condition at the origin.
4. Pole-Placement Design for the Augmented System
When \( (A_a,B_a) \) is controllable, a gain \( K_a \) can be selected so that
\[ \det(sI_{n+p}-(A_a-B_aK_a))=\phi_d(s), \]
where the desired monic characteristic polynomial is
\[ \phi_d(s)= \prod_{j=1}^{n+p}(s-\lambda_j) = s^{n+p}+\alpha_{n+p-1}s^{n+p-1}+\cdots+\alpha_1s+\alpha_0. \]
For a SISO augmented system, Ackermann's formula can be applied directly to \( (A_a,B_a) \):
\[ K_a=e_{n+p}^{\top}\mathcal{C}_a^{-1}\phi_d(A_a), \]
where \( e_{n+p}^{\top}=\begin{bmatrix}0&0&\cdots&1\end{bmatrix} \). The polynomial matrix is
\[ \phi_d(A_a)=A_a^{n+p} +\alpha_{n+p-1}A_a^{n+p-1} +\cdots+\alpha_1A_a+\alpha_0I_{n+p}. \]
For MIMO systems, pole placement has additional degrees of freedom. A numerical algorithm such as Kautsky-Nichols-Van Dooren pole assignment is usually preferred over an explicit symbolic formula because it can improve conditioning and exploit eigenvector freedom.
flowchart TD
A["Plant matrices A, B, C, D"] --> B["Choose integrated outputs"]
B --> C["Build augmented matrices Aa, Ba, Ea"]
C --> D["Check rank of augmented controllability matrix"]
D --> E{"Rank equals n+p?"}
E -->|"No"| F["Redesign outputs, actuators, \nor tracking objective"]
E -->|"Yes"| G["Choose desired closed-loop poles"]
G --> H["Compute augmented gain Ka = [K Ki]"]
H --> I["Verify eigenvalues and simulate tracking"]
5. Steady-State Tracking Proof
Assume \( A_a-B_aK_a \) is Hurwitz and the reference \( r \) is constant. The closed-loop augmented system is
\[ \dot{x}_a=A_{cl}x_a+E_a r,\qquad A_{cl}=A_a-B_aK_a. \]
Since \( A_{cl} \) is Hurwitz, the steady state exists and satisfies
\[ 0=A_{cl}x_{a,ss}+E_a r. \]
The last \( p \) rows of the original augmented dynamics are
\[ \dot{z}=r-y. \]
At steady state, \( \dot{z}_{ss}=0 \). Therefore
\[ 0=r-y_{ss},\qquad y_{ss}=r. \]
Hence, for constant references, stable augmented feedback forces zero steady-state tracking error:
\[ e_{ss}=r-y_{ss}=0. \]
This is the central servo benefit of integral action. Pole placement determines transient behavior, while the integrator imposes the steady-state constraint.
6. Choosing Desired Poles
The augmented system has \( n+p \) closed-loop poles. The designer must assign the original plant-related modes and the additional integral modes. A common guideline is:
\[ \{\lambda_1,\ldots,\lambda_{n+p}\} = \{\text{dominant plant-shaping poles}\} \cup \{\text{faster non-dominant integral poles}\}. \]
Integral poles should not be chosen excessively fast. Very fast integral dynamics can produce large control effort and noise sensitivity. Very slow integral poles remove steady-state error slowly. Thus, pole selection is a trade-off among tracking speed, overshoot, actuator effort, and robustness.
For a dominant second-order pair, one may use the usual linear-control mapping
\[ s^2+2\zeta\omega_n s+\omega_n^2=0, \qquad \lambda_{1,2}=-\zeta\omega_n \pm j\omega_n\sqrt{1-\zeta^2}. \]
The remaining poles are placed farther left, but not so far that the resulting feedback gain becomes numerically large or physically infeasible.
7. Numerical Example
Consider a mass-spring-damper plant with position output:
\[ A= \begin{bmatrix} 0&1\\ -2&-0.6 \end{bmatrix}, \qquad B= \begin{bmatrix} 0\\ 1 \end{bmatrix}, \qquad C= \begin{bmatrix} 1&0 \end{bmatrix}, \qquad D=0. \]
With one integrated output, the augmented matrices are
\[ A_a= \begin{bmatrix} 0&1&0\\ -2&-0.6&0\\ -1&0&0 \end{bmatrix}, \qquad B_a= \begin{bmatrix} 0\\ 1\\ 0 \end{bmatrix}. \]
Choose desired poles \( \{-2,-2.5,-3\} \). The gain \( K_a=\begin{bmatrix}K&K_i\end{bmatrix} \) is selected so that
\[ \det(sI_3-(A_a-B_aK_a)) = (s+2)(s+2.5)(s+3). \]
The resulting closed-loop response tracks a unit step reference with zero steady-state position error, provided the closed-loop poles remain in the open left-half plane.
8. Python Implementation
The Python implementation uses NumPy, SciPy,
and Matplotlib. The function
scipy.signal.place_poles is used for numerical pole
placement.
Chapter26_Lesson3.py
"""
Chapter26_Lesson3.py
Design of state-feedback gains for an augmented system with integral action.
Required libraries:
pip install numpy scipy matplotlib
Optional control-system library:
pip install control
"""
import numpy as np
from scipy.signal import place_poles
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
def build_augmented_system(A, B, C, D=None):
"""Build A_aug and B_aug for z_dot = r - y, y = Cx + Du."""
A = np.asarray(A, dtype=float)
B = np.asarray(B, dtype=float)
C = np.asarray(C, dtype=float)
n = A.shape[0]
p = C.shape[0]
if D is None:
D = np.zeros((p, B.shape[1]))
D = np.asarray(D, dtype=float)
A_aug = np.block([
[A, np.zeros((n, p))],
[-C, np.zeros((p, p))]
])
B_aug = np.vstack((B, -D))
E_aug = np.vstack((np.zeros((n, p)), np.eye(p)))
return A_aug, B_aug, E_aug
def controllability_matrix(A, B):
"""Kalman controllability matrix [B AB ... A^(n-1)B]."""
n = A.shape[0]
return np.hstack([np.linalg.matrix_power(A, k) @ B for k in range(n)])
def main():
A = np.array([[0.0, 1.0], [-2.0, -0.6]])
B = np.array([[0.0], [1.0]])
C = np.array([[1.0, 0.0]])
D = np.array([[0.0]])
A_aug, B_aug, E_aug = build_augmented_system(A, B, C, D)
Co = controllability_matrix(A_aug, B_aug)
rank_co = np.linalg.matrix_rank(Co)
print("Augmented controllability rank:", rank_co, "of", A_aug.shape[0])
desired_poles = np.array([-2.0, -2.5, -3.0])
placed = place_poles(A_aug, B_aug, desired_poles)
K_aug = placed.gain_matrix
Kx = K_aug[:, :A.shape[0]]
Ki = K_aug[:, A.shape[0]:]
print("K_aug =", K_aug)
print("Kx =", Kx)
print("Ki =", Ki)
print("Closed-loop eigenvalues =", np.linalg.eigvals(A_aug - B_aug @ K_aug))
r = np.array([1.0])
def closed_loop_ode(t, xa):
xa = xa.reshape(-1, 1)
dxa = (A_aug - B_aug @ K_aug) @ xa + E_aug @ r.reshape(-1, 1)
return dxa.ravel()
t_eval = np.linspace(0.0, 8.0, 800)
sol = solve_ivp(closed_loop_ode, (t_eval[0], t_eval[-1]), np.zeros(3), t_eval=t_eval)
x = sol.y[:2, :]
z = sol.y[2, :]
y = (C @ x).ravel()
u = (-K_aug @ sol.y).ravel()
e = r[0] - y
print("Final output:", y[-1])
print("Final tracking error:", e[-1])
print("Final integrator state:", z[-1])
plt.figure()
plt.plot(sol.t, y, label="output y")
plt.plot(sol.t, np.ones_like(sol.t) * r[0], "--", label="reference r")
plt.xlabel("time [s]")
plt.ylabel("position")
plt.grid(True)
plt.legend()
plt.title("State feedback with integral action")
plt.show()
plt.figure()
plt.plot(sol.t, u, label="control input u")
plt.xlabel("time [s]")
plt.ylabel("force")
plt.grid(True)
plt.legend()
plt.title("Control effort")
plt.show()
if __name__ == "__main__":
main()
9. C++ Implementation
The C++ implementation uses the header-only Eigen library.
Because Eigen does not provide a built-in pole-placement routine, this
example implements SISO Ackermann placement for the augmented system.
Chapter26_Lesson3.cpp
/*
Chapter26_Lesson3.cpp
SISO augmented-state pole placement with integral action using Eigen.
Build example:
g++ -std=c++17 Chapter26_Lesson3.cpp -I /path/to/eigen -O2 -o Chapter26_Lesson3
Dependency:
Eigen 3.x, header-only: https://eigen.tuxfamily.org
*/
#include <Eigen/Dense>
#include <iostream>
#include <vector>
using Eigen::MatrixXd;
using Eigen::RowVectorXd;
MatrixXd controllabilityMatrix(const MatrixXd& A, const MatrixXd& B) {
int n = static_cast<int>(A.rows());
int m = static_cast<int>(B.cols());
MatrixXd Co(n, n * m);
MatrixXd Ak = MatrixXd::Identity(n, n);
for (int k = 0; k < n; ++k) {
Co.block(0, k * m, n, m) = Ak * B;
Ak = Ak * A;
}
return Co;
}
std::vector<double> polynomialFromRoots(const std::vector<double>& roots) {
std::vector<double> coeff{1.0};
for (double r : roots) {
std::vector<double> next(coeff.size() + 1, 0.0);
for (std::size_t i = 0; i < coeff.size(); ++i) {
next[i] += coeff[i];
next[i + 1] += -r * coeff[i];
}
coeff = next;
}
return coeff;
}
RowVectorXd ackermannSISO(const MatrixXd& A, const MatrixXd& B,
const std::vector<double>& desiredPoles) {
int n = static_cast<int>(A.rows());
MatrixXd Co = controllabilityMatrix(A, B);
MatrixXd CoInv = Co.inverse();
std::vector<double> coeff = polynomialFromRoots(desiredPoles);
MatrixXd phiA = MatrixXd::Zero(n, n);
std::vector<MatrixXd> powers(n + 1);
powers[0] = MatrixXd::Identity(n, n);
for (int k = 1; k <= n; ++k) {
powers[k] = powers[k - 1] * A;
}
for (int power = n; power >= 0; --power) {
double c = coeff[n - power];
phiA += c * powers[power];
}
RowVectorXd en = RowVectorXd::Zero(n);
en(n - 1) = 1.0;
return en * CoInv * phiA;
}
int main() {
MatrixXd A(2, 2);
A << 0.0, 1.0,
-2.0, -0.6;
MatrixXd B(2, 1);
B << 0.0,
1.0;
MatrixXd C(1, 2);
C << 1.0, 0.0;
MatrixXd D(1, 1);
D << 0.0;
int n = 2;
int p = 1;
MatrixXd Aaug = MatrixXd::Zero(n + p, n + p);
Aaug.block(0, 0, n, n) = A;
Aaug.block(n, 0, p, n) = -C;
MatrixXd Baug(n + p, 1);
Baug.block(0, 0, n, 1) = B;
Baug.block(n, 0, p, 1) = -D;
MatrixXd Co = controllabilityMatrix(Aaug, Baug);
std::cout << "Controllability rank estimate: "
<< Co.fullPivLu().rank() << " of " << Aaug.rows() << "\n";
std::vector<double> poles{-2.0, -2.5, -3.0};
RowVectorXd Kaug = ackermannSISO(Aaug, Baug, poles);
std::cout << "K_aug = " << Kaug << "\n";
std::cout << "Kx = " << Kaug.leftCols(n) << "\n";
std::cout << "Ki = " << Kaug.rightCols(p) << "\n";
MatrixXd Acl = Aaug - Baug * Kaug;
std::cout << "A_cl =\n" << Acl << "\n";
return 0;
}
10. Java Implementation
The Java implementation below uses from-scratch matrix utilities so that the pole-placement algebra is visible to students. For larger systems, use numerical libraries such as EJML, Apache Commons Math, or ojAlgo.
Chapter26_Lesson3.java
/*
Chapter26_Lesson3.java
SISO augmented-state pole placement with integral action.
Compile and run:
javac Chapter26_Lesson3.java
java Chapter26_Lesson3
*/
import java.util.Arrays;
public class Chapter26_Lesson3 {
static double[][] multiply(double[][] A, double[][] B) {
int r = A.length, c = B[0].length, inner = B.length;
double[][] M = new double[r][c];
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
for (int k = 0; k < inner; k++) M[i][j] += A[i][k] * B[k][j];
}
}
return M;
}
static double[][] add(double[][] A, double[][] B) {
double[][] M = new double[A.length][A[0].length];
for (int i = 0; i < A.length; i++)
for (int j = 0; j < A[0].length; j++) M[i][j] = A[i][j] + B[i][j];
return M;
}
static double[][] scale(double s, double[][] A) {
double[][] M = new double[A.length][A[0].length];
for (int i = 0; i < A.length; i++)
for (int j = 0; j < A[0].length; j++) M[i][j] = s * A[i][j];
return M;
}
static double[][] identity(int n) {
double[][] I = new double[n][n];
for (int i = 0; i < n; i++) I[i][i] = 1.0;
return I;
}
static double[][] inverse(double[][] A) {
int n = A.length;
double[][] aug = new double[n][2 * n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) aug[i][j] = A[i][j];
aug[i][n + i] = 1.0;
}
for (int col = 0; col < n; col++) {
int pivot = col;
for (int r = col + 1; r < n; r++)
if (Math.abs(aug[r][col]) > Math.abs(aug[pivot][col])) pivot = r;
double[] tmp = aug[col];
aug[col] = aug[pivot];
aug[pivot] = tmp;
double div = aug[col][col];
if (Math.abs(div) < 1e-12) throw new IllegalArgumentException("Singular matrix");
for (int j = 0; j < 2 * n; j++) aug[col][j] /= div;
for (int r = 0; r < n; r++) if (r != col) {
double factor = aug[r][col];
for (int j = 0; j < 2 * n; j++) aug[r][j] -= factor * aug[col][j];
}
}
double[][] inv = new double[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) inv[i][j] = aug[i][n + j];
return inv;
}
static double[] polynomialFromRoots(double[] roots) {
double[] coeff = {1.0};
for (double r : roots) {
double[] next = new double[coeff.length + 1];
for (int i = 0; i < coeff.length; i++) {
next[i] += coeff[i];
next[i + 1] += -r * coeff[i];
}
coeff = next;
}
return coeff;
}
static double[][] controllabilityMatrix(double[][] A, double[][] B) {
int n = A.length;
double[][] H = new double[n][n];
double[][] Ak = identity(n);
for (int block = 0; block < n; block++) {
double[][] col = multiply(Ak, B);
for (int i = 0; i < n; i++) H[i][block] = col[i][0];
Ak = multiply(Ak, A);
}
return H;
}
static double[][] ackermannSISO(double[][] A, double[][] B, double[] desiredPoles) {
int n = A.length;
double[][] CoInv = inverse(controllabilityMatrix(A, B));
double[] coeff = polynomialFromRoots(desiredPoles);
double[][][] powers = new double[n + 1][][];
powers[0] = identity(n);
for (int k = 1; k <= n; k++) powers[k] = multiply(powers[k - 1], A);
double[][] phiA = new double[n][n];
for (int power = n; power >= 0; power--) {
phiA = add(phiA, scale(coeff[n - power], powers[power]));
}
double[][] en = new double[1][n];
en[0][n - 1] = 1.0;
return multiply(multiply(en, CoInv), phiA);
}
public static void main(String[] args) {
double[][] Aaug = {
{0.0, 1.0, 0.0},
{-2.0, -0.6, 0.0},
{-1.0, 0.0, 0.0}
};
double[][] Baug = { {0.0}, {1.0}, {0.0} };
double[] poles = {-2.0, -2.5, -3.0};
double[][] K = ackermannSISO(Aaug, Baug, poles);
System.out.println("K_aug = " + Arrays.deepToString(K));
System.out.println("Kx = [" + K[0][0] + ", " + K[0][1] + "]");
System.out.println("Ki = " + K[0][2]);
}
}
11. MATLAB and Simulink Implementation
MATLAB provides direct support through ctrb,
place, ss, and lsim. The same
script also creates a minimal Simulink model for the augmented
closed-loop system.
Chapter26_Lesson3.m
% Chapter26_Lesson3.m
% Design of state-feedback gains for an augmented system with integral action.
% Requires Control System Toolbox for ctrb, place, ss, lsim.
clear; clc; close all;
A = [0 1; -2 -0.6];
B = [0; 1];
C = [1 0];
D = 0;
n = size(A,1);
p = size(C,1);
Aaug = [A zeros(n,p); -C zeros(p,p)];
Baug = [B; -D];
Eaug = [zeros(n,p); eye(p)];
Co = ctrb(Aaug, Baug);
fprintf('Augmented controllability rank: %d of %d\n', rank(Co), n+p);
poles = [-2 -2.5 -3];
Kaug = place(Aaug, Baug, poles);
Kx = Kaug(:,1:n);
Ki = Kaug(:,n+1:end);
Acl = Aaug - Baug*Kaug;
fprintf('Kaug = '); disp(Kaug);
fprintf('Kx = '); disp(Kx);
fprintf('Ki = '); disp(Ki);
fprintf('Closed-loop eigenvalues:\n'); disp(eig(Acl));
sys_cl = ss(Acl, Eaug, [C zeros(p,p); -Kaug], [zeros(p,p); zeros(size(B,2),p)]);
t = linspace(0,8,800);
r = ones(size(t));
resp = lsim(sys_cl, r, t);
y = resp(:,1);
u = resp(:,2);
figure;
plot(t, y, 'LineWidth', 1.5); hold on;
plot(t, r, '--', 'LineWidth', 1.0);
grid on; xlabel('time [s]'); ylabel('output');
legend('y','r'); title('State feedback with integral action');
figure;
plot(t, u, 'LineWidth', 1.5);
grid on; xlabel('time [s]'); ylabel('u');
title('Control effort');
model = 'Chapter26_Lesson3_Simulink';
if bdIsLoaded(model)
close_system(model,0);
end
new_system(model);
open_system(model);
add_block('simulink/Sources/Step', [model '/reference_step'], 'Position', [80 80 120 110]);
add_block('simulink/Continuous/State-Space', [model '/augmented_closed_loop'], 'Position', [200 60 360 130]);
set_param([model '/augmented_closed_loop'], ...
'A', mat2str(Acl), 'B', mat2str(Eaug), ...
'C', mat2str([C zeros(p,p); -Kaug]), ...
'D', mat2str(zeros(p+size(B,2),p)));
add_block('simulink/Sinks/Scope', [model '/scope_y_u'], 'Position', [430 70 470 120]);
add_line(model, 'reference_step/1', 'augmented_closed_loop/1');
add_line(model, 'augmented_closed_loop/1', 'scope_y_u/1');
save_system(model);
12. Wolfram Mathematica Implementation
Mathematica can carry out the symbolic and numerical matrix operations needed for augmented pole placement. The implementation below uses a direct Ackermann formula construction.
Chapter26_Lesson3.nb
(* Chapter26_Lesson3.nb *)
(* Design of state-feedback gains for an augmented system with integral action. *)
ClearAll[controllabilityMatrix, ackermannSISO, polynomialFromRoots];
controllabilityMatrix[A_, B_] := Module[{n = Length[A]},
ArrayFlatten[{Table[MatrixPower[A, k].B, {k, 0, n - 1}]}]
];
polynomialFromRoots[roots_] := CoefficientList[Expand[Times @@ (s - # & /@ roots)], s];
ackermannSISO[A_, B_, desiredPoles_] := Module[
{n, co, coeffAsc, phiA, en},
n = Length[A];
co = controllabilityMatrix[A, B];
coeffAsc = polynomialFromRoots[desiredPoles];
phiA = Sum[coeffAsc[[k + 1]] MatrixPower[A, k], {k, 0, n}];
en = UnitVector[n, n];
Transpose[{en}].Inverse[co].phiA
];
A = { {0, 1}, {-2, -0.6} };
B = { {0}, {1} };
Cmat = { {1, 0} };
Dmat = { {0} };
n = Length[A];
p = Length[Cmat];
Aaug = ArrayFlatten[{ {A, ConstantArray[0, {n, p}]}, {-Cmat, ConstantArray[0, {p, p}]} }];
Baug = Join[B, -Dmat];
Eaug = Join[ConstantArray[0, {n, p}], IdentityMatrix[p]];
MatrixRank[controllabilityMatrix[Aaug, Baug]]
poles = {-2, -2.5, -3};
Kaug = ackermannSISO[Aaug, Baug, poles];
Kx = Kaug[[All, 1 ;; n]];
Ki = Kaug[[All, n + 1 ;; n + p]];
Acl = Aaug - Baug.Kaug;
Kaug // MatrixForm
Kx // MatrixForm
Ki // MatrixForm
Eigenvalues[Acl]
r = {1};
sol = NDSolve[
{
xa'[t] == Acl.xa[t] + Flatten[Eaug.r],
xa[0] == ConstantArray[0, n + p]
},
xa,
{t, 0, 8}
];
y[t_] := (Cmat.Take[xa[t] /. First[sol], n])[[1]];
u[t_] := (-Kaug.(xa[t] /. First[sol]))[[1]];
Plot[{y[t], 1}, {t, 0, 8}, PlotLegends -> {"y", "r"},
AxesLabel -> {"time [s]", "output"}]
Plot[u[t], {t, 0, 8}, PlotLegends -> {"u"},
AxesLabel -> {"time [s]", "control input"}]
13. Problems and Solutions
Problem 1: For a plant with \( D=0 \), derive the augmented matrices for \( \dot{z}=r-Cx \).
Solution: With \( x_a=\begin{bmatrix}x^{\top}&z^{\top}\end{bmatrix}^{\top} \), the plant equation is \( \dot{x}=Ax+Bu \) and the integrator equation is \( \dot{z}=r-Cx \). Therefore
\[ \dot{x}_a= \begin{bmatrix} A&0\\ -C&0 \end{bmatrix}x_a+ \begin{bmatrix} B\\ 0 \end{bmatrix}u+ \begin{bmatrix} 0\\ I_p \end{bmatrix}r. \]
Problem 2: Explain why controllability of \( (A,B) \) alone does not guarantee arbitrary pole placement for the augmented pair \( (A_a,B_a) \).
Solution: Controllability of the original plant only guarantees that the original plant modes can be moved. The augmented system contains additional integrator states. These states are controllable through the plant output equation. If the plant has an invariant zero at the origin, the integrator mode is structurally blocked from the input. Thus the rank condition
\[ \operatorname{rank} \begin{bmatrix} -A&B\\ C&D \end{bmatrix}=n+p \]
is needed in addition to ordinary plant controllability.
Problem 3: For the numerical example in Section 7, write the augmented closed-loop matrix when \( K_a=\begin{bmatrix}k_1&k_2&k_i\end{bmatrix} \).
Solution: Since \( A_{cl}=A_a-B_aK_a \) and \( B_a=\begin{bmatrix}0&1&0\end{bmatrix}^{\top} \),
\[ A_{cl}= \begin{bmatrix} 0&1&0\\ -2&-0.6&0\\ -1&0&0 \end{bmatrix} - \begin{bmatrix} 0\\ 1\\ 0 \end{bmatrix} \begin{bmatrix} k_1&k_2&k_i \end{bmatrix}. \]
\[ A_{cl}= \begin{bmatrix} 0&1&0\\ -2-k_1&-0.6-k_2&-k_i\\ -1&0&0 \end{bmatrix}. \]
Problem 4: Prove that if \( A_{cl} \) is Hurwitz and \( r \) is constant, then \( y_{ss}=r \).
Solution: Since \( A_{cl} \) is Hurwitz, the forced linear system has a finite steady state \( x_{a,ss} \). At steady state \( \dot{x}_{a,ss}=0 \). In particular, the integrator component satisfies
\[ 0=\dot{z}_{ss}=r-y_{ss}. \]
Hence \( y_{ss}=r \) and the steady-state tracking error is zero.
Problem 5: A designer places the integral pole much farther left than the plant poles. What benefit and drawback should be expected?
Solution: A faster integral pole reduces the time required to remove accumulated steady-state error. However, the required feedback gain usually increases. This may amplify measurement noise, increase actuator demand, and reduce robustness to modeling errors. In practice, integral poles are often placed moderately faster than the dominant poles, not arbitrarily far left.
14. Summary
State-feedback with integral action converts the servo problem into a pole-placement problem for an augmented system. The controller has the form \( u=-Kx-K_i z \), where \( z \) integrates the tracking error. Arbitrary pole placement requires controllability of \( (A_a,B_a) \), not merely controllability of \( (A,B) \). The crucial rank condition at the origin excludes invariant zeros that would prevent zero steady-state tracking. Once the augmented closed-loop matrix is made Hurwitz, constant-reference tracking has zero steady-state error.
15. References
- Kalman, R.E. (1960). On the general theory of control systems. Proceedings of the First International Congress on Automatic Control, 481–492.
- Rosenbrock, H.H. (1967). State-space and multivariable theory. Proceedings of the Institution of Electrical Engineers, 114(1), 1–12.
- Wonham, W.M. (1967). On pole assignment in multi-input controllable linear systems. IEEE Transactions on Automatic Control, 12(6), 660–665.
- 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., & Wonham, W.M. (1976). The internal model principle of control theory. Automatica, 12(5), 457–465.
- Francis, B.A., & Wonham, W.M. (1975). The internal model principle for linear multivariable regulators. Applied Mathematics and Optimization, 2, 170–194.
- Kautsky, J., Nichols, N.K., & Van Dooren, P. (1985). Robust pole assignment in linear state feedback. International Journal of Control, 41(5), 1129–1155.
- Hautus, M.L.J. (1969). Controllability and observability conditions of linear autonomous systems. Proceedings of the Koninklijke Nederlandse Akademie van Wetenschappen, 72, 443–448.