Skip to content

Commit 601b581

Browse files
authored
Merge pull request #187 from murrayrm/fix_warnings-15jan08
Fix deprecation and formatting warnings
2 parents af8d4ee + 2444296 commit 601b581

File tree

5 files changed

+147
-29
lines changed

5 files changed

+147
-29
lines changed

control/freqplot.py

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,22 +171,47 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
171171
#! TODO: Not current implemented; just use subplot for now
172172

173173
if (Plot):
174+
# Set up the axes with labels so that multiple calls to
175+
# bode_plot will superimpose the data. This was implicit
176+
# before matplotlib 2.1, but changed after that (See
177+
# https://github.com/matplotlib/matplotlib/issues/9024).
178+
# The code below should work on all cases.
179+
180+
# Get the current figure
181+
fig = plt.gcf()
182+
ax_mag = None
183+
ax_phase = None
184+
185+
# Get the current axes if they already exist
186+
for ax in fig.axes:
187+
if ax.get_label() == 'control-bode-magnitude':
188+
ax_mag = ax
189+
elif ax.get_label() == 'control-bode-phase':
190+
ax_phase = ax
191+
192+
# If no axes present, create them from scratch
193+
if ax_mag is None or ax_phase is None:
194+
plt.clf()
195+
ax_mag = plt.subplot(211, label = 'control-bode-magnitude')
196+
ax_phase = plt.subplot(212, label = 'control-bode-phase',
197+
sharex=ax_mag)
198+
174199
# Magnitude plot
175-
ax_mag = plt.subplot(211);
176200
if dB:
177-
pltline = ax_mag.semilogx(omega_plot, 20 * np.log10(mag), *args, **kwargs)
201+
pltline = ax_mag.semilogx(omega_plot, 20 * np.log10(mag),
202+
*args, **kwargs)
178203
else:
179204
pltline = ax_mag.loglog(omega_plot, mag, *args, **kwargs)
180205

181206
if nyquistfrq_plot:
182-
ax_mag.axvline(nyquistfrq_plot, color=pltline[0].get_color())
207+
ax_mag.axvline(nyquistfrq_plot,
208+
color=pltline[0].get_color())
183209

184210
# Add a grid to the plot + labeling
185211
ax_mag.grid(True, which='both')
186212
ax_mag.set_ylabel("Magnitude (dB)" if dB else "Magnitude")
187213

188214
# Phase plot
189-
ax_phase = plt.subplot(212, sharex=ax_mag);
190215
if deg:
191216
phase_plot = phase * 180. / math.pi
192217
else:
@@ -353,28 +378,50 @@ def gangof4_plot(P, C, omega=None):
353378
L = P * C;
354379
S = feedback(1, L);
355380
T = L * S;
356-
381+
382+
# Set up the axes with labels so that multiple calls to
383+
# gangof4_plot will superimpose the data. See details in bode_plot.
384+
plot_axes = {'t' : None, 's' : None, 'ps' : None, 'cs' : None}
385+
for ax in plt.gcf().axes:
386+
label = ax.get_label()
387+
if label.startswith('control-gangof4-'):
388+
key = label[len('control-gangof4-'):]
389+
if key not in plot_axes:
390+
raise RuntimeError("unknown gangof4 axis type '{}'".format(label))
391+
plot_axes[key] = ax
392+
393+
# if any of the axes are missing, start from scratch
394+
if any((ax is None for ax in plot_axes.values())):
395+
plt.clf()
396+
plot_axes = {'t' : plt.subplot(221,label='control-gangof4-t'),
397+
'ps' : plt.subplot(222,label='control-gangof4-ps'),
398+
'cs' : plt.subplot(223,label='control-gangof4-cs'),
399+
's' : plt.subplot(224,label='control-gangof4-s')}
400+
401+
#
357402
# Plot the four sensitivity functions
403+
#
404+
358405
#! TODO: Need to add in the mag = 1 lines
359406
mag_tmp, phase_tmp, omega = T.freqresp(omega);
360407
mag = np.squeeze(mag_tmp)
361408
phase = np.squeeze(phase_tmp)
362-
plt.subplot(221); plt.loglog(omega, mag);
409+
plot_axes['t'].loglog(omega, mag);
363410

364411
mag_tmp, phase_tmp, omega = (P * S).freqresp(omega);
365412
mag = np.squeeze(mag_tmp)
366413
phase = np.squeeze(phase_tmp)
367-
plt.subplot(222); plt.loglog(omega, mag);
414+
plot_axes['ps'].loglog(omega, mag);
368415

369416
mag_tmp, phase_tmp, omega = (C * S).freqresp(omega);
370417
mag = np.squeeze(mag_tmp)
371418
phase = np.squeeze(phase_tmp)
372-
plt.subplot(223); plt.loglog(omega, mag);
419+
plot_axes['cs'].loglog(omega, mag);
373420

374421
mag_tmp, phase_tmp, omega = S.freqresp(omega);
375422
mag = np.squeeze(mag_tmp)
376423
phase = np.squeeze(phase_tmp)
377-
plt.subplot(224); plt.loglog(omega, mag);
424+
plot_axes['s'].loglog(omega, mag);
378425

379426
#
380427
# Utility functions

control/statesp.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,18 +1078,18 @@ def ss(*args):
10781078
output equations:
10791079
10801080
.. math::
1081-
\dot x = A \cdot x + B \cdot u
1081+
\\dot x = A \\cdot x + B \\cdot u
10821082
1083-
y = C \cdot x + D \cdot u
1083+
y = C \\cdot x + D \\cdot u
10841084
10851085
``ss(A, B, C, D, dt)``
10861086
Create a discrete-time state space system from the matrices of
10871087
its state and output equations:
10881088
10891089
.. math::
1090-
x[k+1] = A \cdot x[k] + B \cdot u[k]
1090+
x[k+1] = A \\cdot x[k] + B \\cdot u[k]
10911091
1092-
y[k] = C \cdot x[k] + D \cdot u[ki]
1092+
y[k] = C \\cdot x[k] + D \\cdot u[ki]
10931093
10941094
The matrices can be given as *array like* data types or strings.
10951095
Everything that the constructor of :class:`numpy.matrix` accepts is

control/tests/freqresp_test.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import unittest
1010
import numpy as np
11+
import control as ctrl
1112
from control.statesp import StateSpace
1213
from control.matlab import ss, tf, bode
1314
from control.exception import slycot_check
@@ -34,6 +35,50 @@ def test_siso(self):
3435
systf = tf(sys)
3536
bode(systf)
3637

38+
def test_superimpose(self):
39+
# Test to make sure that multiple calls to plots superimpose their
40+
# data on the same axes unless told to do otherwise
41+
42+
# Generate two plots in a row; should be on the same axes
43+
plt.figure(1); plt.clf()
44+
ctrl.bode_plot(ctrl.tf([1], [1,2,1]))
45+
ctrl.bode_plot(ctrl.tf([5], [1, 1]))
46+
47+
# Check to make sure there are two axes and that each axes has two lines
48+
assert len(plt.gcf().axes) == 2
49+
for ax in plt.gcf().axes:
50+
# Make sure there are 2 lines in each subplot
51+
assert len(ax.get_lines()) == 2
52+
53+
# Generate two plots as a list; should be on the same axes
54+
plt.figure(2); plt.clf();
55+
ctrl.bode_plot([ctrl.tf([1], [1,2,1]), ctrl.tf([5], [1, 1])])
56+
57+
# Check to make sure there are two axes and that each axes has two lines
58+
assert len(plt.gcf().axes) == 2
59+
for ax in plt.gcf().axes:
60+
# Make sure there are 2 lines in each subplot
61+
assert len(ax.get_lines()) == 2
62+
63+
# Generate two separate plots; only the second should appear
64+
plt.figure(3); plt.clf();
65+
ctrl.bode_plot(ctrl.tf([1], [1,2,1]))
66+
plt.clf()
67+
ctrl.bode_plot(ctrl.tf([5], [1, 1]))
68+
69+
# Check to make sure there are two axes and that each axes has one line
70+
assert len(plt.gcf().axes) == 2
71+
for ax in plt.gcf().axes:
72+
# Make sure there is only 1 line in the subplot
73+
assert len(ax.get_lines()) == 1
74+
75+
# Now add a line to the magnitude plot and make sure if is there
76+
for ax in plt.gcf().axes:
77+
if ax.get_label() == 'control-bode-magnitude':
78+
break
79+
ax.semilogx([1e-2, 1e1], 20 * np.log10([1, 1]), 'k-')
80+
assert len(ax.get_lines()) == 2
81+
3782
def test_doubleint(self):
3883
# 30 May 2016, RMM: added to replicate typecast bug in freqresp.py
3984
A = np.matrix('0, 1; 0, 0');

doc/control.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ Frequency domain plotting
4444
gangof4_plot
4545
nichols_plot
4646

47+
Note: For plotting commands that create multiple axes on the same plot, the
48+
individual axes can be retrieved using the axes label (retrieved using the
49+
`get_label` method for the matplotliib axes object). The following labels
50+
are currently defined:
51+
52+
* Bode plots: `control-bode-magnitude`, `control-bode-phase`
53+
* Gang of 4 plots: `control-gangof4-s`, `control-gangof4-cs`,
54+
`control-gangof4-ps`, `control-gangof4-t`
55+
4756
Time domain simulation
4857
======================
4958

examples/pvtol-nested.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,26 +81,45 @@
8181
T = feedback(L, 1);
8282

8383
# Compute stability margins
84-
#! Not yet implemented
85-
# (gm, pm, wgc, wpc) = margin(L);
84+
(gm, pm, wgc, wpc) = margin(L);
85+
print("Gain margin: %g at %g" % (gm, wgc))
86+
print("Phase margin: %g at %g" % (pm, wpc))
8687

87-
#! TODO: this figure has something wrong; axis limits mismatch
8888
figure(6); clf;
89-
bode(L);
89+
bode(L, logspace(-4, 3));
9090

91-
# Add crossover line
92-
subplot(211); hold(True);
93-
loglog([1e-4, 1e3], [1, 1], 'k-')
91+
# Add crossover line to the magnitude plot
92+
#
93+
# Note: in matplotlib before v2.1, the following code worked:
94+
#
95+
# subplot(211); hold(True);
96+
# loglog([1e-4, 1e3], [1, 1], 'k-')
97+
#
98+
# In later versions of matplotlib the call to subplot will clear the
99+
# axes and so we have to extract the axes that we want to use by hand.
100+
# In addition, hold() is deprecated so we no longer require it.
101+
#
102+
for ax in gcf().axes:
103+
if ax.get_label() == 'control-bode-magnitude':
104+
break
105+
ax.semilogx([1e-4, 1e3], 20 * np.log10([1, 1]), 'k-')
94106

107+
#
95108
# Replot phase starting at -90 degrees
96-
bode(L, logspace(-4, 3));
109+
#
110+
# Get the phase plot axes
111+
for ax in gcf().axes:
112+
if ax.get_label() == 'control-bode-phase':
113+
break
114+
115+
# Recreate the frequency response and shift the phase
97116
(mag, phase, w) = freqresp(L, logspace(-4, 3));
98117
phase = phase - 360;
99-
subplot(212);
100-
semilogx([1e-4, 1e3], [-180, -180], 'k-')
101-
hold(True);
102-
semilogx(w, np.squeeze(phase), 'b-')
103-
axis([1e-4, 1e3, -360, 0]);
118+
119+
# Replot the phase by hand
120+
ax.semilogx([1e-4, 1e3], [-180, -180], 'k-')
121+
ax.semilogx(w, np.squeeze(phase), 'b-')
122+
ax.axis([1e-4, 1e3, -360, 0]);
104123
xlabel('Frequency [deg]'); ylabel('Phase [deg]');
105124
# set(gca, 'YTick', [-360, -270, -180, -90, 0]);
106125
# set(gca, 'XTick', [10^-4, 10^-2, 1, 100]);
@@ -109,7 +128,6 @@
109128
# Nyquist plot for complete design
110129
#
111130
figure(7); clf;
112-
axis([-700, 5300, -3000, 3000]); hold(True);
113131
nyquist(L, (0.0001, 1000));
114132
axis([-700, 5300, -3000, 3000]);
115133

@@ -118,7 +136,6 @@
118136

119137
# Expanded region
120138
figure(8); clf; subplot(231);
121-
axis([-10, 5, -20, 20]); hold(True);
122139
nyquist(L);
123140
axis([-10, 5, -20, 20]);
124141

@@ -136,7 +153,7 @@
136153

137154
figure(9);
138155
(Yvec, Tvec) = step(T, linspace(0, 20));
139-
plot(Tvec.T, Yvec.T); hold(True);
156+
plot(Tvec.T, Yvec.T);
140157

141158
(Yvec, Tvec) = step(Co*S, linspace(0, 20));
142159
plot(Tvec.T, Yvec.T);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy