Skip to content

Commit d32b77c

Browse files
committed
improvements to ESS partitioning
1 parent e95213e commit d32b77c

File tree

3 files changed

+70
-23
lines changed

3 files changed

+70
-23
lines changed

src/ExtendedStateSpace.jl

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,15 @@ Partition `P` into an [`ExtendedStateSpace`](@ref).
654654
"""
655655
function partition(P::AbstractStateSpace; u=nothing, y=nothing,
656656
w = nothing,
657-
z = nothing
657+
z = nothing,
658+
B1 = nothing,
659+
B2 = nothing,
660+
C1 = nothing,
661+
C2 = nothing,
662+
D11 = nothing,
663+
D12 = nothing,
664+
D21 = nothing,
665+
D22 = nothing,
658666
)
659667
if w === nothing
660668
w = setdiff(1:P.nu, u)
@@ -672,8 +680,16 @@ function partition(P::AbstractStateSpace; u=nothing, y=nothing,
672680
y = vcat(y)
673681
z = vcat(z)
674682
w = vcat(w)
675-
ss(P.A, P.B[:, w], P.B[:, u], P.C[z, :], P.C[y, :],
676-
P.D[z, w], P.D[z, u], P.D[y, w], P.D[y, u], P.timeevol)
683+
ss(P.A,
684+
B1 === nothing ? P.B[:, w] : B1,
685+
B2 === nothing ? P.B[:, u] : B2,
686+
C1 === nothing ? P.C[z, :] : C1,
687+
C2 === nothing ? P.C[y, :] : C2 ,
688+
D11 === nothing ? P.D[z, w] : D11,
689+
D12 === nothing ? P.D[z, u] : D12,
690+
D21 === nothing ? P.D[y, w] : D21,
691+
D22 === nothing ? P.D[y, u] : D22,
692+
P.timeevol)
677693
end
678694

679695
"""

src/lqg.jl

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -268,13 +268,13 @@ end
268268
"""
269269
extended_controller(l::LQGProblem, L = lqr(l), K = kalman(l))
270270
271-
Returns an expression for the controller that is obtained when state-feedback `u = -L(xᵣ-x̂)` is combined with a Kalman filter with gain `K` that produces state estimates x̂. The controller is an instance of `ExtendedStateSpace` where `C2 = -L, D21 = L` and `B2 = K`.
271+
Returns an expression for the controller that is obtained when state-feedback `u = L(xᵣ-x̂)` is combined with a Kalman filter with gain `K` that produces state estimates x̂. The controller is an instance of `ExtendedStateSpace` where `C2 = -L, D21 = L` and `B2 = K`.
272272
273-
The returned system has *inputs* `[xᵣ; y]` and outputs the control signal `u`. If a reference model `R` is used to generate state references `xᵣ`, the controller from `e = ry - y -> u` is given by
273+
The returned system has *inputs* `[xᵣ; y]` and outputs the control signal `u`. If a reference model `R` is used to generate state references `xᵣ`, the controller from `(ry, y) -> u` where `ry - y = e` is given by
274274
```julia
275275
Ce = extended_controller(l)
276-
Ce = named_ss(Ce; x = :xC, y = :u, u = [R.y; :y^l.ny]) # Name the inputs of Ce the same as the outputs of `R`.
277-
connect([R, Ce]; u1 = R.y, y1 = R.y, w1 = [:ry^l.ny, :y^l.ny], z1=[:u])
276+
Ce = named_ss(ss(Ce); x = :xC, y = :u, u = [R.y; :y^l.ny]) # Name the inputs of Ce the same as the outputs of `R`.
277+
connect([R, Ce]; u1 = R.y, y1 = R.y, w1 = [R.u; :y^l.ny], z1=[:u])
278278
```
279279
280280
Since the negative part of the feedback is built into the returned system, we have
@@ -283,9 +283,12 @@ C = observer_controller(l)
283283
Ce = extended_controller(l)
284284
system_mapping(Ce) == -C
285285
```
286+
287+
Please note, without the reference pre-filter, the DC gain from references to controlled outputs may not be identity.
286288
"""
287289
function extended_controller(l::LQGProblem, L = lqr(l), K = kalman(l))
288-
A,B,C,D = ssdata(system_mapping(l))
290+
P = system_mapping(l)
291+
A,B,C,D = ssdata(P)
289292
Ac = A - B*L - K*C + K*D*L # 8.26b
290293
nx = l.nx
291294
B1 = zeros(nx, nx) # dynamics not affected by r
@@ -341,21 +344,28 @@ Return the feedforward controller ``C_{ff}`` that maps references to plant input
341344
342345
See also [`observer_controller`](@ref).
343346
"""
344-
function ff_controller(l::LQGProblem, L = lqr(l), K = kalman(l))
347+
function ff_controller(l::LQGProblem, L = lqr(l), K = kalman(l); comp_dc = true)
345348
Ae,Be,Ce,De = ssdata(system_mapping(l, identity))
346349
Ac = Ae - Be*L - K*Ce + K*De*L # 8.26c
347-
Bc = Be*static_gain_compensation(l, L)
348350
Cc = L
349351
Dc = 0
350-
return 1 - ss(Ac, Bc, Cc, Dc, l.timeevol)
352+
if comp_dc
353+
Lr = static_gain_compensation(l, L)
354+
Bc = Be*Lr
355+
return Lr - ss(Ac, Bc, Cc, Dc, l.timeevol)
356+
else
357+
Bc = Be
358+
return I(size(Cc, 1)) - ss(Ac, Bc, Cc, Dc, l.timeevol)
359+
end
360+
end
351361
end
352362

353363
"""
354364
closedloop(l::LQGProblem, L = lqr(l), K = kalman(l))
355365
356366
Closed-loop system as defined in Glad and Ljung eq. 8.28. Note, this definition of closed loop is not the same as lft(P, K), which has B1 instead of B2 as input matrix. Use `lft(l)` to get the system from disturbances to controlled variables `w -> z`.
357367
358-
The return value will be the closed loop from reference only, other disturbance signals (B1) are ignored. See [`feedback`](@ref) for a more advanced option.
368+
The return value will be the closed loop from filtred reference only, other disturbance signals (B1) are ignored. See [`feedback`](@ref) for a more advanced option. This function assumes that the control signal is computed as `u = r̃ - Lx̂` (not `u = L(xᵣ - x̂)`), i.e., the feedforward signal `r̃` is added directly to the plant input. `r̃` must thus be produced by an inverse-like model that takes state references and output the feedforward signal.
359369
360370
Use `static_gain_compensation` to adjust the gain from references acting on the input B2, `dcgain(closedloop(l))*static_gain_compensation(l) ≈ I`
361371
"""

src/named_systems2.jl

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -697,30 +697,51 @@ end
697697

698698
function partition(P::NamedStateSpace; u=nothing, y=nothing,
699699
w = nothing,
700-
z = nothing
700+
z = nothing,
701+
B1 = nothing,
702+
B2 = nothing,
703+
C1 = nothing,
704+
C2 = nothing,
705+
D11 = nothing,
706+
D12 = nothing,
707+
D21 = nothing,
708+
D22 = nothing,
701709
)
702710
if w === nothing
703-
w = names2indices(setdiff(P.u, u), P.u)
704-
u = names2indices(u, P.u)
711+
inds = names2indices(u, P.u)
712+
w = setdiff(1:P.nu, inds)
713+
u = inds
705714
end
706715
if z === nothing
707-
z = names2indices(setdiff(P.y, y), P.y)
708-
y = names2indices(y, P.y)
716+
inds = names2indices(y, P.y)
717+
z = setdiff(1:P.ny, inds)
718+
y = inds
709719
end
710720
if u === nothing
711-
u = names2indices(setdiff(P.u, w), P.u)
712-
w = names2indices(w, P.u)
721+
inds = names2indices(w, P.u)
722+
u = setdiff(1:P.nu, inds)
723+
w = inds
713724
end
714725
if y === nothing
715-
y = names2indices(setdiff(P.y, z), P.y)
716-
z = names2indices(z, P.y)
726+
inds = names2indices(z, P.y)
727+
y = setdiff(1:P.ny, inds)
728+
z = inds
717729
end
718730
u = vcat(u)
719731
y = vcat(y)
720732
z = vcat(z)
721733
w = vcat(w)
722-
ss(P.A, P.B[:, w], P.B[:, u], P.C[z, :], P.C[y, :],
723-
P.D[z, w], P.D[z, u], P.D[y, w], P.D[y, u], P.timeevol)
734+
ss(P.A,
735+
B1 === nothing ? P.B[:, w] : B1,
736+
B2 === nothing ? P.B[:, u] : B2,
737+
C1 === nothing ? P.C[z, :] : C1,
738+
C2 === nothing ? P.C[y, :] : C2 ,
739+
D11 === nothing ? P.D[z, w] : D11,
740+
D12 === nothing ? P.D[z, u] : D12,
741+
D21 === nothing ? P.D[y, w] : D21,
742+
D22 === nothing ? P.D[y, u] : D22,
743+
P.timeevol
744+
)
724745
end
725746

726747
function CS.c2d(s::NamedStateSpace{Continuous}, Ts::Real, method::Symbol = :zoh, args...;

0 commit comments

Comments
 (0)