1- __all__ = ['sisotool' , 'pid_designer ' ]
1+ __all__ = ['sisotool' , 'rootlocus_pid_designer ' ]
22
33from control .exception import ControlMIMONotImplemented
44from .freqplot import bode_plot
@@ -180,19 +180,21 @@ def _SisotoolUpdate(sys, fig, K, bode_plot_params, tvect=None):
180180 fig .subplots_adjust (top = 0.9 ,wspace = 0.3 ,hspace = 0.35 )
181181 fig .canvas .draw ()
182182
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' ,
185186 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
188190
189191 Uses `Sisotool` to investigate the effect of adding or subtracting an
190192 amount `deltaK` to the proportional, integral, or derivative (PID) gains of
191193 a controller. One of the PID gains, `Kp`, `Ki`, or `Kd`, respectively, can
192194 be modified at a time. `Sisotool` plots the step response, frequency
193195 response, and root locus.
194196
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
196198 plot to try a different value. Each click updates plots and prints
197199 the corresponding `deltaK`. To tune all three PID gains, repeatedly call
198200 `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',
240242 plant : :class:`LTI` (:class:`TransferFunction` or :class:`StateSpace` system)
241243 The dynamical system to be controlled
242244 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'`
244246 (proportional, integral, or derative)
245247 sign : int (optional)
246248 The sign of deltaK gain perturbation
247249 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)
250252 Kp0, Ki0, Kd0 : float (optional)
251253 Initial values for proportional, integral, and derivative gains,
252254 respectively
@@ -257,16 +259,24 @@ def pid_designer(plant, gain='P', sign=+1, input_signal='r',
257259 C_ff : float or :class:`LTI` system (optional)
258260 Feedforward controller. If :class:`LTI`, must have timebase that is
259261 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.
260271 """
261272
262273 plant = _convert_to_statespace (plant )
263274 if plant .ninputs == 1 :
264275 plant = ss2io (plant , inputs = 'u' , outputs = 'y' )
265276 elif plant .ninputs == 2 :
266- plant = ss2io (plant , inputs = ( 'u' , 'd' ) , outputs = 'y' )
277+ plant = ss2io (plant , inputs = [ 'u' , 'd' ] , outputs = 'y' )
267278 else :
268279 raise ValueError ("plant must have one or two inputs" )
269- #plant = ss2io(plant, inputs='u', outputs='y')
270280 C_ff = ss2io (_convert_to_statespace (C_ff ), inputs = 'r' , outputs = 'uff' )
271281 dt = common_timebase (plant , C_ff )
272282
@@ -277,29 +287,30 @@ def pid_designer(plant, gain='P', sign=+1, input_signal='r',
277287 else :
278288 u_summer = summing_junction (['ufb' , 'uff' , 'd' ], 'u' )
279289
280- prop = tf (1 ,1 )
281290 if isctime (plant ):
282- integ = tf (1 ,[1 , 0 ])
291+ prop = tf (1 , 1 )
292+ integ = tf (1 , [1 , 0 ])
283293 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 )
287298
288- # add signal names
299+ # add signal names by turning into iosystems
289300 prop = tf2io (prop , inputs = 'e' , outputs = 'prop_e' )
290301 integ = tf2io (integ , inputs = 'e' , outputs = 'int_e' )
291302 if derivative_in_feedback_path :
292- deriv = tf2io (- deriv , inputs = 'y' , outputs = 'deriv_ ' )
303+ deriv = tf2io (- deriv , inputs = 'y' , outputs = 'deriv ' )
293304 else :
294- deriv = tf2io (deriv , inputs = 'e' , outputs = 'deriv_ ' )
305+ deriv = tf2io (deriv , inputs = 'e' , outputs = 'deriv ' )
295306
296307 # create gain blocks
297308 Kpgain = tf2io (tf (Kp0 , 1 ), inputs = 'prop_e' , outputs = 'ufb' )
298309 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' )
300311
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
303314 if gain in ('P' , 'p' ):
304315 Kpgain = ss2io (ss ([],[],[],[[0 , 1 ], [- sign , Kp0 ]]),
305316 inputs = ['input' , 'prop_e' ], outputs = ['output' , 'ufb' ])
@@ -308,13 +319,15 @@ def pid_designer(plant, gain='P', sign=+1, input_signal='r',
308319 inputs = ['input' , 'int_e' ], outputs = ['output' , 'ufb' ])
309320 elif gain in ('D' , 'd' ):
310321 Kdgain = ss2io (ss ([],[],[],[[0 , 1 ], [- sign , Kd0 ]]),
311- inputs = ['input' , 'deriv_ ' ], outputs = ['output' , 'ufb' ])
322+ inputs = ['input' , 'deriv ' ], outputs = ['output' , 'ufb' ])
312323 else :
313324 raise ValueError (gain + ' gain not recognized.' )
314325
315326 # the second input and output are used by sisotool to plot step response
316327 loop = interconnect ((plant , Kpgain , Kigain , Kdgain , prop , integ , deriv ,
317328 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