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
@@ -202,7 +202,7 @@ 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
@@ -211,28 +211,38 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
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.001; click on a branch of the root
217
- locus 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.
222
-
223
- Note: Clicking on interactive plots feature is not currently compatible
224
- with in-line plots in the Jupyter Notebook including online notebooks.
225
- The alternative is to iteratively explore calling this function with
226
- different initial argument values `Kp0`, `Ki0`, and `Kd0`. If you are
227
- running the notebook on your local computer, it may be possible to spawn
228
- separate interactive plots outside of the notebook with a command, e.g.
229
- `%matplotlib qt`; when you are done, `%matplotlib inline` returns to
230
- inline plots.
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.
231
227
232
228
Example: to examine the effect of varying `Kp` starting from an intial
233
- value of 10, use the arguments `gain='P', Kp0=10`. Suppose a `deltaK`
234
- value of 5 gives satisfactory performance. Then on the next iteration,
235
- 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.
236
246
237
247
By default, all three PID terms are in the forward path C_f in the diagram
238
248
shown below, that is,
@@ -262,11 +272,6 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
262
272
If `plant` is a 2-input system, the disturbance `d` is fed directly into
263
273
its second input rather than being added to `u`.
264
274
265
- Remark: It may be helpful to zoom in using the magnifying glass on the
266
- plot. Just make sure to deactivate magnification mode when you are done by
267
- clicking the magnifying glass. Otherwise you will not be able to be able
268
- to choose a gain on the root locus plot.
269
-
270
275
Parameters
271
276
----------
272
277
plant : :class:`LTI` (:class:`TransferFunction` or :class:`StateSpace` system)
@@ -282,6 +287,8 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
282
287
Kp0, Ki0, Kd0 : float (optional)
283
288
Initial values for proportional, integral, and derivative gains,
284
289
respectively
290
+ deltaK : float (optional)
291
+ Perturbation value for gain specified by the `gain` keywoard.
285
292
tau : float (optional)
286
293
The time constant associated with the pole in the continuous-time
287
294
derivative term. This is required to make the derivative transfer
@@ -302,14 +309,13 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
302
309
303
310
"""
304
311
305
- plant = _convert_to_statespace (plant )
306
312
if plant .ninputs == 1 :
307
- plant = ss2io (plant , inputs = 'u' , outputs = 'y' )
313
+ plant = ss (plant , inputs = 'u' , outputs = 'y' )
308
314
elif plant .ninputs == 2 :
309
- plant = ss2io (plant , inputs = ['u' , 'd' ], outputs = 'y' )
315
+ plant = ss (plant , inputs = ['u' , 'd' ], outputs = 'y' )
310
316
else :
311
317
raise ValueError ("plant must have one or two inputs" )
312
- C_ff = ss2io (_convert_to_statespace (C_ff ), inputs = 'r' , outputs = 'uff' )
318
+ C_ff = ss (_convert_to_statespace (C_ff ), inputs = 'r' , outputs = 'uff' )
313
319
dt = common_timebase (plant , C_ff )
314
320
315
321
# create systems used for interconnections
@@ -329,7 +335,7 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
329
335
deriv = tf ([1 , - 1 ], [dt , 0 ], dt )
330
336
331
337
# add signal names by turning into iosystems
332
- prop = tf2io (prop , )
338
+ prop = tf2io (prop , inputs = 'e' , outputs = 'prop_e' )
333
339
integ = tf2io (integ , inputs = 'e' , outputs = 'int_e' )
334
340
if derivative_in_feedback_path :
335
341
deriv = tf2io (- deriv , inputs = 'y' , outputs = 'deriv' )
@@ -344,13 +350,13 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
344
350
# for the gain that is varied, replace gain block with a special block
345
351
# that has an 'input' and an 'output' that creates loop transfer function
346
352
if gain in ('P' , 'p' ):
347
- Kpgain = ss2io ( ss ([],[],[],[[0 , 1 ], [- sign , Kp0 ]]) ,
353
+ Kpgain = ss ([],[],[],[[0 , 1 ], [- sign , Kp0 ]],
348
354
inputs = ['input' , 'prop_e' ], outputs = ['output' , 'ufb' ])
349
355
elif gain in ('I' , 'i' ):
350
- Kigain = ss2io ( ss ([],[],[],[[0 , 1 ], [- sign , Ki0 ]]) ,
356
+ Kigain = ss ([],[],[],[[0 , 1 ], [- sign , Ki0 ]],
351
357
inputs = ['input' , 'int_e' ], outputs = ['output' , 'ufb' ])
352
358
elif gain in ('D' , 'd' ):
353
- Kdgain = ss2io ( ss ([],[],[],[[0 , 1 ], [- sign , Kd0 ]]) ,
359
+ Kdgain = ss ([],[],[],[[0 , 1 ], [- sign , Kd0 ]],
354
360
inputs = ['input' , 'deriv' ], outputs = ['output' , 'ufb' ])
355
361
else :
356
362
raise ValueError (gain + ' gain not recognized.' )
@@ -361,6 +367,6 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
361
367
inplist = ['input' , input_signal ],
362
368
outlist = ['output' , 'y' ], check_unused = False )
363
369
if plot :
364
- sisotool (loop , initial_gain = 0.001 )
370
+ sisotool (loop , initial_gain = deltaK )
365
371
cl = loop [1 , 1 ] # closed loop transfer function with initial gains
366
- return StateSpace (cl .A , cl .B , cl .C , cl .D , cl .dt )
372
+ return ss (cl .A , cl .B , cl .C , cl .D , cl .dt )
0 commit comments