59
59
60
60
# Main function: compute a root locus diagram
61
61
def root_locus (sys , kvect = None , xlim = None , ylim = None , plotstr = '-' , Plot = True ,
62
- PrintGain = True ):
62
+ PrintGain = True , grid = False ):
63
63
"""Root locus plot
64
64
65
65
Calculate the root locus by finding the roots of 1+k*TF(s)
@@ -99,9 +99,17 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True,
99
99
mymat = _RLFindRoots (nump , denp , kvect )
100
100
mymat = _RLSortRoots (mymat )
101
101
102
- # Create the plot
103
- if (Plot ):
104
- f = pylab .figure ()
102
+ # Create the Plot
103
+ if Plot :
104
+ figure_number = pylab .get_fignums ()
105
+ figure_title = [pylab .figure (numb ).canvas .get_window_title () for numb in figure_number ]
106
+ new_figure_name = "Root Locus"
107
+ rloc_num = 1
108
+ while new_figure_name in figure_title :
109
+ new_figure_name = "Root Locus " + str (rloc_num )
110
+ rloc_num += 1
111
+ f = pylab .figure (new_figure_name )
112
+
105
113
if PrintGain :
106
114
f .canvas .mpl_connect (
107
115
'button_release_event' , partial (_RLFeedbackClicks , sys = sys ))
@@ -127,6 +135,8 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True,
127
135
ax .set_ylim (ylim )
128
136
ax .set_xlabel ('Real' )
129
137
ax .set_ylabel ('Imaginary' )
138
+ if grid :
139
+ sgrid_func (f )
130
140
131
141
return mymat , kvect
132
142
@@ -305,6 +315,63 @@ def _RLFeedbackClicks(event, sys):
305
315
K = - 1. / sys .horner (s )
306
316
if abs (K .real ) > 1e-8 and abs (K .imag / K .real ) < 0.04 :
307
317
print ("Clicked at %10.4g%+10.4gj gain %10.4g damp %10.4g" %
308
- (s .real , s .imag , K .real , - 1 * s .real / abs (s )))
318
+ (s .real , s .imag , K .real , - 1 * s .real / abs (s )))
319
+
320
+
321
+ def sgrid_func (fig ):
322
+ ax = fig .gca ()
323
+ ylocator = ax .get_yaxis ().get_major_locator ()
324
+ xlocator = ax .get_yaxis ().get_major_locator ()
325
+
326
+ long_xaxis = xlocator ()[- 1 ] - xlocator ()[0 ]
327
+ long_yaxis = ylocator ()[- 1 ] - ylocator ()[0 ]
328
+
329
+ angules = np .arange (- 90 , 80 , 15 )* np .pi / 180
330
+
331
+ # radial lines
332
+ y_over_x = np .tan (angules [1 ::])* ylocator ()[- 1 ]/ xlocator ()[- 1 ]
333
+
334
+ ylim = ax .get_ylim ()
335
+ ytext_pos_lim = ylim [1 ]- (ylim [1 ]- ylim [0 ])* 0.03
336
+
337
+ xlim = ax .get_xlim ()
338
+ xtext_pos_lim = xlim [0 ]+ (xlim [1 ]- xlim [0 ])* 0.0
339
+
340
+ index = 0
341
+ zeta = np .sin (np .pi / 2 - angules [1 ::])
342
+
343
+ for yp in y_over_x :
344
+ ax .plot ([0 , xlocator ()[0 ]], [0 , yp * xlocator ()[0 ]], color = 'gray' ,
345
+ linestyle = 'dashed' , linewidth = 0.5 )
346
+ an = "%.2f" % zeta [index ]
347
+ if yp > 0 :
348
+ xtext_pos = - 1 / yp * ylim [1 ]
349
+ ytext_pos = - yp * xtext_pos_lim
350
+ if np .abs (xtext_pos ) > np .abs (xtext_pos_lim ):
351
+ xtext_pos = xtext_pos_lim
352
+ else :
353
+ ytext_pos = ytext_pos_lim
354
+ ax .annotate (an , textcoords = 'data' , xy = [xtext_pos , ytext_pos ], fontsize = 8 )
355
+ elif yp < 0 :
356
+ xtext_pos = - 1 / yp * ylim [1 ]
357
+ ytext_pos = yp * xtext_pos_lim
358
+ if np .abs (xtext_pos ) > np .abs (xtext_pos_lim ):
359
+ xtext_pos = xtext_pos_lim
360
+ ytext_pos = - ytext_pos
361
+ else :
362
+ ytext_pos = ylim [0 ]
363
+ xtext_pos = - xtext_pos
364
+ ax .annotate (an , textcoords = 'data' , xy = [xtext_pos , ytext_pos ], fontsize = 8 )
365
+ index += 1
366
+
367
+ angules = np .linspace (- 90 , 90 , 20 )* np .pi / 180
368
+ for xt in xlocator ():
369
+ if xt < 0 :
370
+ yp = np .sin (angules )* np .abs (xt )
371
+ xp = - np .cos (angules )* np .abs (xt )
372
+ ax .plot (xp , yp , color = 'gray' ,
373
+ linestyle = 'dashed' , linewidth = 0.5 )
374
+ an = "%.2f" % - xt
375
+ ax .annotate (an , textcoords = 'data' , xy = [xt , 0 ], fontsize = 8 )
309
376
310
377
rlocus = root_locus
0 commit comments