import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
def transfer_function(num, denum):
num = np.array(num, dtype=np.float64)
denum = np.array(denum, dtype=np.float64)
size = len(denum) - len(num)
temp = np.zeros(size)
num = np.concatenate((temp, num))
tf = np.vstack((num, denum))
return tf
def compute_roots(tf, gains):
num, denum = tf[0], tf[1]
num_roots = len(np.roots(denum))
roots = []
for gain in gains:
ch_eq = denum + gain*num
ch_roots = np.roots(ch_eq)
ch_roots.sort()
roots.append(ch_roots)
roots = np.vstack(roots)
return roots
def plot_root_locus(gains, roots):
real_vals = np.real(roots)
imag_vals = np.imag(roots)
colors = ['b', 'm', 'c', 'r', 'g']
fig, ax = plt.subplots()
ax.set_xlabel('Re')
ax.set_ylabel('Im')
ax.axvline(x=0, color='k', lw=1)
ax.grid(True, which='both')
ax.scatter(real_vals[0, :], imag_vals[0, :], marker='x', color='blue')
ax.scatter(real_vals[-1, :], imag_vals[-1, :], marker='o', color='red')
gain_text = ['k = {:1.2f}'.format(k) for k in gains]
temp_real_vals = real_vals[1:-1, :]
temp_imag_vals = imag_vals[1:-1, :]
color_range = range(temp_real_vals.shape[1])
for r, i, j in zip(temp_real_vals.T, temp_imag_vals.T, color_range):
ax.plot(r, i, color=colors[j])
return fig, ax
if __name__ == "__main__":
num = [1, 3]
denum = [1, 9, 32, 44, 21]
GH = transfer_function(num, denum)
gains = np.linspace(0.0, 10.0, num=500)
roots = compute_roots(GH, gains)
fig, ax = plot_root_locus(gains, roots)
plt.show()
# Print the numerical values
print("Numerical Values:")
print("GH Numerator Coefficients:", GH[0])
print("GH Denominator Coefficients:", GH[1])
print("Gains:", gains)
print("Roots:", roots)
aW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKaW1wb3J0IHNlYWJvcm4gYXMgc25zCgpkZWYgdHJhbnNmZXJfZnVuY3Rpb24obnVtLCBkZW51bSk6CiAgbnVtID0gbnAuYXJyYXkobnVtLCBkdHlwZT1ucC5mbG9hdDY0KQogIGRlbnVtID0gbnAuYXJyYXkoZGVudW0sIGR0eXBlPW5wLmZsb2F0NjQpCiAgc2l6ZSA9IGxlbihkZW51bSkgLSBsZW4obnVtKQogIHRlbXAgPSBucC56ZXJvcyhzaXplKQogIG51bSA9IG5wLmNvbmNhdGVuYXRlKCh0ZW1wLCBudW0pKQogIHRmID0gbnAudnN0YWNrKChudW0sIGRlbnVtKSkKICByZXR1cm4gdGYKCmRlZiBjb21wdXRlX3Jvb3RzKHRmLCBnYWlucyk6CiAgbnVtLCBkZW51bSA9IHRmWzBdLCB0ZlsxXQogIG51bV9yb290cyA9IGxlbihucC5yb290cyhkZW51bSkpCiAgcm9vdHMgPSBbXQoKICBmb3IgZ2FpbiBpbiBnYWluczoKICAgIGNoX2VxID0gZGVudW0gKyAgZ2FpbipudW0KICAgIGNoX3Jvb3RzID0gbnAucm9vdHMoY2hfZXEpCiAgICBjaF9yb290cy5zb3J0KCkKICAgIHJvb3RzLmFwcGVuZChjaF9yb290cykKCiAgcm9vdHMgPSBucC52c3RhY2socm9vdHMpCiAgcmV0dXJuIHJvb3RzCgpkZWYgcGxvdF9yb290X2xvY3VzKGdhaW5zLCByb290cyk6CiAgcmVhbF92YWxzID0gbnAucmVhbChyb290cykKICBpbWFnX3ZhbHMgPSBucC5pbWFnKHJvb3RzKQogIGNvbG9ycyA9IFsnYicsICdtJywgJ2MnLCAncicsICdnJ10KCiAgZmlnLCBheCA9IHBsdC5zdWJwbG90cygpCiAgYXguc2V0X3hsYWJlbCgnUmUnKQogIGF4LnNldF95bGFiZWwoJ0ltJykKICBheC5heHZsaW5lKHg9MCwgY29sb3I9J2snLCBsdz0xKQogIGF4LmdyaWQoVHJ1ZSwgd2hpY2g9J2JvdGgnKQoKICBheC5zY2F0dGVyKHJlYWxfdmFsc1swLCA6XSwgaW1hZ192YWxzWzAsIDpdLCBtYXJrZXI9J3gnLCBjb2xvcj0nYmx1ZScpCiAgYXguc2NhdHRlcihyZWFsX3ZhbHNbLTEsIDpdLCBpbWFnX3ZhbHNbLTEsIDpdLCBtYXJrZXI9J28nLCBjb2xvcj0ncmVkJykKCiAgZ2Fpbl90ZXh0ID0gWydrID0gezoxLjJmfScuZm9ybWF0KGspIGZvciBrIGluIGdhaW5zXQoKICB0ZW1wX3JlYWxfdmFscyA9IHJlYWxfdmFsc1sxOi0xLCA6XQogIHRlbXBfaW1hZ192YWxzID0gaW1hZ192YWxzWzE6LTEsIDpdCiAgY29sb3JfcmFuZ2UgPSByYW5nZSh0ZW1wX3JlYWxfdmFscy5zaGFwZVsxXSkKCiAgZm9yIHIsIGksIGogaW4gemlwKHRlbXBfcmVhbF92YWxzLlQsIHRlbXBfaW1hZ192YWxzLlQsIGNvbG9yX3JhbmdlKToKICAgIGF4LnBsb3QociwgaSwgY29sb3I9Y29sb3JzW2pdKQoKICByZXR1cm4gZmlnLCBheAoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICBudW0gPSBbMSwgM10KICBkZW51bSA9IFsxLCA5LCAzMiwgNDQsIDIxXQogIEdIID0gdHJhbnNmZXJfZnVuY3Rpb24obnVtLCBkZW51bSkKCiAgZ2FpbnMgPSBucC5saW5zcGFjZSgwLjAsIDEwLjAsIG51bT01MDApCgogIHJvb3RzID0gY29tcHV0ZV9yb290cyhHSCwgZ2FpbnMpCiAgZmlnLCBheCA9IHBsb3Rfcm9vdF9sb2N1cyhnYWlucywgcm9vdHMpCiAgcGx0LnNob3coKQoKIyBQcmludCB0aGUgbnVtZXJpY2FsIHZhbHVlcwpwcmludCgiTnVtZXJpY2FsIFZhbHVlczoiKQpwcmludCgiR0ggTnVtZXJhdG9yIENvZWZmaWNpZW50czoiLCBHSFswXSkKcHJpbnQoIkdIIERlbm9taW5hdG9yIENvZWZmaWNpZW50czoiLCBHSFsxXSkKcHJpbnQoIkdhaW5zOiIsIGdhaW5zKQpwcmludCgiUm9vdHM6Iiwgcm9vdHMp