3
3
from control .exception import ControlMIMONotImplemented
4
4
from .freqplot import bode_plot
5
5
from .timeresp import step_response
6
- from .namedio import issiso , common_timebase , isctime , isdtime
6
+ from .namedio import common_timebase , isctime , isdtime
7
7
from .xferfcn import tf
8
8
from .iosys import ss
9
9
from .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
12
12
from . import config
13
13
import numpy as np
14
14
import matplotlib .pyplot as plt
@@ -22,8 +22,8 @@ def sisotool(sys, initial_gain=None, xlim_rlocus=None, ylim_rlocus=None,
22
22
plotstr_rlocus = 'C0' , rlocus_grid = False , omega = None , dB = None ,
23
23
Hz = None , deg = None , omega_limits = None , omega_num = None ,
24
24
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
+
27
27
The left two plots contain the bode magnitude and phase diagrams.
28
28
The top right plot is a clickable root locus plot, clicking on the
29
29
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,
32
32
Parameters
33
33
----------
34
34
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.
50
49
initial_gain : float, optional
51
50
Initial gain to use for plotting root locus. Defaults to 1
52
51
(config.defaults['sisotool.initial_gain']).
53
52
xlim_rlocus : tuple or list, optional
54
- control of x-axis range, normally with tuple
53
+ Control of x-axis range, normally with tuple
55
54
(see :doc:`matplotlib:api/axes_api`).
56
55
ylim_rlocus : tuple or list, optional
57
56
control of y-axis range
58
57
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).
60
59
rlocus_grid : boolean (default = False)
61
60
If True plot s- or z-plane grid.
62
61
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.
64
63
dB : boolean
65
- If True, plot result in dB for the bode plot
64
+ If True, plot result in dB for the bode plot.
66
65
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).
68
68
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).
70
70
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.
74
74
omega_num : int
75
75
Number of samples to plot. Defaults to
76
76
config.defaults['freqplot.number_of_samples'].
77
77
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.
79
79
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.
81
81
82
82
Examples
83
83
--------
@@ -202,28 +202,47 @@ def _SisotoolUpdate(sys, fig, K, bode_plot_params, tvect=None):
202
202
# contributed by Sawyer Fuller, minster@uw.edu 2021.11.02, based on
203
203
# an implementation in Matlab by Martin Berg.
204
204
def 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 ,
206
206
C_ff = 0 , derivative_in_feedback_path = False ,
207
207
plot = True ):
208
208
"""Manual PID controller design based on root locus using Sisotool
209
209
210
- Uses `Sisotool ` to investigate the effect of adding or subtracting an
210
+ Uses `sisotool ` to investigate the effect of adding or subtracting an
211
211
amount `deltaK` to the proportional, integral, or derivative (PID) gains of
212
212
a controller. One of the PID gains, `Kp`, `Ki`, or `Kd`, respectively, can
213
213
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.
222
227
223
228
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.
227
246
228
247
By default, all three PID terms are in the forward path C_f in the diagram
229
248
shown below, that is,
@@ -253,26 +272,23 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
253
272
If `plant` is a 2-input system, the disturbance `d` is fed directly into
254
273
its second input rather than being added to `u`.
255
274
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
-
261
275
Parameters
262
276
----------
263
277
plant : :class:`LTI` (:class:`TransferFunction` or :class:`StateSpace` system)
264
- The dynamical system to be controlled
278
+ The dynamical system to be controlled.
265
279
gain : string (optional)
266
280
Which gain to vary by `deltaK`. Must be one of `'P'`, `'I'`, or `'D'`
267
- (proportional, integral, or derative)
281
+ (proportional, integral, or derative).
268
282
sign : int (optional)
269
- The sign of deltaK gain perturbation
283
+ The sign of deltaK gain perturbation.
270
284
input : string (optional)
271
285
The input used for the step response; must be `'r'` (reference) or
272
- `'d'` (disturbance) (see figure above)
286
+ `'d'` (disturbance) (see figure above).
273
287
Kp0, Ki0, Kd0 : float (optional)
274
288
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.
276
292
tau : float (optional)
277
293
The time constant associated with the pole in the continuous-time
278
294
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',
291
307
closedloop : class:`StateSpace` system
292
308
The closed-loop system using initial gains.
293
309
310
+ Notes
311
+ -----
312
+ When running using iPython or Jupyter, use `%matplotlib` to configure
313
+ the session for interactive support.
314
+
294
315
"""
295
316
296
- plant = _convert_to_statespace (plant )
297
317
if plant .ninputs == 1 :
298
- plant = ss2io (plant , inputs = 'u' , outputs = 'y' )
318
+ plant = ss (plant , inputs = 'u' , outputs = 'y' )
299
319
elif plant .ninputs == 2 :
300
- plant = ss2io (plant , inputs = ['u' , 'd' ], outputs = 'y' )
320
+ plant = ss (plant , inputs = ['u' , 'd' ], outputs = 'y' )
301
321
else :
302
322
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' )
304
324
dt = common_timebase (plant , C_ff )
305
325
306
326
# create systems used for interconnections
@@ -335,13 +355,13 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
335
355
# for the gain that is varied, replace gain block with a special block
336
356
# that has an 'input' and an 'output' that creates loop transfer function
337
357
if gain in ('P' , 'p' ):
338
- Kpgain = ss2io ( ss ([],[],[],[[0 , 1 ], [- sign , Kp0 ]]) ,
358
+ Kpgain = ss ([],[],[],[[0 , 1 ], [- sign , Kp0 ]],
339
359
inputs = ['input' , 'prop_e' ], outputs = ['output' , 'ufb' ])
340
360
elif gain in ('I' , 'i' ):
341
- Kigain = ss2io ( ss ([],[],[],[[0 , 1 ], [- sign , Ki0 ]]) ,
361
+ Kigain = ss ([],[],[],[[0 , 1 ], [- sign , Ki0 ]],
342
362
inputs = ['input' , 'int_e' ], outputs = ['output' , 'ufb' ])
343
363
elif gain in ('D' , 'd' ):
344
- Kdgain = ss2io ( ss ([],[],[],[[0 , 1 ], [- sign , Kd0 ]]) ,
364
+ Kdgain = ss ([],[],[],[[0 , 1 ], [- sign , Kd0 ]],
345
365
inputs = ['input' , 'deriv' ], outputs = ['output' , 'ufb' ])
346
366
else :
347
367
raise ValueError (gain + ' gain not recognized.' )
@@ -352,6 +372,6 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
352
372
inplist = ['input' , input_signal ],
353
373
outlist = ['output' , 'y' ], check_unused = False )
354
374
if plot :
355
- sisotool (loop , kvect = ( 0. ,) )
375
+ sisotool (loop , initial_gain = deltaK )
356
376
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