8000 Merge pull request #997 from wueestry/nyquist_improvements · python-control/python-control@4acc78b · GitHub
[go: up one dir, main page]

Skip to content

Commit 4acc78b

Browse files
authored
Merge pull request #997 from wueestry/nyquist_improvements
Add extra information to nyquist plots
2 parents feeb56a + 10d2010 commit 4acc78b

File tree

2 files changed

+73
-5
lines changed

2 files changed

+73
-5
lines changed

control/freqplot.py

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,9 @@ def gen_zero_centered_series(val_min, val_max, period):
10601060
'nyquist.max_curve_magnitude': 20, # clip large values
10611061
'nyquist.max_curve_offset': 0.02, # offset of primary/mirror
10621062
'nyquist.start_marker': 'o', # marker at start of curve
1063-
'nyquist.start_marker_size': 4, # size of the maker
1063+
'nyquist.start_marker_size': 4, # size of the marker
1064+
'nyquist.circle_style': # style for unit circles
1065+
{'color': 'black', 'linestyle': 'dashed', 'linewidth': 1}
10641066
}
10651067

10661068

@@ -1477,8 +1479,8 @@ def nyquist_response(
14771479

14781480
def nyquist_plot(
14791481
data, omega=None, plot=None, label_freq=0, color=None, label=None,
1480-
return_contour=None, title=None, legend_loc='upper right',
1481-
ax=None, **kwargs):
1482+
return_contour=None, title=None, legend_loc='upper right', ax=None,
1483+
unit_circle=False, mt_circles=None, ms_circles=None, **kwargs):
14821484
"""Nyquist plot for a system.
14831485
14841486
Generates a Nyquist plot for the system over a (optional) frequency
@@ -1501,7 +1503,13 @@ def nyquist_plot(
15011503
``omega_limits``.
15021504
color : string, optional
15031505
Used to specify the color of the line and arrowhead.
1504-
1506+
unit_circle : bool, optional
1507+
If ``True``, display the unit circle, to read gain crossover frequency.
1508+
mt_circles : array_like, optional
1509+
Draw circles corresponding to the given magnitudes of sensitivity.
1510+
ms_circles : array_like, optional
1511+
Draw circles corresponding to the given magnitudes of complementary
1512+
sensitivity.
15051513
**kwargs : :func:`matplotlib.pyplot.plot` keyword properties, optional
15061514
Additional keywords (passed to `matplotlib`)
15071515
@@ -1856,7 +1864,48 @@ def _parse_linestyle(style_name, allow_false=False):
18561864
# Mark the -1 point
18571865
plt.plot([-1], [0], 'r+')
18581866

1859-
# Label the frequencies of the points
1867+
#
1868+
# Draw circles for gain crossover and sensitivity functions
1869+
#
1870+
theta = np.linspace(0, 2*np.pi, 100)
1871+
cos = np.cos(theta)
1872+
sin = np.sin(theta)
1873+
label_pos = 15
1874+
1875+
# Display the unit circle, to read gain crossover frequency
1876+
if unit_circle:
1877+
plt.plot(cos, sin, **config.defaults['nyquist.circle_style'])
1878+
1879+
# Draw circles for given magnitudes of sensitivity
1880+
if ms_circles is not None:
1881+
for ms in ms_circles:
1882+
pos_x = -1 + (1/ms)*cos
1883+
pos_y = (1/ms)*sin
1884+
plt.plot(
1885+
pos_x, pos_y, **config.defaults['nyquist.circle_style'])
1886+
plt.text(pos_x[label_pos], pos_y[label_pos], ms)
1887+
1888+
# Draw circles for given magnitudes of complementary sensitivity
1889+
if mt_circles is not None:
1890+
for mt in mt_circles:
1891+
if mt != 1:
1892+
ct = -mt**2/(mt**2-1) # Mt center
1893+
rt = mt/(mt**2-1) # Mt radius
1894+
pos_x = ct+rt*cos
1895+
pos_y = rt*sin
1896+
plt.plot(
1897+
pos_x, pos_y,
1898+
**config.defaults['nyquist.circle_style'])
1899+
plt.text(pos_x[label_pos], pos_y[label_pos], mt)
1900+
else:
1901+
_, _, ymin, ymax = plt.axis()
1902+
pos_y = np.linspace(ymin, ymax, 100)
1903+
plt.vlines(
1904+
-0.5, ymin=ymin, ymax=ymax,
1905+
**config.defaults['nyquist.circle_style'])
1906+
plt.text(-0.5, pos_y[label_pos], 1)
1907+
1908+
# Label the frequencies of the points on the Nyquist curve
18601909
if label_freq:
18611910
ind = slice(None, None, label_freq)
18621911
omega_sys = np.imag(splane_contour[np.real(splane_contour) == 0])

control/tests/nyquist_test.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,22 @@ def test_nyquist_arrows(arrows):
214214
assert _Z(sys) == response.count + _P(sys)
215215

216216

217+
def test_sensitivity_circles():
218+
A = np.array([
219+
[-3.56355873, -1.22980795, -1.5626527 , -0.4626829],
220+
[-8.52361371, -3.60331459, -3.71574266, -0.43839201],
221+
[-2.50458726, -0.72361335, -1.77795489, -0.4038419],
222+
[-0.281183 , 0.23391825, 0.19096003, -0.9771515]])
223+
B = np.array([[-0.], [-1.42827213], [ 0.76806551], [-1.07987454]])
224+
C = np.array([[-0., 0.35557249, 0.35941791, -0.]])
225+
D = np.array([[0]])
226+
sys1 = ct.ss(A, B, C, D)
227+
sys2 = ct.ss(A, B, C, D, dt=0.1)
228+
plt.figure()
229+
ct.nyquist_plot(sys1, unit_circle=True, mt_circles=[0.9,1,1.1,1.2], ms_circles=[0.9,1,1.1,1.2])
230+
ct.nyquist_plot(sys2, unit_circle=True, mt_circles=[0.9,1,1.1,1.2], ms_circles=[0.9,1,1.1,1.2])
231+
232+
217233
def test_nyquist_encirclements():
218234
# Example 14.14: effect of friction in a cart-pendulum system
219235
s = ct.tf('s')
@@ -518,6 +534,9 @@ def test_nyquist_frd():
518534
test_nyquist_arrows(3)
519535
test_nyquist_arrows([0.1, 0.5, 0.9])
520536

537+
print("Test sensitivity circles")
538+
test_sensitivity_circles()
539+
521540
print("Stability checks")
522541
test_nyquist_encirclements()
523542

0 commit comments

Comments
 (0)
0