6161from .sisotool import _SisotoolUpdate
6262from .grid import sgrid , zgrid
6363from . import config
64+ import warnings
6465
6566__all__ = ['root_locus' , 'rlocus' ]
6667
7677# Main function: compute a root locus diagram
7778def root_locus (sys , kvect = None , xlim = None , ylim = None ,
7879 plotstr = None , plot = True , print_gain = None , grid = None , ax = None ,
79- ** kwargs ):
80+ initial_gain = None , ** kwargs ):
8081
8182 """Root locus plot
8283
@@ -88,8 +89,8 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
8889 ----------
8990 sys : LTI object
9091 Linear input/output systems (SISO only, for now).
91- kvect : float or array_like, optional
92- List of gains to use in computing diagram .
92+ kvect : array_like, optional
93+ Gains to use in computing plot of closed-loop poles .
9394 xlim : tuple or list, optional
9495 Set limits of x axis, normally with tuple
9596 (see :doc:`matplotlib:api/axes_api`).
@@ -107,6 +108,8 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
107108 If True plot omega-damping grid. Default is False.
108109 ax : :class:`matplotlib.axes.Axes`
109110 Axes on which to create root locus plot
111+ initial_gain : float, optional
112+ Used by :func:`sisotool` to indicate initial gain.
110113
111114 Returns
112115 -------
@@ -126,15 +129,13 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
126129 """
127130 # Check to see if legacy 'Plot' keyword was used
128131 if 'Plot' in kwargs :
129- import warnings
130132 warnings .warn ("'Plot' keyword is deprecated in root_locus; "
131133 "use 'plot'" , FutureWarning )
132134 # Map 'Plot' keyword to 'plot' keyword
133135 plot = kwargs .pop ('Plot' )
134136
135137 # Check to see if legacy 'PrintGain' keyword was used
136138 if 'PrintGain' in kwargs :
137- import warnings
138139 warnings .warn ("'PrintGain' keyword is deprecated in root_locus; "
139140 "use 'print_gain'" , FutureWarning )
140141 # Map 'PrintGain' keyword to 'print_gain' keyword
@@ -146,12 +147,17 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
146147 print_gain = config ._get_param (
147148 'rlocus' , 'print_gain' , print_gain , _rlocus_defaults )
148149
149- if not sys .issiso ():
150+ # Check for sisotool mode
151+ sisotool = kwargs .get ('sisotool' , False )
152+
153+ # make sure siso. sisotool has different requirements
154+ if not sys .issiso () and not sisotool :
150155 raise ControlMIMONotImplemented (
151156 'sys must be single-input single-output (SISO)' )
152157
158+ sys_loop = sys [0 ,0 ]
153159 # Convert numerator and denominator to polynomials if they aren't
154- (nump , denp ) = _systopoly1d (sys )
160+ (nump , denp ) = _systopoly1d (sys_loop )
155161
156162 # if discrete-time system and if xlim and ylim are not given,
157163 # that we a view of the unit circle
@@ -161,16 +167,16 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
161167 xlim = (- 1.3 , 1.3 )
162168
163169 if kvect is None :
164- start_roots = _RLFindRoots (nump , denp , 1 )
165170 kvect , root_array , xlim , ylim = _default_gains (nump , denp , xlim , ylim )
171+ recompute_on_zoom = True
166172 else :
167173 kvect = np .atleast_1d (kvect )
168- start_roots = _RLFindRoots (nump , denp , kvect [0 ])
169174 root_array = _RLFindRoots (nump , denp , kvect )
170175 root_array = _RLSortRoots (root_array )
176+ recompute_on_zoom = False
171177
172- # Check for sisotool mode
173- sisotool = False if 'sisotool' not in kwargs else True
178+ if sisotool :
179+ start_roots = _RLFindRoots ( nump , denp , initial_gain )
174180
175181 # Make sure there were no extraneous keywords
176182 if not sisotool and kwargs :
@@ -204,7 +210,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
204210 zeta = - 1 * s .real / abs (s )
205211 fig .suptitle (
206212 "Clicked at: %10.4g%+10.4gj gain: %10.4g damp: %10.4g" %
207- (s .real , s .imag , kvect [ 0 ] , zeta ),
213+ (s .real , s .imag , initial_gain , zeta ),
208214 fontsize = 12 if int (mpl .__version__ [0 ]) == 1 else 10 )
209215 fig .canvas .mpl_connect (
210216 'button_release_event' ,
@@ -214,14 +220,16 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
214220 bode_plot_params = kwargs ['bode_plot_params' ],
215221 tvect = kwargs ['tvect' ]))
216222
217- # zoom update on xlim/ylim changed, only then data on new limits
218- # is available, i.e., cannot combine with _RLClickDispatcher
219- dpfun = partial (
220- _RLZoomDispatcher , sys = sys , ax_rlocus = ax , plotstr = plotstr )
221- # TODO: the next too lines seem to take a long time to execute
222- # TODO: is there a way to speed them up? (RMM, 6 Jun 2019)
223- ax .callbacks .connect ('xlim_changed' , dpfun )
224- ax .callbacks .connect ('ylim_changed' , dpfun )
223+
224+ if recompute_on_zoom :
225+ # update gains and roots when xlim/ylim change. Only then are
226+ # data on available. I.e., cannot combine with _RLClickDispatcher
227+ dpfun = partial (
228+ _RLZoomDispatcher , sys = sys , ax_rlocus = ax , plotstr = plotstr )
229+ # TODO: the next too lines seem to take a long time to execute
230+ # TODO: is there a way to speed them up? (RMM, 6 Jun 2019)
231+ ax .callbacks .connect ('xlim_changed' , dpfun )
232+ ax .callbacks .connect ('ylim_changed' , dpfun )
225233
226234 # plot open loop poles
227235 poles = array (denp .r )
@@ -552,7 +560,8 @@ def _RLSortRoots(roots):
552560
553561def _RLZoomDispatcher (event , sys , ax_rlocus , plotstr ):
554562 """Rootlocus plot zoom dispatcher"""
555- nump , denp = _systopoly1d (sys )
563+ sys_loop = sys [0 ,0 ]
564+ nump , denp = _systopoly1d (sys_loop )
556565 xlim , ylim = ax_rlocus .get_xlim (), ax_rlocus .get_ylim ()
557566
558567 kvect , mymat , xlim , ylim = _default_gains (
@@ -584,7 +593,8 @@ def _RLClickDispatcher(event, sys, fig, ax_rlocus, plotstr, sisotool=False,
584593
585594def _RLFeedbackClicksPoint (event , sys , fig , ax_rlocus , sisotool = False ):
586595 """Display root-locus gain feedback point for clicks on root-locus plot"""
587- (nump , denp ) = _systopoly1d (sys )
596+ sys_loop = sys [0 ,0 ]
597+ (nump , denp ) = _systopoly1d (sys_loop )
588598
589599 xlim = ax_rlocus .get_xlim ()
590600 ylim = ax_rlocus .get_ylim ()
@@ -595,10 +605,10 @@ def _RLFeedbackClicksPoint(event, sys, fig, ax_rlocus, sisotool=False):
595605 # Catch type error when event click is in the figure but not in an axis
596606 try :
597607 s = complex (event .xdata , event .ydata )
598- K = - 1. / sys (s )
599- K_xlim = - 1. / sys (
608+ K = - 1. / sys_loop (s )
609+ K_xlim = - 1. / sys_loop (
600610 complex (event .xdata + 0.05 * abs (xlim [1 ] - xlim [0 ]), event .ydata ))
601- K_ylim = - 1. / sys (
611+ K_ylim = - 1. / sys_loop (
602612 complex (event .xdata , event .ydata + 0.05 * abs (ylim [1 ] - ylim [0 ])))
603613
604614 except TypeError :
0 commit comments