# 32. State space representation¶

The “standard” or most commonly used state space representation is

\begin{align} \dot{x} &= Ax + Bu \\ y &= Cx + Du \end{align}

Take note that Seborg uses a slightly different version:

\begin{align} \dot{x} &= Ax + Bu + Ed\\ y &= Cx \end{align}

This second version can not represent pure gain systems as it effectively assumes $$D=0$$. It is also possible to stack $$u$$ and $$d$$ from the bottom form into one input vector, so the $$E$$ matrix really doesn’t add much. As you may infer, I prefer the top version and it is also the version used by most libraries.

:

import numpy
import numpy.linalg


## 32.1. Converting between state space and transfer function forms¶

There is good support in various libraries for converting systems with numeric coefficients between transfer function and state space representation.

### 32.1.1. Scipy.signal¶

The scipy.signal library handles conversion between transfer function coefficients and state space matrices easily. Note that scipi.signal only handles SISO transfer functions.

:

import scipy.signal

:

G = scipy.signal.lti(1, [1, 1])

:

G

:

TransferFunctionContinuous(
array([1.]),
array([1., 1.]),
dt: None
)


This object allows us to access the numerator and denominator

:

G.num, G.den

:

(array([1.]), array([1., 1.]))


To convert to state space, we can use the .to_ss() method

:

Gss = G.to_ss()

:

Gss.A, Gss.B, Gss.C, Gss.D

:

(array([[-1.]]), array([[1.]]), array([[1.]]), array([[0.]]))


We can build another object using the state space matrices instead of the Laplace form

:

G2ss = scipy.signal.lti(Gss.A, Gss.B, Gss.C, Gss.D)
G2ss

:

StateSpaceContinuous(
array([[-1.]]),
array([[1.]]),
array([[1.]]),
array([[0.]]),
dt: None
)


We can convert to transfer function form using .to_tf() (there is a small warning about bad coefficients, but the answer is reliable).

:

G2 = G2ss.to_tf()

C:\Users\Admin\anaconda3\lib\site-packages\scipy\signal\filter_design.py:1630: BadCoefficients: Badly conditioned filter coefficients (numerator): the results may be meaningless
warnings.warn("Badly conditioned filter coefficients (numerator): the "


We can now access the numerator and denominator again:

:

G2.num, G2.den

:

(array([1.]), array([1., 1.]))


Instead of building objects we can also use the functions in scipy.signal.lti_conversion:

:

scipy.signal.lti_conversion.tf2ss(1, [1, 1])

:

(array([[-1.]]), array([[1.]]), array([[1.]]), array([[0.]]))

:

scipy.signal.lti_conversion.ss2tf(-1, 1, 1, 0)

:

(array([[0., 1.]]), array([1., 1.]))


### 32.1.2. Control library¶

The control library (at least from version 0.8.0) does a good job with these conversions as well.

:

import control

:

Gtf = control.tf(, [1, 1])
Gtf

:

$$\frac{1}{s + 1}$$

In the control library we convert the system using ss (short for state space) to get a State Space representation:

:

Gss = control.ss(Gtf)
Gss

:

$\left( \begin{array}{rll|rll} -1\phantom{.}&\hspace{-1em}&\hspace{-1em}\phantom{\cdot}&1\phantom{.}&\hspace{-1em}&\hspace{-1em}\phantom{\cdot}\\ \hline 1\phantom{.}&\hspace{-1em}&\hspace{-1em}\phantom{\cdot}&0\phantom{.}&\hspace{-1em}&\hspace{-1em}\phantom{\cdot}\\ \end{array}\right)$
:

Gss.A

:

array([[-1.]])


## 32.2. Symbolic conversion¶

It is easy to convert state space models to transfer functions since the Laplace transform is a linear operator:

$\dot{x} = Ax + Bu \quad \therefore \quad sX(s) = AX(s) + BU(s) \quad X(s) = (sI - A)^{-1}BU(s)$
$y = Cx + Du \quad \therefore \quad Y(s) = CX(s) + DU(s) \quad Y(s) = \underbrace{(C(sI - A)^{-1}B + D)}_{G(s)}U(s)$

This conversion is handled for symbolic matrices by tbcontrol.symbolic.ss2tf

:

import sympy

:

import tbcontrol
tbcontrol.expectversion('0.1.8')
import tbcontrol.symbolic

:

s = sympy.symbols('s')

:

A, B, C, D = [sympy.Matrix(m) for m in [G2ss.A, G2ss.B, G2ss.C, G2ss.D]]

:

A, B, C, D

:

(Matrix([[-1.0]]), Matrix([[1.0]]), Matrix([[1.0]]), Matrix([[0.0]]))

:

G = tbcontrol.symbolic.ss2tf(A, B, C, D, s)
G

:

$\displaystyle \left[\begin{matrix}\frac{1.0}{s + 1.0}\end{matrix}\right]$

Note that ss2tf returns a sympy Matrix. To get the SISO result, we need to index into the matrix:

:

G[0, 0]

:

$\displaystyle \frac{1.0}{s + 1.0}$

## 32.3. Analysis¶

Notice that the roots of the characteristic function correspond with the eigenvalues of the A matrix. The numerator and denominator of control transfer functions are stored as lists of lists to accomodate MIMO systems.

:

Gtf.pole()

:

array([-1.])

:

numpy.roots(Gtf.den)

:

array([-1.])

:

numpy.linalg.eig(Gss.A)

:

(array([-1.]), array([[1.]]))