@@ -88,7 +88,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
8888 ----------
8989 sys : LTI object
9090 Linear input/output systems (SISO only, for now).
91- kvect : list or ndarray , optional
91+ kvect : float or array_like , optional
9292 List of gains to use in computing diagram.
9393 xlim : tuple or list, optional
9494 Set limits of x axis, normally with tuple
@@ -110,10 +110,11 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
110110
111111 Returns
112112 -------
113- rlist : ndarray
114- Computed root locations, given as a 2D array
115- klist : ndarray or list
116- Gains used. Same as klist keyword argument if provided.
113+ roots : ndarray
114+ Closed-loop root locations, arranged in which each row corresponds
115+ to a gain in gains
116+ gains : ndarray
117+ Gains used. Same as kvect keyword argument if provided.
117118
118119 Notes
119120 -----
@@ -145,10 +146,12 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
145146 print_gain = config ._get_param (
146147 'rlocus' , 'print_gain' , print_gain , _rlocus_defaults )
147148
148- sys_loop = sys if sys .issiso () else sys [0 , 0 ]
149+ if not sys .issiso ():
150+ raise ControlMIMONotImplemented (
151+ 'sys must be single-input single-output (SISO)' )
149152
150153 # Convert numerator and denominator to polynomials if they aren't
151- (nump , denp ) = _systopoly1d (sys_loop )
154+ (nump , denp ) = _systopoly1d (sys )
152155
153156 # if discrete-time system and if xlim and ylim are not given,
154157 # that we a view of the unit circle
@@ -158,12 +161,13 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
158161 xlim = (- 1.3 , 1.3 )
159162
160163 if kvect is None :
161- start_mat = _RLFindRoots (nump , denp , [ 1 ] )
162- kvect , mymat , xlim , ylim = _default_gains (nump , denp , xlim , ylim )
164+ start_roots = _RLFindRoots (nump , denp , 1 )
165+ kvect , root_array , xlim , ylim = _default_gains (nump , denp , xlim , ylim )
163166 else :
164- start_mat = _RLFindRoots (nump , denp , [kvect [0 ]])
165- mymat = _RLFindRoots (nump , denp , kvect )
166- mymat = _RLSortRoots (mymat )
167+ kvect = np .atleast_1d (kvect )
168+ start_roots = _RLFindRoots (nump , denp , kvect [0 ])
169+ root_array = _RLFindRoots (nump , denp , kvect )
170+ root_array = _RLSortRoots (root_array )
167171
168172 # Check for sisotool mode
169173 sisotool = False if 'sisotool' not in kwargs else True
@@ -190,10 +194,10 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
190194 ax_rlocus = fig .axes [0 ], plotstr = plotstr ))
191195 elif sisotool :
192196 fig .axes [1 ].plot (
193- [root .real for root in start_mat ],
194- [root .imag for root in start_mat ],
197+ [root .real for root in start_roots ],
198+ [root .imag for root in start_roots ],
195199 marker = 's' , markersize = 6 , zorder = 20 , color = 'k' , label = 'gain_point' )
196- s = start_mat [0 ][0 ]
200+ s = start_roots [0 ][0 ]
197201 if isdtime (sys , strict = True ):
198202 zeta = - np .cos (np .angle (np .log (s )))
199203 else :
@@ -229,7 +233,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
229233 ax .plot (real (zeros ), imag (zeros ), 'o' )
230234
231235 # Now plot the loci
232- for index , col in enumerate (mymat .T ):
236+ for index , col in enumerate (root_array .T ):
233237 ax .plot (real (col ), imag (col ), plotstr , label = 'rootlocus' )
234238
235239 # Set up plot axes and labels
@@ -257,7 +261,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
257261 (0 , 0 ), radius = 1.0 , linestyle = ':' , edgecolor = 'k' ,
258262 linewidth = 0.75 , fill = False , zorder = - 20 ))
259263
260- return mymat , kvect
264+ return root_array , kvect
261265
262266
263267def _default_gains (num , den , xlim , ylim , zoom_xlim = None , zoom_ylim = None ):
@@ -509,28 +513,27 @@ def _RLFindRoots(nump, denp, kvect):
509513 """Find the roots for the root locus."""
510514 # Convert numerator and denominator to polynomials if they aren't
511515 roots = []
512- for k in np .array (kvect , ndmin = 1 ):
516+ for k in np .atleast_1d (kvect ):
513517 curpoly = denp + k * nump
514518 curroots = curpoly .r
515519 if len (curroots ) < denp .order :
516520 # if I have fewer poles than open loop, it is because i have
517521 # one at infinity
518- curroots = np .insert (curroots , len ( curroots ) , np .inf )
522+ curroots = np .append (curroots , np .inf )
519523
520524 curroots .sort ()
521525 roots .append (curroots )
522526
523- mymat = row_stack (roots )
524- return mymat
527+ return row_stack (roots )
525528
526529
527- def _RLSortRoots (mymat ):
528- """Sort the roots from sys. _RLFindRoots, so that the root
530+ def _RLSortRoots (roots ):
531+ """Sort the roots from _RLFindRoots, so that the root
529532 locus doesn't show weird pseudo-branches as roots jump from
530533 one branch to another."""
531534
532- sorted = zeros_like (mymat )
533- for n , row in enumerate (mymat ):
535+ sorted = zeros_like (roots )
536+ for n , row in enumerate (roots ):
534537 if n == 0 :
535538 sorted [n , :] = row
536539 else :
@@ -539,7 +542,7 @@ def _RLSortRoots(mymat):
539542 # previous row
540543 available = list (range (len (prevrow )))
541544 for elem in row :
542- evect = elem - prevrow [available ]
545+ evect = elem - prevrow [available ]
543546 ind1 = abs (evect ).argmin ()
544547 ind = available .pop (ind1 )
545548 sorted [n , ind ] = elem
@@ -549,9 +552,7 @@ def _RLSortRoots(mymat):
549552
550553def _RLZoomDispatcher (event , sys , ax_rlocus , plotstr ):
551554 """Rootlocus plot zoom dispatcher"""
552- sys_loop = sys if sys .issiso () else sys [0 ,0 ]
553-
554- nump , denp = _systopoly1d (sys_loop )
555+ nump , denp = _systopoly1d (sys )
555556 xlim , ylim = ax_rlocus .get_xlim (), ax_rlocus .get_ylim ()
556557
557558 kvect , mymat , xlim , ylim = _default_gains (
@@ -583,9 +584,7 @@ def _RLClickDispatcher(event, sys, fig, ax_rlocus, plotstr, sisotool=False,
583584
584585def _RLFeedbackClicksPoint (event , sys , fig , ax_rlocus , sisotool = False ):
585586 """Display root-locus gain feedback point for clicks on root-locus plot"""
586- sys_loop = sys if sys .issiso () else sys [0 ,0 ]
587-
588- (nump , denp ) = _systopoly1d (sys_loop )
587+ (nump , denp ) = _systopoly1d (sys )
589588
590589 xlim = ax_rlocus .get_xlim ()
591590 ylim = ax_rlocus .get_ylim ()
@@ -596,10 +595,10 @@ def _RLFeedbackClicksPoint(event, sys, fig, ax_rlocus, sisotool=False):
596595 # Catch type error when event click is in the figure but not in an axis
597596 try :
598597 s = complex (event .xdata , event .ydata )
599- K = - 1. / sys_loop (s )
600- K_xlim = - 1. / sys_loop (
598+ K = - 1. / sys (s )
599+ K_xlim = - 1. / sys (
601600 complex (event .xdata + 0.05 * abs (xlim [1 ] - xlim [0 ]), event .ydata ))
602- K_ylim = - 1. / sys_loop (
601+ K_ylim = - 1. / sys (
603602 complex (event .xdata , event .ydata + 0.05 * abs (ylim [1 ] - ylim [0 ])))
604603
605604 except TypeError :
0 commit comments