From f1af2e599e466048fcf58a43a183c42d04db4f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryan=20W=C3=BCest?= Date: Wed, 24 Apr 2024 09:41:26 +0200 Subject: [PATCH 1/4] Add possibility to plot unit, ms and mt_circle for nyquist --- control/freqplot.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/control/freqplot.py b/control/freqplot.py index a63ef20d3..481ec4387 100644 --- a/control/freqplot.py +++ b/control/freqplot.py @@ -1477,8 +1477,8 @@ def nyquist_response( def nyquist_plot( data, omega=None, plot=None, label_freq=0, color=None, label=None, - return_contour=None, title=None, legend_loc='upper right', - ax=None, **kwargs): + return_contour=None, title=None, legend_loc='upper right', ax=None, + unit_circle=False, mt_circles=None, ms_circles=None, **kwargs): """Nyquist plot for a system. Generates a Nyquist plot for the system over a (optional) frequency @@ -1501,7 +1501,13 @@ def nyquist_plot( ``omega_limits``. color : string, optional Used to specify the color of the line and arrowhead. - + unit_circle : bool, optional + If ``True``, display the unit circle, to read gain crossover frequency. + mt_circles : array_like, optional + Draws circles corresponding to the given magnitudes of sensitivity. + ms_circles : array_like, optional + Draws circles corresponding to the given magnitudes in complementary + sensitivity. **kwargs : :func:`matplotlib.pyplot.plot` keyword properties, optional Additional keywords (passed to `matplotlib`) @@ -1856,6 +1862,27 @@ def _parse_linestyle(style_name, allow_false=False): # Mark the -1 point plt.plot([-1], [0], 'r+') + theta = np.linspace(0, 2*np.pi, 100) + cos = np.cos(theta) + sin = np.sin(theta) + + if unit_circle: + plt.plot(cos, sin, color="black", linestyle='dashed', linewidth=1) + + if ms_circles is not None: + for ms in ms_circles: + plt.plot(-1 + (1/ms)*cos, (1/ms)*sin, color="black", linestyle="dashed", linewidth=1) + + if mt_circles is not None: + for mt in mt_circles: + if mt != 1: + ct = -mt**2/(mt**2-1) # Mt center + rt = mt/(mt**2-1) # Mt radius + plt.plot(ct+rt*cos, rt*sin, color="black", linestyle="dashed", linewidth=1) + else: + _, _, ymin, ymax = plt.axis() + plt.vlines(-0.5, ymin=ymin, ymax=ymax, colors="black", linestyles="dashed", linewidth=1) + # Label the frequencies of the points if label_freq: ind = slice(None, None, label_freq) From c8cf6c3cc155c70919d4b2ba27c7cb59d44f014a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryan=20W=C3=BCest?= Date: Mon, 29 Apr 2024 09:29:15 +0200 Subject: [PATCH 2/4] Adding sensitivity circle tests --- control/tests/nyquist_test.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/control/tests/nyquist_test.py b/control/tests/nyquist_test.py index 8354932d7..af9505354 100644 --- a/control/tests/nyquist_test.py +++ b/control/tests/nyquist_test.py @@ -214,6 +214,22 @@ def test_nyquist_arrows(arrows): assert _Z(sys) == response.count + _P(sys) +def test_sensitivity_circles(): + A = np.array([ + [-3.56355873, -1.22980795, -1.5626527 , -0.4626829], + [-8.52361371, -3.60331459, -3.71574266, -0.43839201], + [-2.50458726, -0.72361335, -1.77795489, -0.4038419], + [-0.281183 , 0.23391825, 0.19096003, -0.9771515]]) + B = np.array([[-0.], [-1.42827213], [ 0.76806551], [-1.07987454]]) + C = np.array([[-0., 0.35557249, 0.35941791, -0.]]) + D = np.array([[0]]) + sys1 = ct.ss(A, B, C, D) + sys2 = ct.ss(A, B, C, D, dt=0.1) + plt.figure() + 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]) + 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]) + + def test_nyquist_encirclements(): # Example 14.14: effect of friction in a cart-pendulum system s = ct.tf('s') @@ -518,6 +534,9 @@ def test_nyquist_frd(): test_nyquist_arrows(3) test_nyquist_arrows([0.1, 0.5, 0.9]) + print("Test sensitivity circles") + test_sensitivity_circles() + print("Stability checks") test_nyquist_encirclements() From 2168b105e0ab35227c575b02a95c526a04b7b3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryan=20W=C3=BCest?= Date: Mon, 29 Apr 2024 09:38:52 +0200 Subject: [PATCH 3/4] Adding circle labels by default --- control/freqplot.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/control/freqplot.py b/control/freqplot.py index 481ec4387..a041be79b 100644 --- a/control/freqplot.py +++ b/control/freqplot.py @@ -1861,27 +1861,36 @@ def _parse_linestyle(style_name, allow_false=False): # Mark the -1 point plt.plot([-1], [0], 'r+') - + theta = np.linspace(0, 2*np.pi, 100) cos = np.cos(theta) sin = np.sin(theta) + label_pos = 15 if unit_circle: plt.plot(cos, sin, color="black", linestyle='dashed', linewidth=1) if ms_circles is not None: for ms in ms_circles: - plt.plot(-1 + (1/ms)*cos, (1/ms)*sin, color="black", linestyle="dashed", linewidth=1) - + pos_x = -1 + (1/ms)*cos + pos_y = (1/ms)*sin + plt.plot(pos_x, pos_y, color="black", linestyle="dashed", linewidth=1) + plt.text(pos_x[label_pos], pos_y[label_pos], ms) + if mt_circles is not None: for mt in mt_circles: if mt != 1: ct = -mt**2/(mt**2-1) # Mt center rt = mt/(mt**2-1) # Mt radius - plt.plot(ct+rt*cos, rt*sin, color="black", linestyle="dashed", linewidth=1) + pos_x = ct+rt*cos + pos_y = rt*sin + plt.plot(pos_x, pos_y, color="black", linestyle="dashed", linewidth=1) + plt.text(pos_x[label_pos], pos_y[label_pos], mt) else: _, _, ymin, ymax = plt.axis() + pos_y = np.linspace(ymin, ymax, 100) plt.vlines(-0.5, ymin=ymin, ymax=ymax, colors="black", linestyles="dashed", linewidth=1) + plt.text(-0.5, pos_y[label_pos], 1) # Label the frequencies of the points if label_freq: From 10d201014b6faf3ab6fdbf85c0947d22d4c33158 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sun, 30 Jun 2024 09:09:29 -0700 Subject: [PATCH 4/4] rebase on main + allow circle styles to be set via defaults --- control/freqplot.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/control/freqplot.py b/control/freqplot.py index a041be79b..8917690ba 100644 --- a/control/freqplot.py +++ b/control/freqplot.py @@ -1060,7 +1060,9 @@ def gen_zero_centered_series(val_min, val_max, period): 'nyquist.max_curve_magnitude': 20, # clip large values 'nyquist.max_curve_offset': 0.02, # offset of primary/mirror 'nyquist.start_marker': 'o', # marker at start of curve - 'nyquist.start_marker_size': 4, # size of the maker + 'nyquist.start_marker_size': 4, # size of the marker + 'nyquist.circle_style': # style for unit circles + {'color': 'black', 'linestyle': 'dashed', 'linewidth': 1} } @@ -1504,9 +1506,9 @@ def nyquist_plot( unit_circle : bool, optional If ``True``, display the unit circle, to read gain crossover frequency. mt_circles : array_like, optional - Draws circles corresponding to the given magnitudes of sensitivity. + Draw circles corresponding to the given magnitudes of sensitivity. ms_circles : array_like, optional - Draws circles corresponding to the given magnitudes in complementary + Draw circles corresponding to the given magnitudes of complementary sensitivity. **kwargs : :func:`matplotlib.pyplot.plot` keyword properties, optional Additional keywords (passed to `matplotlib`) @@ -1861,22 +1863,29 @@ def _parse_linestyle(style_name, allow_false=False): # Mark the -1 point plt.plot([-1], [0], 'r+') - + + # + # Draw circles for gain crossover and sensitivity functions + # theta = np.linspace(0, 2*np.pi, 100) cos = np.cos(theta) sin = np.sin(theta) label_pos = 15 + # Display the unit circle, to read gain crossover frequency if unit_circle: - plt.plot(cos, sin, color="black", linestyle='dashed', linewidth=1) + plt.plot(cos, sin, **config.defaults['nyquist.circle_style']) + # Draw circles for given magnitudes of sensitivity if ms_circles is not None: for ms in ms_circles: pos_x = -1 + (1/ms)*cos pos_y = (1/ms)*sin - plt.plot(pos_x, pos_y, color="black", linestyle="dashed", linewidth=1) + plt.plot( + pos_x, pos_y, **config.defaults['nyquist.circle_style']) plt.text(pos_x[label_pos], pos_y[label_pos], ms) + # Draw circles for given magnitudes of complementary sensitivity if mt_circles is not None: for mt in mt_circles: if mt != 1: @@ -1884,15 +1893,19 @@ def _parse_linestyle(style_name, allow_false=False): rt = mt/(mt**2-1) # Mt radius pos_x = ct+rt*cos pos_y = rt*sin - plt.plot(pos_x, pos_y, color="black", linestyle="dashed", linewidth=1) + plt.plot( + pos_x, pos_y, + **config.defaults['nyquist.circle_style']) plt.text(pos_x[label_pos], pos_y[label_pos], mt) else: _, _, ymin, ymax = plt.axis() pos_y = np.linspace(ymin, ymax, 100) - plt.vlines(-0.5, ymin=ymin, ymax=ymax, colors="black", linestyles="dashed", linewidth=1) + plt.vlines( + -0.5, ymin=ymin, ymax=ymax, + **config.defaults['nyquist.circle_style']) plt.text(-0.5, pos_y[label_pos], 1) - # Label the frequencies of the points + # Label the frequencies of the points on the Nyquist curve if label_freq: ind = slice(None, None, label_freq) omega_sys = np.imag(splane_contour[np.real(splane_contour) == 0]) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy