Skip to content

Commit 65fdce2

Browse files
committed
added unmeasured disturbances at input for all StateEstimator
1 parent 2470717 commit 65fdce2

File tree

7 files changed

+132
-103
lines changed

7 files changed

+132
-103
lines changed

docs/src/internals/state_estim.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Estimator Initialization
44

55
```@docs
6+
ModelPredictiveControl.init_estimstoch
67
ModelPredictiveControl.init_integrators
78
ModelPredictiveControl.augment_model
89
ModelPredictiveControl.init_ukf

src/estimator/kalman.jl

Lines changed: 94 additions & 63 deletions
Large diffs are not rendered by default.

src/estimator/luenberger.jl

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ struct Luenberger <: StateEstimator
2020
Ĉm ::Matrix{Float64}
2121
D̂dm ::Matrix{Float64}
2222
K::Matrix{Float64}
23-
function Luenberger(model, i_ym, nint_ym, p̂)
24-
nym, nyu, nxs, nx̂, As, Cs, nint_ym = init_estimstoch(model, i_ym, nint_ym)
25-
Â, B̂u, Ĉ, B̂d, D̂d = augment_model(model, As, Cs)
23+
function Luenberger(model, i_ym, nint_u, nint_ym, p̂)
24+
nym, nyu = validate_ym(model, i_ym)
25+
As, Cs_u, Cs_y, nxs, nint_u, nint_ym = init_estimstoch(model, i_ym, nint_u, nint_ym)
26+
nx̂ = model.nx + nxs
27+
Â, B̂u, Ĉ, B̂d, D̂d = augment_model(model, As, Cs_u, Cs_y)
2628
K = try
2729
place(Â, Ĉ, p̂, :o)[:, i_ym]
2830
catch
@@ -35,7 +37,7 @@ struct Luenberger <: StateEstimator
3537
model,
3638
lastu0, x̂,
3739
i_ym, nx̂, nym, nyu, nxs,
38-
As, Cs, nint_ym,
40+
As, Cs_u, Cs_y, nint_u, nint_ym,
3941
Â, B̂u, B̂d, Ĉ, D̂d,
4042
Ĉm, D̂dm,
4143
K
@@ -47,6 +49,7 @@ end
4749
Luenberger(
4850
model::LinModel;
4951
i_ym = 1:model.ny,
52+
nint_u = 0,
5053
nint_ym = default_nint(model, i_ym),
5154
p̂ = 1e-3*(0:(model.nx + sum(nint_ym)-1)) .+ 0.5)
5255
)
@@ -55,7 +58,7 @@ Construct a Luenberger observer with the [`LinModel`](@ref) `model`.
5558
5659
`i_ym` provides the `model` output indices that are measured ``\mathbf{y^m}``, the rest are
5760
unmeasured ``\mathbf{y^u}``. `model` matrices are augmented with the stochastic model, which
58-
is specified by the numbers of output integrator `nint_ym` (see [`SteadyKalmanFilter`](@ref)
61+
is specified by the numbers of integrator `nint_u` and `nint_ym` (see [`SteadyKalmanFilter`](@ref)
5962
Extended Help). The argument `p̂` is a vector of `model.nx + sum(nint_ym)` elements
6063
specifying the observer poles/eigenvalues (near ``z=0.5`` by default). The method computes
6164
the observer gain ``\mathbf{K}`` with [`place`](https://juliacontrol.github.io/ControlSystems.jl/stable/lib/synthesis/#ControlSystemsBase.place).
@@ -76,15 +79,16 @@ Luenberger estimator with a sample time Ts = 0.5 s, LinModel and:
7679
function Luenberger(
7780
model::LinModel;
7881
i_ym::IntRangeOrVector = 1:model.ny,
79-
nint_ym::IntVectorOrInt = default_nint(model, i_ym),
82+
nint_u ::IntVectorOrInt = 0,
83+
nint_ym::IntVectorOrInt = default_nint(model, i_ym, nint_u),
8084
= 1e-3*(0:(model.nx + sum(nint_ym)-1)) .+ 0.5
8185
)
8286
nx = model.nx
8387
if length(p̂) model.nx + sum(nint_ym)
8488
error("p̂ length ($(length(p̂))) ≠ nx ($nx) + integrator quantity ($(sum(nint_ym)))")
8589
end
8690
any(abs.(p̂) .≥ 1) && error("Observer poles p̂ should be inside the unit circles.")
87-
return Luenberger(model, i_ym, nint_ym, p̂)
91+
return Luenberger(model, i_ym, nint_u, nint_ym, p̂)
8892
end
8993

9094

src/predictive_control.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -913,14 +913,14 @@ with [`getestimates!`](@ref). The method also returns an empty ``\mathbf{P_s}``
913913
it is useless except for [`InternalModel`](@ref) estimators.
914914
"""
915915
function init_stochpred(estim::StateEstimator, Hp)
916-
As, Cs = estim.As, estim.Cs
916+
As, Cs_y = estim.As, estim.Cs_y
917917
nxs = estim.nxs
918918
Ms = Matrix{Float64}(undef, Hp*nxs, nxs)
919919
for i = 1:Hp
920920
iRow = (1:nxs) .+ nxs*(i-1)
921921
Ms[iRow, :] = As^i
922922
end
923-
Js = repeatdiag(Cs, Hp)
923+
Js = repeatdiag(Cs_y, Hp)
924924
Ks = Js*Ms
925925
Ps = zeros(estim.model.ny*Hp, 0)
926926
return Ks, Ps

src/state_estim.jl

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -70,38 +70,31 @@ function remove_op!(estim::StateEstimator, u, d, ym)
7070
end
7171

7272
"""
73-
init_estimstoch(model, i_ym, nint_ym)
73+
init_estimstoch(model, i_ym, nint_u, nint_ym)
7474
7575
Init stochastic model matrices from integrator specifications for state estimation.
7676
"""
77-
function init_estimstoch(model, i_ym, nint_ym; nint_u=0)
78-
validate_ym(model, i_ym)
79-
nu, nx, ny = model.nu, model.nx, model.ny
80-
nym, nyu = length(i_ym), ny - length(i_ym)
77+
function init_estimstoch(model, i_ym, nint_u, nint_ym)
78+
nu, ny, nym = model.nu, model.ny, length(i_ym)
8179
As_u , Cs_u , nint_u = init_integrators(nint_u , nu , "u")
8280
As_ym, Cs_ym, nint_ym = init_integrators(nint_ym, nym, "ym")
8381
As_y, _ , Cs_y = stoch_ym2y(model, i_ym, As_ym, [], Cs_ym, [])
8482
nxs_u, nxs_y = size(As_u, 1), size(As_y, 1)
8583
# combines input and output stochastic models:
86-
As = [As_u zeros(nxs_u, nxs_y); zeros(nxs_y, nxs_u) As_y]
84+
As = [As_u zeros(nxs_u, nxs_y); zeros(nxs_y, nxs_u) As_y]
8785
Cs_u = [Cs_u zeros(nu, nxs_y)]
8886
Cs_y = [zeros(ny, nxs_u) Cs_y]
89-
90-
#Bs = B*Ks (nx × nxs)
91-
#Cs_u # nu x nxs_u
92-
#B # nx x nu
93-
#K_res = B*Cs_u # (nx × nu) * (nu × nxs_u) = (nx × nxs_u)
94-
9587
nxs = nxs_u + nxs_y
96-
nx̂ = nx + nxs
97-
return nym, nyu, nx̂, nxs, As, Cs_u, Cs_y, nint_u, nint_ym
88+
return As, Cs_u, Cs_y, nxs, nint_u, nint_ym
9889
end
9990

10091
"Validate the specified measured output indices `i_ym`."
10192
function validate_ym(model::SimModel, i_ym)
10293
if length(unique(i_ym)) length(i_ym) || maximum(i_ym) > model.ny
10394
error("Measured output indices i_ym should contains valid and unique indices")
10495
end
96+
nym, nyu = length(i_ym), model.ny - length(i_ym)
97+
return nym, nyu
10598
end
10699

107100
"Convert the measured outputs stochastic model `stoch_ym` to all outputs `stoch_y`."
@@ -139,7 +132,7 @@ where ``\mathbf{e}(k)`` is an unknown zero mean white noise. The estimations doe
139132
1. for the unmodeled disturbances at measured outputs ``\mathbf{y^m}``
140133
2. for the unmodeled disturbances at manipulated inputs ``\mathbf{u}``
141134
"""
142-
function init_integrators(nint::Vector{Int}, nys, varname::String)
135+
function init_integrators(nint::IntVectorOrInt, nys, varname::String)
143136
if nint == 0 # alias for no integrator at all
144137
nint = fill(0, nys)
145138
end
@@ -192,16 +185,17 @@ function augment_model(model::LinModel, As, Cs_u, Cs_y; verify_obsv=true)
192185
D̂d = model.Dd
193186
# observability on Ĉ instead of Ĉm, since it would always return false when nym ≠ ny:
194187
if verify_obsv && !observability(Â, Ĉ)[:isobservable]
195-
error("The augmented model is unobservable. You may try to use 0 "*
196-
"integrator on model integrating outputs with nint_ym parameter.")
188+
error("The augmented model is unobservable. You may try to use 0 integrator on "*
189+
"model integrating outputs with nint_ym parameter. Adding integrators at both "*
190+
"inputs (nint_u) and outputs (nint_ym) can also violate observability.")
197191
end
198192
return Â, B̂u, Ĉ, B̂d, D̂d
199193
end
200194
"No need to augment the model if `model` is not a [`LinModel`](@ref)."
201-
augment_model(::SimModel, _ , _ ) = nothing
195+
augment_model(::SimModel, _ , _ , _ ) = nothing
202196

203197
@doc raw"""
204-
default_nint(model::LinModel, i_ym=1:model.ny)
198+
default_nint(model::LinModel, i_ym=1:model.ny, nint_u)
205199
206200
Get default integrator quantity per measured outputs `nint_ym` for [`LinModel`](@ref).
207201
@@ -221,24 +215,27 @@ julia> nint_ym = default_nint(model)
221215
1
222216
```
223217
"""
224-
function default_nint(model::LinModel, i_ym::IntRangeOrVector = 1:model.ny)
218+
function default_nint(model::LinModel, i_ym=1:model.ny, nint_u=0)
219+
validate_ym(model, i_ym)
225220
nint_ym = fill(0, length(i_ym))
226221
for i in eachindex(i_ym)
227222
nint_ym[i] = 1
228-
Asm, Csm = init_integrators(i_ym, nint_ym)
229-
As , _ , Cs = stoch_ym2y(model, i_ym, Asm, [], Csm, [])
230-
 , _ , Ĉ = augment_model(model, As, Cs, verify_obsv=false)
223+
As, Cs_u, Cs_y = init_estimstoch(model, i_ym, nint_u, nint_ym)
224+
Â, _ , Ĉ = augment_model(model, As, Cs_u, Cs_y, verify_obsv=false)
231225
# observability on Ĉ instead of Ĉm, since it would always return false when nym ≠ ny
232226
observability(Â, Ĉ)[:isobservable] || (nint_ym[i] = 0)
233227
end
234228
return nint_ym
235229
end
236230
"""
237-
default_nint(model::SimModel, i_ym=1:model.ny)
231+
default_nint(model::SimModel, i_ym=1:model.ny, nint_u=0)
238232
239233
One integrator on each measured output by default if `model` is not a [`LinModel`](@ref).
240234
"""
241-
default_nint(::SimModel, i_ym::IntRangeOrVector = 1:model.ny) = fill(1, length(i_ym))
235+
function default_nint(model::SimModel, i_ym=1:model.ny, nint_u=0)
236+
validate_ym(model, i_ym)
237+
return fill(1, length(i_ym))
238+
end
242239

243240
@doc raw"""
244241
f̂(estim::StateEstimator, x̂, u, d)

src/yo.md

Lines changed: 0 additions & 6 deletions
This file was deleted.

test/test_state_estim.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ sys = [ tf(1.90,[18.0,1]) tf(1.90,[18.0,1]) tf(1.90,[18.0,1]);
4545
@test_throws ErrorException SteadyKalmanFilter(linmodel3, nint_ym=[1, 0, 0])
4646
model_unobs = LinModel([1 0;0 1.5], [1;0][:,:], [1 0], zeros(2,0), zeros(1,0),1,1,2,1,0)
4747
@test_throws ErrorException SteadyKalmanFilter(model_unobs, nint_ym=[1])
48+
@test_throws ErrorException SteadyKalmanFilter(LinModel(tf(1, [1,0]), 1), nint_ym=[1])
49+
@test_throws ErrorException SteadyKalmanFilter(linmodel1, nint_u=[1,1], nint_ym=[1,1])
4850
end
4951

5052
@testset "SteadyKalmanFilter estimator methods" begin

0 commit comments

Comments
 (0)