32. State space representation¶
The “standard” or most commonly used state space representation is
Take note that Seborg uses a slightly different version:
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]:
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]:
[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:
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]:
Note that ss2tf
returns a sympy Matrix. To get the SISO result, we need to index into the matrix:
[23]:
G[0, 0]
[23]:
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.]]))