Stability, poles, and zeros
Contents
12.4. Stability, poles, and zeros#
Most of the filters we’ve encountered so far have been stable, in the sense that if an input signal
As an example of an unstable system, consider
Plugging
which grows exponentially and without bound. This system is unstable.
While it is relatively straightforward to determine that (12.9) is unstable, it is not always so easy. This leads us to the question: how can we determine in general whether a given system is stable?
12.4.1. Transfer functions and polynomials#
Recall that for a linear IIR filter with feed-forward coefficients
More specifically,
Strictly speaking, these are polynomials in
It turns out that we can learn quite a bit about a filter’s behavior by examining the properties of these polynomials, and specifically, finding their roots by solving
12.4.1.1. Roots and algebra#
From the fundamental theorem of algebra, we have that any polynomial
has
For example, the polynomial
and then setting each factor to 0 independently (since either being 0 is sufficient to ensure the product is 0):
As a second example, consider
This also is a degree-2 polynomial, so it should have two solutions. However, if we factor the polynomial, it turns out that the solutions are not unique:
In this case, we say that there is one solution
The fundamental theorem of algebra does not require that all solutions be unique.
As a final example, take
or equivalently,
The fundamental theorem of algebra allows for roots to be complex.
12.4.2. Zeros#
For the moment, let’s focus on the numerator of the transfer function, i.e., the polynomial
The location in the complex plane of the zeros (roots of
Before going further, it may be helpful to visualize what we’re talking about. To make things concrete, we’ll analyze a Type-2 Chebyshev filter constructed by the following code block:
fs = 8000 # Sampling rate
fc = 1000 # Cutoff frequency
fstop = 1500 # Stop-band 1KHz above cutoff
attenuation = 80 # we'll require 80 dB attenuation in the stop band
ripple = 6 # we'll allow 6 dB ripple in the passband
# Compute the order for this filter, which in this case is 9
order, wn = scipy.signal.cheb2ord(fc, fstop, ripple, attenuation, fs=fs)
b, a = scipy.signal.cheby2(order, attenuation, wn, fs=fs)
Once we have the filter coefficients b
and a
, we can obtain the zeros of the filter by the following code:
zeros, _, _ = scipy.signal.tf2zpk(b, a)
The function tf2zpk
takes in a transfer function (represented by b
and a
) and returns the zeros (z), poles (p), and gain (k) of the filter.
We’ll come back to poles and gain later on, so for now we’ll retain just the zeros.
If we print the zeros, we’ll see a sequence of 9 complex numbers:
array([ 0.38141995-0.92440187j, 0.26658886-0.96381034j,
-0.02490664-0.99968978j, -0.57559933-0.81773187j,
-0.57559933+0.81773187j, -0.02490664+0.99968978j,
0.26658886+0.96381034j, 0.38141995+0.92440187j,
-1. +0.j ])
We can then plot these values in the complex plane, and compare them to the frequency response of the filter (generated by freqz
as in the previous section).
Fig. 12.5 Left: The zeros of an order-9 Type-2 Chebyshev filter with cutoff frequency
From Fig. 12.5, we can observe that the minima in the frequency response curve line up exactly with the zeros of the transfer function. More generally, frequencies near the zeros are also attenuated. You can think of the zeros as physical weights that drag down the frequency response.
This is only part of the story though: we’ll also need to look at the feedback coefficients to get the full picture.
12.4.3. Poles#
So far, we’ve found that the zeros of
The roots of
The intuition behind the name “pole” derives from the idea of the function
Fig. 12.6 The function
If the zeros of
Continuing our previous example, we’ll use the tf2zpk
function to compute the zeros and poles for our filter:
zeros, poles, gain = scipy.signal.tf2zpk(b, a)
Again, this will produce an array of 9 complex numbers for poles
:
array([0.6640386 -0.61973512j, 0.54573487-0.49475044j,
0.44310112-0.35506889j, 0.36783278-0.18847207j,
0.3394446 +0.j , 0.36783278+0.18847207j,
0.44310112+0.35506889j, 0.54573487+0.49475044j,
0.6640386 +0.61973512j])
corresponding to the 9 roots of
Fig. 12.7 Left: The poles (
Once you learn how to read pole-zero plots, they can be a great way to quickly understand the behavior of a system.
For example, in Fig. 12.7, we have zeros at high frequencies (angles approaching
The fact that zeros land exactly on the unit circle tells us that some frequencies will be attenuated severely (practically to zero).
12.4.4. Factored transfer functions#
Once we have the zeros
While this doesn’t change the definition of the filter — it is just another way of writing the same transfer function — this form does have some benefits.
First, it allows us to reason about high-order filters as a cascade of low-order filters:
This observation is often used to simplify the implementation of IIR filters and improve numerical stability (without changing the filter’s behavior). Most commonly, this is done by the second-order sections (SOS) method, e.g.:
# Construct a filter using second-order sections instead of
# feed-forward / feed-back coefficients b and a
sos = scipy.signal.cheby2(order, attenuation, wn, fs=fs, output='sos')
# Instead of lfilter with b and a, use sosfilt
y = scipy.signal.sosfilt(sos, x)
# Or for bidirectional filtering:
y = scipy.signal.sosfiltfilt(sos, x)
Second, it provides a way to reason about the interactions between poles and zeros.
Notably, if we have a pole which is also a zero, that is,
12.4.5. Stability#
Let’s return to our first example of an unstable system,
In standard form, this system has coefficients
The transfer function of this system is
Because the numerator has degree 0, this system has no zeros.
It has one pole, which we can find by solving
Contrast this filter with the first IIR filter we encountered in the previous chapter, the exponential moving average (11.1):
which has coefficients
So what’s different about these two examples?
It turns out that the stability of a filter depends on the location of the poles, and specifically, on their magnitude.
IIR stability
Let
The filter is stable if all poles
The filter is unstable if any pole
This fact gives us a simple test for stability of a filter:
Compute the poles of the filter, e.g., by
scipy.signal.tf2zpk
orscipy.signal.sos2zpk
.Discard any poles which are also zeros.
Compute the magnitude of all remaining poles.
If any pole has magnitude larger than 1, the filter is unstable.
As a corollary to this observation, note that FIR filters have no poles (since
Fig. 12.8 If all poles are contained within the unit circle, that is
12.4.6. Summary#
In this section, we’ve seen how the z-transform allows us to reason about the behavior of a filter through its transfer function.
Specifically, we examined the polynomials