Data Science and Computing with Python for Pilots and Flight Test Engineers
Control System Creation – Code
Introduction
In this lesson we learn how to create control systems in Python, on which we can then later perform system analysis. We will do this with two different Python libraries, which you can then choose from, which one you prefer to work with. This lesson is preceded by the lesson Control System Creation – Theory, which provides some theoretical background to what we will do below (you may wish to consult it, before reading on, if you have not yet).
Let us set up a linear time-invariant system described by the linear ordinary differential equation with constant coefficients
$$ \ddot x(t) + 2 \dot x(t) + 17 x(t) = f(t). $$
As was explained in the lesson on control system creation theory, the transfer function for this system is
$$ H(s) = \frac{1}{s^2 + 2s + 17}. $$
The numerator of the transfer function is the constant polynomial \(N=1\), whereas the denominator is the polynomial \(D=s^2+2s+17\). \(s\) is the complex variable of the Laplace domain (s-domain, s-plane).
The poles of this transfer function are the roots of the denominator, i.e. the poles lie at \(s_1 = -1+4i\) and \(s_2 = -1-4i\). The transfer function has no zeros, because as a constant function the numerator has no roots.
Let us now set up this system in Python by creating an instance of a class, which is capable of representing such a system and which provides methods for its analysis. As we have seen in the previous lesson, a control system can be specified by one of three different representations:
- transfer function (by giving its coefficients) (abbreviation: tf)
- zeros, poles, and gain of the transfer function (abbreviation: zpk)
- state-space representation with the matrices \(A, B, C, D\) (abbreviation: ss)
We will explore all three options below. As usual, we can do everything either with the scipy.signal submodule of the SciPy library or with the control.matlab submodule of the python-control library.
Control System Creation with scipy.signal Submodule of SciPy Library
Linear time-invariant (LTI) control systems are setup with the signal.lti() base class. Instances of the lti class do not exist directly. Instead, the constructor of the base class sets them up as instances of one of its subclasses TransferFunction, ZerosPolesGain, or StateSpace.
We will learn below how to create such an instance with signal.lti(). Depending on the number of arguments we provide to signal.lti(), the constructor knows automatically which of the subclasses to instantiate: 2, 3, or 4 arguments for the three subclasses TransferFunction, ZerosPolesGain, and StateSpace, respectively. Equivalently, we can also instantiate the subclass directly.
Methods are provided to convert an existing system from one representation to another (they are all equivalent in principle, representing the same system). This will be explained further below.
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
Transfer Function Polynomial Coefficients (tf)
Both lines of code below set up the same system as an instance of the TransferFunction subclass of lti. You can choose to use either one or the other. Note that in both cases, we are supplying two arguments, when instantiating the class.
sys_LTI2 = signal.lti([1],[1,2,17])
sys_TF = signal.TransferFunction([1],[1,2,17])
Transfer Function Zeros, Poles, and Gain (z, p, k)
Note that the first list we provide is empty, because the transfer function does not have any zeros (since the polynomial in the numerator has no roots). We still need to provide an empty list as the argument though, because signal.lti() determines based on the number of arguments, which subclass of lti it is supposed to instantiate. For the zeros, poles, and gain definition of a system, the number of required arguments is three.
sys_LTI3 = signal.lti([], [-1+4j, -1-4j], 1)
sys_ZPK = signal.ZerosPolesGain([], [-1+4j, -1-4j], 1)
State Space Representation (ss)
For the state space representation, we need to first define the \(A, B, C, D\) matrices of the system, which correspond to the differential equation of the system given further above. These matrices simply reflect the fact that in this representation, we have converted the second-order linear differential equation into a system of two first-order ones.
A = np.array([[-2.0, -17.0],[1.0, 0.0]])
B = np.array([[1.0],[0.0]])
C = np.array([[0.0, 1.0]])
D = np.array([[0.0]])
# Use either of the two lines below to instantiate the subclass defining the system:
sys_LTI4 = signal.lti(A, B, C, D)
sys_SS = signal.StateSpace(A, B, C, D)
Control System Creation with control.matlab Submodule of python-control Library
With the control.matlab submodule, we will achieve the same as above, but the syntax is slightly different, because this subclass tries to emulate the user experience of the MATLAB Control System Toolbox.
import numpy as np
import matplotlib.pyplot as plt
from control.matlab import *
Transfer Function Polynomial Coefficients (tf)
sys_tf = tf([1],[1,2,17])
Transfer Function Zeros, Poles, and Gain (z, p, k)
State Space Representation (ss)
A = np.array([[-2, -17],[1.0, 0.0]])
B = np.array([[1.0],[0.0]])
C = np.array([[0.0, 1.0]])
D = np.array([[0.0]])
sys_ss = ss(A, B, C, D)