@@ -203,3 +203,202 @@ function blockdiag(mats::AbstractMatrix{T}...) where T
203
203
end
204
204
return res
205
205
end
206
+
207
+
208
+
209
+ """
210
+ `feedback(L)` Returns L/(1+L)
211
+ `feedback(P1,P2)` Returns P1/(1+P1*P2)
212
+ """
213
+ feedback (L:: TransferFunction ) = L/ (1 + L)
214
+ feedback (P1:: TransferFunction , P2:: TransferFunction ) = P1/ (1 + P1* P2)
215
+
216
+ # Efficient implementations
217
+ function feedback (L:: TransferFunction{T} ) where T<: SisoRational
218
+ if size (L) != (1 ,1 )
219
+ error (" MIMO TransferFunction feedback isn't implemented, use L/(1+L)" )
220
+ end
221
+ P = numpoly (L)
222
+ Q = denpoly (L)
223
+ tf (P, P+ Q, L. Ts)
224
+ end
225
+
226
+ function feedback (L:: TransferFunction{T} ) where {T<: SisoZpk }
227
+ if size (L) != (1 ,1 )
228
+ error (" MIMO TransferFunction feedback isn't implemented, use L/(1+L)" )
229
+ end
230
+ # Extract polynomials and create P/(P+Q)
231
+ k = L. matrix[1 ]. k
232
+ denpol = numpoly (L)[1 ]+ denpoly (L)[1 ]
233
+ kden = denpol[end ] # Get coeff of s^n
234
+ # Create siso system
235
+ sisozpk = T (L. matrix[1 ]. z, roots (denpol), k/ kden)
236
+ return TransferFunction {T} (fill (sisozpk,1 ,1 ), L. Ts)
237
+ end
238
+
239
+ """
240
+ `feedback(sys)`
241
+
242
+ `feedback(sys1,sys2)`
243
+
244
+ Forms the negative feedback interconnection
245
+ ```julia
246
+ >-+ sys1 +-->
247
+ | |
248
+ (-)sys2 +
249
+ ```
250
+ If no second system is given, negative identity feedback is assumed
251
+ """
252
+ function feedback (sys:: Union{StateSpace, DelayLtiSystem} )
253
+ ninputs (sys) != noutputs (sys) && error (" Use feedback(sys1, sys2) if number of inputs != outputs" )
254
+ feedback (sys,ss (Matrix {numeric_type(sys)} (I,size (sys)... )))
255
+ end
256
+
257
+ function feedback (sys1:: StateSpace ,sys2:: StateSpace )
258
+ sum (abs .(sys1. D)) != 0 && sum (abs .(sys2. D)) != 0 && error (" There can not be a direct term (D) in both sys1 and sys2" )
259
+ A = [sys1. A+ sys1. B* (- sys2. D)* sys1. C sys1. B* (- sys2. C); sys2. B* sys1. C sys2. A+ sys2. B* sys1. D* (- sys2. C)]
260
+ B = [sys1. B; sys2. B* sys1. D]
261
+ C = [sys1. C sys1. D* (- sys2. C)]
262
+ ss (A,B,C,sys1. D)
263
+ end
264
+
265
+
266
+ """
267
+ feedback(s1::AbstractStateSpace, s2::AbstractStateSpace;
268
+ U1=1:size(s1,2), Y1=1:size(s1,1), U2=1:size(s2,2), Y2=1:size(s2,1),
269
+ W1=1:size(s1,2), Z1=1:size(s1,1), W2=Int[], Z2=Int[],
270
+ w_reorder=nothing, z_reorder=nothing,
271
+ neg_feeback::Bool=true)
272
+
273
+
274
+ `U1`, `Y1`, `U2`, `Y2` contains the indices of the signals that should be connected.
275
+ `W1`, `Z1`, `W2`, `Z2` contains the signal indices of `s1` and `s2` that should be kept.
276
+
277
+ Specify `Wreorder` and `Zreorder` to reorder [W1; W2] and [Z1; Z2] after matrix multiplications.
278
+
279
+ Negative feedback is the default. Specify `neg_feedback=false` for positive feedback.
280
+
281
+ See Zhou etc. for similar (somewhat less symmetric) formulas.
282
+ """
283
+ function feedback (s1:: AbstractStateSpace , s2:: AbstractStateSpace ;
284
+ U1= 1 : size (s1,2 ), Y1= 1 : size (s1,1 ), U2= 1 : size (s2,2 ), Y2= 1 : size (s2,1 ),
285
+ W1= 1 : size (s1,2 ), Z1= 1 : size (s1,1 ), W2= Int[], Z2= Int[],
286
+ w_reorder= nothing , z_reorder= nothing ,
287
+ neg_feedback:: Bool = true )
288
+
289
+ if ! (allunique (Y1) && allunique (Y2))
290
+ warning (" Connecting a single output to multiple inputs." )
291
+ end
292
+ if ! (allunique (U1) && allunique (U2))
293
+ warning (" Connecting multiple outputs to a single input." )
294
+ end
295
+
296
+ α = neg_feedback ? - 1 : 1 # The sign of feedback
297
+
298
+ s1_B1 = s1. B[:,W1]
299
+ s1_B2 = s1. B[:,U1]
300
+ s1_C1 = s1. C[Z1,:]
301
+ s1_C2 = s1. C[Y1,:]
302
+ s1_D11 = s1. D[Z1,W1]
303
+ s1_D12 = s1. D[Z1,U1]
304
+ s1_D21 = s1. D[Y1,W1]
305
+ s1_D22 = s1. D[Y1,U1]
306
+
307
+ s2_B1 = s2. B[:,W2]
308
+ s2_B2 = s2. B[:,U2]
309
+ s2_C1 = s2. C[Z2,:]
310
+ s2_C2 = s2. C[Y2,:]
311
+ s2_D11 = s2. D[Z2,W2]
312
+ s2_D12 = s2. D[Z2,U2]
313
+ s2_D21 = s2. D[Y2,W2]
314
+ s2_D22 = s2. D[Y2,U2]
315
+
316
+
317
+ if iszero (s1_D22) || iszero (s2_D22)
318
+ A = [s1. A + α* s1_B2* s2_D22* s1_C2 α* s1_B2* s2_C2;
319
+ s2_B2* s1_C2 s2. A + α* s2_B2* s1_D22* s2_C2]
320
+
321
+ B = [s1_B1 + α* s1_B2* s2_D22* s1_D21 α* s1_B2* s2_D21;
322
+ s2_B2* s1_D21 s2_B1 + α* s2_B2* s1_D22* s2_D21]
323
+ C = [s1_C1 + α* s1_D12* s2_D22* s1_C2 α* s1_D12* s2_C2;
324
+ s2_D12* s1_C2 s2_C1 + α* s2_D12* s1_D22* s2_C2]
325
+ D = [s1_D11 + α* s1_D12* s2_D22* s1_D21 α* s1_D12* s2_D21;
326
+ s2_D12* s1_D21 s2_D11 + α* s2_D12* s1_D22* s2_D21]
327
+ else
328
+ R1 = inv (I - α* s2_D22* s1_D22) # inv seems to be better than lu
329
+ R2 = inv (I - α* s1_D22* s2_D22)
330
+
331
+ A = [s1. A + α* s1_B2* R1* s2_D22* s1_C2 α* s1_B2* R1* s2_C2;
332
+ s2_B2* R2* s1_C2 s2. A + α* s2_B2* R2* s1_D22* s2_C2]
333
+
334
+ B = [s1_B1 + α* s1_B2* R1* s2_D22* s1_D21 α* s1_B2* R1* s2_D21;
335
+ s2_B2* R2* s1_D21 s2_B1 + α* s2_B2* R2* s1_D22* s2_D21]
336
+ C = [s1_C1 + α* s1_D12* R1* s2_D22* s1_C2 α* s1_D12* R1* s2_C2;
337
+ s2_D12* R2* s1_C2 s2_C1 + α* s2_D12* R2* s1_D22* s2_C2]
338
+ D = [s1_D11 + α* s1_D12* R1* s2_D22* s1_D21 α* s1_D12* R1* s2_D21;
339
+ s2_D12* R2* s1_D21 s2_D11 + α* s2_D12* R2* s1_D22* s2_D21]
340
+ end
341
+ # Return while reorganizing into the desired order
342
+ return StateSpace (A, isnothing (w_reorder) ? B : B[:, w_reorder],
343
+ isnothing (z_reorder) ? C : C[z_reorder,:],
344
+ isnothing (w_reorder) && isnothing (z_reorder) ? D : D[z_reorder, w_reorder])
345
+ # return StateSpace(A, B_tmp, C_tmp, D_tmp)
346
+ end
347
+
348
+ """
349
+ `feedback2dof(P,R,S,T)` Return `BT/(AR+ST)` where B and A are the numerator and denomenator polynomials of `P` respectively
350
+ `feedback2dof(B,A,R,S,T)` Return `BT/(AR+ST)`
351
+ """
352
+ function feedback2dof (P:: TransferFunction ,R,S,T)
353
+ ! issiso (P) && error (" Feedback not implemented for MIMO systems" )
354
+ tf (conv (poly2vec (numpoly (P)[1 ]),T),zpconv (poly2vec (denpoly (P)[1 ]),R,poly2vec (numpoly (P)[1 ]),S))
355
+ end
356
+
357
+ feedback2dof (B,A,R,S,T) = tf (conv (B,T),zpconv (A,R,B,S))
358
+
359
+
360
+ """
361
+ lft(G, Δ, type=:l)
362
+
363
+ Upper linear fractional transformation between systems `G` and `Δ`.
364
+ `G` must have more inputs and outputs than `Δ` has outputs and inputs.
365
+
366
+ For details, see Chapter 9.1 in
367
+ **Zhou, K. and JC Doyle**. Essentials of robust control, Prentice hall (NJ), 1998
368
+ """
369
+ function lft (G, Δ, type= :l ) # QUESTION: Or lft_u, and lft_l instead?
370
+
371
+ if ! (G. nu > Δ. ny && G. ny > Δ. nu)
372
+ error (" Must have G.nu > Δ.ny and G.ny > Δ.nu for lower/upper lft." )
373
+ end
374
+
375
+ if type == :l
376
+ feedback (G, Δ, U1= G. nu- Δ. ny+ 1 : G. nu, Y1= G. ny- Δ. nu+ 1 : G. ny, W1= 1 : G. ny- Δ. nu, Z1= 1 : G. nu- Δ. ny, neg_feedback= false )
377
+ elseif type == :u
378
+ feedback (G, Δ, U1= 1 : Δ. ny, Y1= 1 : Δ. nu, W1= Δ. nu+ 1 : G. ny, Z1= Δ. nu+ 1 : G. ny, neg_feedback= false )
379
+ else
380
+ error (" Invalid type of lft ($type ). Specify type=:l (:u) for lower (upper) lft." )
381
+ end
382
+ end
383
+
384
+
385
+ """
386
+ starprod(sys1, sys2, dimu, dimy)
387
+
388
+ Compute the Redheffer star product.
389
+
390
+ `length(U1) = length(Y2) = dimu` and `length(Y1) = length(U2) = dimy`
391
+
392
+ For details, see Chapter 9.3 in
393
+ **Zhou, K. and JC Doyle**. Essentials of robust control, Prentice hall (NJ), 1998
394
+ """
395
+ function starprod (G1, G2, dimy:: Int , dimu:: Int )
396
+ feedback (G1, G2, U1= G1. nu- dimu+ 1 : G1. nu, Y1= G1. ny- dimy+ 1 : G1. ny, W1= 1 : G1. nu- dimu, Z1= 1 : G1. ny- dimy,
397
+ U2= 1 : dimy, Y2= 1 : dimu, W2= dimy+ 1 : G2. nu, Z2= dimu+ 1 : G2. ny,
398
+ neg_feedback= false )
399
+ end
400
+ function starprod (sys1, sys2)
401
+ nu = size (sys2, 2 )
402
+ ny = size (sys2, 1 )
403
+ starp (sys1, sys2, nu, ny)
404
+ end
0 commit comments