@@ -211,36 +211,40 @@ def fun(wdt):
211
211
# Sawyer B. Fuller <minster@uw.edu>, removed a lot of the innards
212
212
# and replaced with analytical polynomial functions for LTI systems.
213
213
#
214
- # idea for the frequency data solution copied/adapted from
214
+ # The idea for the frequency data solution copied/adapted from
215
215
# https://github.com/alchemyst/Skogestad-Python/blob/master/BODE.py
216
216
# Rene van Paassen <rene.vanpaassen@gmail.com>
217
217
#
218
218
# RvP, July 8, 2014, corrected to exclude phase=0 crossing for the gain
219
219
# margin polynomial
220
+ #
220
221
# RvP, July 8, 2015, augmented to calculate all phase/gain crossings with
221
222
# frd data. Correct to return smallest phase
222
223
# margin, smallest gain margin and their frequencies
223
- # RvP, Jun 10, 2017, modified the inclusion of roots found for phase
224
- # crossing to include all >= 0, made subsequent calc
225
- # insensitive to div by 0
226
- # also changed the selection of which crossings to
227
- # return on basis of "A note on the Gain and Phase
224
+ #
225
+ # RvP, Jun 10, 2017, modified the inclusion of roots found for phase crossing
226
+ # to include all >= 0, made subsequent calc insensitive to
227
+ # div by 0. Also changed the selection of which crossings
228
+ # to return on basis of "A note on the Gain and Phase
228
229
# Margin Concepts" Journal of Control and Systems
229
- # Engineering, Yazdan Bavafi-Toosi, Dec 2015, vol 3
230
- # issue 1, pp 51-59, closer to Matlab behavior, but
231
- # not completely identical in edge cases, which don't
232
- # cross but touch gain=1
230
+ # Engineering, Yazdan Bavafi-Toosi, Dec 2015, vol 3 issue
231
+ # 1, pp 51-59, closer to Matlab behavior, but not
232
+ # completely identical in edge cases, which don't cross but
233
+ # touch gain=1.
234
+ #
233
235
# BG, Nov 9, 2020, removed duplicate implementations of the same code
234
236
# for crossover frequencies and enhanced to handle discrete
235
237
# systems
238
+
239
+
236
240
def stability_margins (sysdata , returnall = False , epsw = 0.0 ):
237
241
"""Calculate stability margins and associated crossover frequencies.
238
242
239
243
Parameters
240
244
----------
241
245
sysdata: LTI system or (mag, phase, omega) sequence
242
246
sys : LTI system
243
- Linear SISO system
247
+ Linear SISO system representing the loop transfer function
244
248
mag, phase, omega : sequence of array_like
245
249
Arrays of magnitudes (absolute values, not dB), phases (degrees),
246
250
and corresponding frequencies. Crossover frequencies returned are
@@ -255,18 +259,25 @@ def stability_margins(sysdata, returnall=False, epsw=0.0):
255
259
256
260
Returns
257
261
-------
258
- gm: float or array_like
262
+ gm : float or array_like
259
263
Gain margin
260
- pm: float or array_loke
264
+ pm : float or array_loke
261
265
Phase margin
262
- sm: float or array_like
266
+ sm : float or array_like
263
267
Stability margin, the minimum distance from the Nyquist plot to -1
264
- wg: float or array_like
265
- Frequency for gain margin (at phase crossover, phase = -180 degrees)
266
- wp: float or array_like
267
- Frequency for phase margin (at gain crossover, gain = 1)
268
- ws: float or array_like
269
- Frequency for stability margin (complex gain closest to -1)
268
+ wpc : float or array_like
269
+ Phase crossover frequency (where phase crosses -180 degrees)
270
+ wgc : float or array_like
271
+ Gain crossover frequency (where gain crosses 1)
272
+ wms : float or array_like
273
+ Stability margin frequency (where Nyquist plot is closest to -1)
274
+
275
+ Note that the gain margin is determined by the gain of the loop
276
+ transfer function at the phase crossover frequency(s), the phase
277
+ margin is determined by the phase of the loop transfer function at
278
+ the gain crossover frequency(s), and the stability margin is
279
+ determined by the frequency of maximum sensitivity (given by the
280
+ magnitude of 1/(1+L)).
270
281
"""
271
282
try :
272
283
if isinstance (sysdata , frdata .FRD ):
@@ -398,7 +409,8 @@ def _dstab(w):
398
409
(not SM .shape [0 ] and float ('inf' )) or np .amin (SM ),
399
410
(not gmidx != - 1 and float ('nan' )) or w_180 [gmidx ][0 ],
400
411
(not wc .shape [0 ] and float ('nan' )) or wc [pmidx ][0 ],
401
- (not wstab .shape [0 ] and float ('nan' )) or wstab [SM == np .amin (SM )][0 ])
412
+ (not wstab .shape [0 ] and float ('nan' )) or
413
+ wstab [SM == np .amin (SM )][0 ])
402
414
403
415
404
416
# Contributed by Steffen Waldherr <waldherr@ist.uni-stuttgart.de>
@@ -455,7 +467,7 @@ def margin(*args):
455
467
----------
456
468
sysdata : LTI system or (mag, phase, omega) sequence
457
469
sys : StateSpace or TransferFunction
458
- Linear SISO system
470
+ Linear SISO system representing the loop transfer function
459
471
mag, phase, omega : sequence of array_like
460
472
Input magnitude, phase (in deg.), and frequencies (rad/sec) from
461
473
bode frequency response data
@@ -466,17 +478,16 @@ def margin(*args):
466
478
Gain margin
467
479
pm : float
468
480
Phase margin (in degrees)
469
- wg : float
470
- Frequency for gain margin (at phase crossover, phase = -180 degrees)
471
- wp : float
472
- Frequency for phase margin (at gain crossover, gain = 1)
481
+ wpc : float or array_like
482
+ Phase crossover frequency (where phase crosses -180 degrees)
483
+ wgc : float or array_like
484
+ Gain crossover frequency (where gain crosses 1)
473
485
474
486
Margins are calculated for a SISO open-loop system.
475
487
476
- If there is more than one gain crossover, the one at the smallest
477
- margin (deviation from gain = 1), in absolute sense, is
478
- returned. Likewise the smallest phase margin (in absolute sense)
479
- is returned.
488
+ If there is more than one gain crossover, the one at the smallest margin
489
+ (deviation from gain = 1), in absolute sense, is returned. Likewise the
490
+ smallest phase margin (in absolute sense) is returned.
480
491
481
492
Examples
482
493
--------
@@ -491,6 +502,6 @@ def margin(*args):
491
502
margin = stability_margins (args )
492
503
else :
493
504
raise ValueError ("Margin needs 1 or 3 arguments; received %i."
494
- % len (args ))
505
+ % len (args ))
495
506
496
507
return margin [0 ], margin [1 ], margin [3 ], margin [4 ]
0 commit comments