1
- __all__ = ['sisotool' , 'pid_designer ' ]
1
+ __all__ = ['sisotool' , 'rootlocus_pid_designer ' ]
2
2
3
3
from control .exception import ControlMIMONotImplemented
4
4
from .freqplot import bode_plot
@@ -180,19 +180,21 @@ def _SisotoolUpdate(sys, fig, K, bode_plot_params, tvect=None):
180
180
fig .subplots_adjust (top = 0.9 ,wspace = 0.3 ,hspace = 0.35 )
181
181
fig .canvas .draw ()
182
182
183
- # contributed by Sawyer Fuller, minster@uw.edu 2021.11.02
184
- def pid_designer (plant , gain = 'P' , sign = + 1 , input_signal = 'r' ,
183
+ # contributed by Sawyer Fuller, minster@uw.edu 2021.11.02, based on
184
+ # an implementation in Matlab by Martin Berg.
185
+ def rootlocus_pid_designer (plant , gain = 'P' , sign = + 1 , input_signal = 'r' ,
185
186
Kp0 = 0 , Ki0 = 0 , Kd0 = 0 , tau = 0.01 ,
186
- C_ff = 0 , derivative_in_feedback_path = False ):
187
- """Manual PID controller design using sisotool
187
+ C_ff = 0 , derivative_in_feedback_path = False ,
188
+ noplot = False ):
189
+ """Manual PID controller design based on root locus using Sisotool
188
190
189
191
Uses `Sisotool` to investigate the effect of adding or subtracting an
190
192
amount `deltaK` to the proportional, integral, or derivative (PID) gains of
191
193
a controller. One of the PID gains, `Kp`, `Ki`, or `Kd`, respectively, can
192
194
be modified at a time. `Sisotool` plots the step response, frequency
193
195
response, and root locus.
194
196
195
- When first run, `deltaK` is set to 1 ; click on a branch of the root locus
197
+ When first run, `deltaK` is set to 0 ; click on a branch of the root locus
196
198
plot to try a different value. Each click updates plots and prints
197
199
the corresponding `deltaK`. To tune all three PID gains, repeatedly call
198
200
`pid_designer`, and select a different `gain` each time (`'P'`, `'I'`,
@@ -240,13 +242,13 @@ def pid_designer(plant, gain='P', sign=+1, input_signal='r',
240
242
plant : :class:`LTI` (:class:`TransferFunction` or :class:`StateSpace` system)
241
243
The dynamical system to be controlled
242
244
gain : string (optional)
243
- Which gain to vary by deltaK. Must be one of 'P', 'I', or 'D'
245
+ Which gain to vary by ` deltaK` . Must be one of ` 'P'`, ` 'I'` , or ` 'D'`
244
246
(proportional, integral, or derative)
245
247
sign : int (optional)
246
248
The sign of deltaK gain perturbation
247
249
input : string (optional)
248
- The input used for the step response; must be 'r' (reference) or
249
- 'd' (disturbance) (see figure above)
250
+ The input used for the step response; must be ` 'r'` (reference) or
251
+ ` 'd'` (disturbance) (see figure above)
250
252
Kp0, Ki0, Kd0 : float (optional)
251
253
Initial values for proportional, integral, and derivative gains,
252
254
respectively
@@ -257,16 +259,24 @@ def pid_designer(plant, gain='P', sign=+1, input_signal='r',
257
259
C_ff : float or :class:`LTI` system (optional)
258
260
Feedforward controller. If :class:`LTI`, must have timebase that is
259
261
compatible with plant.
262
+ derivative_in_feedback_path : bool (optional)
263
+ Whether to place the derivative term in feedback transfer function
264
+ `C_b` instead of the forward transfer function `C_f`.
265
+ noplot : bool (optional)
266
+
267
+ Returns
268
+ ----------
269
+ closedloop : class:`StateSpace` system
270
+ The closed-loop system using initial gains.
260
271
"""
261
272
262
273
plant = _convert_to_statespace (plant )
263
274
if plant .ninputs == 1 :
264
275
plant = ss2io (plant , inputs = 'u' , outputs = 'y' )
265
276
elif plant .ninputs == 2 :
266
- plant = ss2io (plant , inputs = ( 'u' , 'd' ) , outputs = 'y' )
277
+ plant = ss2io (plant , inputs = [ 'u' , 'd' ] , outputs = 'y' )
267
278
else :
268
279
raise ValueError ("plant must have one or two inputs" )
269
- #plant = ss2io(plant, inputs='u', outputs='y')
270
280
C_ff = ss2io (_convert_to_statespace (C_ff ), inputs = 'r' , outputs = 'uff' )
271
281
dt = common_timebase (plant , C_ff )
272
282
@@ -277,29 +287,30 @@ def pid_designer(plant, gain='P', sign=+1, input_signal='r',
277
287
else :
278
288
u_summer = summing_junction (['ufb' , 'uff' , 'd' ], 'u' )
279
289
280
- prop = tf (1 ,1 )
281
290
if isctime (plant ):
282
- integ = tf (1 ,[1 , 0 ])
291
+ prop = tf (1 , 1 )
292
+ integ = tf (1 , [1 , 0 ])
283
293
deriv = tf ([1 , 0 ], [tau , 1 ])
284
- else :
285
- integ = tf ([dt / 2 , dt / 2 ],[1 , - 1 ], dt )
286
- deriv = tf ([1 , - 1 ],[dt , 0 ], dt )
294
+ else : # discrete-time
295
+ prop = tf (1 , 1 , dt )
296
+ integ = tf ([dt / 2 , dt / 2 ], [1 , - 1 ], dt )
297
+ deriv = tf ([1 , - 1 ], [dt , 0 ], dt )
287
298
288
- # add signal names
299
+ # add signal names by turning into iosystems
289
300
prop = tf2io (prop , inputs = 'e' , outputs = 'prop_e' )
290
301
integ = tf2io (integ , inputs = 'e' , outputs = 'int_e' )
291
302
if derivative_in_feedback_path :
292
- deriv = tf2io (- deriv , inputs = 'y' , outputs = 'deriv_ ' )
303
+ deriv = tf2io (- deriv , inputs = 'y' , outputs = 'deriv ' )
293
304
else :
294
- deriv = tf2io (deriv , inputs = 'e' , outputs = 'deriv_ ' )
305
+ deriv = tf2io (deriv , inputs = 'e' , outputs = 'deriv ' )
295
306
296
307
# create gain blocks
297
308
Kpgain = tf2io (tf (Kp0 , 1 ), inputs = 'prop_e' , outputs = 'ufb' )
298
309
Kigain = tf2io (tf (Ki0 , 1 ), inputs = 'int_e' , outputs = 'ufb' )
299
- Kdgain = tf2io (tf (Kd0 , 1 ), inputs = 'deriv_ ' , outputs = 'ufb' )
310
+ Kdgain = tf2io (tf (Kd0 , 1 ), inputs = 'deriv ' , outputs = 'ufb' )
300
311
301
- # for the gain that is varied, create a special gain block with an
302
- # 'input' and an 'output' signal to create the loop transfer function
312
+ # for the gain that is varied, replace gain block with a special block
313
+ # that has an 'input' and an 'output' that creates loop transfer function
303
314
if gain in ('P' , 'p' ):
304
315
Kpgain = ss2io (ss ([],[],[],[[0 , 1 ], [- sign , Kp0 ]]),
305
316
inputs = ['input' , 'prop_e' ], outputs = ['output' , 'ufb' ])
@@ -308,13 +319,15 @@ def pid_designer(plant, gain='P', sign=+1, input_signal='r',
308
319
inputs = ['input' , 'int_e' ], outputs = ['output' , 'ufb' ])
309
320
elif gain in ('D' , 'd' ):
310
321
Kdgain = ss2io (ss ([],[],[],[[0 , 1 ], [- sign , Kd0 ]]),
311
- inputs = ['input' , 'deriv_ ' ], outputs = ['output' , 'ufb' ])
322
+ inputs = ['input' , 'deriv ' ], outputs = ['output' , 'ufb' ])
312
323
else :
313
324
raise ValueError (gain + ' gain not recognized.' )
314
325
315
326
# the second input and output are used by sisotool to plot step response
316
327
loop = interconnect ((plant , Kpgain , Kigain , Kdgain , prop , integ , deriv ,
317
328
C_ff , e_summer , u_summer ),
318
- inplist = ['input' , input_signal ], outlist = ['output' , 'y' ])
319
- sisotool (loop )
320
- return loop [1 , 1 ]
329
+ inplist = ['input' , input_signal ],
330
+ outlist = ['output' , 'y' ])
331
+ if ~ noplot :
332
+ sisotool (loop , kvect = (0. ,))
333
+ return _convert_to_statespace (loop [1 , 1 ])
0 commit comments