# 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.

[1]:

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.

[2]:

import scipy.signal

[3]:

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

[4]:

G

[4]:

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


This object allows us to access the numerator and denominator

[5]:

G.num, G.den

[5]:

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


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

[6]:

Gss = G.to_ss()

[7]:

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

[7]:

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


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

[8]:

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

[8]:

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).

[9]:

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:

[10]:

G2.num, G2.den

[10]:

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


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

[11]:

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

[11]:

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

[12]:

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

[12]:

(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.

[13]:

import control

[14]:

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

[14]:

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

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

[15]:

Gss = control.ss(Gtf)
Gss

[15]:

$\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)$
[16]:

Gss.A

[16]:

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

[17]:

import sympy

[18]:

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

[19]:

s = sympy.symbols('s')

[20]:

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

[21]:

A, B, C, D

[21]:

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

[22]:

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

[22]:

$\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:

[23]:

G[0, 0]

[23]:

$\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.

[24]:

Gtf.pole()

[24]:

array([-1.])

[25]:

numpy.roots(Gtf.den[0][0])

[25]:

array([-1.])

[26]:

numpy.linalg.eig(Gss.A)

[26]:

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