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