writeup-6
writeup-6
6
Frequency Response
2024-12-17
Erfan Khadem
Sharif University of Technology
Autumn 2024
Linear Control Systems
By Dr. Ahi
Control CHW No. 6 Erfan Khadem
Table of contents
Note .............................................................................................................................................................. 3
I. Charts and Answers .............................................................................................................................. 3
I.1. Problem No. 6 ...................................................................................................................................... 3
Note .............................................................................................................................................................. 3
Code Output ............................................................................................................................................... 3
Explanation .............................................................................................................................................. 10
Step 3 .......................................................................................................................................................... 10
Step 7 .......................................................................................................................................................... 10
Code ........................................................................................................................................................... 10
I.2. Problem No. 7 .................................................................................................................................... 18
Code Output ............................................................................................................................................. 18
Explanation .............................................................................................................................................. 20
Code ........................................................................................................................................................... 20
2 / 24
Control CHW No. 6 Erfan Khadem
Note
AI Assistance was not used in completing this assignment.
Also, I would like to express my deep gratitude for not including any simulink exercises. I
hate that utility with a passion.
Code Output
3 / 24
Control CHW No. 6 Erfan Khadem
4 / 24
Control CHW No. 6 Erfan Khadem
𝑀𝑝 (𝐺1 )
Fig. 5 — Surface Plot of the ratios of 𝑀𝑝 (𝐺2 ) vs. 𝜁 and 𝜔𝑛
5 / 24
Control CHW No. 6 Erfan Khadem
6 / 24
Control CHW No. 6 Erfan Khadem
7 / 24
Control CHW No. 6 Erfan Khadem
8 / 24
Control CHW No. 6 Erfan Khadem
9 / 24
Control CHW No. 6 Erfan Khadem
Explanation
Step 3
Due to the fact that 𝜁 ≪ 1, we can see that strong peaking behavior happens around 𝑠 ≈
𝑗𝜔𝑛 . Since both transfer functions have the same denominator, and their respective peaking
frequency are close, we can see that:
Step 7
We can see that by increasing 𝜁, the overshoot percentage drops drastically (as demonstrated
in Fig. 4).
Code
import numpy as np
import control
import matplotlib.pyplot as plt
from scipy.signal import TransferFunction, bode, freqresp, step
from sympy import symbols, Function, I, simplify, diff, sqrt
import sympy as sp
import pprint
10 / 24
Control CHW No. 6 Erfan Khadem
wn = 10.0
zeta = 0.05
G1 = TransferFunction(num_G1, den)
# -------------------------------------
# Calculate Maximum Gain
# -------------------------------------
w = np.linspace(0, 5*wn, 1000)
_, G1_resp = freqresp(G1, w)
_, G2_resp = freqresp(G2, w)
G1_mag = np.abs(G1_resp)
G2_mag = np.abs(G2_resp)
G1_max = np.max(G1_mag)
G2_max = np.max(G2_mag)
G1_max_freq = w[np.argmax(G1_mag)]
G2_max_freq = w[np.argmax(G2_mag)]
plt.figure(figsize=(10,6))
plt.plot(w, 20*np.log10(G1_mag), label='|G1(jw)|')
plt.plot(w, 20*np.log10(G2_mag), label='|G2(jw)|', linestyle='--')
plt.xlabel('Frequency (rad/s)')
plt.ylabel('Magnitude (dB)')
plt.title('Frequency Response of G1 and G2')
plt.grid(True)
plt.axvline(G1_max_freq, color='r', linestyle=':', label=f'G1 max at
{G1_max_freq:.2f} rad/s')
plt.axvline(G2_max_freq, color='g', linestyle=':', label=f'G2 max at
{G2_max_freq:.2f} rad/s')
plt.legend()
plt.show()
print('-' * 30)
print(' ' * 30)
11 / 24
Control CHW No. 6 Erfan Khadem
# -------------------------------------
# Analytical Check with sympy
# -------------------------------------
s = symbols('s', complex=True)
omega = symbols('omega', real=True, positive=True)
z, w_n = symbols('zeta omega_n', real=True, positive=True)
G1_mag_sq = sp.simplify(sp.Abs(G1_jw)**2)
G2_mag_sq = sp.simplify(sp.Abs(G2_jw)**2)
# Typically, the maximum occurs around omega ~ w_n for small zeta. Let's just
confirm:
print('-' * 30)
print("G1 critical points:")
print(' ' * 30)
sp.pprint(G1_crit_points)
print('-' * 30)
print(' ' * 30)
print('-' * 30)
print("G2 critical points:")
12 / 24
Control CHW No. 6 Erfan Khadem
13 / 24
Control CHW No. 6 Erfan Khadem
plt.figure(figsize=(10,8))
plt.subplot(2,1,1)
plt.semilogx(w_bode, mag_G1, label='G1')
plt.semilogx(w_bode, mag_G2, label='G2', linestyle='--')
plt.grid(True, which='both')
plt.ylabel('Magnitude (dB)')
plt.title('Bode Plot: Magnitude')
plt.plot(w_bode[G1_max_idx], G1_max_dB, 'ro', label=f"G1 peak = {G1_max_dB:.2f}
dB")
plt.plot(w_bode[G2_max_idx], G2_max_dB, 'go', label=f"G2 peak = {G2_max_dB:.2f}
dB")
plt.legend()
plt.subplot(2,1,2)
plt.semilogx(w_bode, phase_G1, label='G1')
plt.semilogx(w_bode, phase_G2, label='G2', linestyle='--')
plt.grid(True, which='both')
plt.xlabel('Frequency (rad/s)')
plt.ylabel('Phase (deg)')
plt.title('Bode Plot: Phase')
plt.legend()
plt.tight_layout()
plt.show()
for i, zz in enumerate(zeta_values):
for j, wnn in enumerate(wn_values):
G1_temp = TransferFunction([wnn**2],[1,2*zz*wnn,wnn**2])
G2_temp = TransferFunction([wnn, wnn**2],[1,2*zz*wnn,wnn**2])
# Compute frequency response
w_temp = np.linspace(0,5*wnn,500)
_, G1_resp_temp = freqresp(G1_temp, w_temp)
_, G2_resp_temp = freqresp(G2_temp, w_temp)
G1_peaks[i,j] = np.max(np.abs(G1_resp_temp))
G2_peaks[i,j] = np.max(np.abs(G2_resp_temp))
14 / 24
Control CHW No. 6 Erfan Khadem
fig = plt.figure(figsize=(12,5))
ax1 = fig.add_subplot(1,2,1, projection='3d')
ax2 = fig.add_subplot(1,2,2, projection='3d')
plt.show()
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1, projection='3d')
surf_ratio = ax1.plot_surface(ZETA, WNN, G1_peaks.T / G2_peaks.T,
cmap='viridis')
ax1.set_xlabel('Zeta')
ax1.set_ylabel('Wn')
ax1.set_zlabel(r'$\frac{|G1| max}{|G2| max}$')
ax1.set_title('Max |G1| / Max |G2| vs Zeta, Wn')
fig.colorbar(surf_ratio, ax=ax1)
plt.show()
# -------------------------------------
# 4. Nyquist and Nichols Plot for G2(s)
# -------------------------------------
# Nyquist plot:
fig = plt.figure(figsize=(6,6))
# :star: :star:
control.nyquist_plot(control.TransferFunction(num_G2,
den), omega=np.linspace(0,5*wn,2000))
plt.title('Nyquist plot of G2')
plt.show()
# Nichols plot:
# Nichols plot is basically magnitude (dB) vs phase. We can do it manually:
w_nich = np.logspace(-1, 2, 2000)
mag_nich, phase_nich, _ =
control.frequency_response(control.TransferFunction(num_G2, den), omega=w_nich)
mag_dB_nich = 20*np.log10(np.abs(mag_nich))
15 / 24
Control CHW No. 6 Erfan Khadem
plt.figure()
plt.plot(phase_nich*180/np.pi, mag_dB_nich)
plt.title('Manual Nichols plot of G2')
plt.xlabel('Phase (deg)')
plt.ylabel('Gain (dB)')
plt.grid(True)
plt.show()
# Comments on Nyquist/Nichols:
# Nyquist plot: The plot of G2(jw) in the complex plane. For low damping, the
trajectory comes close
# to the negative real axis, indicating the system's resonant nature. The Nyquist
plot helps analyze
# stability through encirclements of the -1 point.
#
# Nichols plot: Plots magnitude (dB) vs phase (deg). Useful in loop shaping.
Shows how the gain and
# phase relate without frequency explicitly. A pronounced peak near resonance
frequency is visible.
# Can be used (with the grid) to find the closed loop response.
# -------------------------------------
# 5. Performance Indices of G2(s)
# -------------------------------------
G2_ctrl = control.TransferFunction(num_G2, den)
GM, PM, Wcg, Wcp = control.margin(G2_ctrl)
print(' ' * 30)
print('-' * 30)
print(' ' * 30)
G2_step_info = control.step_info(G2_ctrl)
16 / 24
Control CHW No. 6 Erfan Khadem
17 / 24
Control CHW No. 6 Erfan Khadem
18 / 24
Control CHW No. 6 Erfan Khadem
19 / 24
Control CHW No. 6 Erfan Khadem
Explanation
By tuning 𝜁 we are effectively setting the quality factor of our filter. As we make 𝜁 larger, the
effective stop band increases linearly, as witnessed by Fig. 12. The parameter 𝜔𝑛 determines
the notch frequency, and should be set to 2 × 𝜋 × 50 rad𝑠 .
Code
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import freqresp, lfilter, TransferFunction
import control
20 / 24
Control CHW No. 6 Erfan Khadem
f0 = 50.0
omega_n_notch = 2*np.pi*f0
zeta_values = np.linspace(5, 7, 500)
plt.figure()
plt.plot(zeta_values, np.array(BW_values, dtype=np.float64) / (2 * np.pi), 'o-')
plt.xlabel('Zeta')
plt.ylabel('50 dB Attenuation Bandwidth (Hz)')
plt.title('50 dB Attenuation Bandwidth vs Zeta for Notch Filter')
plt.grid(True)
plt.show()
# -------------------------------------
# 2. Frequency Response Verification
# -------------------------------------
# Choose a particular zeta and verify attenuation is ~50 dB
chosen_zeta = 6.41
G_notch_chosen = notch_filter(omega_n_notch, chosen_zeta)
# From 1 Hz to 200 Hz
w_check = np.linspace(2 * np.pi, 2*np.pi*200, 100000)
_, H_chosen = freqresp(G_notch_chosen, w_check)
mag_db_chosen = 20*np.log10(np.abs(H_chosen))
21 / 24
Control CHW No. 6 Erfan Khadem
plt.figure()
# No need for semilogx. A linear plot is easier to read in this context.
plt.plot(w_check/(2*np.pi), mag_db_chosen)
plt.axvline(49, color='r', linestyle=':', label='49 Hz')
plt.axvline(51, color='r', linestyle=':', label='51 Hz')
plt.title(f'Bode Diagram of Notch Filter (Chosen Zeta = {chosen_zeta:.3f})')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Magnitude (dB)')
plt.grid(True)
# Check attenuation around 50 Hz
# Note: The attenuation at 50 Hz is ideally infinity. But here we get
# ~120 dB. This is due to frequency resolution (we are not evaluating the
# TF at exactly 50Hz, we are evaluating at something like 50.00001 Hz)
# As a side effect this makes the plot more readable :shrug:
def annotate_attenuation_at_frequency(freq, text_xy):
around_f_idx = np.argmin(np.abs(w_check - 2 * np.pi * freq))
atten_f = mag_db_chosen[around_f_idx]
plt.annotate(f"Attenuation at {freq} Hz: {atten_f:.1f} dB",
xy=(freq, atten_f), xycoords='data',
xytext=text_xy, textcoords='data',
arrowprops=dict(arrowstyle="->"))
# -------------------------------------
# 3. Implementation and Testing
# -------------------------------------
# Create a test signal: combination of 10 Hz and 50 Hz sinusoids
fs = 10_000
t = np.linspace(0, 0.5, int(fs*0.5), endpoint=False) # 0.5s duration
signal_10Hz = np.sin(2*np.pi*10*t)
signal_50Hz = np.sin(2*np.pi*50*t)
test_signal = signal_10Hz + signal_50Hz
22 / 24
Control CHW No. 6 Erfan Khadem
plt.figure()
plt.plot(freqs[idx], np.abs(freq_spectrum[idx]), label='Original Signal')
plt.plot(freqs[idx], np.abs(filtered_signal_freq[idx]), label='Filtered
Signal')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Amplitude')
plt.title('Filter Frequency Response')
plt.grid(True)
plt.legend()
plt.show()
23 / 24
Control CHW No. 6 Erfan Khadem
# -------------------------------------
# Summary in Comments:
#
# Notch filters are commonly used in control systems to reject specific harmonic
disturbances.
# By placing a pair of complex conjugate zeros at the disturbance frequency and
suitable poles close by,
# we achieve a deep attenuation notch. The damping ratio (zeta) controls how
sharp or broad this notch is:
# - Smaller zeta -> narrower but deeper notch (good for very specific frequency
rejection).
# - Larger zeta -> wider notch but with less attenuation.
#
# Real-world applications include filtering out line-frequency hum in audio
systems, vibration modes
# in mechanical systems, or rejecting a known periodic disturbance in a control
loop.
# See Twin-T filter. Also see bootstrapped Twin-T filter, which utilizes feedback
to
# control the quality factor of the system. In real world scenarios around 40
to 60 dBs worth
# of attenuation can be extracted from common off the shelf non-precision
components on a breadboard.
24 / 24