Root locus in depth: the rules proved, difficult cases, MATLAB and Python
Angle and magnitude conditions, proof of all the rules, circular locus with a zero, departure angles, open-loop unstable process, locus with respect to a parameter, verified MATLAB and Python examples.
This page goes deeper into the lesson on the root locus. There the sketching rules are stated; here they are proved one by one, starting from the angle condition. Then we study the cases that cause difficulty in exercises: departure angles from complex poles, the circular locus of a system with a zero, the process that is unstable in open loop, the locus with respect to a parameter other than the gain. At the end are the examples in MATLAB and Python, with verified code.
Notation:
, with
monic of degree
and zeros
,
monic of degree
and poles
; the closed-loop poles are the roots of
, for
.
The rules, proved
Angle and magnitude conditions.
is equivalent to
, a negative real number. A complex number is real and negative if and only if its argument is
(up to multiples of
). Since
, the angle condition follows; the magnitude gives the value of
. Every rule below is a consequence of these two.
Rule 1 (symmetry).
and
have real coefficients, so the complex roots of
come in conjugate pairs: the locus is symmetric about the real axis.
Rule 2 (start and end). The polynomial
has degree
for every
: there are
branches. As
the equation becomes
: the branches start from the poles. Dividing by
,
: as
,
roots tend to the zeros. The other
must go to infinity, because a polynomial of degree
has
roots and only
can stay finite.
Rule 3 (real axis). For real
, every real pole or zero to the left of
contributes an angle of 0°, every real pole or zero to the right contributes 180°, and a complex pair contributes opposite angles that cancel. The sum is
, and it equals
modulo
if and only if that number is odd.
Rule 4 (asymptotes and centroid). For large
,
![Rendered by QuickLaTeX.com \[ \frac{D(s)}{N(s)} = s^{n-m}\cdot\frac{1 - \frac{\sum p_i}{s} + \cdots}{1 - \frac{\sum z_i}{s} + \cdots} = s^{n-m}\left(1 - \frac{\sum p_i - \sum z_i}{s} + O(s^{-2})\right) \approx \left(s - \sigma_a\right)^{n-m} \]](https://www.electronics-engineering.com/wp-content/ql-cache/quicklatex.com-9af29827fc9451a092b78e68fe1f55ba_l3.png)
with
, because
(using the coefficients of
and
: the second coefficient of a monic polynomial is minus the sum of its roots). The locus equation becomes
:
. These are half-lines starting from
with the angles of the lesson. ∎
Rule 5 (breakaway). Where two branches meet,
has a double root
:
and
. Eliminating
:
, which is the numerator of
. Equivalently, on the real stretch of the locus
, and the breakaway point is a maximum (or minimum) of
: there two real roots merge and become complex. ∎
Rule 6 (departure angles). Near a pole
, all factors except
have almost constant argument, equal to their value at
. The angle condition then gives the direction of departure:
![]()
and in the same way the arrival angle at a zero
.
Rule 7 (sum of the poles). If
, the coefficient of
in
does not depend on
(because
has degree at most
), so the sum of the closed-loop poles is constant. If one pair moves to the right, another branch must move to the left by the same amount: a very useful check on sketches.
Rule 8 (imaginary axis). The crossing points are the solutions of
: setting real and imaginary parts to zero gives
and
. It is the same information as the zero row in the Routh table.

A zero and a circular locus
![]()
Real axis: the stretch between 0 and
and everything to the left of
. Breakaway points:
gives
, i.e.
(with
) and
(with
).
Theorem. The complex part of the locus is the circle centred at
(the zero) with radius
.
Proof. With
, the equation
has complex roots with
,
. From
:
, i.e.
. ∎
In general, with two real poles and one real zero the locus is always a circle centred at the zero, with radius equal to the geometric mean of the distances of the zero from the two poles (here
). The zero “attracts” the branches and keeps them in the left half-plane for every
: this is the effect of derivative action (lead network or PD).

Complex poles: the departure angle
![]()
Poles at
and
. Departure angle from
:
;
. So
: the branch leaves upwards and to the right, and crosses the imaginary axis.
Crossing:
. With
: real part
, imaginary part
. So
and
. Asymptotes: centroid
, angles
and
.

A process that is unstable in open loop
![]()
The pole at
makes the process unstable on its own. Characteristic equation:
. By the Routh criterion (second order: all coefficients positive) the closed loop is stable for
. The locus shows it: the branches start from 0 and from 2, meet on the real axis between the two poles, describe a circle around the zero at
(centre
, radius
) and cross the imaginary axis at
for
.
This is the typical situation of an inverted pendulum, a magnetic levitator or a fighter aircraft that is unstable by design: the feedback must have a minimum gain, and a failure of the controller is catastrophic.

The locus with respect to another parameter
The method works for any parameter that appears linearly in the characteristic equation. Suppose we want to know how the poles of
move as the damping
varies. Rewrite the equation as
![]()
which is a root locus with “gain”
, poles at
and a zero at 0. The locus is a circle of radius 2 centred at the origin (the poles have modulus
as long as they are complex): damping changes the angle of the poles but not the natural frequency
. For
the poles coincide at
(critical damping).

The locus in MATLAB and Python
The locus of the lesson example,
, with the search for the gain that gives damping 0.5.
MATLAB (Control System Toolbox):
G = tf(1, [1 6 8 0]); % G(s) = 1/(s(s+2)(s+4))
rlocus(G); sgrid(0.5, []) % locus and damping line 0.5
[K, poles] = rlocfind(G) % click on the desired point of the locus
T = feedback(8.3*G, 1); stepinfo(T)
Python (free libraries numpy and python-control):
import numpy as np
import control as ct
G = ct.tf([1], [1, 6, 8, 0]) # G(s) = 1/(s(s+2)(s+4))
rlist, klist = ct.root_locus(G, plot=False)
# gain for which the dominant pair has damping 0.5
for K in np.linspace(0.5, 47, 50000):
p = np.roots([1, 6, 8, K])
c = p[np.argmax(p.imag)]
if c.imag > 0 and -c.real / abs(c) <= 0.5:
print(f"K = {K:.2f}, poles = {np.round(p, 3)}")
break
# critical gain: roots on the imaginary axis
print(np.roots([1, 6, 8, 48]))
T = ct.feedback(8.3 * G, 1)
print(ct.step_info(T)["Overshoot"])
Running it prints
with poles
and
, then the roots
and
for
, and finally an overshoot of 15.5 %: slightly less than the 16.3 % of a pure second-order system with
, because of the third pole at
, which damps the response a little. With plot=True (or with ct.root_locus_plot in more recent versions of the library) the plot is drawn with matplotlib.

Exercises covering all the cases on this page are on the exercise page linked at the top.



