Chapter 16: Controllable Canonical Form

Lesson 2: Construction of CCF from Transfer Function Data

This lesson develops a systematic construction of the controllable canonical form directly from SISO transfer-function coefficients. We derive the companion state matrix, input vector, output vector, and direct feedthrough term; prove that the realization reproduces the original transfer function; and implement the construction in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica.

1. Problem Statement and Assumptions

In Lesson 1, the controllable canonical form was introduced as a companion realization whose state matrix exposes the coefficients of the characteristic polynomial. In this lesson, we start from transfer function data and construct the matrices \( A_c,B_c,C_c,D_c \) explicitly.

Consider a SISO continuous-time transfer function \( G(s) \):

\[ G(s)=\frac{N(s)}{D(s)}. \]

Assume the denominator is of degree \( n \) and has been normalized to monic form:

\[ D(s)=s^n+a_1s^{n-1}+a_2s^{n-2}+\cdots+a_{n-1}s+a_n. \]

The goal is to build a realization

\[ \dot{\mathbf{x} }=A_c\mathbf{x}+B_cu,\qquad y=C_c\mathbf{x}+D_cu \]

such that

\[ G(s)=C_c(sI-A_c)^{-1}B_c+D_c. \]

We assume that the transfer function is proper, meaning that the numerator degree is not larger than the denominator degree. If the numerator degree is strictly smaller than \( n \), then the system is strictly proper and \( D_c=0 \).

2. Construction Pipeline

The construction uses the denominator coefficients to form the companion matrix and uses the numerator coefficients to form the output row. The following diagram shows the complete workflow.

flowchart TD
  A["Transfer function data: numerator and denominator"] --> B["Normalize denominator to monic form"]
  B --> C["Extract denominator \ncoefficients a1 ... an"]
  C --> D["Build companion matrix Ac"]
  D --> E["Set input vector Bc = last basis vector"]
  B --> F["Separate direct feedthrough term Dc if \nnumerator degree equals denominator degree"]
  F --> G["Compute strictly proper remainder"]
  G --> H["Reverse remainder coefficients to form Cc"]
  H --> I["State-space realization Ac, Bc, Cc, Dc"]
        

The most common mistake is to place the numerator coefficients into \( C_c \) in the wrong order. The controllable canonical form used here has the state resolvent vector proportional to \( [1,\;s,\;s^2,\;\ldots,\;s^{n-1}]^T \), so the output row must be ordered from the constant coefficient to the highest coefficient of the strictly proper numerator.

3. Strictly Proper Transfer Function

First consider the strictly proper case:

\[ G(s)=\frac{b_1s^{n-1}+b_2s^{n-2}+\cdots+b_{n-1}s+b_n} {s^n+a_1s^{n-1}+a_2s^{n-2}+\cdots+a_{n-1}s+a_n}. \]

The controllable canonical form realization is

\[ 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_n & -a_{n-1} & -a_{n-2} & \cdots & -a_1 \end{bmatrix}, \quad B_c= \begin{bmatrix} 0\\ 0\\ \vdots\\ 0\\ 1 \end{bmatrix}. \]

The output row is

\[ C_c= \begin{bmatrix} b_n & b_{n-1} & \cdots & b_2 & b_1 \end{bmatrix}, \qquad D_c=0. \]

The denominator coefficients enter the last row of \( A_c \) in reverse order. The numerator coefficients enter \( C_c \) in reverse order. This reversal is not arbitrary; it follows from the resolvent calculation in the proof below.

flowchart LR
  A["Denominator: 1, a1, a2, ..., an"] --> B["Last row of Ac: -an, ..., -a2, -a1"]
  C["Strict numerator: b1, b2, ..., bn"] --> D["Output row Cc: bn, ..., b2, b1"]
  E["Input vector"] --> F["Bc has 1 in final state equation"]
        

4. Proper but Not Strictly Proper Transfer Function

Now suppose the numerator degree equals the denominator degree:

\[ N(s)=\beta_0s^n+\beta_1s^{n-1}+\beta_2s^{n-2} +\cdots+\beta_{n-1}s+\beta_n. \]

Since \( D(s) \) is monic, the direct feedthrough term is

\[ D_c=\beta_0. \]

Then subtract the direct part from the numerator:

\[ R(s)=N(s)-D_cD(s). \]

Expanding the remainder gives

\[ R(s)=r_1s^{n-1}+r_2s^{n-2}+\cdots+r_{n-1}s+r_n, \]

where

\[ r_i=\beta_i-D_ca_i,\qquad i=1,2,\ldots,n. \]

The matrices are then

\[ C_c= \begin{bmatrix} r_n & r_{n-1} & \cdots & r_2 & r_1 \end{bmatrix}, \qquad D_c=\beta_0. \]

Thus the non-strictly-proper case is reduced to a strictly proper realization of the remainder plus a direct feedthrough term.

5. Proof that the CCF Realization Reproduces the Transfer Function

We prove the strictly proper case first. Let \( \mathbf{z}(s)=(sI-A_c)^{-1}B_c \). Then

\[ (sI-A_c)\mathbf{z}(s)=B_c. \]

Write \( \mathbf{z}(s)= [z_1(s),z_2(s),\ldots,z_n(s)]^T \) . From the first \( n-1 \) rows of \( (sI-A_c)\mathbf{z}=B_c \), we obtain

\[ sz_1-z_2=0,\quad sz_2-z_3=0,\quad \ldots,\quad sz_{n-1}-z_n=0. \]

Therefore,

\[ z_2=sz_1,\quad z_3=s^2z_1,\quad \ldots,\quad z_n=s^{n-1}z_1. \]

The last row gives

\[ a_nz_1+a_{n-1}z_2+\cdots+a_2z_{n-1}+(s+a_1)z_n=1. \]

Substituting \( z_k=s^{k-1}z_1 \) yields

\[ \left(a_n+a_{n-1}s+\cdots+a_2s^{n-2} +a_1s^{n-1}+s^n\right)z_1=1. \]

Hence,

\[ z_1=\frac{1}{D(s)} \]

and

\[ (sI-A_c)^{-1}B_c= \frac{1}{D(s)} \begin{bmatrix} 1\\ s\\ s^2\\ \vdots\\ s^{n-1} \end{bmatrix}. \]

Therefore,

\[ C_c(sI-A_c)^{-1}B_c = \frac{b_n+b_{n-1}s+\cdots+b_2s^{n-2}+b_1s^{n-1} }{D(s)} = \frac{N(s)}{D(s)}. \]

This proves the strictly proper case. For the proper case, write

\[ \frac{N(s)}{D(s)} = D_c+\frac{R(s)}{D(s)}, \qquad R(s)=N(s)-D_cD(s). \]

Applying the strictly proper construction to \( R(s)/D(s) \) gives

\[ G(s)=C_c(sI-A_c)^{-1}B_c+D_c. \]

6. Worked Example

Construct the controllable canonical form for

\[ G(s)=\frac{2s^2+5s+3}{s^3+4s^2+6s+8}. \]

Here \( n=3 \) and

\[ a_1=4,\qquad a_2=6,\qquad a_3=8. \]

The numerator is strictly proper relative to the cubic denominator:

\[ b_1=2,\qquad b_2=5,\qquad b_3=3. \]

Therefore,

\[ A_c= \begin{bmatrix} 0 & 1 & 0\\ 0 & 0 & 1\\ -8 & -6 & -4 \end{bmatrix}, \quad B_c= \begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix}, \quad C_c= \begin{bmatrix} 3 & 5 & 2 \end{bmatrix}, \quad D_c=0. \]

To verify,

\[ (sI-A_c)^{-1}B_c = \frac{1}{s^3+4s^2+6s+8} \begin{bmatrix} 1\\ s\\ s^2 \end{bmatrix}. \]

Hence,

\[ C_c(sI-A_c)^{-1}B_c = \frac{3+5s+2s^2}{s^3+4s^2+6s+8} = \frac{2s^2+5s+3}{s^3+4s^2+6s+8}. \]

7. Algorithms and Implementations

The same algorithm can be implemented in any programming language. The required input is only the numerator and denominator coefficient arrays in descending powers of \( s \).

Chapter16_Lesson2.py

"""
Chapter16_Lesson2.py

Construction of Controllable Canonical Form (CCF) from SISO transfer-function data.
"""

from __future__ import annotations

import numpy as np


def trim_leading_zeros(coeffs, tol=1e-12):
    coeffs = np.asarray(coeffs, dtype=float).flatten()
    if coeffs.size == 0:
        raise ValueError("Coefficient vector cannot be empty.")
    idx = 0
    while idx < coeffs.size - 1 and abs(coeffs[idx]) < tol:
        idx += 1
    return coeffs[idx:]


def controllable_canonical_form(num, den):
    num = trim_leading_zeros(num)
    den = trim_leading_zeros(den)

    if den[0] == 0:
        raise ValueError("Leading denominator coefficient must be nonzero.")

    leading = den[0]
    den = den / leading
    num = num / leading

    n = len(den) - 1
    if n < 1:
        raise ValueError("Denominator degree must be at least one.")
    if len(num) > n + 1:
        raise ValueError("Improper transfer function: numerator degree exceeds denominator degree.")

    num_full = np.zeros(n + 1)
    num_full[-len(num):] = num

    Dfeed = float(num_full[0])
    rem = num_full.copy()
    rem -= Dfeed * den
    rem_strict = rem[1:]

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

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

    C = rem_strict[::-1].reshape(1, n)

    return A, B, C, np.array([[Dfeed]])


def transfer_from_state_space(A, B, C, Dfeed):
    import scipy.signal as sig
    num, den = sig.ss2tf(A, B, C, Dfeed)
    return num[0], den


def main():
    num = [2, 5, 3]
    den = [1, 4, 6, 8]

    A, B, C, Dfeed = controllable_canonical_form(num, den)

    print("A =\n", A)
    print("B =\n", B)
    print("C =\n", C)
    print("D =\n", Dfeed)

    try:
        num_back, den_back = transfer_from_state_space(A, B, C, Dfeed)
        print("Reconstructed numerator coefficients:", num_back)
        print("Reconstructed denominator coefficients:", den_back)
    except Exception as exc:
        print("SciPy validation skipped:", exc)


if __name__ == "__main__":
    main()
      

Chapter16_Lesson2.cpp

/*
Chapter16_Lesson2.cpp

Construction of Controllable Canonical Form (CCF) from SISO transfer-function data.

Compile:
    g++ -std=c++17 Chapter16_Lesson2.cpp -o Chapter16_Lesson2
*/

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

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

std::vector<double> trimLeadingZeros(const std::vector<double>& c, double tol = 1e-12) {
    if (c.empty()) {
        throw std::invalid_argument("Coefficient vector cannot be empty.");
    }
    std::size_t i = 0;
    while (i + 1 < c.size() && std::abs(c[i]) < tol) {
        ++i;
    }
    return std::vector<double>(c.begin() + static_cast<long>(i), c.end());
}

struct StateSpace {
    Matrix A;
    Matrix B;
    Matrix C;
    double D;
};

StateSpace controllableCanonicalForm(std::vector<double> num, std::vector<double> den) {
    num = trimLeadingZeros(num);
    den = trimLeadingZeros(den);

    if (std::abs(den[0]) < 1e-12) {
        throw std::invalid_argument("Leading denominator coefficient must be nonzero.");
    }

    const double leading = den[0];
    for (double& v : den) v /= leading;
    for (double& v : num) v /= leading;

    const int n = static_cast<int>(den.size()) - 1;
    if (n < 1) {
        throw std::invalid_argument("Denominator degree must be at least one.");
    }
    if (static_cast<int>(num.size()) > n + 1) {
        throw std::invalid_argument("Improper transfer function: numerator degree exceeds denominator degree.");
    }

    std::vector<double> numFull(n + 1, 0.0);
    const int offset = (n + 1) - static_cast<int>(num.size());
    for (int i = 0; i < static_cast<int>(num.size()); ++i) {
        numFull[offset + i] = num[i];
    }

    const double Dfeed = numFull[0];
    std::vector<double> rem(n + 1, 0.0);
    for (int i = 0; i <= n; ++i) {
        rem[i] = numFull[i] - Dfeed * den[i];
    }

    Matrix A(n, std::vector<double>(n, 0.0));
    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] = -den[n - j];
    }

    Matrix B(n, std::vector<double>(1, 0.0));
    B[n - 1][0] = 1.0;

    Matrix C(1, std::vector<double>(n, 0.0));
    for (int j = 0; j < n; ++j) {
        C[0][j] = rem[n - j];
    }

    return {A, B, C, Dfeed};
}

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) << std::setprecision(6) << std::fixed << v << " ";
        }
        std::cout << "\n";
    }
}

int main() {
    try {
        std::vector<double> num{2, 5, 3};
        std::vector<double> den{1, 4, 6, 8};

        StateSpace ss = controllableCanonicalForm(num, den);

        printMatrix("A", ss.A);
        printMatrix("B", ss.B);
        printMatrix("C", ss.C);
        std::cout << "D = " << ss.D << "\n";
    } catch (const std::exception& ex) {
        std::cerr << "Error: " << ex.what() << "\n";
        return 1;
    }
    return 0;
}
      

Chapter16_Lesson2.java

/*
Chapter16_Lesson2.java

Construction of Controllable Canonical Form (CCF) from SISO transfer-function data.

Compile:
    javac Chapter16_Lesson2.java
Run:
    java Chapter16_Lesson2
*/

import java.util.Arrays;

public class Chapter16_Lesson2 {
    static class StateSpace {
        double[][] A;
        double[][] B;
        double[][] C;
        double D;

        StateSpace(double[][] A, double[][] B, double[][] C, double D) {
            this.A = A;
            this.B = B;
            this.C = C;
            this.D = D;
        }
    }

    static double[] trimLeadingZeros(double[] coeffs) {
        if (coeffs.length == 0) {
            throw new IllegalArgumentException("Coefficient vector cannot be empty.");
        }
        int i = 0;
        while (i + 1 < coeffs.length && Math.abs(coeffs[i]) < 1e-12) {
            i++;
        }
        return Arrays.copyOfRange(coeffs, i, coeffs.length);
    }

    static StateSpace controllableCanonicalForm(double[] numerator, double[] denominator) {
        double[] num = trimLeadingZeros(numerator);
        double[] den = trimLeadingZeros(denominator);

        if (Math.abs(den[0]) < 1e-12) {
            throw new IllegalArgumentException("Leading denominator coefficient must be nonzero.");
        }

        double leading = den[0];
        for (int i = 0; i < den.length; i++) den[i] /= leading;
        for (int i = 0; i < num.length; i++) num[i] /= leading;

        int n = den.length - 1;
        if (n < 1) {
            throw new IllegalArgumentException("Denominator degree must be at least one.");
        }
        if (num.length > n + 1) {
            throw new IllegalArgumentException("Improper transfer function: numerator degree exceeds denominator degree.");
        }

        double[] numFull = new double[n + 1];
        int offset = (n + 1) - num.length;
        for (int i = 0; i < num.length; i++) {
            numFull[offset + i] = num[i];
        }

        double dFeed = numFull[0];
        double[] rem = new double[n + 1];
        for (int i = 0; i <= n; i++) {
            rem[i] = numFull[i] - dFeed * den[i];
        }

        double[][] A = new double[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] = -den[n - j];
        }

        double[][] B = new double[n][1];
        B[n - 1][0] = 1.0;

        double[][] C = new double[1][n];
        for (int j = 0; j < n; j++) {
            C[0][j] = rem[n - j];
        }

        return new StateSpace(A, B, C, dFeed);
    }

    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[] num = {2, 5, 3};
        double[] den = {1, 4, 6, 8};

        StateSpace ss = controllableCanonicalForm(num, den);

        printMatrix("A", ss.A);
        printMatrix("B", ss.B);
        printMatrix("C", ss.C);
        System.out.printf("D = %.6f%n", ss.D);
    }
}
      

Chapter16_Lesson2.m

% Chapter16_Lesson2.m
%
% Construction of Controllable Canonical Form (CCF) from SISO transfer-function data.

clear; clc;

num = [2 5 3];
den = [1 4 6 8];

[A, B, C, D] = controllableCanonicalForm(num, den);

disp('A ='); disp(A);
disp('B ='); disp(B);
disp('C ='); disp(C);
disp('D ='); disp(D);

try
    [Atf, Btf, Ctf, Dtf] = tf2ss(num, den);
    disp('MATLAB tf2ss realization:');
    disp('Atf ='); disp(Atf);
    disp('Btf ='); disp(Btf);
    disp('Ctf ='); disp(Ctf);
    disp('Dtf ='); disp(Dtf);
catch ME
    disp(['tf2ss comparison skipped: ', ME.message]);
end

function [A, B, C, Dfeed] = controllableCanonicalForm(num, den)
    num = trimLeadingZeros(num);
    den = trimLeadingZeros(den);

    if abs(den(1)) < 1e-12
        error('Leading denominator coefficient must be nonzero.');
    end

    leading = den(1);
    den = den / leading;
    num = num / leading;

    n = length(den) - 1;
    if n < 1
        error('Denominator degree must be at least one.');
    end
    if length(num) > n + 1
        error('Improper transfer function: numerator degree exceeds denominator degree.');
    end

    numFull = zeros(1, n + 1);
    numFull(end - length(num) + 1:end) = num;

    Dfeed = numFull(1);
    rem = numFull - Dfeed * den;

    A = zeros(n, n);
    if n > 1
        A(1:n-1, 2:n) = eye(n-1);
    end
    A(n, :) = -fliplr(den(2:end));

    B = zeros(n, 1);
    B(n) = 1;

    C = fliplr(rem(2:end));
end

function out = trimLeadingZeros(c)
    idx = 1;
    while idx < length(c) && abs(c(idx)) < 1e-12
        idx = idx + 1;
    end
    out = c(idx:end);
end
      

Chapter16_Lesson2.nb

(* Chapter16_Lesson2.nb *)

ClearAll[TrimLeadingZeros, ControllableCanonicalForm];

TrimLeadingZeros[c_List, tol_: 10^-12] := Module[{i = 1},
  If[Length[c] == 0, Print["Coefficient vector cannot be empty."]; Abort[]];
  While[i < Length[c] && Abs[c[[i]]] < tol, i++];
  c[[i ;;]]
];

ControllableCanonicalForm[numIn_List, denIn_List] := Module[
  {num, den, leading, n, numFull, dFeed, rem, A, B, C},
  
  num = TrimLeadingZeros[N[numIn]];
  den = TrimLeadingZeros[N[denIn]];
  
  If[Abs[den[[1]]] < 10^-12,
    Print["Leading denominator coefficient must be nonzero."];
    Abort[];
  ];
  
  leading = den[[1]];
  den = den/leading;
  num = num/leading;
  
  n = Length[den] - 1;
  If[n < 1, Print["Denominator degree must be at least one."]; Abort[]];
  If[Length[num] > n + 1,
    Print["Improper transfer function: numerator degree exceeds denominator degree."];
    Abort[];
  ];
  
  numFull = ConstantArray[0, n + 1];
  numFull[[-Length[num] ;;]] = num;
  
  dFeed = numFull[[1]];
  rem = numFull - dFeed den;
  
  A = ConstantArray[0, {n, n}];
  Do[A[[i, i + 1]] = 1, {i, 1, n - 1}];
  A[[n, All]] = -Reverse[den[[2 ;;]]];
  
  B = ConstantArray[0, {n, 1}];
  B[[n, 1]] = 1;
  
  C = {Reverse[rem[[2 ;;]]]};
  
  <|"A" -> A, "B" -> B, "C" -> C, "D" -> { {dFeed} }|>
];

num = {2, 5, 3};
den = {1, 4, 6, 8};

ss = ControllableCanonicalForm[num, den];

MatrixForm[ss["A"]]
MatrixForm[ss["B"]]
MatrixForm[ss["C"]]
MatrixForm[ss["D"]]

s = Symbol["s"];
Gss = ss["C"].Inverse[s IdentityMatrix[Length[ss["A"]]] - ss["A"]].ss["B"] + ss["D"];
FullSimplify[Gss[[1, 1]]]
      

8. MATLAB/Simulink Interpretation

In MATLAB, the realization can be constructed from scratch as shown above, or compared with tf2ss. Note that MATLAB may return an equivalent realization in a different canonical convention. Equality of transfer functions is the important criterion:

\[ C_c(sI-A_c)^{-1}B_c+D_c = C_m(sI-A_m)^{-1}B_m+D_m. \]

In Simulink, the matrices \( A_c,B_c,C_c,D_c \) can be inserted into a State-Space block. For the worked example, use

\[ A_c= \begin{bmatrix} 0 & 1 & 0\\ 0 & 0 & 1\\ -8 & -6 & -4 \end{bmatrix}, \quad B_c= \begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix}, \quad C_c= \begin{bmatrix} 3 & 5 & 2 \end{bmatrix}, \quad D_c=0. \]

The Simulink output response should match the transfer-function block with numerator [2 5 3] and denominator [1 4 6 8].

9. Numerical and Conceptual Remarks

The controllable canonical form is mathematically transparent, but it is not always numerically well conditioned. For high-order systems, small changes in polynomial coefficients can produce large changes in root locations and large sensitivity in the companion matrix. Therefore, CCF is excellent for analysis and teaching, but balanced, modal, or numerically scaled realizations may be preferable for high-order computational work.

Nevertheless, CCF is fundamental because it directly links three objects:

\[ \text{denominator coefficients} \quad \Longleftrightarrow \quad A_c \quad \Longleftrightarrow \quad \text{open-loop poles}. \]

It also prepares the ground for pole-placement design in later chapters, because the last row of \( A_c \) is directly modified by state feedback.

10. Problems and Solutions

Problem 1: Construct the controllable canonical form for

\[ G(s)=\frac{4s+7}{s^2+3s+2}. \]

Solution:

Here \( n=2 \), \( a_1=3 \), \( a_2=2 \), \( b_1=4 \), and \( b_2=7 \). Therefore,

\[ A_c= \begin{bmatrix} 0 & 1\\ -2 & -3 \end{bmatrix}, \quad B_c= \begin{bmatrix} 0\\ 1 \end{bmatrix}, \quad C_c= \begin{bmatrix} 7 & 4 \end{bmatrix}, \quad D_c=0. \]

Verification:

\[ (sI-A_c)^{-1}B_c= \frac{1}{s^2+3s+2} \begin{bmatrix} 1\\ s \end{bmatrix}. \]

Hence,

\[ C_c(sI-A_c)^{-1}B_c = \frac{7+4s}{s^2+3s+2} = \frac{4s+7}{s^2+3s+2}. \]

Problem 2: Construct the controllable canonical form for the proper transfer function

\[ G(s)=\frac{2s^2+9s+10}{s^2+4s+5}. \]

Solution:

Since numerator and denominator have the same degree, \( D_c=2 \). The remainder is

\[ R(s)=(2s^2+9s+10)-2(s^2+4s+5)=s. \]

Thus \( r_1=1 \) and \( r_2=0 \). The denominator gives \( a_1=4 \) and \( a_2=5 \). Therefore,

\[ A_c= \begin{bmatrix} 0 & 1\\ -5 & -4 \end{bmatrix}, \quad B_c= \begin{bmatrix} 0\\ 1 \end{bmatrix}, \quad C_c= \begin{bmatrix} 0 & 1 \end{bmatrix}, \quad D_c=2. \]

Verification:

\[ C_c(sI-A_c)^{-1}B_c+D_c = \frac{s}{s^2+4s+5}+2 = \frac{2s^2+9s+10}{s^2+4s+5}. \]

Problem 3: Let

\[ G(s)=\frac{b_1s^{n-1}+b_2s^{n-2}+\cdots+b_n} {s^n+a_1s^{n-1}+\cdots+a_n}. \]

Prove that the CCF realization is controllable.

Solution:

The controllability matrix is

\[ \mathcal{C} = \begin{bmatrix} B_c & A_cB_c & A_c^2B_c & \cdots & A_c^{n-1}B_c \end{bmatrix}. \]

For the companion pair \( (A_c,B_c) \), \( B_c=e_n \), and repeated multiplication by \( A_c \) generates a basis whose determinant is nonzero. More directly, the PBH condition for the companion pair gives

\[ \operatorname{rank} \begin{bmatrix} \lambda I-A_c & B_c \end{bmatrix} =n \quad \text{for every eigenvalue } \lambda \text{ of } A_c. \]

If a left eigenvector \( q^T \) satisfied \( q^TA_c=\lambda q^T \) and \( q^TB_c=0 \), then its last component would be zero. The companion recurrence would force all other components to be zero, contradicting the definition of an eigenvector. Hence the pair is controllable.

Problem 4: Explain why the numerator coefficients do not affect controllability of the CCF realization.

Solution:

Controllability depends only on the pair \( (A_c,B_c) \). In the CCF construction, \( A_c \) is determined only by the denominator coefficients and \( B_c=e_n \). The numerator coefficients appear only in \( C_c \) and \( D_c \). Therefore, changing the numerator changes the input-output map but does not change reachability of the internal state coordinates in this realization.

Problem 5: For

\[ G(s)=\frac{s^3+2s^2+s+4}{s^3+5s^2+8s+6}, \]

find \( D_c \) and \( C_c \).

Solution:

Because the numerator and denominator have the same degree, \( D_c=1 \). The remainder is

\[ R(s)= (s^3+2s^2+s+4)-(s^3+5s^2+8s+6) = -3s^2-7s-2. \]

Hence \( r_1=-3 \), \( r_2=-7 \), and \( r_3=-2 \). Therefore,

\[ C_c= \begin{bmatrix} -2 & -7 & -3 \end{bmatrix}, \qquad D_c=1. \]

11. Summary

The controllable canonical form can be constructed directly from transfer-function coefficients. The denominator polynomial determines the companion matrix \( A_c \) and the input vector \( B_c \). The strictly proper numerator determines the output row \( C_c \). If the transfer function is proper but not strictly proper, the leading numerator coefficient becomes the direct feedthrough term \( D_c \), and \( C_c \) is constructed from the strictly proper remainder. The proof follows from the explicit expression for \( (sI-A_c)^{-1}B_c \).

12. References

  1. Kalman, R.E. (1963). Mathematical description of linear dynamical systems. Journal of the Society for Industrial and Applied Mathematics, Series A: Control, 1(2), 152–192.
  2. Gilbert, E.G. (1963). Controllability and observability in multivariable control systems. SIAM Journal on Control, 1(2), 128–151.
  3. Rosenbrock, H.H. (1962). The stability of multivariable systems. Proceedings of the Institution of Electrical Engineers, 109(43), 457–464.
  4. Ho, B.L., & Kalman, R.E. (1966). Effective construction of linear state-variable models from input/output functions. Regelungstechnik, 14, 545–548.
  5. Luenberger, D.G. (1967). Canonical forms for linear multivariable systems. IEEE Transactions on Automatic Control, 12(3), 290–293.
  6. Wonham, W.M. (1967). On pole assignment in multi-input controllable linear systems. IEEE Transactions on Automatic Control, 12(6), 660–665.
  7. Kailath, T. (1980). Linear systems theory and realization methods. Proceedings of the IEEE, 68(1), 79–96.