Skip to content

Commit 991daf4

Browse files
authored
Merge branch 'master' into bode_margins_plot
2 parents 0cf3989 + a007fcc commit 991daf4

28 files changed

+654
-628
lines changed

.travis.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ env:
2828
before_install:
2929
# Install gfortran for testing slycot; use apt-get instead of conda in
3030
# order to include the proper CXXABI dependency (updated in GCC 4.9)
31-
# Also need to include liblapack here, to make sure paths are right
3231
- if [[ "$SLYCOT" != "" ]]; then
3332
sudo apt-get update -qq;
34-
sudo apt-get install gfortran liblapack-dev;
33+
sudo apt-get install gfortran;
3534
fi
3635
# Install display manager to allow testing of plotting functions
3736
- export DISPLAY=:99.0
@@ -51,6 +50,10 @@ before_install:
5150
- conda info -a
5251
- conda create -q -n test-environment python="$TRAVIS_PYTHON_VERSION" pip coverage
5352
- source activate test-environment
53+
# Install openblas if slycot is being used
54+
- if [[ "$SLYCOT" != "" ]]; then
55+
conda install openblas;
56+
fi
5457
# Make sure to look in the right place for python libraries (for slycot)
5558
- export LIBRARY_PATH="$HOME/miniconda/envs/test-environment/lib"
5659
# coveralls not in conda repos => install via pip instead

control/bdalg.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@
6262
__all__ = ['series', 'parallel', 'negate', 'feedback', 'append', 'connect']
6363

6464
def series(sys1, *sysn):
65-
"""Return the series connection (... * sys3 *) sys2 * sys1
65+
"""Return the series connection (... \* sys3 \*) sys2 \* sys1
6666
6767
Parameters
6868
----------
6969
sys1: scalar, StateSpace, TransferFunction, or FRD
70-
*sysn: other scalars, StateSpaces, TransferFunctions, or FRDs
70+
sysn: other scalars, StateSpaces, TransferFunctions, or FRDs
7171
7272
Returns
7373
-------

control/canonical.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ def observable_form(xsys):
133133
Wrz = obsv(zsys.A, zsys.C)
134134

135135
# Transformation from one form to another
136-
Tzx = inv(Wrz) * Wrx
136+
Tzx = solve(Wrz, Wrx) # matrix left division, Tzx = inv(Wrz) * Wrx
137+
138+
if matrix_rank(Tzx) != xsys.states:
139+
raise ValueError("Transformation matrix singular to working precision.")
137140

138141
# Finally, compute the output matrix
139142
zsys.B = Tzx * xsys.B

control/freqplot.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
# Bode plot
6363
def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
6464
Plot=True, omega_limits=None, omega_num=None,margins=None, *args, **kwargs):
65-
"""Bode plot for a system
65+
"""
66+
Bode plot for a system
6667
6768
Plots a Bode plot for the system over a (optional) frequency range.
6869
@@ -87,7 +88,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
8788
number of samples
8889
margins : boolean
8990
if True, plot gain and phase margin
90-
*args, **kwargs:
91+
\*args, \**kwargs:
9192
Additional options to matplotlib (color, linestyle, etc)
9293
9394
Returns
@@ -300,7 +301,8 @@ def genZeroCenteredSeries(val_min, val_max, period):
300301
# Nyquist plot
301302
def nyquist_plot(syslist, omega=None, Plot=True, color='b',
302303
labelFreq=0, *args, **kwargs):
303-
"""Nyquist plot for a system
304+
"""
305+
Nyquist plot for a system
304306
305307
Plots a Nyquist plot for the system over a (optional) frequency range.
306308
@@ -314,7 +316,7 @@ def nyquist_plot(syslist, omega=None, Plot=True, color='b',
314316
If True, plot magnitude
315317
labelFreq : int
316318
Label every nth frequency on the plot
317-
*args, **kwargs:
319+
\*args, \**kwargs:
318320
Additional options to matplotlib (color, linestyle, etc)
319321
320322
Returns
@@ -330,6 +332,7 @@ def nyquist_plot(syslist, omega=None, Plot=True, color='b',
330332
--------
331333
>>> sys = ss("1. -2; 3. -4", "5.; 7", "6. 8", "9.")
332334
>>> real, imag, freq = nyquist_plot(sys)
335+
333336
"""
334337
# If argument was a singleton, turn it into a list
335338
if (not getattr(syslist, '__iter__', False)):

control/lti.py

+15-13
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ def zero(sys):
264264
return sys.zero()
265265

266266
def damp(sys, doprint=True):
267-
'''
267+
"""
268268
Compute natural frequency, damping ratio, and poles of a system
269269
270270
The function takes 1 or 2 parameters
@@ -285,24 +285,26 @@ def damp(sys, doprint=True):
285285
poles: array
286286
Pole locations
287287
288-
289288
Algorithm
290-
--------
291-
If the system is continuous,
292-
wn = abs(poles)
293-
Z = -real(poles)/poles.
289+
---------
290+
If the system is continuous,
291+
wn = abs(poles)
292+
Z = -real(poles)/poles.
293+
294+
If the system is discrete, the discrete poles are mapped to their
295+
equivalent location in the s-plane via
294296
295-
If the system is discrete, the discrete poles are mapped to their
296-
equivalent location in the s-plane via
297-
s = log10(poles)/dt
298-
and
299-
wn = abs(s)
300-
Z = -real(s)/wn.
297+
s = log10(poles)/dt
298+
299+
and
300+
301+
wn = abs(s)
302+
Z = -real(s)/wn.
301303
302304
See Also
303305
--------
304306
pole
305-
'''
307+
"""
306308
wn, damping, poles = sys.damp()
307309
if doprint:
308310
print('_____Eigenvalue______ Damping___ Frequency_')

control/margins.py

+18-14
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,12 @@ def stability_margins(sysdata, returnall=False, epsw=0.0):
108108
Linear SISO system
109109
mag, phase, omega : sequence of array_like
110110
Arrays of magnitudes (absolute values, not dB), phases (degrees),
111-
and corresponding frequencies. Crossover frequencies returned are
111+
and corresponding frequencies. Crossover frequencies returned are
112112
in the same units as those in `omega` (e.g., rad/sec or Hz).
113113
returnall: bool, optional
114-
If true, return all margins found. If false (default), return only the
115-
minimum stability margins. For frequency data or FRD systems, only one
116-
margin is found and returned.
114+
If true, return all margins found. If False (default), return only the
115+
minimum stability margins. For frequency data or FRD systems, only
116+
margins in the given frequency region can be found and returned.
117117
epsw: float, optional
118118
Frequencies below this value (default 0.0) are considered static gain,
119119
and not returned as margin.
@@ -127,11 +127,11 @@ def stability_margins(sysdata, returnall=False, epsw=0.0):
127127
sm: float or array_like
128128
Stability margin, the minimum distance from the Nyquist plot to -1
129129
wg: float or array_like
130-
Gain margin crossover frequency (where phase crosses -180 degrees)
130+
Frequency for gain margin (at phase crossover, phase = -180 degrees)
131131
wp: float or array_like
132-
Phase margin crossover frequency (where gain crosses 0 dB)
132+
Frequency for phase margin (at gain crossover, gain = 1)
133133
ws: float or array_like
134-
Stability margin frequency (where Nyquist plot is closest to -1)
134+
Frequency for stability margin (complex gain closest to -1)
135135
"""
136136

137137
try:
@@ -340,18 +340,22 @@ def margin(*args):
340340
Gain margin
341341
pm : float
342342
Phase margin (in degrees)
343-
Wcg : float
344-
Gain crossover frequency (corresponding to phase margin)
345-
Wcp : float
346-
Phase crossover frequency (corresponding to gain margin) (in rad/sec)
343+
wg: float
344+
Frequency for gain margin (at phase crossover, phase = -180 degrees)
345+
wp: float
346+
Frequency for phase margin (at gain crossover, gain = 1)
347347
348-
Margins are of SISO open-loop. If more than one crossover frequency is
349-
detected, returns the lowest corresponding margin.
348+
Margins are calculated for a SISO open-loop system.
349+
350+
If there is more than one gain crossover, the one at the smallest
351+
margin (deviation from gain = 1), in absolute sense, is
352+
returned. Likewise the smallest phase margin (in absolute sense)
353+
is returned.
350354
351355
Examples
352356
--------
353357
>>> sys = tf(1, [1, 2, 1, 0])
354-
>>> gm, pm, Wcg, Wcp = margin(sys)
358+
>>> gm, pm, wg, wp = margin(sys)
355359
356360
"""
357361
if len(args) == 1:

control/modelsimp.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ def modred(sys, ELIM, method='matchdc'):
125125
Raises
126126
------
127127
ValueError
128-
* if `method` is not either ``'matchdc'`` or ``'truncate'``
129-
* if eigenvalues of `sys.A` are not all in left half plane
128+
- if `method` is not either ``'matchdc'`` or ``'truncate'``
129+
- if eigenvalues of `sys.A` are not all in left half plane
130130
(`sys` must be stable)
131131
132132
Examples

control/robust.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,16 @@ def mixsyn(g,w1=None,w2=None,w3=None):
336336
Returns
337337
-------
338338
k: synthesized controller; StateSpace object
339-
cl: closed system mapping evaluation inputs to evaluation outputs; if p is the augmented plant, with
340-
[z] = [p11 p12] [w], then cl is the system from w->z with u=-k*y. StateSpace object.
339+
cl: closed system mapping evaluation inputs to evaluation outputs; if
340+
p is the augmented plant, with
341+
[z] = [p11 p12] [w],
341342
[y] [p21 g] [u]
343+
then cl is the system from w->z with u=-k*y. StateSpace object.
342344
343345
info: tuple with entries, in order,
344-
gamma: scalar; H-infinity norm of cl
345-
rcond: array; estimates of reciprocal condition numbers
346-
computed during synthesis. See hinfsyn for details
346+
- gamma: scalar; H-infinity norm of cl
347+
- rcond: array; estimates of reciprocal condition numbers
348+
computed during synthesis. See hinfsyn for details
347349
348350
If a weighting w is scalar, it will be replaced by I*w, where I is
349351
ny-by-ny for w1 and w3, and nu-by-nu for w2.

control/statefbk.py

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
def place(A, B, p):
5353
"""Place closed loop eigenvalues
5454
K = place(A, B, p)
55+
5556
Parameters
5657
----------
5758
A : 2-d array

0 commit comments

Comments
 (0)