Skip to content

Commit 78f0c7f

Browse files
committed
Augmented plant and mixed-sythensis H-infinity design
New functions augw and mixsyn modelled after Matlab and Octave's function of the same names. python-control doesn't currently support input group naming, so that is not implemented.
1 parent 77d0ccd commit 78f0c7f

2 files changed

Lines changed: 469 additions & 0 deletions

File tree

‎control/robust.py‎

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,146 @@ def hinfsyn(P,nmeas,ncon):
171171
CL = StateSpace(Ac, Bc, Cc, Dc)
172172

173173
return K, CL, gam, rcond
174+
175+
176+
def _size_as_needed(w,wname,n):
177+
"""_size_as_needed(w,wname,n) -> w2
178+
If w is scalar, reshape to nxn; otherwise check size is OK. Return w as StateSpace object.
179+
If w is None, return empty StateSpace object.
180+
"""
181+
from . import append, ss
182+
if w is not None:
183+
if not isinstance(w,StateSpace):
184+
w = ss(w)
185+
if 1==w.inputs and 1==w.outputs:
186+
w = append(*(w,)*n)
187+
else:
188+
if w.inputs != n:
189+
msg=("{}: weighting function has {} inputs, expected {}".
190+
format(wname,w.inputs,n))
191+
raise ValueError(msg)
192+
else:
193+
w = ss([],[],[],[])
194+
return w
195+
196+
197+
def augw(g,w1=None,w2=None,w3=None):
198+
"""augw(g,w1=None,w2=None,w3=None) -> p
199+
Augment plant for mixed sensitivity problem
200+
g - LTI object, ny-by-nu
201+
w1 - weighting on S; None, scalar, or k1-by-ny LTI object
202+
w2 - weighting on KS; None, scalar, or k2-by-nu LTI object
203+
w3 - weighting on T; None, scalar, or k3-by-ny LTI object
204+
p - augmented plant; StateSpace object
205+
If a weighting is None, no augmentation is done for it. At least
206+
one weighting must not be None.
207+
If a weighting w is scalar, it will be replaced by I*w, where I is
208+
ny-by-ny for w1 and w3, and nu-by-nu for w2.
209+
210+
See also hinfsyn, mixsyn
211+
"""
212+
from . import append, ss, connect
213+
214+
if w1 is None and w2 is None and w3 is None:
215+
raise ValueError("At least one weighting must not be None")
216+
ny = g.outputs
217+
nu = g.inputs
218+
219+
w1,w2,w3 = [_size_as_needed(w,wname,n)
220+
for w,wname,n in zip((w1,w2,w3),
221+
('w1','w2','w3'),
222+
(ny,nu,ny))]
223+
224+
if not isinstance(g,StateSpace):
225+
g = ss(g)
226+
227+
# w u
228+
# z1 [ w1 | -w1*g ]
229+
# z2 [ 0 | w2 ]
230+
# z3 [ 0 | w3*g ]
231+
# [------+--------- ]
232+
# v [ I | -g ]
233+
234+
# error summer: inputs are -y and r=w
235+
Ie = ss([],[],[],np.eye(ny))
236+
# control: needed to "distribute" control input
237+
Iu = ss([],[],[],np.eye(nu))
238+
239+
sysall = append(w1,w2,w3,Ie,g,Iu)
240+
241+
niw1 = w1.inputs
242+
niw2 = w2.inputs
243+
niw3 = w3.inputs
244+
245+
now1 = w1.outputs
246+
now2 = w2.outputs
247+
now3 = w3.outputs
248+
249+
q = np.zeros((niw1+niw2+niw3+ny+nu,2))
250+
q[:,0] = np.arange(1,q.shape[0]+1)
251+
252+
# Ie -> w1
253+
q[:niw1,1] = np.arange(1+now1+now2+now3,
254+
1+now1+now2+now3+niw1)
255+
256+
# Iu -> w2
257+
q[niw1:niw1+niw2,1] = np.arange(1+now1+now2+now3+2*ny,
258+
1+now1+now2+now3+2*ny+niw2)
259+
260+
# y -> w3
261+
q[niw1+niw2:niw1+niw2+niw3,1] = np.arange(1+now1+now2+now3+ny,
262+
1+now1+now2+now3+ny+niw3)
263+
264+
# -y -> Iy; note the leading -
265+
q[niw1+niw2+niw3:niw1+niw2+niw3+ny,1] = -np.arange(1+now1+now2+now3+ny,
266+
1+now1+now2+now3+2*ny)
267+
268+
# Iu -> G
269+
q[niw1+niw2+niw3+ny:niw1+niw2+niw3+ny+nu,1] = np.arange(1+now1+now2+now3+2*ny,
270+
1+now1+now2+now3+2*ny+nu)
271+
272+
# input indices: to Ie and Iu
273+
ii = np.hstack((np.arange(1+now1+now2+now3,
274+
1+now1+now2+now3+ny),
275+
np.arange(1+now1+now2+now3+ny+nu,
276+
1+now1+now2+now3+ny+nu+nu)))
277+
278+
# output indices
279+
oi = np.arange(1,1+now1+now2+now3+ny)
280+
281+
p = connect(sysall,q,ii,oi)
282+
283+
return p
284+
285+
from collections import namedtuple as _namedtuple
286+
_mixsyn_info = _namedtuple('mixsyn_info',('gamma','rcond'))
287+
288+
def mixsyn(g,w1=None,w2=None,w3=None):
289+
"""mixsyn(g,w1,w2,w3) -> k,cl,info
290+
Mixed-sensitivity H-infinity synthesis
291+
g: LTI; the plant for which controller must be synthesized
292+
w1: weighting on s = (1+g*k)**-1; None, or scalar or k1-by-ny LTI
293+
w2: weighting on k*s; None, or scalar or k2-by-nu LTI
294+
w3: weighting on t = g*k*(1+g*k)**-1; None, or scalar or k3-by-ny LTI
295+
At least one of w1, w2, and w3 must not be None.
296+
k: synthesized controller; StateSpace object
297+
cl: closed system mapping evaluation inputs to evaluation outputs; if p is the augmented plant, with
298+
[z] = [p11 p12] [w], then cl is the system from w->z with u=-k*y. StateSpace object.
299+
[y] [p21 g] [u]
300+
info: namedtuple with fields, in order,
301+
gamma: scalar; H-infinity norm of cl
302+
rcond: array; estimates of reciprocal condition numbers
303+
computed during synthesis. See hinfsyn for details
304+
305+
If a weighting w is scalar, it will be replaced by I*w, where I is
306+
ny-by-ny for w1 and w3, and nu-by-nu for w2.
307+
308+
See also hinfsyn, augw
309+
"""
310+
nmeas = g.outputs
311+
ncon = g.inputs
312+
p = augw(g,w1,w2,w3)
313+
314+
k,cl,gamma,rcond=hinfsyn(p,nmeas,ncon)
315+
info = _mixsyn_info(gamma=gamma,rcond=rcond)
316+
return k,cl,info

0 commit comments

Comments
 (0)