10. Routh-Hurwitz Stability

Lecture 10: Stability Analysis: the Routh-Hurwitz Approach

A useful feedback loop needs a good transient response, stability, and small steady-state errors. The transient-response calculations in the preceding lectures assume that the motion eventually settles. We now examine that assumption directly. The Routh-Hurwitz criterion lets us count right-half-plane poles from a characteristic polynomial and determine a stable gain range without solving for every root.

Natural response and closed-loop poles

The total response of a linear system separates into a forced response and a natural response:

$$y_{\mathrm{total}}(t)=y_{\mathrm{forced}}(t)+y_{\mathrm{natural}}(t).$$

The natural response is the motion caused by initial conditions when the external input is zero. Its modes are determined by the poles of the closed-loop dynamics, or equivalently by the roots of the closed-loop characteristic polynomial $D_{CL}(s)$.

  • Asymptotically stable: every natural mode decays. All poles lie in the open left half-plane (LHP).
  • Marginally stable: every natural mode remains bounded, but some do not decay. In the scalar modal description, imaginary-axis poles are simple and all other poles lie in the LHP.
  • Unstable: some natural mode grows without bound. A right-half-plane (RHP) pole produces exponential growth; a repeated imaginary-axis pole in a scalar mode produces a growing polynomial envelope.
Example pole-zero maps for decaying, bounded nondecaying, and growing modes. Crosses mark poles; circles mark zeros.

Example pole-zero maps for decaying, bounded nondecaying, and growing modes. Crosses mark poles; circles mark zeros.

Qualitative response illustrations retained from the lecture. These curves are not simulations of the three transfer functions in the pole-zero maps.

Qualitative response illustrations retained from the lecture. These curves are not simulations of the three transfer functions in the pole-zero maps.

An unstable oscillation need not tend to positive infinity: its amplitude can grow while its sign alternates. Repeated LHP poles, by contrast, still yield decaying terms such as $t^k e^{-at}$ with $a\gt 0$.

Use the characteristic polynomial of the full closed-loop dynamics. A pole-zero cancellation in a reference-to-output transfer function can hide an internal unstable mode. In state space, imaginary-axis eigenvalues must be semisimple, meaning their Jordan blocks have size one; repeated eigenvalues alone do not imply instability.

Why avoid computing every pole?

Computers are indispensable, but a root calculation can be sensitive when roots are repeated or nearly repeated. Consider

$$D(s)=(s+1)^n.$$

Every exact root is $-1$. If we expand the polynomial and pass its coefficients to a floating-point root finder, the computed roots can form a cluster around $-1$. The cluster reflects numerical sensitivity, not different physical poles.

# Author: Xu Chen, University of Washington
from math import comb
import numpy as np

for n in (12, 20):
    a = [comb(n, k) for k in range(n + 1)]
    p = np.roots(a)
    print(n, np.max(np.abs(p + 1)))
Numerical roots of expanded repeated-pole polynomials. The exact roots all coincide at minus one; computed clusters depend on the numerical platform.

Numerical roots of expanded repeated-pole polynomials. The exact roots all coincide at minus one; computed clusters depend on the numerical platform.

The sensitivity also has a simple analytical example. If a small constant perturbation changes the polynomial to

$$(s+1)^{12}+\delta=0,$$

then every perturbed root satisfies $|s+1|=|\delta|^{1/12}$. Even $\delta=10^{-12}$ gives a displacement of $0.1$.

Python experiment: vary the polynomial order

Use the live repeated-pole Python demo and the repository setup instructions. From the repository root, run:

python demos/repeated_poles_app.py

Open the printed local URL and vary $n$ from 2 to 40. Compare the exact repeated root with the computed cluster. The browser runs Python/NumPy in a worker; its first load requires internet access to obtain Pyodide and NumPy. To compute directly with local Python instead, run python demos/repeated_poles_app.py --n 20.

Open this laboratory in a full window

Routh avoids explicit root finding when we need a pole count or a symbolic gain condition. It does not make floating-point arithmetic infallible: numerical Routh calculations also require care near zero pivots and stability boundaries.

Quick checks before building an array

Assume a real polynomial with positive leading coefficient. Strict LHP stability requires every coefficient to be positive.

  1. Mixed nonzero coefficient signs rule out strict stability. For example, $s^2-4s+4=(s-2)^2$ has RHP poles at $+2$.
  2. A missing power rules out strict stability. The examples $s^2-1$ and $s^2+4$ have roots $\pm1$ and $\pm2j$, respectively. The former is unstable; the latter has bounded, nondecaying natural modes.
  3. Positive coefficients are necessary, not sufficient. In particular,
$$s^3+s^2+4s+30=(s+3)(s^2-2s+10)$$

has roots $-3$ and $1\pm3j$, so it is unstable despite having positive coefficients.

Real, imaginary, and quadrantal symmetric pole patterns.

Real, imaginary, and quadrantal symmetric pole patterns.

The quadrantal example is

$$s^4+6s^2+25=(s^2-2s+5)(s^2+2s+5),$$

with roots $\pm1\pm2j$. An even polynomial has roots symmetric about the origin; real coefficients also give conjugate pairs. A missing coefficient alone does not guarantee this symmetry. For example, $s^3+s+1$ has a missing power but is not an even polynomial.

Constructing the Routh array

The Routh criterion counts RHP roots through sign changes down the first column. Start with a quartic:

$$D(s)=a_4s^4+a_3s^3+a_2s^2+a_1s+a_0.$$

Place alternating coefficients in the first two rows, padding with zeros as needed:

$$\begin{array}{c|ccc} s^4&a_4&a_2&a_0\\ s^3&a_3&a_1&0\\ s^2&b_1&b_2&0\\ s^1&c_1&0&0\\ s^0&b_2&0&0 \end{array}$$

Each new coefficient uses two rows above it. Multiply the lower-left entry by the upper-right entry, subtract the upper-left entry times the lower-right entry, and divide by the lower-left pivot:

$$\begin{aligned} b_1&=\frac{a_3a_2-a_4a_1}{a_3},& b_2&=\frac{a_3a_0-a_4(0)}{a_3}=a_0,\\ c_1&=\frac{b_1a_1-a_3b_2}{b_1}. \end{aligned}$$

These formulas assume nonzero pivots. A positive scaling of an entire row does not change its contribution to the sign count. Resolve zero pivots and entire zero rows before drawing a conclusion. No RHP poles and no imaginary-axis poles means asymptotic stability; a zero sign-change count alone does not exclude marginal stability.

Worked quartic: table first, then arithmetic

For $D(s)=s^4+6s^3+11s^2+6s+2$, the completed array is

$$\begin{array}{c|ccc} s^4&1&11&2\\ s^3&6&6&0\\ s^2&10&2&0\\ s^1&24/5&0&0\\ s^0&2&0&0 \end{array}$$
Diagonal-product construction for the first coefficient of the next row.

Diagonal-product construction for the first coefficient of the next row.

The three calculations are

$$\begin{aligned} b_1&=\frac{6(11)-1(6)}6=10,\\ b_2&=\frac{6(2)-1(0)}6=2,\\ c_1&=\frac{10(6)-6(2)}{10}=\frac{24}{5}. \end{aligned}$$

The first column is $1,6,10,24/5,2$. Every entry is positive, and no exceptional row occurs, so all four poles lie in the LHP.

Stable open loop does not guarantee stable feedback

Consider unity negative feedback around

$$G(s)=\frac{1000}{(s+2)(s+3)(s+5)},\qquad H(s)=1.$$
Unity negative feedback: the output is subtracted from the reference before entering G.

Unity negative feedback: the output is subtracted from the reference before entering G.

The open-loop poles $-2,-3,-5$ are stable. To analyze the feedback loop, first form its transfer function:

$$\begin{aligned} T(s)&=\frac{G(s)}{1+G(s)}\\ &=\frac{1000}{(s+2)(s+3)(s+5)+1000}. \end{aligned}$$

Expanding the denominator gives

$$D_{CL}(s)=s^3+10s^2+31s+1030.$$

The Routh array and the calculation of its negative entry are

$$\begin{array}{c|cc} s^3&1&31\\ s^2&10&1030\\ s^1&-72&0\\ s^0&1030&0 \end{array} \qquad \frac{10(31)-1(1030)}{10}=-72.$$

The signs $+,+,-,+$ change twice. The closed loop has two RHP poles and is unstable. This is why stability must be checked after closing the loop.

Special case 1: a zero first entry

A zero first entry with other nonzero entries in the row prevents the next division. Replace only that first entry by $\epsilon\gt 0$, continue symbolically, and take $\epsilon\to0^+$ after finding the signs.

For

$$D(s)=s^5+2s^4+3s^3+6s^2+5s+3,$$

the first two rows are $[1,3,5]$ and $[2,6,3]$. The next row is $[0,7/2,0]$: this is not an entire row of zeros. Replacing the first zero gives

$$\begin{array}{c|ccc} s^5&1&3&5\\ s^4&2&6&3\\ s^3&\epsilon&7/2&0\\ s^2&6-7/\epsilon&3&0\\ s^1&\dfrac72-\dfrac{3\epsilon}{6-7/\epsilon}&0&0\\ s^0&3&0&0 \end{array}$$

Keep the small pivot symbolic until its sign is known. The limits are

$$6-\frac7\epsilon\longrightarrow-\infty,\qquad \frac72-\frac{3\epsilon}{6-7/\epsilon}\longrightarrow\frac72\gt 0.$$

The first-column signs are $+,+,+,-,+,+$. Two sign changes mean two RHP poles, so the system is unstable.

Special case 2: an entire row of zeros

An entire zero row signals an origin-symmetric factor. Its roots may be real pairs, imaginary pairs, or quadrantal groups; the zero row does not by itself prove marginal stability. Use the row immediately above it to form an auxiliary polynomial $A(s)$, differentiate, and replace the zero row with the coefficients of $A'(s)$.

Consider

$$D(s)=s^5+7s^4+6s^3+42s^2+8s+56.$$

The zero row appears at $s^3$:

$$\begin{array}{c|ccc} s^5&1&6&8\\ s^4&7&42&56\\ \boxed{s^3}&\boxed{0}&\boxed{0}&\boxed{0} \end{array}$$

Both computed entries vanish:

$$\frac{7(6)-1(42)}7=0,\qquad\frac{7(8)-1(56)}7=0.$$

The row above gives

$$A(s)=7s^4+42s^2+56,\qquad A'(s)=28s^3+84s.$$

Replace the $s^3$ row by $[28,84,0]$. Divide it by 28 and the $s^4$ row by 7, both positive scalings. The completed array is

$$\begin{array}{c|ccc} s^5&1&6&8\\ s^4&1&6&8\\ \boxed{s^3}&\boxed{1}&\boxed{3}&\boxed{0}\\ s^2&3&8&0\\ s^1&1/3&0&0\\ s^0&8&0&0 \end{array}$$

Here $s^2$ starts with $(1\cdot6-1\cdot3)/1=3$, and $s^1$ starts with $(3\cdot3-1\cdot8)/3=1/3$. There are no RHP poles. To locate the symmetric roots, factor

$$P(s)=\frac{A(s)}7=(s^2+2)(s^2+4),\qquad D(s)=(s+7)P(s).$$
The zero-row example has simple imaginary-axis poles and one LHP pole.

The zero-row example has simple imaginary-axis poles and one LHP pole.

The poles are $\pm j\sqrt2$, $\pm2j$, and $-7$. The imaginary-axis poles are simple, so the natural response is bounded but does not decay: the system is marginally stable.

Finding a stabilizing gain range

Now use the same unity-negative-feedback structure with

$$G(s)=\frac{K}{s(s+7)(s+11)}.$$

The closed-loop transfer function is

$$T(s)=\frac{G(s)}{1+G(s)}=\frac{K}{s^3+18s^2+77s+K}.$$

Its Routh array is

$$\begin{array}{c|cc} s^3&1&77\\ s^2&18&K\\ s^1&\dfrac{1386-K}{18}&0\\ s^0&K&0 \end{array}$$

Every first-column entry is positive precisely when $K\gt 0$ and $1386-K\gt 0$. Thus

$$\boxed{0\lt K\lt 1386}$$

is the asymptotically stable range. For $K\gt 1386$, two sign changes give two RHP poles. For $K\lt 0$, one sign change gives one RHP pole. At $K=0$, the full characteristic polynomial has a pole at zero; the zero reference-to-output transfer function obtained by setting the numerator to zero must not be used to infer internal asymptotic stability.

The upper boundary and oscillation frequency

At $K=1386$, the $s^1$ row vanishes. Keep the array in view:

$$\begin{array}{c|cc} s^3&1&77\\ s^2&18&1386\\ \boxed{s^1}&\boxed{0}&\boxed{0} \end{array}$$

The auxiliary polynomial and replacement row are

$$A(s)=18s^2+1386,\qquad A'(s)=36s,\qquad s^1:[36,0].$$

The final row is $[1386,0]$. Factoring the characteristic polynomial gives

$$D(s)=(s+18)(s^2+77),\qquad T(s)=\frac{1386}{(s+18)(s^2+77)}.$$

The simple poles $\pm j\sqrt{77}$ produce sustained oscillations. Their imaginary part gives the oscillation frequency:

$$\omega=\sqrt{77}\approx8.775\ \mathrm{rad/s},\qquad \text{period}=\frac{2\pi}{\omega}\approx0.716\ \mathrm{s}.$$
Unit-step response at K = 1386 with zero initial conditions. The decaying transient leaves a sustained oscillation; the marked period is about 0.716 seconds.

Unit-step response at K = 1386 with zero initial conditions. The decaying transient leaves a sustained oscillation; the marked period is about 0.716 seconds.

For reference, with $\omega=\sqrt{77}$ the exact unit-step response is

$$y(t)=1-\frac{77}{401}e^{-18t}-\frac{324}{401}\cos(\omega t) -\frac{18\omega}{401}\sin(\omega t).$$

Python experiment: vary the feedback gain

Run the gain laboratory from the repository root:

python demos/routh_gain_app.py

Compare $K=1000$, $1386$, and $1800$. Read the first-column signs, locate the poles, and explain the decaying, sustained, or growing response. Vary the displayed time window to distinguish a slow decay from a sustained oscillation.

Open this laboratory in a full window

This app uses Python/SciPy unit-step simulations with zero initial conditions. Its slider selects a precomputed gain grid from $-100$ to $2400$ in steps of 20, plus the exact boundary $1386$; it does not run Python again when the slider moves. The exported app works offline. These are illustrative mathematical simulations, not measured experimental data.

Summary

Stability belongs to the closed-loop dynamics. The Routh first column counts RHP poles, while exceptional rows require an epsilon limit or an auxiliary polynomial. A zero RHP count must be combined with an imaginary-axis check before declaring asymptotic stability. In a parameterized problem, the first-column inequalities give the stable gain range, and the auxiliary polynomial at a boundary gives the oscillation frequency.

For a state-space model, apply the same test to $\det(sI-A)$. Numerical root finding remains useful, but repeated poles and stability boundaries are reasons to check its output against symbolic structure.

References