diff --git a/control/freqplot.py b/control/freqplot.py index 452928236..750d84d27 100644 --- a/control/freqplot.py +++ b/control/freqplot.py @@ -608,7 +608,7 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None, be 'right' (default), 'left', or 'none'. warn_nyquist : bool, optional - If set to `False', turn off warnings about frequencies above Nyquist. + If set to 'False', turn off warnings about frequencies above Nyquist. *args : :func:`matplotlib.pyplot.plot` positional properties, optional Additional arguments for `matplotlib` plots (color, linestyle, etc) diff --git a/control/margins.py b/control/margins.py index 02d615e05..a35be18e9 100644 --- a/control/margins.py +++ b/control/margins.py @@ -211,28 +211,32 @@ def fun(wdt): # Sawyer B. Fuller , removed a lot of the innards # and replaced with analytical polynomial functions for LTI systems. # -# idea for the frequency data solution copied/adapted from +# The idea for the frequency data solution copied/adapted from # https://github.com/alchemyst/Skogestad-Python/blob/master/BODE.py # Rene van Paassen # # RvP, July 8, 2014, corrected to exclude phase=0 crossing for the gain # margin polynomial +# # RvP, July 8, 2015, augmented to calculate all phase/gain crossings with # frd data. Correct to return smallest phase # margin, smallest gain margin and their frequencies -# RvP, Jun 10, 2017, modified the inclusion of roots found for phase -# crossing to include all >= 0, made subsequent calc -# insensitive to div by 0 -# also changed the selection of which crossings to -# return on basis of "A note on the Gain and Phase +# +# RvP, Jun 10, 2017, modified the inclusion of roots found for phase crossing +# to include all >= 0, made subsequent calc insensitive to +# div by 0. Also changed the selection of which crossings +# to return on basis of "A note on the Gain and Phase # Margin Concepts" Journal of Control and Systems -# Engineering, Yazdan Bavafi-Toosi, Dec 2015, vol 3 -# issue 1, pp 51-59, closer to Matlab behavior, but -# not completely identical in edge cases, which don't -# cross but touch gain=1 +# Engineering, Yazdan Bavafi-Toosi, Dec 2015, vol 3 issue +# 1, pp 51-59, closer to Matlab behavior, but not +# completely identical in edge cases, which don't cross but +# touch gain=1. +# # BG, Nov 9, 2020, removed duplicate implementations of the same code # for crossover frequencies and enhanced to handle discrete # systems + + def stability_margins(sysdata, returnall=False, epsw=0.0): """Calculate stability margins and associated crossover frequencies. @@ -240,7 +244,7 @@ def stability_margins(sysdata, returnall=False, epsw=0.0): ---------- sysdata: LTI system or (mag, phase, omega) sequence sys : LTI system - Linear SISO system + Linear SISO system representing the loop transfer function mag, phase, omega : sequence of array_like Arrays of magnitudes (absolute values, not dB), phases (degrees), and corresponding frequencies. Crossover frequencies returned are @@ -255,18 +259,25 @@ def stability_margins(sysdata, returnall=False, epsw=0.0): Returns ------- - gm: float or array_like + gm : float or array_like Gain margin - pm: float or array_loke + pm : float or array_loke Phase margin - sm: float or array_like + sm : float or array_like Stability margin, the minimum distance from the Nyquist plot to -1 - wg: float or array_like - Frequency for gain margin (at phase crossover, phase = -180 degrees) - wp: float or array_like - Frequency for phase margin (at gain crossover, gain = 1) - ws: float or array_like - Frequency for stability margin (complex gain closest to -1) + wpc : float or array_like + Phase crossover frequency (where phase crosses -180 degrees) + wgc : float or array_like + Gain crossover frequency (where gain crosses 1) + wms : float or array_like + Stability margin frequency (where Nyquist plot is closest to -1) + + Note that the gain margin is determined by the gain of the loop + transfer function at the phase crossover frequency(s), the phase + margin is determined by the phase of the loop transfer function at + the gain crossover frequency(s), and the stability margin is + determined by the frequency of maximum sensitivity (given by the + magnitude of 1/(1+L)). """ try: if isinstance(sysdata, frdata.FRD): @@ -399,7 +410,8 @@ def _dstab(w): (not SM.shape[0] and float('inf')) or np.amin(SM), (not gmidx != -1 and float('nan')) or w_180[gmidx][0], (not wc.shape[0] and float('nan')) or wc[pmidx][0], - (not wstab.shape[0] and float('nan')) or wstab[SM==np.amin(SM)][0]) + (not wstab.shape[0] and float('nan')) or + wstab[SM == np.amin(SM)][0]) # Contributed by Steffen Waldherr @@ -456,7 +468,7 @@ def margin(*args): ---------- sysdata : LTI system or (mag, phase, omega) sequence sys : StateSpace or TransferFunction - Linear SISO system + Linear SISO system representing the loop transfer function mag, phase, omega : sequence of array_like Input magnitude, phase (in deg.), and frequencies (rad/sec) from bode frequency response data @@ -467,17 +479,16 @@ def margin(*args): Gain margin pm : float Phase margin (in degrees) - wg: float - Frequency for gain margin (at phase crossover, phase = -180 degrees) - wp: float - Frequency for phase margin (at gain crossover, gain = 1) + wpc : float or array_like + Phase crossover frequency (where phase crosses -180 degrees) + wgc : float or array_like + Gain crossover frequency (where gain crosses 1) Margins are calculated for a SISO open-loop system. - If there is more than one gain crossover, the one at the smallest - margin (deviation from gain = 1), in absolute sense, is - returned. Likewise the smallest phase margin (in absolute sense) - is returned. + If there is more than one gain crossover, the one at the smallest margin + (deviation from gain = 1), in absolute sense, is returned. Likewise the + smallest phase margin (in absolute sense) is returned. Examples -------- @@ -492,6 +503,6 @@ def margin(*args): margin = stability_margins(args) else: raise ValueError("Margin needs 1 or 3 arguments; received %i." - % len(args)) + % len(args)) return margin[0], margin[1], margin[3], margin[4] diff --git a/doc/descfcn.rst b/doc/descfcn.rst index 240bbb894..05f6bd94a 100644 --- a/doc/descfcn.rst +++ b/doc/descfcn.rst @@ -59,7 +59,7 @@ nonlinearity constructors are predefined: .. code:: python - backlash_nonlinearity(b) # backlash nonlinearity with width b + friction_backlash_nonlinearity(b) # backlash nonlinearity with width b relay_hysteresis_nonlinearity(b, c) # relay output of amplitude b with # hysteresis of half-width c saturation_nonlinearity(ub[, lb]) # saturation nonlinearity with upper @@ -81,6 +81,6 @@ Module classes and functions :toctree: generated/ ~control.DescribingFunctionNonlinearity - ~control.backlash_nonlinearity + ~control.friction_backlash_nonlinearity ~control.relay_hysteresis_nonlinearity ~control.saturation_nonlinearity