https://dynamics-and-control.readthedocs.io/en/latest/2_Control/2_Laplace_domain_analysis_of_control_systems/SymPy%20Routh%20Array.html

In [16]:
import sympy

sympy.init_printing()
s = sympy.Symbol('s')
a_0, a_1, a_2, a_3, a_4 = sympy.symbols('a_0:5')
p = a_0 + a_1*s**1 + a_2*s**2 + a_3*s**3 + a_4*s**4
p = sympy.Poly(p, s)
p
Out[16]:
$\displaystyle \operatorname{Poly}{\left( a_{4} s^{4} + a_{3} s^{3} + a_{2} s^{2} + a_{1} s + a_{0}, s, domain=\mathbb{Z}\left[a_{0}, a_{1}, a_{2}, a_{3}, a_{4}\right] \right)}$
In [18]:
!pip install tbcontrol
Collecting tbcontrol
  Downloading tbcontrol-0.2.1-py3-none-any.whl (24 kB)
Requirement already satisfied: numpy in /usr/local/lib/python3.9/dist-packages (from tbcontrol) (1.22.3)
Requirement already satisfied: scipy in /usr/local/lib/python3.9/dist-packages (from tbcontrol) (1.8.1)
Collecting tqdm (from tbcontrol)
  Downloading tqdm-4.66.1-py3-none-any.whl.metadata (57 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 57.6/57.6 kB 2.8 MB/s eta 0:00:00
Requirement already satisfied: packaging in /usr/local/lib/python3.9/dist-packages (from tbcontrol) (21.3)
Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/local/lib/python3.9/dist-packages (from packaging->tbcontrol) (3.0.9)
Downloading tqdm-4.66.1-py3-none-any.whl (78 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 78.3/78.3 kB 4.7 MB/s eta 0:00:00
Installing collected packages: tqdm, tbcontrol
Successfully installed tbcontrol-0.2.1 tqdm-4.66.1
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
In [19]:
from tbcontrol.symbolic import routh
In [20]:
help(routh)
Help on function routh in module tbcontrol.symbolic:

routh(p)
    Construct the Routh-Hurwitz array given a polynomial in s
    
    Input: p - a sympy.Poly object
    Output: The Routh-Hurwitz array as a sympy.Matrix object

In [21]:
routh(p)
Out[21]:
$\displaystyle \left[\begin{matrix}a_{4} & a_{2} & a_{0}\\a_{3} & a_{1} & 0\\- \frac{a_{1} a_{4}}{a_{3}} + a_{2} & a_{0} & 0\\\frac{a_{0} a_{3}^{2} + a_{1}^{2} a_{4} - a_{1} a_{2} a_{3}}{a_{1} a_{4} - a_{2} a_{3}} & 0 & 0\\a_{0} & 0 & 0\end{matrix}\right]$
In [25]:
ce = 1*s**3 + 5*s**2 + 6*s + 5
A = routh(sympy.Poly(ce, s))
A
Out[25]:
$\displaystyle \left[\begin{matrix}1 & 6\\5 & 5\\5 & 0\\5 & 0\end{matrix}\right]$

Nyquist Plot

In [30]:
import matplotlib.pyplot as plt
num = [5]
den = [1, 5, 6, 0]
system = ctrl.TransferFunction(num, den)

# Create a Nyquist plot
ctrl.nyquist_plot(system, omega=np.logspace(-10, 10, 1000), plot=True)

# Customize the plot (optional)
plt.title('Nyquist Plot')
plt.xlabel('Real')
plt.ylabel('Imaginary')
plt.axhline(0, color='black', linewidth=0.5)
plt.axvline(0, color='black', linewidth=0.5)
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.show()