1
1
export pid, pid_tf, pid_ss, pid_2dof, pid_ss_2dof, pidplots, leadlink, laglink, leadlinkat, leadlinkcurve, stabregionPID, loopshapingPI, placePI, loopshapingPID
2
2
3
3
"""
4
- C = pid(param_p, param_i, [param_d]; form=:standard, state_space=false, [Tf], [Ts])
4
+ C = pid(param_p, param_i, [param_d]; form=:standard, state_space=false, [Tf], [Ts], filter_order=2 )
5
5
6
6
Calculates and returns a PID controller.
7
7
@@ -13,11 +13,14 @@ The `form` can be chosen as one of the following (determines how the arguments `
13
13
If `state_space` is set to `true`, either `Kd` has to be zero
14
14
or a positive `Tf` has to be provided for creating a filter on
15
15
the input to allow for a state-space realization.
16
- The filter used is `1 / (1 + s*Tf + (s*Tf)^2/2)`, where `Tf` can typically
17
- be chosen as `Ti/N` for a PI controller and `Td/N` for a PID controller,
16
+
17
+ The filter used is either
18
+ - `filter_order = 2` (default): `1 / (1 + s*Tf + (s*Tf)^2/2)` in series with the controller
19
+ - `filter_order = 1`: `1 / (1 + s*Tf)` applied to the derivative term only
20
+
21
+ `Tf` can typically be chosen as `Ti/N` for a PI controller and `Td/N` for a PID controller,
18
22
and `N` is commonly in the range 2 to 20.
19
- A balanced state-space realization is returned, unless `balance = false`
20
- in which case a controllable canonical form is used.
23
+ A balanced state-space realization is returned, unless `balance = false`.
21
24
22
25
For a discrete controller a positive `Ts` can be supplied.
23
26
In this case, the continuous-time controller is discretized using the Tustin method.
@@ -32,11 +35,11 @@ C3 = pid(2., 3, 0; Ts=0.4, state_space=true) # Discrete
32
35
The functions `pid_tf` and `pid_ss` are also exported. They take the same parameters
33
36
and is what is actually called in `pid` based on the `state_space` parameter.
34
37
"""
35
- function pid (param_p, param_i, param_d= zero (typeof (param_p)); form= :standard , Ts= nothing , Tf= nothing , state_space= false , balance= true )
38
+ function pid (param_p, param_i, param_d= zero (typeof (param_p)); form= :standard , Ts= nothing , Tf= nothing , state_space= false , balance= true , filter_order = 2 )
36
39
C = if state_space # Type instability? Can it be fixed easily, does it matter?
37
- pid_ss (param_p, param_i, param_d; form, Tf, balance)
40
+ pid_ss (param_p, param_i, param_d; form, Tf, filter_order, balance)
38
41
else
39
- pid_tf (param_p, param_i, param_d; form, Tf)
42
+ pid_tf (param_p, param_i, param_d; form, Tf, filter_order )
40
43
end
41
44
if Ts === nothing
42
45
return C
48
51
49
52
@deprecate pid (; kp= 0 , ki= 0 , kd= 0 , series = false ) pid (kp, ki, kd; form= series ? :series : :parallel )
50
53
51
- function pid_tf (param_p, param_i, param_d= zero (typeof (param_p)); form= :standard , Tf= nothing )
54
+ function pid_tf (param_p, param_i, param_d= zero (typeof (param_p)); form= :standard , Tf= nothing , filter_order = 2 )
52
55
Kp, Ki, Kd = convert_pidparams_to_parallel (param_p, param_i, param_d, form)
53
- if isnothing (Tf)
54
- if Ki != 0
55
- return tf ([Kd, Kp, Ki], [1 , 0 ])
56
- else
56
+ filter_order ∈ (1 ,2 ) || throw (ArgumentError (" Filter order must be 1 or 2" ))
57
+ if isnothing (Tf) || (Kd == 0 && filter_order == 1 )
58
+ if Ki == 0
57
59
return tf ([Kd, Kp], [1 ])
60
+ else
61
+ return tf ([Kd, Kp, Ki], [1 , 0 ])
58
62
end
59
63
else
60
- if Ki != 0
61
- return tf ([Kd, Kp, Ki], [Tf^ 2 / 2 , Tf, 1 , 0 ])
64
+ if Ki == 0
65
+ if filter_order == 1
66
+ tf ([Kd* Tf + Kd, Kd], [Tf, 1 ])
67
+ else
68
+ return tf ([Kd, Kp], [Tf^ 2 / 2 , Tf, 1 ])
69
+ end
62
70
else
63
- return tf ([Kd, Kp], [Tf^ 2 / 2 , Tf, 1 ])
71
+ if filter_order == 1
72
+ return tf ([Kd + Kp* Tf, Ki* Tf + Kp, Ki], [Tf, 1 , 0 ])
73
+ else
74
+ return tf ([Kd, Kp, Ki], [Tf^ 2 / 2 , Tf, 1 , 0 ])
75
+ end
64
76
end
65
77
end
66
78
end
67
79
68
- function pid_ss (param_p, param_i, param_d= zero (typeof (param_p)); form= :standard , Tf= nothing , balance= true )
80
+ function pid_ss (param_p, param_i, param_d= zero (typeof (param_p)); form= :standard , Tf= nothing , balance= true , filter_order )
69
81
Kp, Ki, Kd = convert_pidparams_to_parallel (param_p, param_i, param_d, form)
70
82
if ! isnothing (Tf)
71
- if Ki != 0
72
- A = [0 1 0 ; 0 0 1 ; 0 - 2 / Tf^ 2 - 2 / Tf]
73
- B = [0 ; 0 ; 1 ]
74
- C = 2 / Tf^ 2 * [Ki Kp Kd]
83
+ if Ki == 0
84
+ if filter_order == 1
85
+ A = [- 1 / Tf;;]
86
+ B = [- Kd/ Tf^ 2 ]
87
+ C = [1.0 ;;]
88
+ D = [Kd/ Tf + Kp;;]
89
+ else # 2
90
+ A = [0 1 ; - 2 / Tf^ 2 - 2 / Tf]
91
+ B = [0 ; 1 ]
92
+ C = 2 / Tf^ 2 * [Kp Kd]
93
+ D = [0.0 ;;]
94
+ end
75
95
else
76
- A = [0 1 ; - 2 / Tf^ 2 - 2 / Tf]
77
- B = [0 ; 1 ]
78
- C = 2 / Tf^ 2 * [Kp Kd]
96
+ if filter_order == 1
97
+ A = [0 0 ; 0 - 1 / Tf]
98
+ B = [Ki; - Kd/ Tf^ 2 ]
99
+ C = [1.0 1 ]
100
+ D = [Kd/ Tf + Kp;;]
101
+ else # 2
102
+ A = [0 1 0 ; 0 0 1 ; 0 - 2 / Tf^ 2 - 2 / Tf]
103
+ B = [0 ; 0 ; 1 ]
104
+ C = 2 / Tf^ 2 * [Ki Kp Kd]
105
+ D = [0.0 ;;]
106
+ end
79
107
end
80
- D = 0
81
108
elseif Kd == 0
82
109
if Ki != 0
83
- A = 0
84
- B = 1
85
- C = Ki # Ti == 0 would result in division by zero, but typically indicates that the user wants no integral action
86
- D = Kp
110
+ A = [ 0.0 ;;]
111
+ B = [ 1.0 ;;]
112
+ C = [Ki;;] # Ti == 0 would result in division by zero, but typically indicates that the user wants no integral action
113
+ D = [Kp;;]
87
114
else
88
115
return ss ([Kp])
89
116
end
@@ -155,13 +182,12 @@ function pid_ss_2dof(param_p, param_i, param_d=zero(typeof(param_p)); form=:stan
155
182
A = [- (1 / Tf);;]
156
183
B = [- kd* c/ (Tf^ 2 ) kd/ (Tf^ 2 )]
157
184
C = [1.0 ]
158
- D = [kd* c/ Tf+ kp* b - (kd/ Tf + kp)]
159
185
else
160
186
A = [0 0 ; 0 - (1 / Tf)]
161
187
B = [ki - ki; - kd* c/ Tf^ 2 kd/ Tf^ 2 ]
162
188
C = [1.0 1 ]
163
- D = [kd* c/ Tf+ kp* b - (kd/ Tf + kp)]
164
189
end
190
+ D = [kd* c/ Tf+ kp* b - (kd/ Tf + kp)]
165
191
K = ss (A, B, C, D)
166
192
balance ? first (balance_statespace (K)) : K
167
193
end
@@ -229,7 +255,7 @@ function pidplots(P::LTISystem, args...;
229
255
pzmap (Ts; title= " Pole-zero map" , kwargs... ) |> display
230
256
end
231
257
if :controller ∈ args
232
- bodeplot (Cs, ω; lab= labels, title= " Controller bode plot" , kwargs... ) |> display
258
+ bodeplot (Cs, ω; lab= repeat ( labels, inner = ( 1 , 2 )) , title= " Controller bode plot" , kwargs... ) |> display
233
259
end
234
260
end
235
261
0 commit comments