@@ -222,6 +222,14 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
222
222
ax .set_xlabel ('Real' )
223
223
ax .set_ylabel ('Imaginary' )
224
224
225
+ # Set up the limits for the plot
226
+ # Note: need to do this before computing grid lines
227
+ if xlim :
228
+ ax .set_xlim (xlim )
229
+ if ylim :
230
+ ax .set_ylim (ylim )
231
+
232
+ # Draw the grid
225
233
if grid and sisotool :
226
234
if isdtime (sys , strict = True ):
227
235
zgrid (ax = ax )
@@ -236,14 +244,9 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
236
244
ax .axhline (0. , linestyle = ':' , color = 'k' , zorder = - 20 )
237
245
ax .axvline (0. , linestyle = ':' , color = 'k' , zorder = - 20 )
238
246
if isdtime (sys , strict = True ):
239
- ax .add_patch (plt .Circle ((0 ,0 ), radius = 1.0 ,
240
- linestyle = ':' , edgecolor = 'k' , linewidth = 1.5 ,
241
- fill = False , zorder = - 20 ))
242
-
243
- if xlim :
244
- ax .set_xlim (xlim )
245
<
10000
code> - if ylim :
246
- ax .set_ylim (ylim )
247
+ ax .add_patch (plt .Circle (
248
+ (0 , 0 ), radius = 1.0 , linestyle = ':' , edgecolor = 'k' ,
249
+ linewidth = 1.5 , fill = False , zorder = - 20 ))
247
250
248
251
return mymat , kvect
249
252
@@ -642,16 +645,21 @@ def _sgrid_func(fig=None, zeta=None, wn=None):
642
645
ax = fig .gca ()
643
646
else :
644
647
ax = fig .axes [1 ]
648
+
649
+ # Get locator function for x-axis tick marks
645
650
xlocator = ax .get_xaxis ().get_major_locator ()
646
651
652
+ # Decide on the location for the labels (?)
647
653
ylim = ax .get_ylim ()
648
654
ytext_pos_lim = ylim [1 ] - (ylim [1 ] - ylim [0 ]) * 0.03
649
655
xlim = ax .get_xlim ()
650
656
xtext_pos_lim = xlim [0 ] + (xlim [1 ] - xlim [0 ]) * 0.0
651
657
658
+ # Create a list of damping ratios, if needed
652
659
if zeta is None :
653
660
zeta = _default_zetas (xlim , ylim )
654
661
662
+ # Figure out the angles for the different damping ratios
655
663
angles = []
656
664
for z in zeta :
657
665
if (z >= 1e-4 ) and (z <= 1 ):
@@ -661,11 +669,8 @@ def _sgrid_func(fig=None, zeta=None, wn=None):
661
669
y_over_x = np .tan (angles )
662
670
663
671
# zeta-constant lines
664
-
665
- index = 0
666
-
667
- for yp in y_over_x :
668
- ax .plot ([0 , xlocator ()[0 ]], [0 , yp * xlocator ()[0 ]], color = 'gray' ,
672
+ for index , yp in enumerate (y_over_x ):
673
+ ax .plot ([0 , xlocator ()[0 ]], [0 , yp * xlocator ()[0 ]], color = 'gray' ,
669
674
linestyle = 'dashed' , linewidth = 0.5 )
670
675
ax .plot ([0 , xlocator ()[0 ]], [0 , - yp * xlocator ()[0 ]], color = 'gray' ,
671
676
linestyle = 'dashed' , linewidth = 0.5 )
@@ -679,45 +684,96 @@ def _sgrid_func(fig=None, zeta=None, wn=None):
679
684
ytext_pos = ytext_pos_lim
680
685
ax .annotate (an , textcoords = 'data' , xy = [xtext_pos , ytext_pos ],
681
686
fontsize = 8 )
682
- index += 1
683
687
ax .plot ([0 , 0 ], [ylim [0 ], ylim [1 ]],
684
688
color = 'gray' , linestyle = 'dashed' , linewidth = 0.5 )
685
689
686
- angles = np .linspace (- 90 , 90 , 20 )* np .pi / 180
690
+ # omega-constant lines
691
+ angles = np .linspace (- 90 , 90 , 20 ) * np .pi / 180
687
692
if wn is None :
688
693
wn = _default_wn (xlocator (), ylim )
689
694
690
695
for om in wn :
691
696
if om < 0 :
692
- yp = np .sin (angles )* np .abs (om )
693
- xp = - np .cos (angles )* np .abs (om )
694
- ax .plot (xp , yp , color = 'gray' ,
695
- linestyle = 'dashed' , linewidth = 0.5 )
696
- an = "%.2f" % - om
697
- ax .annotate (an , textcoords = 'data' , xy = [om , 0 ], fontsize = 8 )
697
+ # Generate the lines for natural frequency curves
698
+ yp = np .sin (angles ) * np .abs (om )
699
+ xp = - np .cos (angles ) * np .abs (om )
700
+
701
+ # Plot the natural frequency contours
702
+ ax .plot (xp , yp , color = 'gray' , linestyle = 'dashed' , linewidth = 0.5 )
703
+
704
+ # Annotate the natural frequencies by listing on x-axis
705
+ # Note: need to filter values for proper plotting in Jupyter
706
+ if (om > xlim [0 ]):
707
+ an = "%.2f" % - om
708
+ ax .annotate (an , textcoords = 'data' , xy = [om , 0 ], fontsize = 8 )
698
709
699
710
700
711
def _default_zetas (xlim , ylim ):
701
- """Return default list of damping coefficients"""
702
- sep1 = - xlim [0 ]/ 4
712
+ """Return default list of damping coefficients
713
+
714
+ This function computes a list of damping coefficients based on the limits
715
+ of the graph. A set of 4 damping coefficients are computed for the x-axis
716
+ and a set of three damping coefficients are computed for the y-axis
717
+ (corresponding to the normal 4:3 plot aspect ratio in `matplotlib`?).
718
+
719
+ Parameters
720
+ ----------
721
+ xlim : array_like
722
+ List of x-axis limits [min, max]
723
+ ylim : array_like
724
+ List of y-axis limits [min, max]
725
+
726
+ Returns
727
+ -------
728
+ zeta : list
729
+ List of default damping coefficients for the plot
730
+
731
+ """
732
+ # Damping coefficient lines that intersect the x-axis
733
+ sep1 = - xlim [0 ] / 4
703
734
ang1 = [np .arctan ((sep1 * i )/ ylim [1 ]) for i in np .arange (1 , 4 , 1 )]
735
+
736
+ # Damping coefficient lines that intersection the y-axis
704
737
sep2 = ylim [1 ] / 3
705
738
ang2 = [np .arctan (- xlim [0 ]/ (ylim [1 ]- sep2 * i )) for i in np .arange (1 , 3 , 1 )]
706
739
740
+ # Put the lines together and add one at -pi/2 (negative real axis)
707
741
angles = np .concatenate ((ang1 , ang2 ))
708
742
angles = np .insert (angles , len (angles ), np .pi / 2 )
743
+
744
+ # Return the damping coefficients corresponding to these angles
709
745
zeta = np .sin (angles )
710
746
return zeta .tolist ()
711
747
712
748
713
749
def _default_wn (xloc , ylim ):
714
- """Return default wn for root locus plot"""
750
+ """Return default wn for root locus plot
751
+
752
+ This function computes a list of natural frequencies based on the grid
753
+ parameters of the graph.
754
+
755
+ Parameters
756
+ ----------
757
+ xloc : array_like
758
+ List of x-axis tick values
759
+ ylim : array_like
760
+ List of y-axis limits [min, max]
761
+
762
+ Returns
763
+ -------
764
+ wn : list
765
+ List of default natural frequencies for the plot
766
+
767
+ """
768
+
769
+ wn = xloc # one frequency per x-axis tick mark
770
+ sep = xloc [1 ]- xloc [0 ] # separation between ticks
715
771
716
- wn = xloc
717
- sep = xloc [1 ]- xloc [0 ]
772
+ # Insert additional frequencies to span the y-axis
718
773
while np .abs (wn [0 ]) < ylim [1 ]:
719
774
wn = np .insert (wn , 0 , wn [0 ]- sep )
720
775
776
+ # If there are too many values, cut them in half
721
777
while len (wn ) > 7 :
722
778
wn = wn [0 :- 1 :2 ]
723
779
0 commit comments