Skip to content

Commit bc74a0a

Browse files
committed
Added support for both matplotlib 1.x.x. and 2.x.x and fixed redrawing issue.
1 parent bde60f5 commit bc74a0a

3 files changed

Lines changed: 69 additions & 38 deletions

File tree

control/freqplot.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
# SUCH DAMAGE.
4141
#
4242
# $Id$
43-
43+
import matplotlib
4444
import matplotlib.pyplot as plt
4545
import scipy as sp
4646
import numpy as np
@@ -192,6 +192,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
192192
fig = plt.gcf()
193193
ax_mag = None
194194
ax_phase = None
195+
sisotool = False
195196

196197
# Get the current axes if they already exist
197198
for ax in fig.axes:
@@ -242,47 +243,47 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
242243
if Hz:
243244
Wcg, Wcp = Wcg/(2*math.pi),Wcp/(2*math.pi)
244245

245-
ax_mag.axhline(y=0 if dB else 1, color='k', linestyle=':')
246-
ax_phase.axhline(y=phase_limit if deg else math.radians(phase_limit), color='k', linestyle=':')
246+
ax_mag.axhline(y=0 if dB else 1, color='k', linestyle=':',zorder=-20)
247+
ax_phase.axhline(y=phase_limit if deg else math.radians(phase_limit), color='k', linestyle=':',zorder=-20)
247248
mag_ylim = ax_mag.get_ylim()
248249
phase_ylim = ax_phase.get_ylim()
249250

250251
if pm != float('inf') and Wcp != float('nan'):
251252
if dB:
252-
ax_mag.semilogx([Wcp, Wcp], [0.,-1e5],color='k', linestyle=':')
253+
ax_mag.semilogx([Wcp, Wcp], [0.,-1e5],color='k', linestyle=':',zorder=-20)
253254
else:
254-
ax_mag.loglog([Wcp,Wcp], [1.,1e-8],color='k',linestyle=':')
255+
ax_mag.loglog([Wcp,Wcp], [1.,1e-8],color='k',linestyle=':',zorder=-20)
255256

256257
if deg:
257-
ax_phase.semilogx([Wcp, Wcp], [1e5, phase_limit+pm],color='k', linestyle=':')
258-
ax_phase.semilogx([Wcp, Wcp], [phase_limit + pm, phase_limit],color='k')
258+
ax_phase.semilogx([Wcp, Wcp], [1e5, phase_limit+pm],color='k', linestyle=':',zorder=-20)
259+
ax_phase.semilogx([Wcp, Wcp], [phase_limit + pm, phase_limit],color='k',zorder=-20)
259260
else:
260-
ax_phase.semilogx([Wcp, Wcp], [1e5, math.radians(phase_limit)+math.radians(pm)],color='k', linestyle=':')
261-
ax_phase.semilogx([Wcp, Wcp], [math.radians(phase_limit) +math.radians(pm), math.radians(phase_limit)],color='k')
261+
ax_phase.semilogx([Wcp, Wcp], [1e5, math.radians(phase_limit)+math.radians(pm)],color='k', linestyle=':',zorder=-20)
262+
ax_phase.semilogx([Wcp, Wcp], [math.radians(phase_limit) +math.radians(pm), math.radians(phase_limit)],color='k',zorder=-20)
262263

263264
if gm != float('inf') and Wcg != float('nan'):
264265
if dB:
265-
ax_mag.semilogx([Wcg, Wcg], [-20.*np.log10(gm), -1e5],color='k', linestyle=':')
266-
ax_mag.semilogx([Wcg, Wcg], [0,-20*np.log10(gm)],color='k')
266+
ax_mag.semilogx([Wcg, Wcg], [-20.*np.log10(gm), -1e5],color='k', linestyle=':',zorder=-20)
267+
ax_mag.semilogx([Wcg, Wcg], [0,-20*np.log10(gm)],color='k',zorder=-20)
267268
else:
268-
ax_mag.loglog([Wcg, Wcg], [1./gm,1e-8],color='k', linestyle=':')
269-
ax_mag.loglog([Wcg, Wcg], [1.,1./gm],color='k')
269+
ax_mag.loglog([Wcg, Wcg], [1./gm,1e-8],color='k', linestyle=':',zorder=-20)
270+
ax_mag.loglog([Wcg, Wcg], [1.,1./gm],color='k',zorder=-20)
270271

271272
if deg:
272-
ax_phase.semilogx([Wcg, Wcg], [1e-8, phase_limit],color='k', linestyle=':')
273+
ax_phase.semilogx([Wcg, Wcg], [1e-8, phase_limit],color='k', linestyle=':',zorder=-20)
273274
else:
274-
ax_phase.semilogx([Wcg, Wcg], [1e-8, math.radians(phase_limit)],color='k', linestyle=':')
275+
ax_phase.semilogx([Wcg, Wcg], [1e-8, math.radians(phase_limit)],color='k', linestyle=':',zorder=-20)
275276

276277
ax_mag.set_ylim(mag_ylim)
277278
ax_phase.set_ylim(phase_ylim)
278279

279280
if sisotool:
280-
ax_mag.text(0.04, 0.06, 'G.M.: %.2f %s\nFreq: %.2f %s'%(20*np.log10(gm) if dB else gm,'dB ' if dB else '\b',Wcg,'Hz' if Hz else 'rad/s'), horizontalalignment='left', verticalalignment='bottom',
281-
transform=ax_mag.transAxes,fontsize=10)
281+
ax_mag.text(0.04, 0.06, 'G.M.: %.2f %s\nFreq: %.2f %s'%(20*np.log10(gm) if dB else gm,'dB ' if dB else '',Wcg,'Hz' if Hz else 'rad/s'), horizontalalignment='left', verticalalignment='bottom',
282+
transform=ax_mag.transAxes,fontsize=8 if int(matplotlib.__version__[0]) == 1 else 6)
282283
ax_phase.text(0.04, 0.06, 'P.M.: %.2f %s\nFreq: %.2f %s'%(pm if deg else math.radians(pm),'deg' if deg else 'rad',Wcp,'Hz' if Hz else 'rad/s'), horizontalalignment='left', verticalalignment='bottom',
283-
transform=ax_phase.transAxes,fontsize=10)
284+
transform=ax_phase.transAxes,fontsize=8 if int(matplotlib.__version__[0]) == 1 else 6)
284285
else:
285-
plt.suptitle('Gm = %.2f %s(at %.2f %s), Pm = %.2f %s (at %.2f %s)'%(20*np.log10(gm) if dB else gm,'dB ' if dB else '\b','Hz' if Hz else 'rad/s',Wcg,pm if deg else math.radians(pm),'deg' if deg else 'rad',Wcp,'Hz' if Hz else 'rad/s'))
286+
plt.suptitle('Gm = %.2f %s(at %.2f %s), Pm = %.2f %s (at %.2f %s)'%(20*np.log10(gm) if dB else gm,'dB ' if dB else '\b',Wcg,'Hz' if Hz else 'rad/s',pm if deg else math.radians(pm),'deg' if deg else 'rad',Wcp,'Hz' if Hz else 'rad/s'))
286287

287288
if nyquistfrq_plot:
288289
ax_phase.axvline(nyquistfrq_plot, color=pltline[0].get_color())

control/rlocus.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
# Packages used by this module
4949
import numpy as np
50+
import matplotlib
5051
from scipy import array, poly1d, row_stack, zeros_like, real, imag
5152
import scipy.signal # signal processing toolbox
5253
import pylab # plotting routines
@@ -131,7 +132,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True,
131132

132133
elif sisotool == True:
133134
f.axes[1].plot([root.real for root in start_mat], [root.imag for root in start_mat], 'm.', marker='s', markersize=8,zorder=20)
134-
f.suptitle("Clicked at: %10.4g%+10.4gj gain: %10.4g damp: %10.4g" % (start_mat[0][0].real, start_mat[0][0].imag, 1, -1 * start_mat[0][0].real / abs(start_mat[0][0])))
135+
f.suptitle("Clicked at: %10.4g%+10.4gj gain: %10.4g damp: %10.4g" % (start_mat[0][0].real, start_mat[0][0].imag, 1, -1 * start_mat[0][0].real / abs(start_mat[0][0])),fontsize = 12 if int(matplotlib.__version__[0]) == 1 else 10)
135136
f.canvas.mpl_connect(
136137
'button_release_event',partial(_RLFeedbackClicksSisotool,sys=sys, fig=f, bode_plot_params=kwargs['bode_plot_params'],tvect=kwargs['tvect']))
137138

@@ -160,7 +161,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True,
160161
elif grid:
161162
_sgrid_func()
162163
else:
163-
ax.axhline(0., linestyle=':', color='k')
164+
ax.axhline(0., linestyle=':', color='k',zorder=-20)
164165
ax.axvline(0., linestyle=':', color='k')
165166
return mymat, kvect
166167

@@ -367,11 +368,14 @@ def _RLSortRoots(mymat):
367368
def _RLFeedbackClicksSisotool(event,sys,fig,bode_plot_params,tvect):
368369
"""Update Sisotool plots if a new point on the root locus plot is clicked
369370
"""
370-
s = complex(event.xdata, event.ydata)
371-
K = -1./sys.horner(s)
372-
ax_rlocus = fig.axes[1]
373-
if _RLFeedbackClicksPoint(event,sys,fig,ax_rlocus,sisotool=True):
374-
_SisotoolUpdate(sys,fig,K.real[0][0],bode_plot_params,tvect)
371+
try:
372+
s = complex(event.xdata, event.ydata)
373+
K = -1. / sys.horner(s)
374+
ax_rlocus = fig.axes[1]
375+
if _RLFeedbackClicksPoint(event, sys, fig, ax_rlocus, sisotool=True):
376+
_SisotoolUpdate(sys, fig, K.real[0][0], bode_plot_params, tvect)
377+
except TypeError:
378+
pass
375379

376380
def _RLFeedbackClicksPoint(event,sys,fig,ax_rlocus=None,sisotool=False):
377381
"""Display root-locus gain feedback point for clicks on the root-locus plot
@@ -388,7 +392,7 @@ def _RLFeedbackClicksPoint(event,sys,fig,ax_rlocus=None,sisotool=False):
388392
print("Clicked at %10.4g%+10.4gj gain %10.4g damp %10.4g" %
389393
(s.real, s.imag, K.real, -1 * s.real / abs(s)))
390394
fig.suptitle("Clicked at: %10.4g%+10.4gj gain: %10.4g damp: %10.4g" %
391-
(s.real, s.imag, K.real, -1 * s.real / abs(s)))
395+
(s.real, s.imag, K.real, -1 * s.real / abs(s)),fontsize = 12 if int(matplotlib.__version__[0]) == 1 else 10)
392396

393397
# Remove the previous points
394398
for line in reversed(ax_rlocus.lines):

control/sisotool.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
from .freqplot import bode_plot
44
from .timeresp import step_response
55
from .lti import issiso
6+
import matplotlib
67
import matplotlib.pyplot as plt
78

8-
def sisotool(sys, kvect = None, xlim = None, ylim = None, plotstr_rlocus = '-',rlocus_grid = False, omega = None, dB = None, Hz = None, deg = None, omega_limits = None, omega_num = None,margins = True, tvect=None):
9+
def sisotool(sys, kvect = None, xlim = None, ylim = None, plotstr_rlocus = 'b' if int(matplotlib.__version__[0]) == 1 else 'C0',rlocus_grid = False, omega = None, dB = None, Hz = None, deg = None, omega_limits = None, omega_num = None,margins = True, tvect=None):
910

1011
from .rlocus import root_locus
1112

@@ -29,7 +30,7 @@ def sisotool(sys, kvect = None, xlim = None, ylim = None, plotstr_rlocus = '-',r
2930
'omega_num' : omega_num,
3031
'sisotool': True,
3132
'fig': fig,
32-
'margins': margins,
33+
'margins': margins
3334
}
3435

3536
# First time call to setup the bode and step response plots
@@ -40,28 +41,53 @@ def sisotool(sys, kvect = None, xlim = None, ylim = None, plotstr_rlocus = '-',r
4041

4142
def _SisotoolUpdate(sys,fig,K,bode_plot_params,tvect=None):
4243

44+
if int(matplotlib.__version__[0]) == 1:
45+
title_font_size = 12
46+
label_font_size = 10
47+
else:
48+
title_font_size = 10
49+
label_font_size = 8
50+
4351
# Get the subaxes and clear them
4452
ax_mag,ax_rlocus,ax_phase,ax_step = fig.axes[0],fig.axes[1],fig.axes[2],fig.axes[3]
4553
ax_mag.cla(),ax_phase.cla(),ax_step.cla()
4654

47-
# Set the titles and labels
48-
ax_mag.set_title('Bode magnitude')
49-
ax_phase.set_title('Bode phase')
50-
ax_rlocus.set_title('Root locus')
51-
ax_step.set_title('Step response')
52-
ax_step.set_xlabel('Time (seconds)')
53-
ax_step.set_ylabel('Amplitude')
54-
5555
# Update the bodeplot
5656
bode_plot_params['syslist'] = sys*K.real
5757
bode_plot(**bode_plot_params)
5858

59+
# Set the titles and labels
60+
ax_mag.set_title('Bode magnitude',fontsize = title_font_size)
61+
ax_mag.set_ylabel(ax_mag.get_ylabel(), fontsize=label_font_size)
62+
63+
ax_phase.set_title('Bode phase',fontsize=title_font_size)
64+
ax_phase.set_xlabel(ax_phase.get_xlabel(),fontsize=label_font_size)
65+
ax_phase.set_ylabel(ax_phase.get_ylabel(),fontsize=label_font_size)
66+
ax_phase.get_xaxis().set_label_coords(0.5, -0.15)
67+
ax_phase.get_shared_x_axes().join(ax_phase, ax_mag)
68+
69+
ax_step.set_title('Step response',fontsize = title_font_size)
70+
ax_step.set_xlabel('Time (seconds)',fontsize=label_font_size)
71+
ax_step.set_ylabel('Amplitude',fontsize=label_font_size)
72+
ax_step.get_xaxis().set_label_coords(0.5, -0.15)
73+
ax_step.get_yaxis().set_label_coords(-0.15, 0.5)
74+
75+
ax_rlocus.set_title('Root locus',fontsize = title_font_size)
76+
ax_rlocus.set_ylabel('Imag', fontsize=label_font_size)
77+
ax_rlocus.set_xlabel('Real', fontsize=label_font_size)
78+
ax_rlocus.get_xaxis().set_label_coords(0.5, -0.15)
79+
ax_rlocus.get_yaxis().set_label_coords(-0.15, 0.5)
80+
5981
# Generate the step response and plot it
6082
sys_closed = (K*sys).feedback(1)
6183
if tvect is None:
6284
tvect, yout = step_response(sys_closed)
6385
else:
6486
tvect, yout = step_response(sys_closed,tvect)
6587
ax_step.plot(tvect, yout)
66-
ax_step.axhline(1.,linestyle=':',color='k')
88+
ax_step.axhline(1.,linestyle=':',color='k',zorder=-20)
89+
90+
# Manually adjust the spacing and draw the canvas
91+
fig.subplots_adjust(top=0.9,wspace = 0.3,hspace=0.35)
92+
fig.canvas.draw()
6793

0 commit comments

Comments
 (0)