Data Science and Computing with Python for Pilots and Flight Test Engineers
Zeros and Poles in s-Plane
Regardless of the representation that we choose to create our system in, the zeros and poles of the system’s transfer function are easily obtained and plotted in the complex s-plane (Laplace domain).
The zeros of a transfer function tell us for which frequencies the forcing function in the differential equation of the system has no power.
The poles of the transfer function tell us about the free response and dynamic stability of the system. They inform us of the decay/growth time constant (if the pole is on the real axis of the s-plane) or oscillation frequency and total damping (if the pole (or complex conjugate pole pair) has a non-zero imaginary part). It turns out that if the real part of a pole is negative, the motion is damped (i.e. the system is dynamically stable and converges back to its equilibrium after a disturbance). If the real part of the pole is positive, the system exhibits exponential growth (and therefore dynamic instability). A system becomes dynamically unstable, as soon as even just one of its poles has a positive real part.
Pole Plotting Function
The function below can plot the zeros and poles in the complex s-plane, provided with feed it with a list or array of zeros and poles (we will show you afterwards, how to get those).
import numpy as np
import matplotlib.pyplot as plt
def splane_plot(zeros, poles, filename=None):
# Extract real and imaginary part of zeros:
zerox = [number.real for number in zeros]
zeroy = [number.imag for number in zeros]
# Extract real and imaginary part of poles:
polex = np.array([number.real for number in poles])
poley = np.array([number.imag for number in poles])
# Plot the complex s-Plane:
plt.scatter(zerox, zeroy, color='blue', marker='o')
plt.scatter(polex, poley, color='red', marker='x')
plt.ylabel('Imaginary')
plt.xlabel('Real')
plt.title('Zeros and Poles in $s$-Plane')
if filename is not None:
plt.savefig(filename)
plt.show()
Manual Computation of Zeros and Poles
Let us assume that we have a system described by the second-order linear differential equation
$$ \ddot x(t) + 2 \dot x(t) + 17 = f(t),$$
where \(x(t)\) is the (one-dimensional) state of the system and \(f(t)\) is the forcing function (input signal). As we have seen before, this system has transfer function
$$ H(s) = \frac{1}{s^2+2s+17}. $$
Since the zeros of the transfer function are the roots of the numerator and the poles of the transfer function are the roots of the denominator, we can compute the zeros and poles with the code below. We set up the numerator and denominator as polynomials (in Python as arrays of their coeffficients, starting with the highest-order non-zero coefficient) and we apply the NumPy function numpy.roots() to these coefficient arrays.
import numpy as np
tf_numerator = np.array([1])
tf_denominator = np.array([1, 2, 17]) # starting with highest coefficient.
zeros = np.roots(tf_numerator)
poles = np.roots(tf_denominator)
But we can also use the scipy.signal and control.matlab submodules for it, which make it more convenient and probably less prone to human error, as is shown below.
Zeros and Poles with the scipy.signal Submodule of the SciPy Library
Obtaining the zeros and poles of the transfer function of the control system after creating an instance of the system is straightforward.
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
We set up our control system in transfer function representation here, but we could use zpk (zeros, poles, gain) and state-space representations instead as well.
sys_TF = signal.TransferFunction([1],[1,2,17])
Zeros and poles are automatically stored as data members of any of the scipy.signal.lti subclasses and are directly accessible by typing:
zeros = sys.zeros
poles = sys.poles
To plot them in the s-plane with the function shown at the beginning of this lesson, you simply call the function with the lists of zeros and poles obtained above as arguments:
splane_plot(sys.zeros, sys.poles)