Skip to content

Commit 33c768b

Browse files
authored
Merge pull request #206 from JuliaControl/manual_estimator
added: `ManualEstimator` to turn off built-in state estimation
2 parents 79b35cb + b86dbd7 commit 33c768b

File tree

9 files changed

+127
-5
lines changed

9 files changed

+127
-5
lines changed

docs/src/public/state_estim.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ MovingHorizonEstimator
8181
InternalModel
8282
```
8383

84+
## ManualEstimator
85+
86+
```@docs
87+
ManualEstimator
88+
```
89+
8490
## Default Model Augmentation
8591

8692
```@docs

src/ModelPredictiveControl.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export savetime!, periodsleep
3636
export StateEstimator, InternalModel, Luenberger
3737
export SteadyKalmanFilter, KalmanFilter, UnscentedKalmanFilter, ExtendedKalmanFilter
3838
export MovingHorizonEstimator
39+
export ManualEstimator
3940
export default_nint, initstate!
4041
export PredictiveController, ExplicitMPC, LinMPC, NonLinMPC, setconstraint!, moveinput!
4142
export TranscriptionMethod, SingleShooting, MultipleShooting

src/controller/construct.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
const MSG_LINMODEL_ERR = "estim.model type must be a LinModel, see ManualEstimator docstring "*
2+
"to use a nonlinear state estimator with a linear controller"
3+
14
struct PredictiveControllerBuffer{NT<:Real}
25
u ::Vector{NT}
36
::Vector{NT}

src/controller/explicitmpc.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ function ExplicitMPC(
157157
N_Hc = Diagonal(repeat(Nwt, Hc)),
158158
L_Hp = Diagonal(repeat(Lwt, Hp)),
159159
) where {NT<:Real, SE<:StateEstimator{NT}}
160-
isa(estim.model, LinModel) || error("estim.model type must be a LinModel")
160+
isa(estim.model, LinModel) || error(MSG_LINMODEL_ERR)
161161
nk = estimate_delays(estim.model)
162162
if Hp nk
163163
@warn("prediction horizon Hp ($Hp) ≤ estimated number of delays in model "*

src/controller/linmpc.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ function LinMPC(
256256
transcription::TranscriptionMethod = DEFAULT_LINMPC_TRANSCRIPTION,
257257
optim::JM = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
258258
) where {NT<:Real, SE<:StateEstimator{NT}, JM<:JuMP.GenericModel}
259-
isa(estim.model, LinModel) || error("estim.model type must be a LinModel")
259+
isa(estim.model, LinModel) || error(MSG_LINMODEL_ERR)
260260
nk = estimate_delays(estim.model)
261261
if Hp nk
262262
@warn("prediction horizon Hp ($Hp) ≤ estimated number of delays in model "*

src/estimator/kalman.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -978,9 +978,9 @@ differentiation. This estimator is allocation-free if `model` simulations do not
978978
- `σR=fill(1,length(i_ym))` or *`sigmaR`* : main diagonal of the sensor noise covariance
979979
``\mathbf{R}`` of `model` measured outputs, specified as a standard deviation vector.
980980
- `nint_u=0`: integrator quantity for the stochastic model of the unmeasured disturbances at
981-
the manipulated inputs (vector), use `nint_u=0` for no integrator (see Extended Help).
981+
the manipulated inputs (vector), use `nint_u=0` for no integrator.
982982
- `nint_ym=default_nint(model,i_ym,nint_u)` : same than `nint_u` but for the unmeasured
983-
disturbances at the measured outputs, use `nint_ym=0` for no integrator (see Extended Help).
983+
disturbances at the measured outputs, use `nint_ym=0` for no integrator.
984984
- `σQint_u=fill(1,sum(nint_u))` or *`sigmaQint_u`* : same than `σQ` but for the unmeasured
985985
disturbances at manipulated inputs ``\mathbf{Q_{int_u}}`` (composed of integrators).
986986
- `σPint_u_0=fill(1,sum(nint_u))` or *`sigmaPint_u_0`* : same than `σP_0` but for the unmeasured

src/estimator/manual.jl

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
struct ManualEstimator{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
2+
model::SM
3+
lastu0::Vector{NT}
4+
x̂op ::Vector{NT}
5+
f̂op ::Vector{NT}
6+
x̂0 ::Vector{NT}
7+
i_ym::Vector{Int}
8+
nx̂ ::Int
9+
nym::Int
10+
nyu::Int
11+
nxs::Int
12+
As ::Matrix{NT}
13+
Cs_u::Matrix{NT}
14+
Cs_y::Matrix{NT}
15+
nint_u ::Vector{Int}
16+
nint_ym::Vector{Int}
17+
::Matrix{NT}
18+
B̂u ::Matrix{NT}
19+
::Matrix{NT}
20+
B̂d ::Matrix{NT}
21+
D̂d ::Matrix{NT}
22+
Ĉm ::Matrix{NT}
23+
D̂dm ::Matrix{NT}
24+
direct::Bool
25+
corrected::Vector{Bool}
26+
buffer::StateEstimatorBuffer{NT}
27+
function ManualEstimator{NT}(
28+
model::SM, i_ym, nint_u, nint_ym
29+
) where {NT<:Real, SM<:SimModel{NT}}
30+
nu, ny, nd, nk = model.nu, model.ny, model.nd, model.nk
31+
nym, nyu = validate_ym(model, i_ym)
32+
As, Cs_u, Cs_y, nint_u, nint_ym = init_estimstoch(model, i_ym, nint_u, nint_ym)
33+
nxs = size(As, 1)
34+
nx̂ = model.nx + nxs
35+
Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op = augment_model(model, As, Cs_u, Cs_y)
36+
Ĉm, D̂dm = Ĉ[i_ym, :], D̂d[i_ym, :]
37+
lastu0 = zeros(NT, nu)
38+
x̂0 = [zeros(NT, model.nx); zeros(NT, nxs)]
39+
direct = false
40+
corrected = [true]
41+
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd, nk)
42+
return new{NT, SM}(
43+
model,
44+
lastu0, x̂op, f̂op, x̂0,
45+
i_ym, nx̂, nym, nyu, nxs,
46+
As, Cs_u, Cs_y, nint_u, nint_ym,
47+
Â, B̂u, Ĉ, B̂d, D̂d, Ĉm, D̂dm,
48+
direct, corrected,
49+
buffer
50+
)
51+
end
52+
end
53+
54+
@doc raw"""
55+
ManualEstimator(model::SimModel; <keyword arguments>)
56+
57+
Construct a manual state estimator for `model` ([`LinModel`](@ref) or [`NonLinModel`](@ref)).
58+
59+
This [`StateEstimator`](@ref) type allows the construction of [`PredictiveController`](@ref)
60+
objects but turns off the built-in state estimation. The user must manually provides the
61+
estimate ``\mathbf{x̂}_{k}(k)`` or ``\mathbf{x̂}_{k-1}(k)`` through [`setstate!`](@ref) at
62+
each time step. Calling [`preparestate!`](@ref) and [`updatestate!`](@ref) will not modify
63+
the estimate. See Extended Help for usage examples.
64+
65+
# Arguments
66+
- `model::SimModel` : (deterministic) model for the estimations.
67+
- `i_ym=1:model.ny` : `model` output indices that are measured ``\mathbf{y^m}``, the rest
68+
are unmeasured ``\mathbf{y^u}``.
69+
- `nint_u=0`: integrator quantity for the stochastic model of the unmeasured disturbances at
70+
the manipulated inputs (vector), use `nint_u=0` for no integrator (see Extended Help).
71+
- `nint_ym=default_nint(model,i_ym,nint_u)` : same than `nint_u` but for the unmeasured
72+
disturbances at the measured outputs, use `nint_ym=0` for no integrator (see Extended Help).
73+
74+
# Examples
75+
```jldoctest
76+
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);
77+
78+
julia> estim = ManualEstimator(model, nint_ym=0) # disable augmentation with integrators
79+
ManualEstimator estimator with a sample time Ts = 0.5 s, LinModel and:
80+
1 manipulated inputs u (0 integrating states)
81+
2 estimated states x̂
82+
2 measured outputs ym (0 integrating states)
83+
0 unmeasured outputs yu
84+
0 measured disturbances d
85+
```
86+
87+
# Extended Help
88+
!!! details "Extended Help"
89+
A first use case is a linear predictive controller based on nonlinear state estimation.
90+
The [`ManualEstimator`](@ref) serves as a wrapper to provide the minimal required
91+
information to construct a [`PredictiveController`](@ref). e.g.:
92+
93+
```julia
94+
a=1
95+
```
96+
"""
97+
function ManualEstimator(
98+
model::SM;
99+
i_ym::AbstractVector{Int} = 1:model.ny,
100+
nint_u ::IntVectorOrInt = 0,
101+
nint_ym::IntVectorOrInt = default_nint(model, i_ym, nint_u),
102+
) where {NT<:Real, SM<:SimModel{NT}}
103+
return ManualEstimator{NT}(model, i_ym, nint_u, nint_ym)
104+
end
105+
106+
"""
107+
update_estimate!(estim::ManualEstimator, y0m, d0, u0)
108+
109+
Do nothing for [`ManualEstimator`](@ref).
110+
"""
111+
update_estimate!(estim::ManualEstimator, y0m, d0, u0) = nothing

src/model/nonlinmodel.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ functions are defined as:
9090
```
9191
where ``\mathbf{x}``, ``\mathbf{y}``, ``\mathbf{u}``, ``\mathbf{d}`` and ``\mathbf{p}`` are
9292
respectively the state, output, manipulated input, measured disturbance and parameter
93-
vectors. As a mather of fact, the parameter argument `p` can be any Julia objects but use a
93+
vectors. As a matter of fact, the parameter argument `p` can be any Julia objects but use a
9494
mutable type if you want to change them later e.g.: a vector. If the dynamics is a function
9595
of the time, simply add a measured disturbance defined as ``d(t) = t``. The functions can be
9696
implemented in two possible ways:

src/state_estim.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ include("estimator/kalman.jl")
2626
include("estimator/luenberger.jl")
2727
include("estimator/mhe.jl")
2828
include("estimator/internal_model.jl")
29+
include("estimator/manual.jl")
2930

3031
function Base.show(io::IO, estim::StateEstimator)
3132
nu, nd = estim.model.nu, estim.model.nd

0 commit comments

Comments
 (0)