33from control .exception import ControlMIMONotImplemented
44from .freqplot import bode_plot
55from .timeresp import step_response
6- from .namedio import issiso , common_timebase , isctime , isdtime
6+ from .namedio import common_timebase , isctime , isdtime
77from .xferfcn import tf
88from .iosys import ss
99from .bdalg import append , connect
10- from .iosys import tf2io , ss2io , summing_junction , interconnect
11- from control .statesp import _convert_to_statespace , StateSpace
10+ from .iosys import ss , tf2io , summing_junction , interconnect
11+ from control .statesp import _convert_to_statespace
1212from . import config
1313import numpy as np
1414import matplotlib .pyplot as plt
@@ -22,8 +22,8 @@ def sisotool(sys, initial_gain=None, xlim_rlocus=None, ylim_rlocus=None,
2222 plotstr_rlocus = 'C0' , rlocus_grid = False , omega = None , dB = None ,
2323 Hz = None , deg = None , omega_limits = None , omega_num = None ,
2424 margins_bode = True , tvect = None , kvect = None ):
25- """
26- Sisotool style collection of plots inspired by MATLAB's sisotool.
25+ """Sisotool style collection of plots inspired by MATLAB's sisotool.
26+
2727 The left two plots contain the bode magnitude and phase diagrams.
2828 The top right plot is a clickable root locus plot, clicking on the
2929 root locus will change the gain of the system. The bottom left plot
@@ -32,52 +32,52 @@ def sisotool(sys, initial_gain=None, xlim_rlocus=None, ylim_rlocus=None,
3232 Parameters
3333 ----------
3434 sys : LTI object
35- Linear input/output systems. If sys is SISO, use the same
36- system for the root locus and step response. If it is desired to
37- see a different step response than feedback(K*sys,1), such as a
38- disturbance response, sys can be provided as a two-input, two-output
39- system (e.g. by using :func:`bdgalg.connect' or
40- :func:`iosys.interconnect`). For two-input, two-output
41- system, sisotool inserts the negative of the selected gain K between
42- the first output and first input and uses the second input and output
43- for computing the step response. To see the disturbance response,
44- configure your plant to have as its second input the disturbance input.
45- To view the step response with a feedforward controller, give your
46- plant two identical inputs, and sum your feedback controller and your
47- feedforward controller and multiply them into your plant's second
48- input. It is also possible to accomodate a system with a gain in the
49- feedback.
35+ Linear input/output systems. If sys is SISO, use the same system for
36+ the root locus and step response. If it is desired to see a different
37+ step response than feedback(K*sys,1), such as a disturbance response,
38+ sys can be provided as a two-input, two-output system (e.g. by using
39+ :func:`bdgalg.connect' or :func:`iosys.interconnect`). For two-input,
40+ two-output system, sisotool inserts the negative of the selected gain
41+ K between the first output and first input and uses the second input
42+ and output for computing the step response. To see the disturbance
43+ response, configure your plant to have as its second input the
44+ disturbance input. To view the step response with a feedforward
45+ controller, give your plant two identical inputs, and sum your
46+ feedback controller and your feedforward controller and multiply them
47+ into your plant's second input. It is also possible to accomodate a
48+ system with a gain in the feedback.
5049 initial_gain : float, optional
5150 Initial gain to use for plotting root locus. Defaults to 1
5251 (config.defaults['sisotool.initial_gain']).
5352 xlim_rlocus : tuple or list, optional
54- control of x-axis range, normally with tuple
53+ Control of x-axis range, normally with tuple
5554 (see :doc:`matplotlib:api/axes_api`).
5655 ylim_rlocus : tuple or list, optional
5756 control of y-axis range
5857 plotstr_rlocus : :func:`matplotlib.pyplot.plot` format string, optional
59- plotting style for the root locus plot(color, linestyle, etc)
58+ Plotting style for the root locus plot(color, linestyle, etc).
6059 rlocus_grid : boolean (default = False)
6160 If True plot s- or z-plane grid.
6261 omega : array_like
63- List of frequencies in rad/sec to be used for bode plot
62+ List of frequencies in rad/sec to be used for bode plot.
6463 dB : boolean
65- If True, plot result in dB for the bode plot
64+ If True, plot result in dB for the bode plot.
6665 Hz : boolean
67- If True, plot frequency in Hz for the bode plot (omega must be provided in rad/sec)
66+ If True, plot frequency in Hz for the bode plot (omega must be
67+ provided in rad/sec).
6868 deg : boolean
69- If True, plot phase in degrees for the bode plot (else radians)
69+ If True, plot phase in degrees for the bode plot (else radians).
7070 omega_limits : array_like of two values
71- Limits of the to generate frequency vector.
72- If Hz=True the limits are in Hz otherwise in rad/s. Ignored if omega
73- is provided, and auto-generated if omitted.
71+ Limits of the to generate frequency vector. If Hz=True the limits
72+ are in Hz otherwise in rad/s. Ignored if omega is provided, and
73+ auto-generated if omitted.
7474 omega_num : int
7575 Number of samples to plot. Defaults to
7676 config.defaults['freqplot.number_of_samples'].
7777 margins_bode : boolean
78- If True, plot gain and phase margin in the bode plot
78+ If True, plot gain and phase margin in the bode plot.
7979 tvect : list or ndarray, optional
80- List of timesteps to use for closed loop step response
80+ List of timesteps to use for closed loop step response.
8181
8282 Examples
8383 --------
@@ -202,28 +202,47 @@ def _SisotoolUpdate(sys, fig, K, bode_plot_params, tvect=None):
202202# contributed by Sawyer Fuller, minster@uw.edu 2021.11.02, based on
203203# an implementation in Matlab by Martin Berg.
204204def rootlocus_pid_designer (plant , gain = 'P' , sign = + 1 , input_signal = 'r' ,
205- Kp0 = 0 , Ki0 = 0 , Kd0 = 0 , tau = 0.01 ,
205+ Kp0 = 0 , Ki0 = 0 , Kd0 = 0 , deltaK = 0.001 , tau = 0.01 ,
206206 C_ff = 0 , derivative_in_feedback_path = False ,
207207 plot = True ):
208208 """Manual PID controller design based on root locus using Sisotool
209209
210- Uses `Sisotool ` to investigate the effect of adding or subtracting an
210+ Uses `sisotool ` to investigate the effect of adding or subtracting an
211211 amount `deltaK` to the proportional, integral, or derivative (PID) gains of
212212 a controller. One of the PID gains, `Kp`, `Ki`, or `Kd`, respectively, can
213213 be modified at a time. `Sisotool` plots the step response, frequency
214- response, and root locus.
215-
216- When first run, `deltaK` is set to 0; click on a branch of the root locus
217- plot to try a different value. Each click updates plots and prints
218- the corresponding `deltaK`. To tune all three PID gains, repeatedly call
219- `rootlocus_pid_designer`, and select a different `gain` each time (`'P'`,
220- `'I'`, or `'D'`). Make sure to add the resulting `deltaK` to your chosen
221- initial gain on the next iteration.
214+ response, and root locus of the closed-loop system controlling the
215+ dynamical system specified by `plant`. Can be used with either non-
216+ interactive plots (e.g. in a Jupyter Notebook), or interactive plots.
217+
218+ To use non-interactively, choose starting-point PID gains `Kp0`, `Ki0`,
219+ and `Kd0` (you might want to start with all zeros to begin with), select
220+ which gain you would like to vary (e.g. gain=`'P'`, `'I'`, or `'D'`), and
221+ choose a value of `deltaK` (default 0.001) to specify by how much you
222+ would like to change that gain. Repeatedly run `rootlocus_pid_designer`
223+ with different values of `deltaK` until you are satisfied with the
224+ performance for that gain. Then, to tune a different gain, e.g. `'I'`,
225+ make sure to add your chosen `deltaK` to the previous gain you you were
226+ tuning.
222227
223228 Example: to examine the effect of varying `Kp` starting from an intial
224- value of 10, use the arguments `gain='P', Kp0=10`. Suppose a `deltaK`
225- value of 5 gives satisfactory performance. Then on the next iteration,
226- to tune the derivative gain, use the arguments `gain='D', Kp0=15`.
229+ value of 10, use the arguments `gain='P', Kp0=10` and try varying values
230+ of `deltaK`. Suppose a `deltaK` of 5 gives satisfactory performance. Then,
231+ to tune the derivative gain, add your selected `deltaK` to `Kp0` in the
232+ next call using the arguments `gain='D', Kp0=15`, to see how adding
233+ different values of `deltaK` to your derivative gain affects performance.
234+
235+ To use with interactive plots, you will need to enable interactive mode
236+ if you are in a Jupyter Notebook, e.g. using `%matplotlib`. See
237+ `Interactive Plots <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.ion.html>`_
238+ for more information. Click on a branch of the root locus plot to try
239+ different values of `deltaK`. Each click updates plots and prints the
240+ corresponding `deltaK`. It may be helpful to zoom in using the magnifying
241+ glass on the plot to get more locations to click. Just make sure to
242+ deactivate magnification mode when you are done by clicking the magnifying
243+ glass. Otherwise you will not be able to be able to choose a gain on the
244+ root locus plot. When you are done, `%matplotlib inline` returns to inline,
245+ non-interactive ploting.
227246
228247 By default, all three PID terms are in the forward path C_f in the diagram
229248 shown below, that is,
@@ -253,26 +272,23 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
253272 If `plant` is a 2-input system, the disturbance `d` is fed directly into
254273 its second input rather than being added to `u`.
255274
256- Remark: It may be helpful to zoom in using the magnifying glass on the
257- plot. Just ake sure to deactivate magnification mode when you are done by
258- clicking the magnifying glass. Otherwise you will not be able to be able
259- to choose a gain on the root locus plot.
260-
261275 Parameters
262276 ----------
263277 plant : :class:`LTI` (:class:`TransferFunction` or :class:`StateSpace` system)
264- The dynamical system to be controlled
278+ The dynamical system to be controlled.
265279 gain : string (optional)
266280 Which gain to vary by `deltaK`. Must be one of `'P'`, `'I'`, or `'D'`
267- (proportional, integral, or derative)
281+ (proportional, integral, or derative).
268282 sign : int (optional)
269- The sign of deltaK gain perturbation
283+ The sign of deltaK gain perturbation.
270284 input : string (optional)
271285 The input used for the step response; must be `'r'` (reference) or
272- `'d'` (disturbance) (see figure above)
286+ `'d'` (disturbance) (see figure above).
273287 Kp0, Ki0, Kd0 : float (optional)
274288 Initial values for proportional, integral, and derivative gains,
275- respectively
289+ respectively.
290+ deltaK : float (optional)
291+ Perturbation value for gain specified by the `gain` keywoard.
276292 tau : float (optional)
277293 The time constant associated with the pole in the continuous-time
278294 derivative term. This is required to make the derivative transfer
@@ -291,16 +307,20 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
291307 closedloop : class:`StateSpace` system
292308 The closed-loop system using initial gains.
293309
310+ Notes
311+ -----
312+ When running using iPython or Jupyter, use `%matplotlib` to configure
313+ the session for interactive support.
314+
294315 """
295316
296- plant = _convert_to_statespace (plant )
297317 if plant .ninputs == 1 :
298- plant = ss2io (plant , inputs = 'u' , outputs = 'y' )
318+ plant = ss (plant , inputs = 'u' , outputs = 'y' )
299319 elif plant .ninputs == 2 :
300- plant = ss2io (plant , inputs = ['u' , 'd' ], outputs = 'y' )
320+ plant = ss (plant , inputs = ['u' , 'd' ], outputs = 'y' )
301321 else :
302322 raise ValueError ("plant must have one or two inputs" )
303- C_ff = ss2io (_convert_to_statespace (C_ff ), inputs = 'r' , outputs = 'uff' )
323+ C_ff = ss (_convert_to_statespace (C_ff ), inputs = 'r' , outputs = 'uff' )
304324 dt = common_timebase (plant , C_ff )
305325
306326 # create systems used for interconnections
@@ -335,13 +355,13 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
335355 # for the gain that is varied, replace gain block with a special block
336356 # that has an 'input' and an 'output' that creates loop transfer function
337357 if gain in ('P' , 'p' ):
338- Kpgain = ss2io ( ss ([],[],[],[[0 , 1 ], [- sign , Kp0 ]]) ,
358+ Kpgain = ss ([],[],[],[[0 , 1 ], [- sign , Kp0 ]],
339359 inputs = ['input' , 'prop_e' ], outputs = ['output' , 'ufb' ])
340360 elif gain in ('I' , 'i' ):
341- Kigain = ss2io ( ss ([],[],[],[[0 , 1 ], [- sign , Ki0 ]]) ,
361+ Kigain = ss ([],[],[],[[0 , 1 ], [- sign , Ki0 ]],
342362 inputs = ['input' , 'int_e' ], outputs = ['output' , 'ufb' ])
343363 elif gain in ('D' , 'd' ):
344- Kdgain = ss2io ( ss ([],[],[],[[0 , 1 ], [- sign , Kd0 ]]) ,
364+ Kdgain = ss ([],[],[],[[0 , 1 ], [- sign , Kd0 ]],
345365 inputs = ['input' , 'deriv' ], outputs = ['output' , 'ufb' ])
346366 else :
347367 raise ValueError (gain + ' gain not recognized.' )
@@ -352,6 +372,6 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
352372 inplist = ['input' , input_signal ],
353373 outlist = ['output' , 'y' ], check_unused = False )
354374 if plot :
355- sisotool (loop , kvect = ( 0. ,) )
375+ sisotool (loop , initial_gain = deltaK )
356376 cl = loop [1 , 1 ] # closed loop transfer function with initial gains
357- return StateSpace (cl .A , cl .B , cl .C , cl .D , cl .dt )
377+ return ss (cl .A , cl .B , cl .C , cl .D , cl .dt )
0 commit comments