Skip to content

Commit 0fb9d94

Browse files
committed
enable deltaK in sisotool for non-interactive use, docstring improvements
1 parent 7f4e2ed commit 0fb9d94

File tree

1 file changed

+45
-39
lines changed

1 file changed

+45
-39
lines changed

control/sisotool.py

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
from control.exception import ControlMIMONotImplemented
44
from .freqplot import bode_plot
55
from .timeresp import step_response
6-
from .namedio import issiso, common_timebase, isctime, isdtime
6+
from .namedio import common_timebase, isctime, isdtime
77
from .xferfcn import tf
88
from .iosys import ss
99
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
1212
from . import config
1313
import numpy as np
1414
import matplotlib.pyplot as plt
@@ -202,7 +202,7 @@ 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.
204204
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,
206206
C_ff=0, derivative_in_feedback_path=False,
207207
plot=True):
208208
"""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',
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.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.
231227
232228
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.
236246
237247
By default, all three PID terms are in the forward path C_f in the diagram
238248
shown below, that is,
@@ -262,11 +272,6 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
262272
If `plant` is a 2-input system, the disturbance `d` is fed directly into
263273
its second input rather than being added to `u`.
264274
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-
270275
Parameters
271276
----------
272277
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',
282287
Kp0, Ki0, Kd0 : float (optional)
283288
Initial values for proportional, integral, and derivative gains,
284289
respectively
290+
deltaK : float (optional)
291+
Perturbation value for gain specified by the `gain` keywoard.
285292
tau : float (optional)
286293
The time constant associated with the pole in the continuous-time
287294
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',
302309
303310
"""
304311

305-
plant = _convert_to_statespace(plant)
306312
if plant.ninputs == 1:
307-
plant = ss2io(plant, inputs='u', outputs='y')
313+
plant = ss(plant, inputs='u', outputs='y')
308314
elif plant.ninputs == 2:
309-
plant = ss2io(plant, inputs=['u', 'd'], outputs='y')
315+
plant = ss(plant, inputs=['u', 'd'], outputs='y')
310316
else:
311317
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')
313319
dt = common_timebase(plant, C_ff)
314320

315321
# create systems used for interconnections
@@ -329,7 +335,7 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
329335
deriv = tf([1, -1], [dt, 0], dt)
330336

331337
# add signal names by turning into iosystems
332-
prop = tf2io(prop, )
338+
prop = tf2io(prop, inputs='e', outputs='prop_e')
333339
integ = tf2io(integ, inputs='e', outputs='int_e')
334340
if derivative_in_feedback_path:
335341
deriv = tf2io(-deriv, inputs='y', outputs='deriv')
@@ -344,13 +350,13 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
344350
# for the gain that is varied, replace gain block with a special block
345351
# that has an 'input' and an 'output' that creates loop transfer function
346352
if gain in ('P', 'p'):
347-
Kpgain = ss2io(ss([],[],[],[[0, 1], [-sign, Kp0]]),
353+
Kpgain = ss([],[],[],[[0, 1], [-sign, Kp0]],
348354
inputs=['input', 'prop_e'], outputs=['output', 'ufb'])
349355
elif gain in ('I', 'i'):
350-
Kigain = ss2io(ss([],[],[],[[0, 1], [-sign, Ki0]]),
356+
Kigain = ss([],[],[],[[0, 1], [-sign, Ki0]],
351357
inputs=['input', 'int_e'], outputs=['output', 'ufb'])
352358
elif gain in ('D', 'd'):
353-
Kdgain = ss2io(ss([],[],[],[[0, 1], [-sign, Kd0]]),
359+
Kdgain = ss([],[],[],[[0, 1], [-sign, Kd0]],
354360
inputs=['input', 'deriv'], outputs=['output', 'ufb'])
355361
else:
356362
raise ValueError(gain + ' gain not recognized.')
@@ -361,6 +367,6 @@ def rootlocus_pid_designer(plant, gain='P', sign=+1, input_signal='r',
361367
inplist=['input', input_signal],
362368
outlist=['output', 'y'], check_unused=False)
363369
if plot:
364-
sisotool(loop, initial_gain=0.001)
370+
sisotool(loop, initial_gain=deltaK)
365371
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

Comments
 (0)