Skip to content

Commit 87714bd

Browse files
committed
Add input handling to disk_margin, clean up column width/comments
1 parent 0bebc1d commit 87714bd

File tree

1 file changed

+23
-16
lines changed

1 file changed

+23
-16
lines changed

control/margins.py

+23-16
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313
import matplotlib
1414
import matplotlib.pyplot as plt
1515

16-
from . import frdata, freqplot, xferfcn
16+
from . import frdata, freqplot, xferfcn, statesp
1717
from .exception import ControlMIMONotImplemented
1818
from .iosys import issiso
19-
from . import ss
2019
from .ctrlutil import mag2db
2120
try:
2221
from slycot import ab13md
2322
except ImportError:
2423
ab13md = None
2524

26-
__all__ = ['stability_margins', 'phase_crossover_frequencies', 'margin', 'disk_margins', 'disk_margin_plot']
25+
__all__ = ['stability_margins', 'phase_crossover_frequencies', 'margin',\
26+
'disk_margins', 'disk_margin_plot']
2727

2828
# private helper functions
2929
def _poly_iw(sys):
@@ -525,12 +525,12 @@ def margin(*args):
525525
return margin[0], margin[1], margin[3], margin[4]
526526

527527
def disk_margins(L, omega, skew = 0.0, returnall = False):
528-
"""Compute disk-based stability margins for SISO or MIMO LTI system.
528+
"""Compute disk-based stability margins for SISO or MIMO LTI loop transfer function.
529529
530530
Parameters
531531
----------
532532
L : SISO or MIMO LTI system
533-
Loop transfer function, e.g. P*C or C*P
533+
Loop transfer function, i.e., P*C or C*P
534534
omega : ndarray
535535
1d array of (non-negative) frequencies (rad/s) at which to evaluate
536536
the disk-based stability margins
@@ -594,13 +594,21 @@ def disk_margins(L, omega, skew = 0.0, returnall = False):
594594
Control Systems Magazine, Vol. 24, Nr. 1, Feb., pp. 60-76, 2004.
595595
"""
596596

597-
# Check for prerequisites
597+
# First argument must be a system
598+
if not isinstance(L, (statesp.StateSpace, xferfcn.TransferFunction)):
599+
raise ValueError("Loop gain must be state-space or transfer function object")
600+
601+
# Loop transfer function must be square
602+
if statesp.ss(L).B.shape[1] != statesp.ss(L).C.shape[0]:
603+
raise ValueError("Loop gain must be square (n_inputs = n_outputs)")
604+
605+
# Need slycot if L is MIMO, for mu calculation
598606
if (not L.issiso()) and (ab13md == None):
599607
raise ControlMIMONotImplemented("Need slycot to compute MIMO disk_margins")
600608

601609
# Get dimensions of feedback system
602-
ny,_ = ss(L).C.shape
603-
I = ss([], [], [], np.eye(ny))
610+
num_loops = statesp.ss(L).C.shape[0]
611+
I = statesp.ss([], [], [], np.eye(num_loops))
604612

605613
# Loop sensitivity function
606614
S = I.feedback(L)
@@ -628,7 +636,8 @@ def disk_margins(L, omega, skew = 0.0, returnall = False):
628636
# For the MIMO case, the norm on (S + (skew - I)/2) assumes a
629637
# single complex uncertainty block diagonal uncertainty structure.
630638
# AB13MD provides an upper bound on this norm at the given frequency.
631-
DM[ii] = 1.0/ab13md(ST_jw[ii], np.array(ny*[1]), np.array(ny*[2]))[0]
639+
DM[ii] = 1.0/ab13md(ST_jw[ii], np.array(num_loops*[1]),\
640+
np.array(num_loops*[2]))[0]
632641

633642
# Disk-based gain margin (dB) and phase margin (deg)
634643
with np.errstate(divide = 'ignore', invalid = 'ignore'):
@@ -669,20 +678,18 @@ def disk_margins(L, omega, skew = 0.0, returnall = False):
669678
(not gmidx != -1 and float('inf')) or DGM[gmidx][0],
670679
(not DPM.shape[0] and float('inf')) or DPM[pmidx][0])
671680

672-
def disk_margin_plot(alpha_max, skew = 0.0, ax = None):
681+
def disk_margin_plot(alpha_max, skew, ax = None):
673682
"""Plot region of allowable gain/phase variation, given worst-case disk margin.
674683
675684
Parameters
676685
----------
677-
alpha_max : float
678-
worst-case disk margin(s) across all (relevant) frequencies.
679-
Note that skew may be a scalar or list.
680-
skew : float, optional, default = 0
686+
alpha_max : float (scalar or list)
687+
worst-case disk margin(s) across all frequencies. May be a scalar or list.
688+
skew : float (scalar or list)
681689
skew parameter(s) for disk margin calculation.
682690
skew = 0 uses the "balanced" sensitivity function 0.5*(S - T)
683691
skew = 1 uses the sensitivity function S
684692
skew = -1 uses the complementary sensitivity function T
685-
Note that skew may be a scalar or list.
686693
ax : axes to plot bounding curve(s) onto
687694
688695
Returns
@@ -707,7 +714,7 @@ def disk_margin_plot(alpha_max, skew = 0.0, ax = None):
707714
>> omega = np.logspace(-1, 2, 1001)
708715
>>
709716
>> s = control.tf('s') # Laplace variable
710-
>> L = 6.25*(s + 3)*(s + 5)/(s*(s + 1)**2*(s**2 + 0.18*s + 100)) # loop transfer function
717+
>> L = 6.25*(s + 3)*(s + 5)/(s*(s + 1)**2*(s**2 + 0.18*s + 100)) # loop gain
711718
>>
712719
>> DM_plot = []
713720
>> DM_plot.append(control.disk_margins(L, omega, skew = -1.0)[0]) # T-based (T)

0 commit comments

Comments
 (0)