Skip to content
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
29 changes: 15 additions & 14 deletions control/freqplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ def bode_plot(syslist, omega=None,
config.defaults['freqplot.number_of_samples'].
margins : bool
If True, plot gain and phase margin.
*args : `matplotlib` plot positional properties, optional
*args : :func:`matplotlib.pyplot.plot` positional properties, optional
Additional arguments for `matplotlib` plots (color, linestyle, etc)
**kwargs : `matplotlib` plot keyword properties, optional
**kwargs : :func:`matplotlib.pyplot.plot` keyword properties, optional
Additional keywords (passed to `matplotlib`)

Returns
Expand All @@ -128,21 +128,22 @@ def bode_plot(syslist, omega=None,
----------------
grid : bool
If True, plot grid lines on gain and phase plots. Default is set by
config.defaults['bode.grid'].
`config.defaults['bode.grid']`.


The default values for Bode plot configuration parameters can be reset
using the `config.defaults` dictionary, with module name 'bode'.

Notes
-----
1. Alternatively, you may use the lower-level method (mag, phase, freq)
= sys.freqresp(freq) to generate the frequency response for a system,
but it returns a MIMO response.
1. Alternatively, you may use the lower-level method
``(mag, phase, freq) = sys.freqresp(freq)`` to generate the frequency
response for a system, but it returns a MIMO response.

2. If a discrete time model is given, the frequency response is plotted
along the upper branch of the unit circle, using the mapping z = exp(j
\\omega dt) where omega ranges from 0 to pi/dt and dt is the discrete
timebase. If not timebase is specified (dt = True), dt is set to 1.
along the upper branch of the unit circle, using the mapping z = exp(j
\\omega dt) where omega ranges from 0 to pi/dt and dt is the discrete
timebase. If not timebase is specified (dt = True), dt is set to 1.

Examples
--------
Expand Down Expand Up @@ -464,9 +465,9 @@ def nyquist_plot(syslist, omega=None, plot=True, label_freq=0,
Label every nth frequency on the plot
arrowhead_width : arrow head width
arrowhead_length : arrow head length
*args : `matplotlib` plot positional properties, optional
*args : :func:`matplotlib.pyplot.plot` positional properties, optional
Additional arguments for `matplotlib` plots (color, linestyle, etc)
**kwargs : `matplotlib` plot keyword properties, optional
**kwargs : :func:`matplotlib.pyplot.plot` keyword properties, optional
Additional keywords (passed to `matplotlib`)

Returns
Expand Down Expand Up @@ -539,13 +540,13 @@ def nyquist_plot(syslist, omega=None, plot=True, label_freq=0,
ax = plt.gca()
# Plot arrow to indicate Nyquist encirclement orientation
ax.arrow(x[0], y[0], (x[1]-x[0])/2, (y[1]-y[0])/2, fc=c, ec=c,
head_width=arrowhead_width,
head_width=arrowhead_width,
head_length=arrowhead_length)

plt.plot(x, -y, '-', color=c, *args, **kwargs)
ax.arrow(
x[-1], -y[-1], (x[-1]-x[-2])/2, (y[-1]-y[-2])/2,
fc=c, ec=c, head_width=arrowhead_width,
fc=c, ec=c, head_width=arrowhead_width,
head_length=arrowhead_length)

# Mark the -1 point
Expand Down Expand Up @@ -601,7 +602,7 @@ def gangof4_plot(P, C, omega=None, **kwargs):
Linear input/output systems (process and control)
omega : array
Range of frequencies (list or bounds) in rad/sec
**kwargs : `matplotlib` plot keyword properties, optional
**kwargs : :func:`matplotlib.pyplot.plot` keyword properties, optional
Additional keywords (passed to `matplotlib`)

Returns
Expand Down
11 changes: 6 additions & 5 deletions control/iosys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1514,8 +1514,9 @@ def find_eqpt(sys, x0, u0=[], y0=None, t=0, params={},
return_y : bool, optional
If True, return the value of output at the equilibrium point.
return_result : bool, optional
If True, return the `result` option from the scipy root function used
to compute the equilibrium point.
If True, return the `result` option from the
:func:`scipy.optimize.root` function used to compute the equilibrium
point.

Returns
-------
Expand All @@ -1529,9 +1530,9 @@ def find_eqpt(sys, x0, u0=[], y0=None, t=0, params={},
If `return_y` is True, returns the value of the outputs at the
equilibrium point, or `None` if no equilibrium point was found and
`return_result` was False.
result : scipy root() result object, optional
If `return_result` is True, returns the `result` from the scipy root
function.
result : :class:`scipy.optimize.OptimizeResult`, optional
If `return_result` is True, returns the `result` from the
:func:`scipy.optimize.root` function.

"""
from scipy.optimize import root
Expand Down
6 changes: 2 additions & 4 deletions control/nichols.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,9 @@ def nichols_grid(cl_mags=None, cl_phases=None, line_style='dotted'):
Array of closed-loop phases defining the iso-phase lines on a custom
Nichols chart. Must be in the range -360 < cl_phases < 0
line_style : string, optional
.. seealso:: https://matplotlib.org/gallery/lines_bars_and_markers/linestyles.html
:doc:`Matplotlib linestyle \
<matplotlib:gallery/lines_bars_and_markers/linestyles>`

Returns
-------
None
"""
# Default chart size
ol_phase_min = -359.99
Expand Down
2 changes: 1 addition & 1 deletion control/phaseplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def phase_plot(odefun, X=None, Y=None, scale=1, X0=None, T=None,
func : callable(x, t, ...)
Computes the time derivative of y (compatible with odeint).
The function should be the same for as used for
scipy.integrate. Namely, it should be a function of the form
:mod:`scipy.integrate`. Namely, it should be a function of the form
dxdt = F(x, t) that accepts a state x of dimension 2 and
returns a derivative dx/dt of dimension 2.

Expand Down
2 changes: 1 addition & 1 deletion control/pzmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def pzmap(sys, plot=True, grid=False, title='Pole Zero Map', **kwargs):
----------
sys: LTI (StateSpace or TransferFunction)
Linear system for which poles and zeros are computed.
plot: bool
plot: bool, optional
If ``True`` a graph is generated with Matplotlib,
otherwise the poles and zeros are only computed and returned.
grid: boolean (default = False)
Expand Down
16 changes: 10 additions & 6 deletions control/rlocus.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@

# Main function: compute a root locus diagram
def root_locus(sys, kvect=None, xlim=None, ylim=None,
plotstr=None, plot=True, print_gain=None, grid=None, ax=None,
plotstr=None, plot=True, print_gain=None, grid=None, ax=None,
**kwargs):

"""Root locus plot
Expand All @@ -91,18 +91,22 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
kvect : list or ndarray, optional
List of gains to use in computing diagram.
xlim : tuple or list, optional
Set limits of x axis, normally with tuple (see matplotlib.axes).
Set limits of x axis, normally with tuple
(see :doc:`matplotlib:api/axes_api`).
ylim : tuple or list, optional
Set limits of y axis, normally with tuple (see matplotlib.axes).
Set limits of y axis, normally with tuple
(see :doc:`matplotlib:api/axes_api`).
plotstr : :func:`matplotlib.pyplot.plot` format string, optional
plotting style specification
plot : boolean, optional
If True (default), plot root locus diagram.
print_gain : bool
If True (default), report mouse clicks when close to the root locus
branches, calculate gain, damping and print.
grid : bool
If True plot omega-damping grid. Default is False.
ax : Matplotlib axis
axis on which to create root locus plot
ax : :class:`matplotlib.axes.Axes`
Axes on which to create root locus plot

Returns
-------
Expand Down Expand Up @@ -160,7 +164,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
fig = kwargs['fig']
ax = fig.axes[1]
else:
if ax is None:
if ax is None:
ax = plt.gca()
fig = ax.figure
ax.set_title('Root Locus')
Expand Down
4 changes: 2 additions & 2 deletions control/robust.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ def hinfsyn(P, nmeas, ncon):
rcond: 4-vector, reciprocal condition estimates of:
1: control transformation matrix
2: measurement transformation matrix
3: X-Ricatti equation
4: Y-Ricatti equation
3: X-Riccati equation
4: Y-Riccati equation
TODO: document significance of rcond

Raises
Expand Down
5 changes: 3 additions & 2 deletions control/sisotool.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ def sisotool(sys, kvect = None, xlim_rlocus = None, ylim_rlocus = None,
kvect : list or ndarray, optional
List of gains to use for plotting root locus
xlim_rlocus : tuple or list, optional
control of x-axis range, normally with tuple (see matplotlib.axes)
control of x-axis range, normally with tuple
(see :doc:`matplotlib:api/axes_api`).
ylim_rlocus : tuple or list, optional
control of y-axis range
plotstr_rlocus : Additional options to matplotlib
plotstr_rlocus : :func:`matplotlib.pyplot.plot` format string, optional
plotting style for the root locus plot(color, linestyle, etc)
rlocus_grid: boolean (default = False)
If True plot s-plane grid.
Expand Down
45 changes: 26 additions & 19 deletions control/statefbk.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
# Pole placement
def place(A, B, p):
"""Place closed loop eigenvalues

K = place(A, B, p)

Parameters
Expand All @@ -69,21 +70,24 @@ def place(A, B, p):
K : 2-d array
Gain such that A - B K has eigenvalues given in p

Algorithm
---------
This is a wrapper function for scipy.signal.place_poles, which
implements the Tits and Yang algorithm [1]. It will handle SISO,
MISO, and MIMO systems. If you want more control over the algorithm,
use scipy.signal.place_poles directly.

[1] A.L. Tits and Y. Yang, "Globally convergent algorithms for robust
pole assignment by state feedback, IEEE Transactions on Automatic
Control, Vol. 41, pp. 1432-1452, 1996.
Notes
-----
Algorithm
This is a wrapper function for :func:`scipy.signal.place_poles`, which
implements the Tits and Yang algorithm [1]_. It will handle SISO,
MISO, and MIMO systems. If you want more control over the algorithm,
use :func:`scipy.signal.place_poles` directly.

Limitations
-----------
The algorithm will not place poles at the same location more
than rank(B) times.
The algorithm will not place poles at the same location more
than rank(B) times.

References
----------
.. [1] A.L. Tits and Y. Yang, "Globally convergent algorithms for robust
pole assignment by state feedback, IEEE Transactions on Automatic
Control, Vol. 41, pp. 1432-1452, 1996.

Examples
--------
Expand Down Expand Up @@ -227,11 +231,11 @@ def lqe(A, G, C, QN, RN, NN=None):
Linear quadratic estimator design (Kalman filter) for continuous-time
systems. Given the system

Given the system
.. math::
x = Ax + Bu + Gw
y = Cx + Du + v


x &= Ax + Bu + Gw \\\\
y &= Cx + Du + v

with unbiased process noise w and measurement noise v with covariances

.. math:: E{ww'} = QN, E{vv'} = RN, E{wv'} = NN
Expand Down Expand Up @@ -260,11 +264,14 @@ def lqe(A, G, C, QN, RN, NN=None):
Kalman estimator gain
P: 2D array
Solution to Riccati equation

.. math::
A P + P A^T - (P C^T + G N) R^-1 (C P + N^T G^T) + G Q G^T = 0

A P + P A^T - (P C^T + G N) R^{-1} (C P + N^T G^T) + G Q G^T = 0

E: 1D array
Eigenvalues of estimator poles eig(A - L C)


Examples
--------
Expand Down Expand Up @@ -381,7 +388,7 @@ def lqr(*args, **keywords):
See Also
--------
lqe

"""

# Make sure that SLICOT is installed
Expand Down
23 changes: 12 additions & 11 deletions control/statesp.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
_statesp_defaults = {
'statesp.use_numpy_matrix': True,
'statesp.default_dt': None,
'statesp.remove_useless_states': True,
'statesp.remove_useless_states': True,
}


Expand Down Expand Up @@ -149,7 +149,7 @@ class StateSpace(LTI):
Setting dt = 0 specifies a continuous system, while leaving dt = None
means the system timebase is not specified. If 'dt' is set to True, the
system will be treated as a discrete time system with unspecified sampling
time. The default value of 'dt' is None and can be changed by changing the
time. The default value of 'dt' is None and can be changed by changing the
value of ``control.config.defaults['statesp.default_dt']``.

"""
Expand Down Expand Up @@ -788,15 +788,15 @@ def minreal(self, tol=0.0):

# TODO: add discrete time check
def returnScipySignalLTI(self):
"""Return a list of a list of scipy.signal.lti objects.
"""Return a list of a list of :class:`scipy.signal.lti` objects.

For instance,

>>> out = ssobject.returnScipySignalLTI()
>>> out[3][5]

is a signal.scipy.lti object corresponding to the transfer function from
the 6th input to the 4th output."""
is a :class:`scipy.signal.lti` object corresponding to the transfer
function from the 6th input to the 4th output."""

# Preallocate the output.
out = [[[] for _ in range(self.inputs)] for _ in range(self.outputs)]
Expand All @@ -809,8 +809,9 @@ def returnScipySignalLTI(self):
return out

def append(self, other):
"""Append a second model to the present model. The second
model is converted to state-space if necessary, inputs and
"""Append a second model to the present model.

The second model is converted to state-space if necessary, inputs and
outputs are appended and their order is preserved"""
if not isinstance(other, StateSpace):
other = _convertToStateSpace(other)
Expand Down Expand Up @@ -870,8 +871,8 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):

prewarp_frequency : float within [0, infinity)
The frequency [rad/s] at which to match with the input continuous-
time system's magnitude and phase (the gain=1 crossover frequency,
for example). Should only be specified with method='bilinear' or
time system's magnitude and phase (the gain=1 crossover frequency,
for example). Should only be specified with method='bilinear' or
'gbt' with alpha=0.5 and ignored otherwise.

Returns
Expand All @@ -881,7 +882,7 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):

Notes
-----
Uses the command 'cont2discrete' from scipy.signal
Uses :func:`scipy.signal.cont2discrete`

Examples
--------
Expand All @@ -896,7 +897,7 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):
if (method=='bilinear' or (method=='gbt' and alpha==0.5)) and \
prewarp_frequency is not None:
Twarp = 2*np.tan(prewarp_frequency*Ts/2)/prewarp_frequency
else:
else:
Twarp = Ts
Ad, Bd, C, D, _ = cont2discrete(sys, Twarp, method, alpha)
return StateSpace(Ad, Bd, C, D, Ts)
Expand Down
Loading