Skip to content

Small documentation updates #547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion control/freqplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
73 changes: 42 additions & 31 deletions control/margins.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,36 +211,40 @@ def fun(wdt):
# Sawyer B. Fuller <minster@uw.edu>, 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 <rene.vanpaassen@gmail.com>
#
# 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.

Parameters
----------
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
Expand All @@ -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):
Expand Down Expand Up @@ -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 <waldherr@ist.uni-stuttgart.de>
Expand Down Expand Up @@ -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
Expand All @@ -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
--------
Expand All @@ -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]
4 changes: 2 additions & 2 deletions doc/descfcn.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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