Merge pull request #883 from sawyerbfuller/rlpd_blank · python-control/python-control@c06a205

@@ -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

@@ -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.

81818282

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.

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

209209210-

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.

222227223228

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.

227246228247

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`.

255274256-

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.

293309310+

Notes

311+

-----

312+

When running using iPython or Jupyter, use `%matplotlib` to configure

313+

the session for interactive support.

314+294315

"""

295316296-

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)

305325306326

# 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)