W10_FrequencyResponse

English translation

!pip install control
Collecting control
  Downloading control-0.10.0-py3-none-any.whl (513 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 513.9/513.9 kB 5.5 MB/s eta 0:00:00
[?25hRequirement already satisfied: numpy>=1.23 in /usr/local/lib/python3.10/dist-packages (from control) (1.25.2)
Requirement already satisfied: scipy>=1.8 in /usr/local/lib/python3.10/dist-packages (from control) (1.11.4)
Requirement already satisfied: matplotlib>=3.6 in /usr/local/lib/python3.10/dist-packages (from control) (3.7.1)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.6->control) (1.2.1)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.6->control) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.6->control) (4.51.0)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.6->control) (1.4.5)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.6->control) (24.0)
Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.6->control) (9.4.0)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.6->control) (3.1.2)
Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.6->control) (2.8.2)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib>=3.6->control) (1.16.0)
Installing collected packages: control
Successfully installed control-0.10.0
import numpy as np
import control as ctl
import matplotlib.pyplot as plt

Plotting a Nyquist plot (polar plot)

G1(s)=0.25s−0.5G2(s)=0.5s−0.5G3(s)=1.0s−0.5\begin{aligned} G_1(s) &= \frac{0.25}{s - 0.5} \\ G_2(s) &= \frac{0.5}{s - 0.5} \\ G_3(s) &= \frac{1.0}{s - 0.5} \end{aligned}

s=ctl.tf([1, 0], [1])
G_1 = 0.25/(s-0.5)
G_1.name='G_1'
G_2 = 0.5/(s-0.5)
G_2.name='G_2'
G_3 = 1.0/(s-0.5)
G_3.name='G_3'
fig1 = ctl.nyquist_plot(G_1)
fig2 = ctl.nyquist_plot(G_2)
fig3 = ctl.nyquist_plot(G_3)
plt.show()
<Figure size 640x480 with 1 Axes>
실행 결과

Plotting a Bode plot

G(s)=1s2+0.5s+1G(s) = \frac{1}{s^2 + 0.5s + 1}

s = ctl.tf([1,0],[1])
G = 1/(s**2+0.5*s+1)
G.name='G(s)'
out = ctl.bode(G)
<Figure size 640x480 with 2 Axes>
실행 결과

Examining frequency response in the time domain

omega = 2   # 0.1 ~ 10
t = np.arange(0,100,0.1)
u = np.sin(omega*t)
t,y = ctl.forced_response(G, t, u)
input = plt.plot(t,u)
output = plt.plot(t,y)
plt.xlabel('Time')
plt.legend(['input','output'])
plt.axis([0, 100, -3, 3])
plt.show()
<Figure size 640x480 with 1 Axes>
실행 결과
current_omega = ctl.bode(G,omega,'ks')
overall = ctl.bode(G,np.arange(0.1,10,0.1))
plt.show()
<Figure size 640x480 with 2 Axes>
실행 결과

Checking bandwidth (cut-off frequency)

ctl.bandwidth(G)
1.4839084518312828
ctl.bandwidth(G, -10)
2.0000000000008864

Plotting a more complex Bode plot

G_dc = ctl.tf([5],[1])
G_dc.name = 'DC'
G_p1 = 1/s
G_p1.name = 'Pole1'
G_p2 = 1/(0.5*s+1)
G_p2.name = 'Pole2'
G_p34 = 1/(s**2/2500+s*0.6/50+1)
G_p34.name = 'Pole3&4'
G_z1 = 0.1*s+1
G_z1.name = 'Zero1'
G_all = G_dc*G_p1*G_p2*G_p34*G_z1
G_all.name = 'All'
G_list = [G_dc, G_p1, G_p2, G_p34, G_z1, G_all]
out = ctl.bode(G_list, plot_phase=False)

<Figure size 640x480 with 1 Axes>
실행 결과
for sys in G_list:
  print(sys.name, ctl.bandwidth(sys))
DC inf
Pole1 nan
Pole2 1.9952566902229083
Pole3&4 72.6529421994053
Zero1 inf
All nan