Skip to content

Commit 4f288ee

Browse files
committed
More work on interconnections, fixes discrete-time for existing feedback fcn
1 parent 472cb96 commit 4f288ee

File tree

3 files changed

+111
-87
lines changed

3 files changed

+111
-87
lines changed

src/connections.jl

Lines changed: 75 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -255,77 +255,80 @@ function feedback(sys::Union{StateSpace, DelayLtiSystem})
255255
end
256256

257257
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)]
258+
if sys1.Ts != sys2.Ts # FIXME: replace with common_sample_time
259+
error("Sampling time mismatch")
260+
end
261+
262+
!(iszero(sys1.D) || iszero(sys2.D)) && error("There cannot be a direct term (D) in both sys1 and sys2")
263+
A = [sys1.A+sys1.B*(-sys2.D)*sys1.C sys1.B*(-sys2.C);
264+
sys2.B*sys1.C sys2.A+sys2.B*sys1.D*(-sys2.C)]
260265
B = [sys1.B; sys2.B*sys1.D]
261266
C = [sys1.C sys1.D*(-sys2.C)]
262-
ss(A,B,C,sys1.D)
267+
268+
ss(A, B, C, sys1.D, sys1.Ts)
263269
end
264270

265271

266272
"""
267273
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,n
271-
pos_feeback::Bool=false)
274+
U1=:, Y1=:, U2=:, Y2=:, W1=:, Z1=:, W2=Int[], Z2=Int[],
275+
Wperm=:, Zperm=:, pos_feedback::Bool=false)
272276
273277
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.
278+
`U1`, `Y1`, `U2`, `Y2` contain the indices of the signals that should be connected.
279+
`W1`, `Z1`, `W2`, `Z2` contain the signal indices of `s1` and `s2` that should be kept.
276280
277-
Specify `Wperm` and `Zperm` to reorder [W1; W2] and [Z1; Z2] in the resulting statespace model.
281+
Specify `Wperm` and `Zperm` to reorder [w1; w2] and [z1; z2] in the resulting statespace model.
278282
279283
Negative feedback is the default. Specify `pos_feedback=true` for positive feedback.
280284
281285
See Zhou etc. for similar (somewhat less symmetric) formulas.
282286
"""
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-
Wperm=nothing, Zperm=nothing,
287-
pos_feedback::Bool=false)
288-
287+
@views function feedback(sys1::AbstractStateSpace, sys2::AbstractStateSpace;
288+
U1=:, Y1=:, U2=:, Y2=:, W1=:, Z1=:, W2=Int[], Z2=Int[],
289+
Wperm=:, Zperm=:, pos_feedback::Bool=false)
289290

291+
if sys1.Ts != sys2.Ts # FIXME: replace with common_sample_time
292+
error("Sampling time mismatch")
293+
end
290294

295+
#= must define allunique(c::Colon) = true for the following checks to work
291296
if !allunique(Y1); @warn "Connecting single output to multiple inputs Y1=$Y1"; end
292297
if !allunique(Y2); @warn "Connecting single output to multiple inputs Y2=$Y2"; end
293298
if !allunique(U1); @warn "Connecting multiple outputs to a single input U1=$U1"; end
294299
if !allunique(U2); @warn "Connecting a single output to multiple inputs U2=$U2"; end
300+
=#
295301

296-
if length(U1) != length(Y2)
297-
error("Dimensions of input u1 ($(length(U1))) and output y2 ($(length(Y2))) must be equal")
302+
if (U1 isa Colon ? size(sys1, 2) : length(U1)) != (Y2 isa Colon ? size(sys2, 1) : length(Y2))
303+
error("Lengths of U1 ($U1) and Y2 ($Y2) must be equal")
298304
end
299-
if length(U2) != length(Y1)
300-
error("Dimensions of input u2 ($(length(U2))) and output y1 ($(length(Y2))) must be equal")
305+
if (U2 isa Colon ? size(sys2, 2) : length(U2)) != (Y1 isa Colon ? size(sys1, 1) : length(Y1))
306+
error("Lengths of U1 ($U2) and Y2 ($Y1) must be equal")
301307
end
302308

303-
304-
305309
α = pos_feedback ? 1 : -1 # The sign of feedback
306310

307-
s1_B1 = s1.B[:,W1]
308-
s1_B2 = s1.B[:,U1]
309-
s1_C1 = s1.C[Z1,:]
310-
s1_C2 = s1.C[Y1,:]
311-
s1_D11 = s1.D[Z1,W1]
312-
s1_D12 = s1.D[Z1,U1]
313-
s1_D21 = s1.D[Y1,W1]
314-
s1_D22 = s1.D[Y1,U1]
315-
316-
s2_B1 = s2.B[:,W2]
317-
s2_B2 = s2.B[:,U2]
318-
s2_C1 = s2.C[Z2,:]
319-
s2_C2 = s2.C[Y2,:]
320-
s2_D11 = s2.D[Z2,W2]
321-
s2_D12 = s2.D[Z2,U2]
322-
s2_D21 = s2.D[Y2,W2]
323-
s2_D22 = s2.D[Y2,U2]
324-
311+
s1_B1 = sys1.B[:,W1]
312+
s1_B2 = sys1.B[:,U1]
313+
s1_C1 = sys1.C[Z1,:]
314+
s1_C2 = sys1.C[Y1,:]
315+
s1_D11 = sys1.D[Z1,W1]
316+
s1_D12 = sys1.D[Z1,U1]
317+
s1_D21 = sys1.D[Y1,W1]
318+
s1_D22 = sys1.D[Y1,U1]
319+
320+
s2_B1 = sys2.B[:,W2]
321+
s2_B2 = sys2.B[:,U2]
322+
s2_C1 = sys2.C[Z2,:]
323+
s2_C2 = sys2.C[Y2,:]
324+
s2_D11 = sys2.D[Z2,W2]
325+
s2_D12 = sys2.D[Z2,U2]
326+
s2_D21 = sys2.D[Y2,W2]
327+
s2_D22 = sys2.D[Y2,U2]
325328

326329
if iszero(s1_D22) || iszero(s2_D22)
327-
A = [s1.A + α*s1_B2*s2_D22*s1_C2 α*s1_B2*s2_C2;
328-
s2_B2*s1_C2 s2.A + α*s2_B2*s1_D22*s2_C2]
330+
A = [sys1.A + α*s1_B2*s2_D22*s1_C2 α*s1_B2*s2_C2;
331+
s2_B2*s1_C2 sys2.A + α*s2_B2*s1_D22*s2_C2]
329332

330333
B = [s1_B1 + α*s1_B2*s2_D22*s1_D21 α*s1_B2*s2_D21;
331334
s2_B2*s1_D21 s2_B1 + α*s2_B2*s1_D22*s2_D21]
@@ -334,26 +337,34 @@ function feedback(s1::AbstractStateSpace, s2::AbstractStateSpace;
334337
D = [s1_D11 + α*s1_D12*s2_D22*s1_D21 α*s1_D12*s2_D21;
335338
s2_D12*s1_D21 s2_D11 + α*s2_D12*s1_D22*s2_D21]
336339
else
337-
R1 = inv(I - α*s2_D22*s1_D22) # inv seems to be better than lu
338-
R2 = inv(I - α*s1_D22*s2_D22)
339-
340-
A = [s1.A + α*s1_B2*R1*s2_D22*s1_C2 α*s1_B2*R1*s2_C2;
341-
s2_B2*R2*s1_C2 s2.A + α*s2_B2*R2*s1_D22*s2_C2]
342-
343-
B = [s1_B1 + α*s1_B2*R1*s2_D22*s1_D21 α*s1_B2*R1*s2_D21;
340+
# inv seems to be better than lu
341+
R1 = try
342+
inv*I - s2_D22*s1_D22) # slightly faster than α*inv(I - α*s2_D22*s1_D22)
343+
catch
344+
error("Illposed feedback interconnection, I - α*s2_D22*s1_D22 or I - α*s2_D22*s1_D22 not invertible")
345+
end
346+
347+
R2 = try
348+
inv(I - α*s1_D22*s2_D22)
349+
catch
350+
error("Illposed feedback interconnection, I - α*s2_D22*s1_D22 or I - α*s2_D22*s1_D22 not invertible")
351+
end
352+
353+
A = [sys1.A + s1_B2*R1*s2_D22*s1_C2 s1_B2*R1*s2_C2;
354+
s2_B2*R2*s1_C2 sys2.A + α*s2_B2*R2*s1_D22*s2_C2]
355+
356+
B = [s1_B1 + s1_B2*R1*s2_D22*s1_D21 s1_B2*R1*s2_D21;
344357
s2_B2*R2*s1_D21 s2_B1 + α*s2_B2*R2*s1_D22*s2_D21]
345-
C = [s1_C1 + α*s1_D12*R1*s2_D22*s1_C2 α*s1_D12*R1*s2_C2;
358+
C = [s1_C1 + s1_D12*R1*s2_D22*s1_C2 s1_D12*R1*s2_C2;
346359
s2_D12*R2*s1_C2 s2_C1 + α*s2_D12*R2*s1_D22*s2_C2]
347-
D = [s1_D11 + α*s1_D12*R1*s2_D22*s1_D21 α*s1_D12*R1*s2_D21;
360+
D = [s1_D11 + s1_D12*R1*s2_D22*s1_D21 s1_D12*R1*s2_D21;
348361
s2_D12*R2*s1_D21 s2_D11 + α*s2_D12*R2*s1_D22*s2_D21]
349362
end
350-
# Return while reorganizing into the desired order
351-
return StateSpace(A, isnothing(Wperm) ? B : B[:, Wperm],
352-
isnothing(Zperm) ? C : C[Zperm,:],
353-
isnothing(Wperm) && isnothing(Zperm) ? D : D[Zperm, Wperm])
354-
#return StateSpace(A, B_tmp, C_tmp, D_tmp)
363+
364+
return StateSpace(A, B[:, Wperm], C[Zperm,:], D[Zperm, Wperm], sys1.Ts)
355365
end
356366

367+
357368
"""
358369
`feedback2dof(P,R,S,T)` Return `BT/(AR+ST)` where B and A are the numerator and denomenator polynomials of `P` respectively
359370
`feedback2dof(B,A,R,S,T)` Return `BT/(AR+ST)`
@@ -369,28 +380,32 @@ feedback2dof(B,A,R,S,T) = tf(conv(B,T),zpconv(A,R,B,S))
369380
"""
370381
lft(G, Δ, type=:l)
371382
372-
Upper linear fractional transformation between systems `G` and `Δ`.
383+
Lower and upper linear fractional transformation between systems `G` and `Δ`.
384+
385+
Specify `:l` lor lower LFT, and `:u` for upper LFT.
386+
373387
`G` must have more inputs and outputs than `Δ` has outputs and inputs.
374388
375389
For details, see Chapter 9.1 in
376390
**Zhou, K. and JC Doyle**. Essentials of robust control, Prentice hall (NJ), 1998
377391
"""
378-
function lft(G, Δ, type=:l) # QUESTION: Or lft_u, and lft_l instead?
392+
function lft(G, Δ, type=:l)
379393

380394
if !(G.nu > Δ.ny && G.ny > Δ.nu)
381-
error("Must have G.nu > Δ.ny and G.ny > Δ.nu for lower/upper lft.")
395+
error("Must have G.nu > Δ.ny and G.ny > Δ.nu for lower/upper lft")
382396
end
383397

384398
if type == :l
385399
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, pos_feedback=true)
386400
elseif type == :u
387401
feedback(G, Δ, U1=1:Δ.ny, Y1=1:Δ.nu, W1=Δ.nu+1:G.ny, Z1=Δ.nu+1:G.ny, pos_feedback=true)
388402
else
389-
error("Invalid type of lft ($type). Specify type=:l (:u) for lower (upper) lft.")
403+
error("Invalid type of lft ($type), specify type=:l (:u) for lower (upper) lft")
390404
end
391405
end
392406

393407

408+
394409
"""
395410
starprod(sys1, sys2, dimu, dimy)
396411

test/test_connections.jl

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -153,43 +153,55 @@ arr4[1] = ss(0); arr4[2] = ss(1); arr4[3] = ss(2)
153153

154154

155155

156-
156+
# continuous-time systems
157157
G1 = ss(-9, [2 3], [4; 5], 0)
158158
G2 = ss(-6, 7, 8, 0)
159-
159+
G3 = ss(-1, 1, 1, 1) # Not strictly proper
160160
K1 = ss(-1, 1, 1, 0)
161161

162-
G3 = ss(-1, 1, 1, 1) # Not strictly proper
162+
# discrete-time systems
163+
G2d = ss(-6, 7, 8, 0, 1)
164+
G2d2 = ss(-6, 7, 8, 0, 0.5)
165+
K1d = ss(-1, 1, 1, 0, 1)
163166

164-
# Test general feedback interconnections
167+
# Basic feedback interconnections
168+
@test feedback(K1, ss(1.0)) == ss(-2, 1, 1, 0)
169+
@test feedback(K1, 1.0) == ss(-2, 1, 1, 0)
170+
@test feedback(K1d, ss(1.0, 1)) == ss(-2, 1, 1, 0, 1)
171+
@test_broken feedback(G2d, 1.0) == ss(-2, 1, 1, 0, 1)
165172

173+
# Check that errors for sample-time mismatc are thrown
174+
@test_throws ErrorException feedback(G2, K1d)
175+
@test_throws ErrorException feedback(G2d2, K1d) # Sample time mismatch
176+
177+
# Test general feedback interconnections
166178
@test feedback(G1, K1, U1=[1], Y1=[1], W1=[2], Z1=[2]) == ss([-9 -2; 4 -1], [3; 0], [5 0], 0)
167179
@test feedback(G1, K1, U1=[1], Y1=[1], W1=[1], Z1=[1]) == ss([-9 -2; 4 -1], [2; 0], [4 0], 0)
168180
@test feedback(G1, K1, U1=[2], Y1=[2], W1=[1], Z1=[1]) == ss([-9 -3; 5 -1], [2; 0], [4 0], 0)
169181

170182
@test feedback(K1, G1, W1=Int[], Z1=Int[], U2=[1], Y2=[1], W2=[2], Z2=[2]) == ss([-1 -4; 2 -9], [0; 3], [0 5], 0)
171183

184+
# Feedback with scalar
185+
@test feedback(G2, 1, pos_feedback=false) == ss(-62, 7, 8, 0)
186+
@test feedback(G2, 1, pos_feedback=true) == ss(50, 7, 8, 0)
187+
@test feedback(1, G2, pos_feedback=false) == ss(-62, 7, -8, 1)
188+
@test feedback(1, G2, pos_feedback=true) == ss(50, 7, 8, 1)
172189

173-
@test_broken feedback(G3, 1) == ss(-1.5, 2, , 0.5)
174-
190+
# Feedback with scalar (including direct term)
191+
@test feedback(G3, 0.5, pos_feedback=false) ss(-4/3, 2/3, 2/3, 2/3)
192+
@test feedback(G3, 0.5, pos_feedback=true) == ss(0, 2, 2, 2)
193+
@test feedback(0.5, G3, pos_feedback=false) ss(-4/3, 1/3, -1/3, 1/3)
194+
@test feedback(0.5, G3, pos_feedback=true) ss(0, 1, 1, 1)
175195

176-
feedback(G3, 1) == ss(-1.5, 2, )
196+
@test_broken feedback(G3, 1) == ss(-1.5, 0.5, 0.5, 0.5) # Old feedback method
197+
@test feedback(G3, 1, pos_feedback=false) == ss(-1.5, 0.5, 0.5, 0.5)
177198

199+
# Test that errors are thrown for mismatched dimensions
178200
@test_throws ErrorException feedback(G1, K1, U1=1:2, Y2=1)
201+
@test_throws ErrorException feedback(G1, K1, Y2=1:2)
179202

180-
#@test_warn feedback(G1, K1, U1=1:2, Y1=[2], Y2=[1, 1])
181-
#@test_logs feedback(G1, K1, U1=1:2, Y1=[2], Y2=[1, 1])
182-
183-
184-
185-
@test feedback(G1, K1, U1=[1], Y1=[1], W1=[2], Z1=[2], pos_feedback=true) == ss([-9 2; 4 -1], [3; 0], [5 0], 0)
186-
@test feedback(G2, 1, pos_feedback=true) == ss(50, 7, 8, 0)
187-
188-
189-
feedback(G1, K1, U1=[1], Y1=[1], W1=[2], Z1=[2], pos_feedback=true)
190-
191-
# Linear fractional transformations
192203

204+
# Tests of linear fractional transformations (LFTs)
193205
@test lft(G1, G2) == ss([-9 24; 35 -6], [2; 0], [4 0], 0)
194206
@test lft(G1, G2, :l) == ss([-9 24; 35 -6], [2; 0], [4 0], 0)
195207
@test lft(G1, G2, :u) == ss([-9 16; 28 -6], [3; 0], [5 0], 0)
@@ -199,12 +211,11 @@ feedback(G1, K1, U1=[1], Y1=[1], W1=[2], Z1=[2], pos_feedback=true)
199211
@test_throws ErrorException lft(G2, G1, :u)
200212
@test_throws ErrorException lft(G1, G2, :x) # Invalid type of lft
201213

214+
# Redheffer star product
202215
G4 = ss(-6, [7 8], [11; 12], 0)
203216
@test starprod(G1, G4, 1, 1) == ss([-9 33; 35 -6], [2 0; 0 8], [4 0; 0 12], zeros(2,2))
204217

205-
206-
#FIXME: Add more tests
207-
218+
#FIXME: Add more tests for feedback interconnections
208219

209220
@test_broken [D_111 1.0] # Concatenation of discrete system with constant
210221
@test_broken [D_222 fill(1.5, 2, 2)] # Concatenation of discrete system with matrix

test/test_demo_systems.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
@testset "test_demosystems" begin
22

3-
43
# Just some very simple tests of systemdepot
5-
@test size(systemdepot("woodberry")) == (2,2)
6-
@test size(systemdepot("fotd")) == (1,1)
7-
@test size(systemdepot("fotd")) == (1,1)
8-
@test freqresp(systemdepot("fotd", τ=2, T=5), [0.0])[:] == [1.0]
4+
@test size(DemoSystems.woodberry()) == (2,2)
5+
@test size(DemoSystems.fotd()) == (1,1)
6+
@test freqresp(DemoSystems.fotd=2, T=5), [0.0])[:] == [1.0]
97

108

119
Random.seed!(10)

0 commit comments

Comments
 (0)