Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit908fabe

Browse files
authored
Merge pull request#209 from icam0/sisotool_final
Sisotool and dynamic root locus zoom
2 parents626c459 +17c4a95 commit908fabe

File tree

7 files changed

+632
-113
lines changed

7 files changed

+632
-113
lines changed

‎control/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
from .canonicalimport*
6868
from .robustimport*
6969
from .configimport*
70+
from .sisotoolimport*
7071

7172
# Exceptions
7273
from .exceptionimport*

‎control/freqplot.py

Lines changed: 129 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
# SUCH DAMAGE.
4141
#
4242
# $Id$
43-
43+
importmatplotlib
4444
importmatplotlib.pyplotasplt
4545
importscipyassp
4646
importnumpyasnp
@@ -89,7 +89,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
8989
omega_num: int
9090
number of samples
9191
margins : boolean
92-
if True, plot gain and phase margin
92+
If True, plot gain and phase margin
9393
\*args, \**kwargs:
9494
Additional options to matplotlib (color, linestyle, etc)
9595
@@ -193,23 +193,35 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
193193
# The code below should work on all cases.
194194

195195
# Get the current figure
196-
fig=plt.gcf()
197-
ax_mag=None
198-
ax_phase=None
199-
200-
# Get the current axes if they already exist
201-
foraxinfig.axes:
202-
ifax.get_label()=='control-bode-magnitude':
203-
ax_mag=ax
204-
elifax.get_label()=='control-bode-phase':
205-
ax_phase=ax
206-
207-
# If no axes present, create them from scratch
208-
ifax_magisNoneorax_phaseisNone:
209-
plt.clf()
210-
ax_mag=plt.subplot(211,label='control-bode-magnitude')
211-
ax_phase=plt.subplot(212,label='control-bode-phase',
212-
sharex=ax_mag)
196+
197+
if'sisotool'inkwargs:
198+
fig=kwargs['fig']
199+
ax_mag=fig.axes[0]
200+
ax_phase=fig.axes[2]
201+
sisotool=kwargs['sisotool']
202+
delkwargs['fig']
203+
delkwargs['sisotool']
204+
else:
205+
fig=plt.gcf()
206+
ax_mag=None
207+
ax_phase=None
208+
sisotool=False
209+
210+
# Get the current axes if they already exist
211+
foraxinfig.axes:
212+
ifax.get_label()=='control-bode-magnitude':
213+
ax_mag=ax
214+
elifax.get_label()=='control-bode-phase':
215+
ax_phase=ax
216+
217+
# If no axes present, create them from scratch
218+
ifax_magisNoneorax_phaseisNone:
219+
plt.clf()
220+
ax_mag=plt.subplot(211,
221+
label='control-bode-magnitude')
222+
ax_phase=plt.subplot(212,
223+
label='control-bode-phase',
224+
sharex=ax_mag)
213225

214226
# Magnitude plot
215227
ifdB:
@@ -237,58 +249,107 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
237249
ifmargins:
238250
margin=stability_margins(sys)
239251
gm,pm,Wcg,Wcp=margin[0],margin[1],margin[3],margin[4]
240-
ifpm>=0.:
241-
phase_limit=-180.
242-
else:
252+
# TODO: add some documentation describing why this is here
253+
phase_at_cp=phases[0][(np.abs(omegas[0]-Wcp)).argmin()]
254+
ifphase_at_cp>=0.:
243255
phase_limit=180.
256+
else:
257+
phase_limit=-180.
258+
259+
ifHz:
260+
Wcg,Wcp=Wcg/(2*math.pi),Wcp/(2*math.pi)
244261

245-
ax_mag.axhline(y=0ifdBelse1,color='k',linestyle=':')
262+
ax_mag.axhline(y=0ifdBelse1,color='k',linestyle=':',
263+
zorder=-20)
246264
ax_phase.axhline(y=phase_limitifdegelsemath.radians(phase_limit),
247-
color='k',linestyle=':')
265+
color='k',linestyle=':',zorder=-20)
248266
mag_ylim=ax_mag.get_ylim()
249267
phase_ylim=ax_phase.get_ylim()
250268

251269
ifpm!=float('inf')andWcp!=float('nan'):
252270
ifdB:
253-
ax_mag.semilogx([Wcp,Wcp], [0.,-1e5],color='k',linestyle=':')
271+
ax_mag.semilogx([Wcp,Wcp], [0.,-1e5],
272+
color='k',linestyle=':',
273+
zorder=-20)
254274
else:
255-
ax_mag.loglog([Wcp,Wcp], [1.,1e-8],color='k',linestyle=':')
275+
ax_mag.loglog([Wcp,Wcp], [1.,1e-8],color='k',
276+
linestyle=':',zorder=-20)
256277

257278
ifdeg:
258-
ax_phase.semilogx([Wcp,Wcp], [1e5,phase_limit+pm],
259-
color='k',linestyle=':')
260-
ax_phase.semilogx([Wcp,Wcp], [phase_limit+pm,phase_limit],
261-
color='k')
279+
ax_phase.semilogx([Wcp,Wcp],
280+
[1e5,phase_limit+pm],
281+
color='k',linestyle=':',
282+
zorder=-20)
283+
ax_phase.semilogx([Wcp,Wcp],
284+
[phase_limit+pm,phase_limit],
285+
color='k',zorder=-20)
262286
else:
263-
ax_phase.semilogx([Wcp,Wcp], [1e5,math.radians(phase_limit)+
264-
math.radians(pm)],
265-
color='k',linestyle=':')
266-
ax_phase.semilogx([Wcp,Wcp], [math.radians(phase_limit)+
267-
math.radians(pm),
268-
math.radians(phase_limit)],color='k')
287+
ax_phase.semilogx([Wcp,Wcp],
288+
[1e5,math.radians(phase_limit)+
289+
math.radians(pm)],
290+
color='k',linestyle=':',
291+
zorder=-20)
292+
ax_phase.semilogx([Wcp,Wcp],
293+
[math.radians(phase_limit)+
294+
math.radians(pm),
295+
math.radians(phase_limit)],
296+
color='k',zorder=-20)
269297

270298
ifgm!=float('inf')andWcg!=float('nan'):
271299
ifdB:
272-
ax_mag.semilogx([Wcg,Wcg], [-20.*np.log10(gm),-1e5],color='k',
273-
linestyle=':')
274-
ax_mag.semilogx([Wcg,Wcg], [0,-20*np.log10(gm)],color='k')
300+
ax_mag.semilogx([Wcg,Wcg],
301+
[-20.*np.log10(gm),-1e5],
302+
color='k',linestyle=':',
303+
zorder=-20)
304+
ax_mag.semilogx([Wcg,Wcg], [0,-20*np.log10(gm)],
305+
color='k',zorder=-20)
275306
else:
276-
ax_mag.loglog([Wcg,Wcg], [1./gm,1e-8],color='k',linestyle=':')
277-
ax_mag.loglog([Wcg,Wcg], [1.,1./gm],color='k')
307+
ax_mag.loglog([Wcg,Wcg],
308+
[1./gm,1e-8],color='k',
309+
linestyle=':',zorder=-20)
310+
ax_mag.loglog([Wcg,Wcg],
311+
[1.,1./gm],color='k',zorder=-20)
278312

279313
ifdeg:
280-
ax_phase.semilogx([Wcg,Wcg], [1e-8,phase_limit],
281-
color='k',linestyle=':')
314+
ax_phase.semilogx([Wcg,Wcg], [1e-8,phase_limit],
315+
color='k',linestyle=':',
316+
zorder=-20)
282317
else:
283-
ax_phase.semilogx([Wcg,Wcg], [1e-8,math.radians(phase_limit)],
284-
color='k',linestyle=':')
318+
ax_phase.semilogx([Wcg,Wcg],
319+
[1e-8,math.radians(phase_limit)],
320+
color='k',linestyle=':',
321+
zorder=-20)
285322

286323
ax_mag.set_ylim(mag_ylim)
287324
ax_phase.set_ylim(phase_ylim)
288-
plt.suptitle('Gm = %.2f %s(at %.2f rad/s), Pm = %.2f %s (at %.2f rad/s)'%
289-
(20*np.log10(gm)ifdBelsegm,
290-
'dB 'ifdBelse'\b',Wcg,pmifdegelsemath.radians(pm),
291-
'deg'ifdegelse'rad',Wcp))
325+
326+
ifsisotool:
327+
ax_mag.text(0.04,0.06,
328+
'G.M.: %.2f %s\nFreq: %.2f %s'%
329+
(20*np.log10(gm)ifdBelsegm,
330+
'dB 'ifdBelse'',
331+
Wcg,'Hz'ifHzelse'rad/s'),
332+
horizontalalignment='left',
333+
verticalalignment='bottom',
334+
transform=ax_mag.transAxes,
335+
fontsize=8ifint(matplotlib.__version__[0])==1else6)
336+
ax_phase.text(0.04,0.06,
337+
'P.M.: %.2f %s\nFreq: %.2f %s'%
338+
(pmifdegelsemath.radians(pm),
339+
'deg'ifdegelse'rad',
340+
Wcp,'Hz'ifHzelse'rad/s'),
341+
horizontalalignment='left',
342+
verticalalignment='bottom',
343+
transform=ax_phase.transAxes,
344+
fontsize=8ifint(matplotlib.__version__[0])==1else6)
345+
else:
346+
plt.suptitle('Gm = %.2f %s(at %.2f %s), Pm = %.2f %s (at %.2f %s)'%
347+
(20*np.log10(gm)ifdBelsegm,
348+
'dB 'ifdBelse'\b',
349+
Wcg,'Hz'ifHzelse'rad/s',
350+
pmifdegelsemath.radians(pm),
351+
'deg'ifdegelse'rad',
352+
Wcp,'Hz'ifHzelse'rad/s'))
292353

293354
ifnyquistfrq_plot:
294355
ax_phase.axvline(nyquistfrq_plot,color=pltline[0].get_color())
@@ -302,12 +363,19 @@ def gen_zero_centered_series(val_min, val_max, period):
302363
returnnp.arange(v1,v2+1)*period
303364
ifdeg:
304365
ylim=ax_phase.get_ylim()
305-
ax_phase.set_yticks(gen_zero_centered_series(ylim[0],ylim[1],45.))
306-
ax_phase.set_yticks(gen_zero_centered_series(ylim[0],ylim[1],15.),minor=True)
366+
ax_phase.set_yticks(gen_zero_centered_series(ylim[0],
367+
ylim[1],45.))
368+
ax_phase.set_yticks(gen_zero_centered_series(ylim[0],
369+
ylim[1],15.),
370+
minor=True)
307371
else:
308372
ylim=ax_phase.get_ylim()
309-
ax_phase.set_yticks(gen_zero_centered_series(ylim[0],ylim[1],math.pi/4.))
310-
ax_phase.set_yticks(gen_zero_centered_series(ylim[0],ylim[1],math.pi/12.),
373+
ax_phase.set_yticks(gen_zero_centered_series(ylim[0],
374+
ylim[1],
375+
math.pi/4.))
376+
ax_phase.set_yticks(gen_zero_centered_series(ylim[0],
377+
ylim[1],
378+
math.pi/12.),
311379
minor=True)
312380
ax_phase.grid(FalseifmarginselseTrue,which='both')
313381
# ax_mag.grid(which='minor', alpha=0.3)
@@ -316,15 +384,17 @@ def gen_zero_centered_series(val_min, val_max, period):
316384
# ax_phase.grid(which='major', alpha=0.9)
317385

318386
# Label the frequency axis
319-
ax_phase.set_xlabel("Frequency (Hz)"ifHzelse"Frequency (rad/sec)")
387+
ax_phase.set_xlabel("Frequency (Hz)"ifHz
388+
else"Frequency (rad/sec)")
320389

321390
iflen(syslist)==1:
322391
returnmags[0],phases[0],omegas[0]
323392
else:
324393
returnmags,phases,omegas
325394

326395

327-
defnyquist_plot(syslist,omega=None,Plot=True,color=None,labelFreq=0,*args,**kwargs):
396+
defnyquist_plot(syslist,omega=None,Plot=True,color=None,
397+
labelFreq=0,*args,**kwargs):
328398
"""
329399
Nyquist plot for a system
330400
@@ -683,6 +753,10 @@ def gen_prefix(pow1000):
683753
'z',# zepto (10^-21)
684754
'y'][8-pow1000]# yocto (10^-24)
685755

756+
deffind_nearest_omega(omega_list,omega):
757+
omega_list=np.asarray(omega_list)
758+
idx= (np.abs(omega_list-omega)).argmin()
759+
returnomega_list[(np.abs(omega_list-omega)).argmin()]
686760

687761
# Function aliases
688762
bode=bode_plot

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp