Chapter 11: Controllability Tests and Criteria

Lesson 3: Controllability in Companion (Controllable Canonical) Form

This lesson proves why the companion controllable canonical form is controllable by construction, connects the proof to the Kalman rank test and the PBH test, and shows how the structure becomes a practical bridge between polynomial data and state-space controllability computations.

1. Motivation and Position in the Course

In the previous lessons, controllability was tested by the Kalman matrix and the PBH rank condition. The present lesson studies a special realization in which controllability is not an accidental property but is encoded directly into the state matrix and input vector. This form is called the controllable companion form, also commonly called controllable canonical form.

For a SISO continuous-time LTI system \( \dot{\mathbf{x}}=\mathbf{A}\mathbf{x}+\mathbf{B}u \), the pair \( (\mathbf{A},\mathbf{B}) \) is controllable when

\[ \operatorname{rank}\mathcal{C}(\mathbf{A},\mathbf{B})=n,\qquad \mathcal{C}(\mathbf{A},\mathbf{B})= \begin{bmatrix} \mathbf{B}&\mathbf{A}\mathbf{B}&\cdots&\mathbf{A}^{n-1}\mathbf{B} \end{bmatrix}. \]

Companion form will let us see this rank condition almost visually: repeated multiplication by \( \mathbf{A} \) moves the direct input direction through all state coordinates.

flowchart TD
  A["Choose monic polynomial p(s)"] --> B["Build companion matrix A_c"]
  B --> C["Choose input vector B_c"]
  C --> D["Form Q_c = [B_c, A_c B_c, ..., A_c^(n-1) B_c]"]
  D --> E["Inspect anti-diagonal structure"]
  E --> F["det(Q_c) is nonzero"]
  F --> G["Pair is controllable"]
        

2. The Companion Pair for a Monic Polynomial

Let the desired characteristic polynomial be monic:

\[ p(s)=s^n+a_{n-1}s^{n-1}+a_{n-2}s^{n-2}+\cdots+a_1s+a_0. \]

The companion controllable canonical pair used in this lesson is

\[ \mathbf{A}_c= \begin{bmatrix} 0&1&0&\cdots&0\\ 0&0&1&\cdots&0\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ 0&0&0&\cdots&1\\ -a_0&-a_1&-a_2&\cdots&-a_{n-1} \end{bmatrix}, \qquad \mathbf{B}_c= \begin{bmatrix} 0\\0\\\vdots\\0\\1 \end{bmatrix}. \]

This is the phase-variable convention. A reversed-coordinate convention places ones on the subdiagonal and the coefficient column on the right. Both conventions represent the same controllability idea; they are related by a state-reversal similarity transformation.

\[ \mathbf{P}= \begin{bmatrix} 0&0&\cdots&1\\ 0&0&\cdots&0\\ \vdots&\vdots&\ddots&\vdots\\ 1&0&\cdots&0 \end{bmatrix},\qquad \tilde{\mathbf{A}}_c=\mathbf{P}\mathbf{A}_c\mathbf{P}^{-1},\qquad \tilde{\mathbf{B}}_c=\mathbf{P}\mathbf{B}_c. \]

Since similarity transformations preserve the rank of the controllability matrix, either convention is controllable if one of them is controllable.

3. Characteristic Polynomial of the Companion Matrix

The defining reason for the name companion is that \( \mathbf{A}_c \) is built so that its characteristic polynomial is exactly \( p(s) \). One direct proof uses the determinant expansion of \( s\mathbf{I}-\mathbf{A}_c \).

\[ s\mathbf{I}-\mathbf{A}_c= \begin{bmatrix} s&-1&0&\cdots&0\\ 0&s&-1&\cdots&0\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ 0&0&0&\cdots&-1\\ a_0&a_1&a_2&\cdots&s+a_{n-1} \end{bmatrix}. \]

Expanding along the last row or using the standard recurrence for a companion determinant gives

\[ \det(s\mathbf{I}-\mathbf{A}_c) = s^n+a_{n-1}s^{n-1}+\cdots+a_1s+a_0=p(s). \]

Therefore, companion form gives a direct state-space realization whose eigenvalues are the roots of the chosen polynomial. The next question is not its eigenvalues, but whether the chosen input can excite every mode.

4. Kalman-Matrix Proof of Controllability

Theorem: For every coefficient set \( a_0,\dots,a_{n-1} \in \mathbb{R} \), the companion pair \( (\mathbf{A}_c,\mathbf{B}_c) \) above is controllable.

Proof: Define \( \mathbf{q}_{k+1}=\mathbf{A}_c^k\mathbf{B}_c \) for \( k=0,\dots,n-1 \). Since \( \mathbf{B}_c=\mathbf{e}_n \), the first column of the controllability matrix is the last coordinate direction. Because the superdiagonal of \( \mathbf{A}_c \) shifts the last coordinate upward under repeated multiplication, we obtain

\[ \mathbf{A}_c^k\mathbf{B}_c = \begin{bmatrix} 0\\ \vdots\\ 0\\ 1\\ \star\\ \vdots\\ \star \end{bmatrix}, \qquad \text{where the first nonzero }1\text{ appears in row } n-k. \]

Therefore the controllability matrix has the anti-triangular form

\[ \mathcal{C}_c= \begin{bmatrix} 0&0&\cdots&0&1\\ 0&0&\cdots&1&\star\\ \vdots&\vdots&\ddots&\star&\star\\ 0&1&\cdots&\star&\star\\ 1&\star&\cdots&\star&\star \end{bmatrix}. \]

Its anti-diagonal entries are all equal to one. Hence

\[ \det(\mathcal{C}_c)=(-1)^{n(n-1)/2}\neq 0, \qquad \operatorname{rank}(\mathcal{C}_c)=n. \]

By the Kalman rank test, \( (\mathbf{A}_c,\mathbf{B}_c) \) is controllable. Notice that the proof does not depend on the numerical values of the polynomial coefficients; controllability is structural for this pair.

5. PBH Proof of the Same Result

The PBH test says that a pair is controllable if and only if

\[ \operatorname{rank} \begin{bmatrix} \lambda\mathbf{I}-\mathbf{A}&\mathbf{B} \end{bmatrix}=n \quad \text{for every } \lambda\in\sigma(\mathbf{A}). \]

Equivalently, no nonzero left eigenvector \( \mathbf{v}^{T}\mathbf{A}_c=\lambda\mathbf{v}^{T} \) may satisfy \( \mathbf{v}^{T}\mathbf{B}_c=0 \). Since \( \mathbf{B}_c=\mathbf{e}_n \), \( \mathbf{v}^{T}\mathbf{B}_c=v_n \). Suppose, for contradiction, that \( v_n=0 \).

\[ \mathbf{v}^{T}\mathbf{A}_c = \begin{bmatrix} -a_0v_n& v_1-a_1v_n& v_2-a_2v_n& \cdots& v_{n-1}-a_{n-1}v_n \end{bmatrix} = \lambda \begin{bmatrix} v_1&v_2&\cdots&v_n \end{bmatrix}. \]

With \( v_n=0 \), the last scalar equation gives \( v_{n-1}=0 \), then the previous equation gives \( v_{n-2}=0 \), and continuing backward yields \( v_1=\cdots=v_n=0 \). This contradicts the assumption that \( \mathbf{v} \) is an eigenvector. Thus every left eigenvector satisfies \( \mathbf{v}^{T}\mathbf{B}_c\neq 0 \), so PBH confirms controllability.

flowchart TD
  A["Assume left eigenvector v"] --> B["PBH failure would mean v_n = 0"]
  B --> C["Last eigenvector equation gives v_(n-1) = 0"]
  C --> D["Backward recursion gives all v_i = 0"]
  D --> E["Contradiction: eigenvector cannot be zero"]
  E --> F["PBH test passes"]
        

6. Relation to Reachable Directions

The companion pair also gives an intuitive interpretation of actuator authority.

\[ \dot{x}_n=-a_0x_1-a_1x_2-\cdots-a_{n-1}x_n+u. \]

The chain equations above it are

\[ \dot{x}_1=x_2,\qquad \dot{x}_2=x_3,\qquad \dots,\qquad \dot{x}_{n-1}=x_n. \]

Thus an input injected into \( x_n \) propagates through the dynamic chain and eventually generates independent directions in \( x_{n-1},x_{n-2},\dots,x_1 \). This is exactly the geometric content of the span

\[ \mathcal{R}= \operatorname{span}\{\mathbf{B}_c,\mathbf{A}_c\mathbf{B}_c, \dots,\mathbf{A}_c^{n-1}\mathbf{B}_c\} =\mathbb{R}^n. \]

7. Similarity Transformations and Companion Coordinates

Suppose a SISO pair \( (\mathbf{A},\mathbf{B}) \) is already known to be controllable and has characteristic polynomial \( p(s) \). Let

\[ \mathcal{C}= \begin{bmatrix} \mathbf{B}&\mathbf{A}\mathbf{B}&\cdots&\mathbf{A}^{n-1}\mathbf{B} \end{bmatrix}, \qquad \mathcal{C}_c= \begin{bmatrix} \mathbf{B}_c&\mathbf{A}_c\mathbf{B}_c&\cdots& \mathbf{A}_c^{n-1}\mathbf{B}_c \end{bmatrix}. \]

If \( \mathbf{x}=\mathbf{T}\mathbf{z} \), then \( \mathbf{A}_c=\mathbf{T}^{-1}\mathbf{A}\mathbf{T} \) and \( \mathbf{B}_c=\mathbf{T}^{-1}\mathbf{B} \). The controllability matrices satisfy

\[ \mathcal{C}=\mathbf{T}\mathcal{C}_c, \qquad \mathbf{T}=\mathcal{C}\mathcal{C}_c^{-1}. \]

Hence every controllable SISO pair can be expressed in companion controllable canonical coordinates. This fact will become important in pole placement: once the system is in companion form, feedback gains directly alter the last-row coefficients.

8. Feedback Interpretation Preview

Although pole placement is formally introduced later, companion form already reveals why controllability matters for feedback. With \( u=-\mathbf{k}^{T}\mathbf{x}+r \), where \( \mathbf{k}^{T}=[k_1\;k_2\;\cdots\;k_n] \), the closed-loop matrix is

\[ \mathbf{A}_c-\mathbf{B}_c\mathbf{k}^{T} = \begin{bmatrix} 0&1&0&\cdots&0\\ 0&0&1&\cdots&0\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ 0&0&0&\cdots&1\\ -(a_0+k_1)&-(a_1+k_2)&\cdots&-(a_{n-1}+k_n) \end{bmatrix}. \]

Therefore the closed-loop characteristic polynomial is

\[ p_{cl}(s)=s^n+(a_{n-1}+k_n)s^{n-1} +\cdots+(a_1+k_2)s+(a_0+k_1). \]

This coefficient-level transparency is one of the main reasons controllable canonical form is a central teaching model in modern control.

9. Software Notes and Libraries

The computations in this lesson require three operations: construct the companion matrix, build the controllability matrix, and compute rank. In Python, numpy is enough for a from-scratch implementation, while scipy.signal and python-control are common control libraries. In MATLAB, ctrb, ss, and tf are standard Control System Toolbox routines. In C++, Eigen is a common matrix library, but the example below implements rank manually. In Java, EJML or Apache Commons Math can be used for larger systems; the example below is also from scratch. In Wolfram Mathematica, MatrixRank, CharacteristicPolynomial, and symbolic matrix operations make verification concise.

10. Python Implementation

Chapter11_Lesson3.py


"""
Chapter11_Lesson3.py

Controllability in companion (controllable canonical) form.

Convention:
    p(s) = s^n + a_{n-1}s^{n-1} + ... + a_1 s + a_0

    A_c = [[0, 1, 0, ..., 0],
           [0, 0, 1, ..., 0],
           ...
           [0, 0, 0, ..., 1],
           [-a_0, -a_1, ..., -a_{n-1}]]

    B_c = [0, 0, ..., 1]^T
"""

from __future__ import annotations

import numpy as np


def companion_pair(coefficients: list[float]) -> tuple[np.ndarray, np.ndarray]:
    """Build the SISO companion controllable canonical pair (A_c, B_c).

    coefficients = [a0, a1, ..., a_{n-1}]
    """
    n = len(coefficients)
    if n == 0:
        raise ValueError("At least one coefficient is required.")

    A = np.zeros((n, n), dtype=float)
    if n > 1:
        A[:-1, 1:] = np.eye(n - 1)
    A[-1, :] = -np.asarray(coefficients, dtype=float)

    B = np.zeros((n, 1), dtype=float)
    B[-1, 0] = 1.0
    return A, B


def controllability_matrix(A: np.ndarray, B: np.ndarray) -> np.ndarray:
    """Return Q = [B, AB, ..., A^(n-1)B]."""
    n = A.shape[0]
    blocks = []
    Ak = np.eye(n)
    for _ in range(n):
        blocks.append(Ak @ B)
        Ak = Ak @ A
    return np.hstack(blocks)


def kalman_rank(A: np.ndarray, B: np.ndarray, tol: float = 1e-10) -> int:
    """Numerical rank of the Kalman controllability matrix."""
    Q = controllability_matrix(A, B)
    return int(np.linalg.matrix_rank(Q, tol=tol))


def pbh_test(A: np.ndarray, B: np.ndarray, tol: float = 1e-9) -> bool:
    """PBH controllability test: rank([lambda I - A, B]) = n for every eigenvalue."""
    n = A.shape[0]
    eigvals = np.linalg.eigvals(A)
    for lam in eigvals:
        M = np.hstack((lam * np.eye(n) - A, B))
        if np.linalg.matrix_rank(M, tol=tol) < n:
            return False
    return True


def controllable_to_companion_transform(A: np.ndarray, B: np.ndarray, coefficients: list[float]) -> np.ndarray:
    """Return T such that x = T z, A_c = T^{-1} A T, B_c = T^{-1} B.

    For a controllable SISO pair with the same characteristic polynomial
    coefficients as the companion pair:
        T = Q(A,B) Q(A_c,B_c)^{-1}
    """
    Ac, Bc = companion_pair(coefficients)
    Q = controllability_matrix(A, B)
    Qc = controllability_matrix(Ac, Bc)
    if np.linalg.matrix_rank(Q) < A.shape[0]:
        raise ValueError("The supplied pair (A,B) is not controllable.")
    return Q @ np.linalg.inv(Qc)


def main() -> None:
    coefficients = [6.0, 11.0, 6.0]  # p(s)=s^3+6s^2+11s+6
    Ac, Bc = companion_pair(coefficients)
    Qc = controllability_matrix(Ac, Bc)

    print("A_c =\n", Ac)
    print("B_c =\n", Bc)
    print("Q_c = [B, AB, A^2B] =\n", Qc)
    print("det(Q_c) =", np.linalg.det(Qc))
    print("rank(Q_c) =", kalman_rank(Ac, Bc))
    print("PBH controllable?", pbh_test(Ac, Bc))

    # Example: a similarity transform preserves controllability.
    S = np.array([[1.0, 1.0, 0.0], [0.0, 1.0, 1.0], [1.0, 0.0, 1.0]])
    A = S @ Ac @ np.linalg.inv(S)
    B = S @ Bc
    print("\nTransformed realization rank =", kalman_rank(A, B))

    T = controllable_to_companion_transform(A, B, coefficients)
    recovered_Ac = np.linalg.inv(T) @ A @ T
    recovered_Bc = np.linalg.inv(T) @ B
    print("\nRecovered A_c =\n", np.round(recovered_Ac, 10))
    print("Recovered B_c =\n", np.round(recovered_Bc, 10))


if __name__ == "__main__":
    main()
      

11. C++ Implementation

Chapter11_Lesson3.cpp


// Chapter11_Lesson3.cpp
// Controllability in companion controllable canonical form.
// Build A_c, B_c, Q_c = [B, AB, ..., A^(n-1)B], and compute rank by Gaussian elimination.

#include <cmath>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <vector>

using Matrix = std::vector<std::vector<double>>;

Matrix zeros(int rows, int cols) {
    return Matrix(rows, std::vector<double>(cols, 0.0));
}

Matrix identity(int n) {
    Matrix I = zeros(n, n);
    for (int i = 0; i < n; ++i) I[i][i] = 1.0;
    return I;
}

Matrix matmul(const Matrix& A, const Matrix& B) {
    int r = static_cast<int>(A.size());
    int m = static_cast<int>(A[0].size());
    int c = static_cast<int>(B[0].size());
    Matrix C = zeros(r, c);
    for (int i = 0; i < r; ++i) {
        for (int k = 0; k < m; ++k) {
            for (int j = 0; j < c; ++j) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
    return C;
}

Matrix companionA(const std::vector<double>& coeffs) {
    int n = static_cast<int>(coeffs.size());
    Matrix A = zeros(n, n);
    for (int i = 0; i < n - 1; ++i) A[i][i + 1] = 1.0;
    for (int j = 0; j < n; ++j) A[n - 1][j] = -coeffs[j];
    return A;
}

Matrix companionB(int n) {
    Matrix B = zeros(n, 1);
    B[n - 1][0] = 1.0;
    return B;
}

Matrix controllabilityMatrix(const Matrix& A, const Matrix& B) {
    int n = static_cast<int>(A.size());
    Matrix Q = zeros(n, n);
    Matrix Ak = identity(n);
    for (int col = 0; col < n; ++col) {
        Matrix block = matmul(Ak, B);
        for (int row = 0; row < n; ++row) Q[row][col] = block[row][0];
        Ak = matmul(Ak, A);
    }
    return Q;
}

int rank(Matrix M, double tol = 1e-10) {
    int rows = static_cast<int>(M.size());
    int cols = static_cast<int>(M[0].size());
    int r = 0;
    for (int c = 0; c < cols && r < rows; ++c) {
        int pivot = r;
        for (int i = r + 1; i < rows; ++i) {
            if (std::fabs(M[i][c]) > std::fabs(M[pivot][c])) pivot = i;
        }
        if (std::fabs(M[pivot][c]) <= tol) continue;
        std::swap(M[pivot], M[r]);
        double div = M[r][c];
        for (int j = c; j < cols; ++j) M[r][j] /= div;
        for (int i = 0; i < rows; ++i) {
            if (i == r) continue;
            double factor = M[i][c];
            for (int j = c; j < cols; ++j) M[i][j] -= factor * M[r][j];
        }
        ++r;
    }
    return r;
}

void printMatrix(const Matrix& M, const std::string& name) {
    std::cout << name << " =\n";
    for (const auto& row : M) {
        for (double x : row) std::cout << std::setw(12) << x << " ";
        std::cout << "\n";
    }
}

int main() {
    std::vector<double> coeffs = {6.0, 11.0, 6.0}; // p(s)=s^3+6s^2+11s+6
    Matrix A = companionA(coeffs);
    Matrix B = companionB(static_cast<int>(coeffs.size()));
    Matrix Q = controllabilityMatrix(A, B);

    printMatrix(A, "A_c");
    printMatrix(B, "B_c");
    printMatrix(Q, "Q_c");

    std::cout << "rank(Q_c) = " << rank(Q) << "\n";
    std::cout << "The companion pair is controllable when rank(Q_c) equals n.\n";
    return 0;
}
      

12. Java Implementation

Chapter11_Lesson3.java


// Chapter11_Lesson3.java
// Controllability in companion controllable canonical form.
// Compile: javac Chapter11_Lesson3.java
// Run:     java Chapter11_Lesson3

public class Chapter11_Lesson3 {
    static double[][] zeros(int rows, int cols) {
        return new double[rows][cols];
    }

    static double[][] identity(int n) {
        double[][] I = zeros(n, n);
        for (int i = 0; i < n; i++) I[i][i] = 1.0;
        return I;
    }

    static double[][] multiply(double[][] A, double[][] B) {
        int r = A.length;
        int m = A[0].length;
        int c = B[0].length;
        double[][] C = zeros(r, c);
        for (int i = 0; i < r; i++) {
            for (int k = 0; k < m; k++) {
                for (int j = 0; j < c; j++) {
                    C[i][j] += A[i][k] * B[k][j];
                }
            }
        }
        return C;
    }

    static double[][] companionA(double[] coeffs) {
        int n = coeffs.length;
        double[][] A = zeros(n, n);
        for (int i = 0; i < n - 1; i++) A[i][i + 1] = 1.0;
        for (int j = 0; j < n; j++) A[n - 1][j] = -coeffs[j];
        return A;
    }

    static double[][] companionB(int n) {
        double[][] B = zeros(n, 1);
        B[n - 1][0] = 1.0;
        return B;
    }

    static double[][] controllabilityMatrix(double[][] A, double[][] B) {
        int n = A.length;
        double[][] Q = zeros(n, n);
        double[][] Ak = identity(n);
        for (int col = 0; col < n; col++) {
            double[][] block = multiply(Ak, B);
            for (int row = 0; row < n; row++) Q[row][col] = block[row][0];
            Ak = multiply(Ak, A);
        }
        return Q;
    }

    static int rank(double[][] input, double tol) {
        int rows = input.length;
        int cols = input[0].length;
        double[][] M = new double[rows][cols];
        for (int i = 0; i < rows; i++) {
            System.arraycopy(input[i], 0, M[i], 0, cols);
        }

        int r = 0;
        for (int c = 0; c < cols && r < rows; c++) {
            int pivot = r;
            for (int i = r + 1; i < rows; i++) {
                if (Math.abs(M[i][c]) > Math.abs(M[pivot][c])) pivot = i;
            }
            if (Math.abs(M[pivot][c]) <= tol) continue;

            double[] tmp = M[pivot];
            M[pivot] = M[r];
            M[r] = tmp;

            double div = M[r][c];
            for (int j = c; j < cols; j++) M[r][j] /= div;

            for (int i = 0; i < rows; i++) {
                if (i == r) continue;
                double factor = M[i][c];
                for (int j = c; j < cols; j++) M[i][j] -= factor * M[r][j];
            }
            r++;
        }
        return r;
    }

    static void printMatrix(double[][] M, String name) {
        System.out.println(name + " =");
        for (double[] row : M) {
            for (double x : row) System.out.printf("%12.6f ", x);
            System.out.println();
        }
    }

    public static void main(String[] args) {
        double[] coeffs = {6.0, 11.0, 6.0}; // p(s)=s^3+6s^2+11s+6
        double[][] A = companionA(coeffs);
        double[][] B = companionB(coeffs.length);
        double[][] Q = controllabilityMatrix(A, B);

        printMatrix(A, "A_c");
        printMatrix(B, "B_c");
        printMatrix(Q, "Q_c");
        System.out.println("rank(Q_c) = " + rank(Q, 1e-10));
        System.out.println("The companion pair is controllable when rank(Q_c) equals n.");
    }
}
      

13. MATLAB / Simulink Implementation

Chapter11_Lesson3.m


% Chapter11_Lesson3.m
% Controllability in companion controllable canonical form.
% Requires only base MATLAB for the custom controllability matrix.
% If Control System Toolbox is available, compare with ctrb(A,B).

clear; clc;

coeffs = [6 11 6];              % [a0 a1 a2] for p(s)=s^3+6s^2+11s+6
[A_c, B_c] = companion_pair(coeffs);
Q_c = controllability_matrix(A_c, B_c);

disp('A_c ='); disp(A_c);
disp('B_c ='); disp(B_c);
disp('Q_c = [B AB A^2B] ='); disp(Q_c);
fprintf('rank(Q_c) = %d\n', rank(Q_c));
fprintf('det(Q_c)  = %.6g\n', det(Q_c));

% Control System Toolbox comparison
if exist('ctrb', 'file') == 2
    fprintf('rank(ctrb(A_c,B_c)) = %d\n', rank(ctrb(A_c, B_c)));
end

% State-space object and transfer function (if Control System Toolbox exists)
if exist('ss', 'file') == 2
    C_c = [1 0 0];
    D_c = 0;
    sys = ss(A_c, B_c, C_c, D_c);
    disp('State-space model:');
    disp(sys);
end

% Simulink note:
% In Simulink, use a State-Space block with A=A_c, B=B_c, C=eye(n), D=zeros(n,1).
% Then drive it by a Step block and observe all states using a Scope.

function [A, B] = companion_pair(coeffs)
    n = numel(coeffs);
    A = zeros(n);
    if n > 1
        A(1:n-1, 2:n) = eye(n-1);
    end
    A(n, :) = -coeffs(:).';
    B = zeros(n, 1);
    B(n) = 1;
end

function Q = controllability_matrix(A, B)
    n = size(A, 1);
    Q = zeros(n);
    Ak = eye(n);
    for k = 1:n
        Q(:, k) = Ak * B;
        Ak = Ak * A;
    end
end
      

For Simulink, insert a State-Space block with A=A_c, B=B_c, C=eye(n), and D=zeros(n,1). Connect a Step block to the input and a Scope to the states. This verifies that a single input excites the whole companion chain.

14. Wolfram Mathematica Implementation

Chapter11_Lesson3.nb


(* Chapter11_Lesson3.nb *)

ClearAll[CompanionPair, ControllabilityMatrixCustom, coeffs, Ac, Bc, Qc];

CompanionPair[coeffs_List] := Module[{n, A, B},
  n = Length[coeffs];
  A = ConstantArray[0, {n, n}];
  Do[A[[i, i + 1]] = 1, {i, 1, n - 1}];
  A[[n, All]] = -coeffs;
  B = ConstantArray[0, {n, 1}];
  B[[n, 1]] = 1;
  {A, B}
];

ControllabilityMatrixCustom[A_, B_] := Module[{n},
  n = Length[A];
  ArrayFlatten[{Table[MatrixPower[A, k].B, {k, 0, n - 1}]}]
];

coeffs = {6, 11, 6}; (* p(s)=s^3+6s^2+11s+6 *)
{Ac, Bc} = CompanionPair[coeffs];
Qc = ControllabilityMatrixCustom[Ac, Bc];

Ac // MatrixForm
Bc // MatrixForm
Qc // MatrixForm
MatrixRank[Qc]
Det[Qc]

(* Optional verification *)
Eigenvalues[Ac]
CharacteristicPolynomial[Ac, s] // Expand
      

15. Problems and Solutions

Problem 1: For \( p(s)=s^3+6s^2+11s+6 \), construct the companion pair and compute the controllability matrix.

Solution: Here \( a_0=6,\;a_1=11,\;a_2=6 \). Therefore

\[ \mathbf{A}_c= \begin{bmatrix} 0&1&0\\ 0&0&1\\ -6&-11&-6 \end{bmatrix}, \qquad \mathbf{B}_c= \begin{bmatrix} 0\\0\\1 \end{bmatrix}. \]

\[ \mathcal{C}_c= \begin{bmatrix} \mathbf{B}_c&\mathbf{A}_c\mathbf{B}_c&\mathbf{A}_c^2\mathbf{B}_c \end{bmatrix} = \begin{bmatrix} 0&0&1\\ 0&1&-6\\ 1&-6&25 \end{bmatrix}. \]

Since \( \det(\mathcal{C}_c)=-1\neq 0 \), the pair is controllable.

Problem 2: Prove that the determinant of the controllability matrix of the companion pair does not depend on \( a_0,\dots,a_{n-1} \).

Solution: For \( \mathbf{A}_c^k\mathbf{B}_c \), rows above \( n-k \) are zero and row \( n-k \) is one. Thus the first nonzero entries of the columns of \( \mathcal{C}_c \) lie on the anti-diagonal and are all equal to one. Coefficients only appear in entries below that anti-diagonal. Therefore

\[ \det(\mathcal{C}_c)=\det(\mathbf{J})=(-1)^{n(n-1)/2}, \]

where \( \mathbf{J} \) is the reversal matrix. Hence the determinant is always nonzero and independent of the coefficient values.

Problem 3: Use the PBH test to show that the companion input vector cannot be orthogonal to any left eigenvector.

Solution: Let \( \mathbf{v}^{T}\mathbf{A}_c=\lambda\mathbf{v}^{T} \). If \( \mathbf{v}^{T}\mathbf{B}_c=0 \), then \( v_n=0 \). The last left-eigenvector equation gives \( v_{n-1}=0 \); substituting backward gives \( v_{n-2}=0,\dots,v_1=0 \). Hence \( \mathbf{v}=\mathbf{0} \), impossible for an eigenvector. PBH therefore gives controllability.

Problem 4: Consider a controllable SISO pair \( (\mathbf{A},\mathbf{B}) \) with the same characteristic polynomial as \( \mathbf{A}_c \). Show that the transformation \( \mathbf{T}=\mathcal{C}\mathcal{C}_c^{-1} \) maps companion coordinates to the original coordinates.

Solution: If \( \mathbf{x}=\mathbf{T}\mathbf{z} \), then \( \mathbf{B}=\mathbf{T}\mathbf{B}_c \) and \( \mathbf{A}\mathbf{B} =\mathbf{T}\mathbf{A}_c\mathbf{B}_c \). Continuing gives \( \mathbf{A}^k\mathbf{B} =\mathbf{T}\mathbf{A}_c^k\mathbf{B}_c \) for \( k=0,\dots,n-1 \). Therefore \( \mathcal{C}=\mathbf{T}\mathcal{C}_c \), and since \( \mathcal{C}_c \) is nonsingular, \( \mathbf{T}=\mathcal{C}\mathcal{C}_c^{-1} \).

Problem 5: In companion form, find the feedback gains that transform \( p(s)=s^3+6s^2+11s+6 \) into \( p_{cl}(s)=s^3+9s^2+26s+24 \).

Solution: The open-loop coefficients are \( a_0=6,\;a_1=11,\;a_2=6 \). The desired closed-loop coefficients are \( \alpha_0=24,\;\alpha_1=26,\;\alpha_2=9 \). Since

\[ p_{cl}(s)=s^3+(a_2+k_3)s^2+(a_1+k_2)s+(a_0+k_1), \]

the gain is \( k_1=18,\;k_2=15,\;k_3=3 \).

16. Summary

The companion controllable canonical form provides a realization where the Kalman controllability matrix is nonsingular for every monic characteristic polynomial. The proof follows from an anti-diagonal structure in \( [\mathbf{B}_c,\mathbf{A}_c\mathbf{B}_c,\dots, \mathbf{A}_c^{n-1}\mathbf{B}_c] \). The PBH proof reaches the same conclusion by showing that no left eigenvector can be orthogonal to the input vector. Companion form is therefore both a test case for controllability theory and a practical coordinate system for later pole placement.

17. References

  1. Kalman, R.E. (1960). On the general theory of control systems. IFAC Proceedings Volumes, 1(1), 491–502.
  2. Gilbert, E.G. (1963). Controllability and observability in multivariable control systems. Journal of the Society for Industrial and Applied Mathematics, Series A: Control, 1(2), 128–151.
  3. Popov, V.M. (1964). Hyperstability and optimality of automatic systems with several control functions. Revue Roumaine des Sciences Techniques, Série Électrotechnique et Énergétique, 9, 629–690.
  4. Luenberger, D.G. (1967). Canonical forms for linear multivariable systems. IEEE Transactions on Automatic Control, AC-12(3), 290–293.
  5. Hautus, M.L.J. (1969). Controllability and observability conditions of linear autonomous systems. Nederlandsche Akademie van Wetenschappen, Proceedings, Series A, 72, 443–448.
  6. Brunovský, P. (1970). A classification of linear controllable systems. Kybernetika, 6(3), 173–188.
  7. Wonham, W.M. (1967). On pole assignment in multi-input controllable linear systems. IEEE Transactions on Automatic Control, 12(6), 660–665.