Skip to content

Commit 6c5f770

Browse files
authored
Merge pull request #192 from JuliaControl/setstate_cov
added: `setstate!` now allows estimation covariance `P̂` modification (if applicable)
2 parents 4511057 + 1367a55 commit 6c5f770

File tree

8 files changed

+52
-14
lines changed

8 files changed

+52
-14
lines changed

.github/workflows/CI.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
- uses: julia-actions/julia-buildpkg@v1
4444
- uses: julia-actions/julia-runtest@v1
4545
- uses: julia-actions/julia-processcoverage@v1
46-
- uses: codecov/codecov-action@v4
46+
- uses: codecov/codecov-action@v5
4747
with:
4848
token: ${{ secrets.CODECOV_TOKEN }}
4949
fail_ci_if_error: false

src/controller/execute.jl

+3-4
Original file line numberDiff line numberDiff line change
@@ -488,12 +488,11 @@ Call `periodsleep(mpc.estim.model)`.
488488
periodsleep(mpc::PredictiveController, busywait=false) = periodsleep(mpc.estim.model, busywait)
489489

490490
"""
491-
setstate!(mpc::PredictiveController, x̂) -> mpc
491+
setstate!(mpc::PredictiveController, x̂, P̂=nothing) -> mpc
492492
493-
Set `mpc.estim.x̂0` to `x̂ - estim.x̂op` from the argument `x̂`.
493+
Call [`setstate!`](@ref) on `mpc.estim` [`StateEstimator`](@ref).
494494
"""
495-
setstate!(mpc::PredictiveController, x̂) = (setstate!(mpc.estim, x̂); return mpc)
496-
495+
setstate!(mpc::PredictiveController, x̂, P̂=nothing) = (setstate!(mpc.estim, x̂, P̂); return mpc)
497496

498497
@doc raw"""
499498
setmodel!(mpc::PredictiveController, model=mpc.estim.model; <keyword arguments>) -> mpc

src/estimator/execute.jl

+16-3
Original file line numberDiff line numberDiff line change
@@ -330,16 +330,29 @@ function validate_args(estim::StateEstimator, ym, d, u=nothing)
330330
end
331331

332332
"""
333-
setstate!(estim::StateEstimator, x̂) -> estim
333+
setstate!(estim::StateEstimator, x̂, P̂=nothing) -> estim
334334
335-
Set `estim.x̂0` to `x̂ - estim.x̂op` from the argument `x̂`.
335+
Set `estim.x̂0` to `x̂ - estim.x̂op` from the argument `x̂`, and `estim.P̂` to `P̂` if applicable.
336+
337+
The covariance error estimate `P̂` can be set only if `estim` is a [`StateEstimator`](@ref)
338+
that computes it.
336339
"""
337-
function setstate!(estim::StateEstimator, x̂)
340+
function setstate!(estim::StateEstimator, x̂, P̂=nothing)
338341
size(x̂) == (estim.nx̂,) || error("x̂ size must be $((estim.nx̂,))")
339342
estim.x̂0 .=.- estim.x̂op
343+
setstate_cov!(estim, P̂)
340344
return estim
341345
end
342346

347+
"Set the covariance error estimate `estim.P̂` to `P̂`."
348+
function setstate_cov!(estim::StateEstimator, P̂)
349+
if !isnothing(P̂)
350+
size(P̂) == (estim.nx̂, estim.nx̂) || error("P̂ size must be $((estim.nx̂, estim.nx̂))")
351+
estim.P̂ .= to_hermitian(P̂)
352+
end
353+
return nothing
354+
end
355+
343356
@doc raw"""
344357
setmodel!(estim::StateEstimator, model=estim.model; <keyword arguments>) -> estim
345358

src/estimator/internal_model.jl

+6
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,12 @@ function init_internalmodel(As, Bs, Cs, Ds)
226226
return Âs, B̂s
227227
end
228228

229+
"Throw an error if P̂ != nothing."
230+
function setstate_cov!(estim::InternalModel, P̂)
231+
== nothing || error("InternalModel does not compute an estimation covariance matrix P̂.")
232+
return nothing
233+
end
234+
229235
"Update similar values for [`InternalModel`](@ref) estimator."
230236
function setmodel_estimator!(estim::InternalModel, model, _ , _ , _ , _ , _ )
231237
Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op = matrices_internalmodel(model)

src/estimator/luenberger.jl

+6
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ function update_estimate!(estim::Luenberger, y0m, d0, u0)
136136
return predict_estimate_obsv!(estim, y0m, d0, u0)
137137
end
138138

139+
"Throw an error if P̂ != nothing."
140+
function setstate_cov!(estim::Luenberger, P̂)
141+
== nothing || error("Luenberger does not compute an estimation covariance matrix P̂.")
142+
return nothing
143+
end
144+
139145
"Throw an error if `setmodel!` is called on `Luenberger` observer w/o the default values."
140146
function setmodel_estimator!(estim::Luenberger, model, args...)
141147
if estim.model !== model

src/estimator/mhe/execute.jl

+6
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,12 @@ end
650650
"No nonlinear constraints if `model` is a [`LinModel`](@ref), return `g` unchanged."
651651
con_nonlinprog!(g, ::MovingHorizonEstimator, ::LinModel, _ , _ , _ ) = g
652652

653+
"Throw an error if P̂ != nothing."
654+
function setstate_cov!(estim::MovingHorizonEstimator, P̂)
655+
== nothing || error("MovingHorizonEstimator does not compute an estimation covariance matrix P̂.")
656+
return nothing
657+
end
658+
653659
"Update the augmented model, prediction matrices, constrains and data windows for MHE."
654660
function setmodel_estimator!(
655661
estim::MovingHorizonEstimator, model, uop_old, yop_old, dop_old, Q̂, R̂

test/2_test_state_estim.jl

+10-3
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ end
115115
@test [0, 0]
116116
@test isa(x̂, Vector{Float32})
117117
@test_throws ArgumentError updatestate!(skalmanfilter1, [10, 50])
118+
@test_throws ErrorException setstate!(skalmanfilter1, [1,2,3,4], diagm(.1:.1:.4))
118119
end
119120

120121
@testitem "SteadyKalmanFilter set model" setup=[SetupMPCtests] begin
@@ -211,8 +212,9 @@ end
211212
@test evaloutput(kalmanfilter1, d) kalmanfilter1(d) [50, 30]
212213
@test_skip @allocations(evaloutput(kalmanfilter1, d)) == 0
213214
@test initstate!(kalmanfilter1, [10, 50], [50, 30+1]) [zeros(3); [1]]
214-
setstate!(kalmanfilter1, [1,2,3,4])
215+
setstate!(kalmanfilter1, [1,2,3,4], diagm(.1:.1:.4))
215216
@test kalmanfilter1.x̂0 [1,2,3,4]
217+
@test kalmanfilter1. diagm(.1:.1:.4)
216218
for i in 1:40
217219
preparestate!(kalmanfilter1, [50, 30])
218220
updatestate!(kalmanfilter1, [11, 52], [50, 30])
@@ -363,6 +365,7 @@ end
363365
= updatestate!(lo3, [0], [0])
364366
@test [0, 0]
365367
@test isa(x̂, Vector{Float32})
368+
@test_throws ErrorException setstate!(lo1, [1,2,3,4], diagm(.1:.1:.4))
366369
end
367370

368371
@testitem "Luenberger set model" setup=[SetupMPCtests] begin
@@ -484,6 +487,7 @@ end
484487
= updatestate!(internalmodel3, [0], [0])
485488
@test [0]
486489
@test isa(x̂, Vector{Float32})
490+
@test_throws ErrorException setstate!(internalmodel1, [1,2,3,4], diagm(.1:.1:.4))
487491
end
488492

489493
@testitem "InternalModel set model" setup=[SetupMPCtests] begin
@@ -598,8 +602,9 @@ end
598602
@test evaloutput(ukf1, d) ukf1(d) [50, 30]
599603
@test_skip @allocations(evaloutput(ukf1, d)) == 0
600604
@test initstate!(ukf1, [10, 50], [50, 30+1]) zeros(4) atol=1e-9
601-
setstate!(ukf1, [1,2,3,4])
605+
setstate!(ukf1, [1,2,3,4], diagm(.1:.1:.4))
602606
@test ukf1.x̂0 [1,2,3,4]
607+
@test ukf1. diagm(.1:.1:.4)
603608
for i in 1:40
604609
preparestate!(ukf1, [50, 30])
605610
updatestate!(ukf1, [11, 52], [50, 30])
@@ -757,8 +762,9 @@ end
757762
@test evaloutput(ekf1, d) ekf1(d) [50, 30]
758763
@test_skip @allocations(evaloutput(ekf1, d)) == 0
759764
@test initstate!(ekf1, [10, 50], [50, 30+1]) zeros(4);
760-
setstate!(ekf1, [1,2,3,4])
765+
setstate!(ekf1, [1,2,3,4], diagm(.1:.1:.4))
761766
@test ekf1.x̂0 [1,2,3,4]
767+
@test ekf1. diagm(.1:.1:.4)
762768
for i in 1:40
763769
preparestate!(ekf1, [50, 30])
764770
updatestate!(ekf1, [11, 52], [50, 30])
@@ -1055,6 +1061,7 @@ end
10551061
= preparestate!(mhe6, [50, 30], [5])
10561062
@test zeros(6) atol=1e-9
10571063
@test_nowarn ModelPredictiveControl.info2debugstr(info)
1064+
@test_throws ErrorException setstate!(mhe1, [1,2,3,4,5,6], diagm(.1:.1:.6))
10581065
end
10591066

10601067
@testitem "MovingHorizonEstimator fallbacks for arrival covariance estimation" setup=[SetupMPCtests] begin

test/3_test_predictive_control.jl

+4-3
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,12 @@ end
163163
@testitem "LinMPC other methods" setup=[SetupMPCtests] begin
164164
using .SetupMPCtests, ControlSystemsBase, LinearAlgebra
165165
linmodel1 = setop!(LinModel(sys,Ts,i_u=[1,2]), uop=[10,50], yop=[50,30])
166-
mpc1 = LinMPC(linmodel1)
166+
mpc1 = LinMPC(KalmanFilter(linmodel1))
167167
@test initstate!(mpc1, [10, 50], [50, 30+1]) [zeros(3); [1]]
168-
setstate!(mpc1, [1,2,3,4])
168+
setstate!(mpc1, [1,2,3,4], diagm(.1:.1:.4))
169169
@test mpc1.estim.x̂0 [1,2,3,4]
170-
setstate!(mpc1, [0,0,0,0])
170+
@test mpc1.estim. diagm(.1:.1:.4)
171+
setstate!(mpc1, [0,0,0,0], mpc1.estim.P̂_0)
171172
preparestate!(mpc1, [50, 30])
172173
updatestate!(mpc1, mpc1.estim.model.uop, [50, 30])
173174
@test mpc1.estim.x̂0 [0,0,0,0]

0 commit comments

Comments
 (0)