Chapter 16: Controllable Canonical Form

Lesson 5: Advantages and Drawbacks of CCF Representations

This lesson evaluates controllable canonical form as both an analytic coordinate system and a computational representation. We prove why CCF is algebraically attractive for controllability and pole assignment, then examine its limitations: nonphysical states, coefficient sensitivity, poor numerical conditioning, and limited direct usefulness for MIMO systems.

1. Position of CCF in State-Space Analysis

For a strictly proper SISO transfer function with monic denominator, controllable canonical form collects the denominator coefficients into a companion matrix and places the input vector in a fixed coordinate direction. In the convention used in this chapter,

\[ G(s)=\frac{\beta_0 s^{n-1}+\beta_1s^{n-2}+\cdots+\beta_{n-1}} {s^n+a_{n-1}s^{n-1}+a_{n-2}s^{n-2}+\cdots+a_1s+a_0}. \]

\[ \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},\quad \mathbf{B}_c=\begin{bmatrix}0\\0\\\vdots\\0\\1\end{bmatrix},\quad \mathbf{C}_c=\begin{bmatrix}\beta_{n-1}&\beta_{n-2}&\cdots&\beta_0\end{bmatrix}. \]

The primary advantage is not that CCF reveals physical coordinates. It usually does not. Its value is that it exposes a direct bridge between transfer-function coefficients, controllability, and state-feedback polynomial assignment.

flowchart TD
  A["Transfer function coefficients"] --> B["Build companion matrix Ac"]
  B --> C["Bc is fixed unit input direction"]
  C --> D["Controllability is automatic for monic denominator"]
  D --> E["Feedback gains change last row coefficients"]
  E --> F["Desired closed-loop polynomial"]
  B --> G["Possible drawback: artificial coordinates"]
  G --> H["Check conditioning and physical interpretability"]
        

2. Algebraic Advantages of CCF

The first advantage is that controllability is structurally built into the representation. Define the controllability matrix \( \mathcal{C}_c \) by

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

Since \( \mathbf{B}_c=\mathbf{e}_n \), repeated multiplication by \( \mathbf{A}_c \) produces a sequence whose leading entries form a reversed triangular pattern. Thus

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

Hence every realization in this exact CCF convention is controllable independent of the numerical values of \(a_0,\ldots,a_{n-1}\). This makes CCF a convenient teaching and proof vehicle after the Kalman rank test.

The second advantage is coefficient visibility. The characteristic polynomial of \( \mathbf{A}_c \) is exactly the transfer denominator:

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

Therefore, changing the last row of the companion matrix changes the closed-loop polynomial coefficients in a directly readable way.

3. Advantage for SISO State-Feedback Pole Assignment

Let the state-feedback law be \( u=-\mathbf{K}_c\mathbf{x}_c+r \), where \( \mathbf{K}_c=\begin{bmatrix}k_1&k_2&\cdots&k_n\end{bmatrix} \). The closed-loop state matrix is

\[ \mathbf{A}_c-\mathbf{B}_c\mathbf{K}_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+k_1)&-(a_1+k_2)&-(a_2+k_3)&\cdots&-(a_{n-1}+k_n) \end{bmatrix}. \]

If the desired polynomial is

\[ p_d(s)=s^n+\alpha_{n-1}s^{n-1}+\alpha_{n-2}s^{n-2}+ \cdots+\alpha_1s+\alpha_0, \]

then coefficient matching gives the immediate design formula

\[ \boxed{\mathbf{K}_c= \begin{bmatrix}\alpha_0-a_0&\alpha_1-a_1&\cdots&\alpha_{n-1}-a_{n-1}\end{bmatrix}}. \]

This is the cleanest reason CCF is pedagogically important before Ackermann's formula. Ackermann's formula generalizes the same idea to an arbitrary controllable coordinate system, while CCF shows the mechanism without hiding it inside a matrix polynomial.

4. Drawbacks and Failure Modes

CCF is exact and elegant, but it is not always the best representation for engineering computation. The main drawbacks are listed below.

Nonphysical states. The CCF state variables are normally coefficient-generated coordinates, not directly measurable physical variables such as angle, velocity, current, pressure, or position. Therefore a controller designed in CCF must be transformed back to the physical coordinates before implementation.

Coefficient sensitivity. For high-order polynomials, small coefficient perturbations may cause large pole displacement. If \(p(s)=s^n+a_{n-1}s^{n-1}+\cdots+a_0\) and \(p(\lambda)=0\), first-order perturbation theory gives

\[ \Delta \lambda \approx -\frac{\Delta p(\lambda)}{p'(\lambda)}, \qquad \Delta p(\lambda)=\sum_{i=0}^{n-1}\Delta a_i\lambda^i. \]

When \( |p'(\lambda)| \) is small, roots are clustered or repeated, and the pole becomes sensitive. Since CCF stores dynamics as polynomial coefficients, it inherits this coefficient-to-root sensitivity directly.

Numerical conditioning. The transformation from a physical realization to CCF may be ill-conditioned. If \( \mathbf{x}=\mathbf{T}\mathbf{z} \), then \( \mathbf{A}_z=\mathbf{T}^{-1}\mathbf{A}\mathbf{T} \) and \( \mathbf{B}_z=\mathbf{T}^{-1}\mathbf{B} \). A large condition number

\[ \kappa_2(\mathbf{T})=\|\mathbf{T}\|_2\|\mathbf{T}^{-1}\|_2 \]

means that small roundoff or modeling errors in one coordinate system can be strongly amplified in the other.

MIMO limitation. Standard scalar companion CCF is a natural SISO representation. MIMO systems require block companion, Brunovsky, or other canonical structures, and those forms depend on controllability indices. The simple coefficient-matching formula no longer captures all degrees of freedom in multi-input pole assignment.

5. Similarity Transformations and Conditioning

Suppose a physical realization \((\mathbf{A},\mathbf{B},\mathbf{C},D)\) is similar to CCF through \(\mathbf{x}=\mathbf{T}\mathbf{x}_c\). Then

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

If a feedback gain is designed in CCF coordinates, the physical-coordinate feedback is obtained from

\[ u=-\mathbf{K}_c\mathbf{x}_c+r =-\mathbf{K}_c\mathbf{T}^{-1}\mathbf{x}+r, \qquad \boxed{\mathbf{K}=\mathbf{K}_c\mathbf{T}^{-1}}. \]

Therefore, CCF is safest when it is used as an analytic intermediate representation and not blindly used as the final numerical state basis.

flowchart TD
  A["Physical realization: A, B, C, D"] --> B["Similarity transform T"]
  B --> C["CCF realization: Ac, Bc, Cc, D"]
  C --> D["Design Kc by coefficient matching"]
  D --> E["Transform back: K = Kc inv(T)"]
  B --> F["If cond(T) is large"]
  F --> G["Roundoff and model errors are amplified"]
  G --> H["Prefer balanced/modal/physical coordinates"]
        

6. Software Implementations

The following implementations construct CCF, compute the controllability matrix, and demonstrate the direct feedback-gain formula. The same file names are also included in the downloadable package.

Chapter16_Lesson5.py

"""
Chapter16_Lesson5.py
Advantages and drawbacks of controllable canonical form (CCF).

Libraries used:
  - numpy for linear algebra
Optional production libraries for related Modern Control work:
  - scipy.signal, python-control, slycot
"""

import numpy as np


def companion_ccf(a_ascending, beta_descending, d=0.0):
    """
    Build the SISO controllable canonical form

        G(s) = d + (beta_0 s^(n-1) + ... + beta_(n-1)) /
                    (s^n + a_(n-1)s^(n-1) + ... + a_0)

    a_ascending = [a_0, a_1, ..., a_(n-1)]
    beta_descending = [beta_0, beta_1, ..., beta_(n-1)]
    """
    a = np.asarray(a_ascending, dtype=float)
    beta = np.asarray(beta_descending, dtype=float)
    n = len(a)
    if beta.shape[0] != n:
        raise ValueError("beta_descending must have length n")

    A = np.zeros((n, n))
    if n > 1:
        A[:-1, 1:] = np.eye(n - 1)
    A[-1, :] = -a

    B = np.zeros((n, 1))
    B[-1, 0] = 1.0

    # With this shift-up convention, C multiplies [1, s, ..., s^(n-1)] / denominator.
    C = beta[::-1].reshape(1, n)
    D = np.array([[float(d)]])
    return A, B, C, D


def controllability_matrix(A, B):
    n = A.shape[0]
    return np.hstack([np.linalg.matrix_power(A, k) @ B for k in range(n)])


def ccf_feedback_gain(a_ascending, desired_poles):
    """
    For u = -Kx + r in CCF:
        k_i = alpha_(i-1) - a_(i-1)
    where desired polynomial is
        s^n + alpha_(n-1)s^(n-1) + ... + alpha_0.
    """
    a = np.asarray(a_ascending, dtype=float)
    poly_desc = np.poly(np.asarray(desired_poles, dtype=complex)).real
    alpha_ascending = poly_desc[1:][::-1]
    return alpha_ascending - a


def main():
    # Denominator: s^3 + 6s^2 + 11s + 6
    # Numerator:   2s^2 + 5s + 3
    a = [6.0, 11.0, 6.0]
    beta = [2.0, 5.0, 3.0]
    A, B, C, D = companion_ccf(a, beta)

    Qc = controllability_matrix(A, B)
    print("A_c =\n", A)
    print("B_c =\n", B)
    print("C_c =\n", C)
    print("rank(Qc) =", np.linalg.matrix_rank(Qc))
    print("cond(Qc) =", np.linalg.cond(Qc))

    desired_poles = [-2.0, -3.0, -4.0]
    K = ccf_feedback_gain(a, desired_poles)
    Acl = A - B @ K.reshape(1, -1)
    print("K for desired poles", desired_poles, "=", K)
    print("closed-loop eigenvalues =", np.linalg.eigvals(Acl))

    # Drawback demonstration: small coefficient perturbations can move roots noticeably.
    rng = np.random.default_rng(4)
    for eps in [1e-6, 1e-4, 1e-2]:
        perturbed_a = np.asarray(a) * (1.0 + eps * rng.standard_normal(len(a)))
        p_desc = np.r_[1.0, perturbed_a[::-1]]
        print(f"eps={eps:g}, perturbed poles=", np.sort_complex(np.roots(p_desc)))


if __name__ == "__main__":
    main()

Chapter16_Lesson5.cpp

/*
Chapter16_Lesson5.cpp
Portable C++ implementation for CCF construction, controllability rank,
and direct pole-placement gain in controllable canonical form.

Production libraries for larger Modern Control programs:
  - Eigen, Armadillo, Boost.uBLAS, SLICOT wrappers
*/

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

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

Matrix zeros(int r, int c) { return Matrix(r, Vector(c, 0.0)); }

Matrix multiply(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 companionCCF(const Vector& aAscending) {
    int n = static_cast<int>(aAscending.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] = -aAscending[j];
    return A;
}

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

Matrix controllabilityMatrix(Matrix A, Matrix B) {
    int n = static_cast<int>(A.size());
    Matrix Q = zeros(n, n);
    Matrix AkB = B;
    for (int k = 0; k < n; ++k) {
        for (int i = 0; i < n; ++i) Q[i][k] = AkB[i][0];
        AkB = multiply(A, AkB);
    }
    return Q;
}

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

Vector desiredAscendingForExample() {
    // Desired poles -2, -3, -4:
    // (s+2)(s+3)(s+4) = s^3 + 9s^2 + 26s + 24
    return {24.0, 26.0, 9.0};
}

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

int main() {
    Vector a = {6.0, 11.0, 6.0}; // a0, a1, a2 for s^3 + 6s^2 + 11s + 6
    Matrix A = companionCCF(a);
    Matrix B = inputB(static_cast<int>(a.size()));
    Matrix Q = controllabilityMatrix(A, B);

    printMatrix("A_c", A);
    printMatrix("B_c", B);
    printMatrix("Q_c", Q);
    std::cout << "rank(Q_c) = " << rankGaussian(Q) << "\n";

    Vector alpha = desiredAscendingForExample();
    Vector K(a.size());
    for (std::size_t i = 0; i < a.size(); ++i) K[i] = alpha[i] - a[i];

    std::cout << "K = [ ";
    for (double k : K) std::cout << k << " ";
    std::cout << "]\n";
    return 0;
}

Chapter16_Lesson5.java

/*
Chapter16_Lesson5.java
Scratch Java implementation for controllable canonical form diagnostics.

Production Java libraries for control/numerics:
  - EJML, Apache Commons Math, ojAlgo
*/

import java.util.Arrays;

public class Chapter16_Lesson5 {
    static double[][] zeros(int r, int c) {
        return new double[r][c];
    }

    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[][] companionCCF(double[] aAscending) {
        int n = aAscending.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] = -aAscending[j];
        return A;
    }

    static double[][] inputB(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[][] AkB = B;
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) Q[i][k] = AkB[i][0];
            AkB = multiply(A, AkB);
        }
        return Q;
    }

    static int rankGaussian(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++) M[i] = Arrays.copyOf(input[i], cols);

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

            double[] temp = M[pivot];
            M[pivot] = M[rank];
            M[rank] = temp;

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

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

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

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

        printMatrix("A_c", A);
        printMatrix("B_c", B);
        printMatrix("Q_c", Q);
        System.out.println("rank(Q_c) = " + rankGaussian(Q, 1e-10));

        // Desired poles -2, -3, -4 -> s^3 + 9s^2 + 26s + 24
        double[] alphaAscending = {24.0, 26.0, 9.0};
        double[] K = new double[a.length];
        for (int i = 0; i < a.length; i++) K[i] = alphaAscending[i] - a[i];
        System.out.println("K = " + Arrays.toString(K));
    }
}

Chapter16_Lesson5.m

% Chapter16_Lesson5.m
% Advantages and drawbacks of controllable canonical form (CCF).
%
% MATLAB libraries/toolboxes related to this lesson:
%   Control System Toolbox: ss, tf, tf2ss, ctrb, place, canon
%   Simulink: State-Space block for model-level simulation

clear; clc;

% Denominator: s^3 + 6s^2 + 11s + 6
% Numerator:   2s^2 + 5s + 3
a = [6 11 6];          % [a0 a1 a2]
beta = [2 5 3];        % [beta0 beta1 beta2], descending numerator powers
n = length(a);

A = zeros(n);
A(1:n-1,2:n) = eye(n-1);
A(n,:) = -a;
B = zeros(n,1); B(n) = 1;
C = fliplr(beta);
D = 0;

fprintf('A_c =\n'); disp(A);
fprintf('B_c =\n'); disp(B);
fprintf('C_c =\n'); disp(C);

Qc = ctrb(A,B);
fprintf('rank(Qc) = %d\n', rank(Qc));
fprintf('cond(Qc) = %.4e\n', cond(Qc));

sys = ss(A,B,C,D);
fprintf('Transfer function reconstructed from CCF:\n');
disp(tf(sys));

% Direct CCF pole placement.
desiredPoles = [-2 -3 -4];
desiredPoly = poly(desiredPoles);       % [1 alpha2 alpha1 alpha0]
alphaAscending = fliplr(desiredPoly(2:end));
K = alphaAscending - a;
Acl = A - B*K;
fprintf('K =\n'); disp(K);
fprintf('eig(A-BK) =\n'); disp(eig(Acl));

% Comparison with MATLAB place.
K_place = place(A,B,desiredPoles);
fprintf('K from place(A,B,p) =\n'); disp(K_place);

% Simulink construction: creates a State-Space block using the CCF matrices.
if exist('simulink', 'file') == 4 || exist('new_system', 'file') == 2
    model = 'Chapter16_Lesson5_Simulink';
    if bdIsLoaded(model), close_system(model,0); end
    new_system(model);
    add_block('simulink/Sources/Step', [model '/Reference Step'], ...
        'Position', [40 80 90 110]);
    add_block('simulink/Continuous/State-Space', [model '/CCF State-Space'], ...
        'A', mat2str(Acl), 'B', mat2str(B), 'C', mat2str(C), 'D', mat2str(D), ...
        'Position', [150 65 310 125]);
    add_block('simulink/Sinks/Scope', [model '/Output Scope'], ...
        'Position', [380 75 430 115]);
    add_line(model, 'Reference Step/1', 'CCF State-Space/1');
    add_line(model, 'CCF State-Space/1', 'Output Scope/1');
    save_system(model);
    fprintf('Created Simulink model: %s.slx\n', model);
else
    fprintf('Simulink not available; skipped model construction.\n');
end

Chapter16_Lesson5.nb

Notebook[{
  Cell["Chapter16_Lesson5.nb", "Title"],
  Cell["Controllable canonical form: construction, rank, and direct pole placement.", "Text"],
  Cell[BoxData[ToBoxes[
    ClearAll[s, a, beta, n, A, B, Cmat, Qc, desired, K, Acl];
    a = {6, 11, 6};
    beta = {2, 5, 3};
    n = Length[a];
    A = ConstantArray[0, {n, n}];
    Do[A[[i, i + 1]] = 1, {i, 1, n - 1}];
    A[[n]] = -a;
    B = Transpose[{UnitVector[n, n]}];
    Cmat = {Reverse[beta]};
    Qc = Transpose[Table[Flatten[MatrixPower[A, k].B], {k, 0, n - 1}]];
    {A, B, Cmat, MatrixRank[Qc], Det[Qc]}
  ]], "Input"],
  Cell[BoxData[ToBoxes[
    desired = Most[CoefficientList[Expand[(s + 2) (s + 3) (s + 4)], s]];
    K = {desired - a};
    Acl = A - B.K;
    {K, Expand[CharacteristicPolynomial[Acl, s]], Eigenvalues[Acl]}
  ]], "Input"],
  Cell[BoxData[ToBoxes[
    TransferFunctionModel[StateSpaceModel[{A, B, Cmat, {{0}}}], s]
  ]], "Input"]
}]

7. Problems and Solutions

Problem 1 (Controllability of CCF): For \(n=3\), prove that the CCF pair \((\mathbf{A}_c,\mathbf{B}_c)\) is controllable.

Solution: For

\[ \mathbf{A}_c=\begin{bmatrix}0&1&0\\0&0&1\\-a_0&-a_1&-a_2\end{bmatrix}, \qquad \mathbf{B}_c=\begin{bmatrix}0\\0\\1\end{bmatrix}, \]

we obtain

\[ \mathbf{A}_c\mathbf{B}_c=\begin{bmatrix}0\\1\\-a_2\end{bmatrix}, \qquad \mathbf{A}_c^2\mathbf{B}_c=\begin{bmatrix}1\\-a_2\\a_2^2-a_1\end{bmatrix}. \]

Therefore,

\[ \mathcal{C}_c=\begin{bmatrix} 0&0&1\\ 0&1&-a_2\\ 1&-a_2&a_2^2-a_1 \end{bmatrix},\qquad \det(\mathcal{C}_c)=-1. \]

Since the determinant is nonzero, the rank is three, and the pair is controllable.

Problem 2 (Direct Pole Assignment): Consider \(s^3+6s^2+11s+6\). In CCF, compute the feedback gain that assigns closed-loop poles at \(-2,-3,-4\).

Solution: The open-loop coefficient vector is

\[ \begin{bmatrix}a_0&a_1&a_2\end{bmatrix} =\begin{bmatrix}6&11&6\end{bmatrix}. \]

The desired polynomial is

\[ (s+2)(s+3)(s+4)=s^3+9s^2+26s+24. \]

Thus

\[ \begin{bmatrix}\alpha_0&\alpha_1&\alpha_2\end{bmatrix} =\begin{bmatrix}24&26&9\end{bmatrix},\qquad \mathbf{K}_c=\begin{bmatrix}18&15&3\end{bmatrix}. \]

Problem 3 (Why Coefficient Sensitivity Matters): Let \(p(s)=s^2-2\rho s+\rho^2\). Explain why the double pole at \(s=\rho\) is sensitive to perturbation.

Solution: Since

\[ p'(s)=2s-2\rho,\qquad p'(\rho)=0, \]

the first-order root perturbation expression has a zero denominator at the repeated root. This indicates that arbitrarily small coefficient perturbations can split the repeated pole and move the roots in a way that is not well approximated by a regular first-order formula.

Problem 4 (Feedback Gain Transformation): Suppose \(\mathbf{x}=\mathbf{T}\mathbf{x}_c\) and a CCF gain \(\mathbf{K}_c\) has been designed. Derive the gain in physical coordinates.

Solution: Since \(\mathbf{x}_c=\mathbf{T}^{-1}\mathbf{x}\),

\[ u=-\mathbf{K}_c\mathbf{x}_c+r =-\mathbf{K}_c\mathbf{T}^{-1}\mathbf{x}+r. \]

Therefore, the physical-coordinate feedback gain is

\[ \boxed{\mathbf{K}=\mathbf{K}_c\mathbf{T}^{-1}}. \]

Problem 5 (Representation Choice): A fourth-order plant has physically meaningful state variables, but its transformation to CCF has \(\kappa_2(\mathbf{T})=10^8\). Should CCF be used as the final implementation basis?

Solution: Usually no. CCF can still be used to understand the algebra of pole assignment, but a condition number of \(10^8\) indicates severe amplification of numerical errors during coordinate conversion. A physical, modal, balanced, or directly computed state-feedback design is usually preferable for final implementation.

8. Summary

CCF is valuable because it gives an explicit controllable realization, makes the characteristic polynomial visible, and turns SISO pole placement into coefficient matching. Its drawbacks are equally important: CCF states are usually nonphysical, the representation may be highly sensitive to polynomial coefficient perturbations, and similarity transformations to CCF can be ill-conditioned. In practice, CCF is best treated as a theoretical and instructional coordinate system, while implementation should be checked against conditioning, scaling, sensor availability, and actuator structure.

9. References

  1. Kalman, R.E. (1960). On the general theory of control systems. Proceedings of the First International Congress on Automatic Control, 1, 481–492.
  2. Gilbert, E.G. (1963). Controllability and observability in multivariable control systems. SIAM Journal on Control, 1(2), 128–151.
  3. Ho, B.L., & Kalman, R.E. (1966). Effective construction of linear state-variable models from input/output functions. Regelungstechnik, 14, 545–548.
  4. Luenberger, D.G. (1967). Canonical forms for linear multivariable systems. IEEE Transactions on Automatic Control, 12(3), 290–293.
  5. Wonham, W.M. (1967). On pole assignment in multi-input controllable linear systems. IEEE Transactions on Automatic Control, 12(6), 660–665.
  6. Ackermann, J. (1972). Der Entwurf linearer Regelungssysteme im Zustandsraum. Regelungstechnik und Prozess-Datenverarbeitung, 7, 297–300.
  7. Moore, B.C. (1981). Principal component analysis in linear systems: controllability, observability, and model reduction. IEEE Transactions on Automatic Control, 26(1), 17–32.
  8. Kautsky, J., Nichols, N.K., & Van Dooren, P. (1985). Robust pole assignment in linear state feedback. International Journal of Control, 41(5), 1129–1155.