Skip to content

Commit 2d79583

Browse files
committed
added: bumpless transfer for all controller based on LinModel
It supports all `StateEstimator`s including `InternalModel` (new feature).
1 parent 319ef9a commit 2d79583

File tree

6 files changed

+65
-27
lines changed

6 files changed

+65
-27
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ for more detailed examples.
118118
- [x] easily estimate unmeasured disturbances by adding one or more integrators at the:
119119
- [x] manipulated inputs
120120
- [x] measured outputs
121+
- [x] bumpless manual to automatic transfer for control with a proper intial estimate
121122
- [x] observers in predictor form to ease control applications
122123
- [ ] moving horizon estimator that supports:
123124
- [ ] inequality state constraints

src/estimator/internal_model.jl

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,36 @@ function update_estimate!(estim::InternalModel, u, ym, d=empty(estim.x̂))
208208
ŷd = h(model, x̂d, d)
209209
x̂d[:] = f(model, x̂d, u, d) # this also updates estim.x̂ (they are the same object)
210210
# --------------- stochastic model -----------------------
211-
ŷs = zeros(model.ny,1)
211+
ŷs = zeros(model.ny)
212212
ŷs[estim.i_ym] = ym - ŷd[estim.i_ym] # ŷs=0 for unmeasured outputs
213213
x̂s[:] = estim.Âs*x̂s + estim.B̂s*ŷs
214214
return x̂d
215215
end
216216

217-
"Init the stochastic states `estim.x̂s` of [`InternalModel`](@ref) at 0. "
218-
function initstate_post!(estim::InternalModel)
219-
# TODO: best method to initialize internal model stochastic states ? not sure...
220-
estim.x̂s[:] = zeros(estim.nxs)
217+
@doc raw"""
218+
init_estimate!(estim::InternalModel, ::LinModel, u, ym, d)
219+
220+
Init `estim.x̂` \ `x̂d` \ `x̂s` estimate at steady-state for [`InternalModel`](@ref)s.
221+
222+
The deterministic estimates `estim.x̂d` start at steady-state using `u` and `d` arguments:
223+
```math
224+
\mathbf{x̂_d} = \mathbf{(I-Â)^{-1} B̂_u u}
225+
```
226+
Based on `ym` argument and current stochastic outputs estimation ``\mathbf{ŷ_s}``, composed
227+
of the measured ``\mathbf{ŷ_s^m} = \mathbf{y^m} - \mathbf{ŷ_d^m}`` and unmeasured
228+
``\mathbf{ŷ_s^u = 0}`` outputs, the stochastic estimates also start at steady-state:
229+
```math
230+
\mathbf{x̂_s} = \mathbf{(I - Â_s)^{-1} B̂_s ŷ_s}
231+
```
232+
See [`init_internalmodel`](@ref) for details.
233+
"""
234+
function init_estimate!(estim::InternalModel, ::LinModel, u, ym, d)
235+
x̂d, x̂s = estim.x̂d, estim.x̂s
236+
x̂d[:] = (I - estim.Â)\(estim.B̂u*u + estim.B̂d*d)
237+
ŷd = h(estim.model, x̂d, d)
238+
ŷs = zeros(estim.model.ny)
239+
ŷs[estim.i_ym] = ym - ŷd[estim.i_ym] # ŷs=0 for unmeasured outputs
240+
x̂s[:] = (I-estim.Âs)\estim.B̂s*ŷs
221241
return nothing
222242
end
223243

@@ -232,5 +252,3 @@ function print_estim_dim(io::IO, estim::InternalModel, n)
232252
print(io, "$(lpad(nd, n)) measured disturbances d")
233253
end
234254

235-
(estim::InternalModel)(ym, d=empty(estim.x̂)) = evaloutput(estim::InternalModel, ym, d)
236-

src/estimator/kalman.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -724,11 +724,11 @@ function update_estimate!(estim::ExtendedKalmanFilter, u, ym, d=empty(estim.x̂)
724724
return update_estimate_kf!(estim, F̂, Ĥm, u, ym, d)
725725
end
726726

727-
"Initialize the covariance estimate `P̂` for the time-varying Kalman Filters"
728-
function initstate_post!(
729-
estim::Union{KalmanFilter, UnscentedKalmanFilter, ExtendedKalmanFilter}
727+
"Set `estim.P̂` to `estim.P̂0` for the time-varying Kalman Filters."
728+
function init_estimate_cov!(
729+
estim::Union{KalmanFilter, UnscentedKalmanFilter, ExtendedKalmanFilter}, _ , _ , _
730730
)
731-
estim..data[:] = estim.P̂0
731+
estim..data[:] = estim.P̂0 # .data is necessary for Hermitians
732732
return nothing
733733
end
734734

src/state_estim.jl

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,19 @@ function print_estim_dim(io::IO, estim::StateEstimator, n)
5454
end
5555

5656
"""
57-
remove_op!(estim::StateEstimator, u, d, ym) -> u0, d0, ym0
57+
remove_op!(estim::StateEstimator, u, ym, d) -> u0, ym0, d0
5858
5959
Remove operating points on inputs `u`, measured outputs `ym` and disturbances `d`.
6060
6161
Also store current inputs without operating points `u0` in `estim.lastu0`. This field is
6262
used for [`PredictiveController`](@ref) computations.
6363
"""
64-
function remove_op!(estim::StateEstimator, u, d, ym)
64+
function remove_op!(estim::StateEstimator, u, ym, d)
6565
u0 = u - estim.model.uop
66-
d0 = d - estim.model.dop
6766
ym0 = ym - estim.model.yop[estim.i_ym]
67+
d0 = d - estim.model.dop
6868
estim.lastu0[:] = u0
69-
return u0, d0, ym0
69+
return u0, ym0, d0
7070
end
7171

7272
@doc raw"""
@@ -302,7 +302,7 @@ removes the operating points with [`remove_op!`](@ref) and call [`init_estimate!
302302
bumpless manual to automatic transfer. See [`init_estimate!`](@ref) for details.
303303
- Else, `estim.x̂` is left unchanged. Use [`setstate!`](@ref) to manually modify it.
304304
305-
If applicable, it also sets the error covariance `estim.P̂` to ``\mathbf{P̂}(0)``.
305+
If applicable, it also sets the error covariance `estim.P̂` to `estim.P̂0`.
306306
307307
# Examples
308308
```jldoctest
@@ -324,15 +324,15 @@ true
324324
"""
325325
function initstate!(estim::StateEstimator, u, ym, d=empty(estim.x̂))
326326
# --- init state estimate ----
327-
u0, d0, ym0 = remove_op!(estim, u, d, ym)
327+
u0, ym0, d0 = remove_op!(estim, u, ym, d)
328328
init_estimate!(estim, estim.model, u0, ym0, d0)
329329
# --- init covariance error estimate, if applicable ---
330-
initstate_post!(estim)
330+
init_estimate_cov!(estim, u0, ym0, d0)
331331
return estim.
332332
end
333333

334-
"By default, do nothing at the end of `initstate!` (used to init the covariance estimate)."
335-
initstate_post!(::StateEstimator) = nothing
334+
"By default, [`StateEstimator`](@ref)s do not need covariance error estimate."
335+
init_estimate_cov!(::StateEstimator, _ , _ , _ ) = nothing
336336

337337
@doc raw"""
338338
init_estimate!(estim::StateEstimator, model::LinModel, u, ym, d)
@@ -347,7 +347,7 @@ constraint ``\mathbf{ŷ^m} = \mathbf{y^m}`` engenders the following system to s
347347
\mathbf{Ĉ^m}
348348
\end{bmatrix} \mathbf{x̂} =
349349
\begin{bmatrix}
350-
\mathbf{B̂_u u} + \mathbf{B̂_d d} \\
350+
\mathbf{B̂_u u} + \mathbf{B̂_d d} \\
351351
\mathbf{y^m} - \mathbf{D̂_d^m d}
352352
\end{bmatrix}
353353
```
@@ -358,8 +358,13 @@ function init_estimate!(estim::StateEstimator, ::LinModel, u, ym, d)
358358
Â, B̂u, Ĉ, B̂d, D̂d = estim.Â, estim.B̂u, estim.Ĉ, estim.B̂d, estim.D̂d
359359
Ĉm, D̂dm = Ĉ[estim.i_ym, :], D̂d[estim.i_ym, :] # measured outputs ym only
360360
estim.x̂[:] = [(I - Â); Ĉm]\[B̂u*u + B̂d*d; ym - D̂dm*d]
361+
return nothing
361362
end
362-
"Left `estim.x̂` estimate unchanged if `model` is not a [`LinModel`](@ref)."
363+
"""
364+
init_estimate!(::StateEstimator, ::SimModel, _ , _ , _ )
365+
366+
Left `estim.x̂` estimate unchanged if `model` is not a [`LinModel`](@ref).
367+
"""
363368
init_estimate!(::StateEstimator, ::SimModel, _ , _ , _ ) = nothing
364369

365370
@doc raw"""
@@ -405,7 +410,7 @@ julia> x̂ = updatestate!(kf, [1], [0]) # x̂[2] is the integrator state (nint_y
405410
```
406411
"""
407412
function updatestate!(estim::StateEstimator, u, ym, d=empty(estim.x̂))
408-
u0, d0, ym0 = remove_op!(estim, u, d, ym)
413+
u0, ym0, d0 = remove_op!(estim, u, ym, d)
409414
update_estimate!(estim, u0, ym0, d0)
410415
return estim.
411416
end

test/test_sim_model.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,18 @@ end
8080

8181
@testset "LinModel sim methods" begin
8282
linmodel1 = setop!(LinModel(Gss), uop=[10,50], yop=[50,30])
83-
84-
@test updatestate!(linmodel1, [10, 50]) zeros(2)
83+
@test updatestate!(linmodel1, [10, 50]) zeros(2)
8584
@test updatestate!(linmodel1, [10, 50], Float64[]) zeros(2)
8685
@test linmodel1.x zeros(2)
87-
@test evaloutput(linmodel1) linmodel1() [50,30]
86+
@test evaloutput(linmodel1) linmodel1() [50,30]
8887
@test evaloutput(linmodel1, Float64[]) linmodel1(Float64[]) [50,30]
89-
9088
x = initstate!(linmodel1, [10, 60])
9189
@test evaloutput(linmodel1) [50 + 19.0, 30 + 7.4]
9290
@test updatestate!(linmodel1, [10, 60]) x
91+
linmodel2 = LinModel(append(tf(1, [1, 0]), tf(2, [10, 1])), 1.0)
92+
x = initstate!(linmodel2, [10, 3])
93+
@test evaloutput(linmodel2) [0, 6]
94+
@test updatestate!(linmodel2, [0, 3]) x
9395

9496
@test_throws DimensionMismatch updatestate!(linmodel1, zeros(2), zeros(1))
9597
@test_throws DimensionMismatch evaloutput(linmodel1, zeros(1))

test/test_state_estim.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ end
6464
@test evaloutput(skalmanfilter1) skalmanfilter1() [50, 30]
6565
@test evaloutput(skalmanfilter1, Float64[]) skalmanfilter1(Float64[]) [50, 30]
6666
@test initstate!(skalmanfilter1, [10, 50], [50, 30+1]) [zeros(3); [1]]
67+
linmodel2 = LinModel(append(tf(1, [1, 0]), tf(2, [10, 1])), 1.0)
68+
skalmanfilter2 = SteadyKalmanFilter(linmodel2, nint_u=[1, 1])
69+
x = initstate!(skalmanfilter2, [10, 3], [0.5, 6+0.1])
70+
@test evaloutput(skalmanfilter2) [0.5, 6+0.1]
71+
@test updatestate!(skalmanfilter2, [0, 3], [0.5, 6+0.1]) x
6772
setstate!(skalmanfilter1, [1,2,3,4])
6873
@test skalmanfilter1. [1,2,3,4]
6974
for i in 1:100
@@ -299,6 +304,13 @@ end
299304
@test internalmodel1.x̂s ones(2)
300305
@test ModelPredictiveControl.evalŷ(internalmodel1, [51,31], Float64[]) [51,31]
301306
@test initstate!(internalmodel1, [10, 50], [50, 30]) zeros(2)
307+
linmodel2 = LinModel(append(tf(3, [5, 1]), tf(2, [10, 1])), 1.0)
308+
stoch_ym = append(tf([2.5, 1],[1.2, 1, 0]),tf([1.5, 1], [1.3, 1, 0]))
309+
estim = InternalModel(linmodel2; stoch_ym)
310+
initstate!(estim, [1, 2], [3+0.1, 4+0.5])
311+
@test estim.x̂d estim.*estim.x̂d + estim.B̂u*[1, 2]
312+
ŷs = [3+0.1, 4+0.5] - estim()
313+
@test estim.x̂s estim.Âs*estim.x̂s + estim.B̂s*ŷs
302314
@test internalmodel1.x̂s zeros(2)
303315
setstate!(internalmodel1, [1,2])
304316
@test internalmodel1. [1,2]

0 commit comments

Comments
 (0)