Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed: RungeKutta no longer uses DiffCache #183

Merged
merged 8 commits into from
Mar 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "ModelPredictiveControl"
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
authors = ["Francis Gagnon"]
version = "1.5.0"
version = "1.5.1"

[deps]
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
Expand All @@ -12,7 +12,6 @@ JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
OSQP = "ab2f91bb-94b4-55e3-9ba0-7f65df51de79"
PreallocationTools = "d236fae5-4411-538c-8e31-a6e3d9e00b46"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
ProgressLogging = "33c8b6b6-d38a-422a-b730-caa89a2f386c"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand All @@ -29,7 +28,6 @@ JuMP = "1.21"
LinearAlgebra = "1.10"
Logging = "1.10"
OSQP = "0.8"
PreallocationTools = "0.4.14"
PrecompileTools = "1"
ProgressLogging = "0.1"
Random = "1.10"
Expand Down
2 changes: 0 additions & 2 deletions src/ModelPredictiveControl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import JuMP
import JuMP: MOIU, MOI, GenericModel, Model, optimizer_with_attributes, register
import JuMP: @variable, @operator, @constraint, @objective

import PreallocationTools: DiffCache, get_tmp # TODO: remove this dep if possible (with Cache of DI.jl)

import OSQP, Ipopt

export SimModel, LinModel, NonLinModel
Expand Down
17 changes: 10 additions & 7 deletions src/controller/execute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,24 +113,27 @@ julia> round.(getinfo(mpc)[:Ŷ], digits=3)
```
"""
function getinfo(mpc::PredictiveController{NT}) where NT<:Real
model, transcription = mpc.estim.model, mpc.transcription
nΔŨ = mpc.Hc*model.nu + mpc.nϵ
model, buffer, transcription = mpc.estim.model, mpc.buffer, mpc.transcription
nΔŨ, nK = mpc.Hc*model.nu + mpc.nϵ, mpc.Hp*model.nk
nŶe, nUe = (mpc.Hp+1)*model.ny, (mpc.Hp+1)*model.nu
nX̂0, nÛ0 = mpc.estim.nx̂*mpc.Hp, model.nu*mpc.Hp
Z̃ = mpc.Z̃
info = Dict{Symbol, Any}()
ΔŨ = Vector{NT}(undef, nΔŨ)
x̂0end = similar(mpc.estim.x̂0)
ΔŨ = Vector{NT}(undef, nΔŨ)
x̂0end = similar(mpc.estim.x̂0)
K0 = Vector{NT}(undef, nK)
Ue, Ŷe = Vector{NT}(undef, nUe), Vector{NT}(undef, nŶe)
U0, Ŷ0 = similar(mpc.Uop), similar(mpc.Yop)
Û0, X̂0 = Vector{NT}(undef, nÛ0), Vector{NT}(undef, nX̂0)
U, Ŷ = similar(mpc.Uop), similar(mpc.Yop)
U, Ŷ = buffer.U, buffer.Ŷ
D̂ = buffer.D̂
U0 = getU0!(U0, mpc, Z̃)
ΔŨ = getΔŨ!(ΔŨ, mpc, transcription, Z̃)
Ŷ0, x̂0end = predict!(Ŷ0, x̂0end, X̂0, Û0, mpc, model, transcription, U0, Z̃)
Ŷ0, x̂0end = predict!(Ŷ0, x̂0end, X̂0, Û0, K0, mpc, model, transcription, U0, Z̃)
Ue, Ŷe = extended_vectors!(Ue, Ŷe, mpc, U0, Ŷ0)
U .= U0 .+ mpc.Uop
Ŷ .= Ŷ0 .+ mpc.Yop
D̂ .= mpc.D̂0 + mpc.Dop
J = obj_nonlinprog!(Ŷ0, U0, mpc, model, Ue, Ŷe, ΔŨ)
Ŷs = similar(mpc.Yop)
predictstoch!(Ŷs, mpc, mpc.estim)
Expand All @@ -140,7 +143,7 @@ function getinfo(mpc::PredictiveController{NT}) where NT<:Real
info[:U] = U
info[:u] = info[:U][1:model.nu]
info[:d] = mpc.d0 + model.dop
info[:D̂] = mpc.D̂0 + mpc.Dop
info[:D̂] =
info[:ŷ] = mpc.ŷ
info[:Ŷ] = Ŷ
info[:x̂end] = x̂0end + mpc.estim.x̂op
Expand Down
2 changes: 1 addition & 1 deletion src/controller/explicitmpc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ optim_objective!(mpc::ExplicitMPC) = lmul!(-1, ldiv!(mpc.Z̃, mpc.H̃_chol, mpc.

"Compute the predictions but not the terminal states if `mpc` is an [`ExplicitMPC`](@ref)."
function predict!(
Ŷ0, x̂0end, _ , _ , mpc::ExplicitMPC, ::LinModel, ::TranscriptionMethod, _ , Z̃
Ŷ0, x̂0end, _ , _ , _ , mpc::ExplicitMPC, ::LinModel, ::TranscriptionMethod, _ , Z̃
)
# in-place operations to reduce allocations :
Ŷ0 .= mul!(Ŷ0, mpc.Ẽ, Z̃) .+ mpc.F
Expand Down
46 changes: 28 additions & 18 deletions src/controller/nonlinmpc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -578,15 +578,17 @@ Inspired from: [User-defined operators with vector outputs](@extref JuMP User-de
function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT<:Real
# ----- common cache for Jfunc, gfuncs, geqfuncs called with floats -------------------
model = mpc.estim.model
nu, ny, nx̂, nϵ, Hp, Hc = model.nu, model.ny, mpc.estim.nx̂, mpc.nϵ, mpc.Hp, mpc.Hc
nu, ny, nx̂, nϵ, nk = model.nu, model.ny, mpc.estim.nx̂, mpc.nϵ, model.nk
Hp, Hc = mpc.Hp, mpc.Hc
ng, nc, neq = length(mpc.con.i_g), mpc.con.nc, mpc.con.neq
nZ̃, nU, nŶ, nX̂ = length(mpc.Z̃), Hp*nu, Hp*ny, Hp*nx̂
nZ̃, nU, nŶ, nX̂, nK = length(mpc.Z̃), Hp*nu, Hp*ny, Hp*nx̂, Hp*nk
nΔŨ, nUe, nŶe = nu*Hc + nϵ, nU + nu, nŶ + ny
strict = Val(true)
myNaN = convert(JNT, NaN) # NaN to force update_simulations! at first call:
Z̃ ::Vector{JNT} = fill(myNaN, nZ̃)
ΔŨ::Vector{JNT} = zeros(JNT, nΔŨ)
x̂0end::Vector{JNT} = zeros(JNT, nx̂)
K0::Vector{JNT} = zeros(JNT, nK)
Ue::Vector{JNT}, Ŷe::Vector{JNT} = zeros(JNT, nUe), zeros(JNT, nŶe)
U0::Vector{JNT}, Ŷ0::Vector{JNT} = zeros(JNT, nU), zeros(JNT, nŶ)
Û0::Vector{JNT}, X̂0::Vector{JNT} = zeros(JNT, nU), zeros(JNT, nX̂)
Expand All @@ -596,18 +598,18 @@ function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT
function Jfunc(Z̃arg::Vararg{T, N}) where {N, T<:Real}
if isdifferent(Z̃arg, Z̃)
Z̃ .= Z̃arg
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq, mpc, Z̃)
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
end
return obj_nonlinprog!(Ŷ0, U0, mpc, model, Ue, Ŷe, ΔŨ)::T
end
function Jfunc!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq)
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq, mpc, Z̃)
function Jfunc!(Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq)
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃)
return obj_nonlinprog!(Ŷ0, U0, mpc, model, Ue, Ŷe, ΔŨ)
end
Z̃_∇J = fill(myNaN, nZ̃)
∇J_context = (
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
Cache(Û0), Cache(X̂0),
Cache(Û0), Cache(K0), Cache(X̂0),
Cache(gc), Cache(g), Cache(geq),
)
∇J_prep = prepare_gradient(Jfunc!, mpc.gradient, Z̃_∇J, ∇J_context...; strict)
Expand All @@ -631,19 +633,23 @@ function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT
gfunc_i = function (Z̃arg::Vararg{T, N}) where {N, T<:Real}
if isdifferent(Z̃arg, Z̃)
Z̃ .= Z̃arg
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq, mpc, Z̃)
update_predictions!(
ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃
)
end
return g[i]::T
end
gfuncs[i] = gfunc_i
end
function gfunc!(g, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, geq)
return update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq, mpc, Z̃)
function gfunc!(g, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, geq)
return update_predictions!(
ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃
)
end
Z̃_∇g = fill(myNaN, nZ̃)
∇g_context = (
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
Cache(Û0), Cache(X̂0),
Cache(Û0), Cache(K0), Cache(X̂0),
Cache(gc), Cache(geq),
)
# temporarily enable all the inequality constraints for sparsity detection:
Expand Down Expand Up @@ -678,19 +684,23 @@ function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT
geqfunc_i = function (Z̃arg::Vararg{T, N}) where {N, T<:Real}
if isdifferent(Z̃arg, Z̃)
Z̃ .= Z̃arg
update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq, mpc, Z̃)
update_predictions!(
ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃
)
end
return geq[i]::T
end
geqfuncs[i] = geqfunc_i
end
function geqfunc!(geq, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g)
return update_predictions!(ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq, mpc, Z̃)
function geqfunc!(geq, Z̃, ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g)
return update_predictions!(
ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc, Z̃
)
end
Z̃_∇geq = fill(myNaN, nZ̃)
∇geq_context = (
Cache(ΔŨ), Cache(x̂0end), Cache(Ue), Cache(Ŷe), Cache(U0), Cache(Ŷ0),
Cache(Û0), Cache(X̂0),
Cache(Û0), Cache(K0), Cache(X̂0),
Cache(gc), Cache(g)
)
∇geq_prep = prepare_jacobian(geqfunc!, geq, mpc.jacobian, Z̃_∇geq, ∇geq_context...; strict)
Expand All @@ -716,7 +726,7 @@ end

"""
update_predictions!(
ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq,
ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq,
mpc::PredictiveController, Z̃
) -> nothing

Expand All @@ -725,17 +735,17 @@ Update in-place all vectors for the predictions of `mpc` controller at decision
The method mutates all the arguments before the `mpc` argument.
"""
function update_predictions!(
ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, X̂0, gc, g, geq, mpc::PredictiveController, Z̃
ΔŨ, x̂0end, Ue, Ŷe, U0, Ŷ0, Û0, K0, X̂0, gc, g, geq, mpc::PredictiveController, Z̃
)
model, transcription = mpc.estim.model, mpc.transcription
U0 = getU0!(U0, mpc, Z̃)
ΔŨ = getΔŨ!(ΔŨ, mpc, transcription, Z̃)
Ŷ0, x̂0end = predict!(Ŷ0, x̂0end, X̂0, Û0, mpc, model, transcription, U0, Z̃)
Ŷ0, x̂0end = predict!(Ŷ0, x̂0end, X̂0, Û0, K0, mpc, model, transcription, U0, Z̃)
Ue, Ŷe = extended_vectors!(Ue, Ŷe, mpc, U0, Ŷ0)
ϵ = getϵ(mpc, Z̃)
gc = con_custom!(gc, mpc, Ue, Ŷe, ϵ)
g = con_nonlinprog!(g, mpc, model, transcription, x̂0end, Ŷ0, gc, ϵ)
geq = con_nonlinprogeq!(geq, X̂0, Û0, mpc, model, transcription, U0, Z̃)
geq = con_nonlinprogeq!(geq, X̂0, Û0, K0, mpc, model, transcription, U0, Z̃)
return nothing
end

Expand Down
38 changes: 22 additions & 16 deletions src/controller/transcription.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1028,7 +1028,7 @@ getU0!(U0, mpc::PredictiveController, Z̃) = (mul!(U0, mpc.P̃u, Z̃) .+ mpc.Tu_

@doc raw"""
predict!(
Ŷ0, x̂0end, _ , _ ,
Ŷ0, x̂0end, _ , _ , _ ,
mpc::PredictiveController, model::LinModel, transcription::TranscriptionMethod,
_ , Z̃
) -> Ŷ0, x̂0end
Expand All @@ -1040,7 +1040,7 @@ the terminal constraints applied on ``\mathbf{x̂}_{k-1}(k+H_p)``. The computati
identical for any [`TranscriptionMethod`](@ref) if the model is linear.
"""
function predict!(
Ŷ0, x̂0end, _ , _ ,
Ŷ0, x̂0end, _, _, _,
mpc::PredictiveController, ::LinModel, ::TranscriptionMethod,
_ , Z̃
)
Expand All @@ -1052,29 +1052,31 @@ end

@doc raw"""
predict!(
Ŷ0, x̂0end, X̂0, Û0,
Ŷ0, x̂0end, X̂0, Û0, K0,
mpc::PredictiveController, model::NonLinModel, transcription::SingleShooting,
U0, _
) -> Ŷ0, x̂0end

Compute vectors if `model` is a [`NonLinModel`](@ref) and for [`SingleShooting`](@ref).

The method mutates `Ŷ0`, `x̂0end`, `X̂0` and `Û0` arguments.
The method mutates `Ŷ0`, `x̂0end`, `X̂0`, `Û0` and `K0` arguments.
"""
function predict!(
Ŷ0, x̂0end, X̂0, Û0,
Ŷ0, x̂0end, X̂0, Û0, K0,
mpc::PredictiveController, model::NonLinModel, ::SingleShooting,
U0, _
)
nu, nx̂, ny, nd, Hp, Hc = model.nu, mpc.estim.nx̂, model.ny, model.nd, mpc.Hp, mpc.Hc
nu, nx̂, ny, nd, nk = model.nu, mpc.estim.nx̂, model.ny, model.nd, model.nk
Hp, Hc = mpc.Hp, mpc.Hc
D̂0 = mpc.D̂0
x̂0 = @views mpc.estim.x̂0[1:nx̂]
d0 = @views mpc.d0[1:nd]
for j=1:Hp
u0 = @views U0[(1 + nu*(j-1)):(nu*j)]
û0 = @views Û0[(1 + nu*(j-1)):(nu*j)]
k0 = @views K0[(1 + nk*(j-1)):(nk*j)]
x̂0next = @views X̂0[(1 + nx̂*(j-1)):(nx̂*j)]
f̂!(x̂0next, û0, mpc.estim, model, x̂0, u0, d0)
f̂!(x̂0next, û0, k0, mpc.estim, model, x̂0, u0, d0)
x̂0next .+= mpc.estim.f̂op .- mpc.estim.x̂op
x̂0 = @views X̂0[(1 + nx̂*(j-1)):(nx̂*j)]
d0 = @views D̂0[(1 + nd*(j-1)):(nd*j)]
Expand All @@ -1088,7 +1090,7 @@ end

@doc raw"""
predict!(
Ŷ0, x̂0end, _ , _ ,
Ŷ0, x̂0end, _ , _ , _ ,
mpc::PredictiveController, model::NonLinModel, transcription::MultipleShooting,
U0, Z̃
) -> Ŷ0, x̂0end
Expand All @@ -1098,7 +1100,7 @@ Compute vectors if `model` is a [`NonLinModel`](@ref) and for [`MultipleShooting
The method mutates `Ŷ0` and `x̂0end` arguments.
"""
function predict!(
Ŷ0, x̂0end, _, _,
Ŷ0, x̂0end, _, _, _,
mpc::PredictiveController, model::NonLinModel, ::MultipleShooting,
U0, Z̃
)
Expand Down Expand Up @@ -1204,17 +1206,20 @@ end

"""
con_nonlinprogeq!(
geq, X̂0, Û0, mpc::PredictiveController, model::NonLinModel, ::MultipleShooting, U0, Z̃
geq, X̂0, Û0, K0
mpc::PredictiveController, model::NonLinModel, ::MultipleShooting, U0, Z̃
)

Nonlinear equality constrains for [`NonLinModel`](@ref) and [`MultipleShooting`](@ref).

The method mutates the `geq`, `X̂0` and `Û0` vectors in argument.
The method mutates the `geq`, `X̂0`, `Û0` and `K0` vectors in argument.
"""
function con_nonlinprogeq!(
geq, X̂0, Û0, mpc::PredictiveController, model::NonLinModel, ::MultipleShooting, U0, Z̃
geq, X̂0, Û0, K0,
mpc::PredictiveController, model::NonLinModel, ::MultipleShooting, U0, Z̃
)
nx̂, nu, nd, Hp, Hc = mpc.estim.nx̂, model.nu, model.nd, mpc.Hp, mpc.Hc
nu, nx̂, ny, nd, nk = model.nu, mpc.estim.nx̂, model.ny, model.nd, model.nk
Hp, Hc = mpc.Hp, mpc.Hc
nΔU, nX̂ = nu*Hc, nx̂*Hp
D̂0 = mpc.D̂0
X̂0_Z̃ = @views Z̃[(nΔU+1):(nΔU+nX̂)]
Expand All @@ -1225,7 +1230,8 @@ function con_nonlinprogeq!(
u0 = @views U0[(1 + nu*(j-1)):(nu*j)]
û0 = @views Û0[(1 + nu*(j-1)):(nu*j)]
x̂0next = @views X̂0[(1 + nx̂*(j-1)):(nx̂*j)]
f̂!(x̂0next, û0, mpc.estim, model, x̂0, u0, d0)
k0 = @views K0[(1 + nk*(j-1)):(nk*j)]
f̂!(x̂0next, û0, k0, mpc.estim, model, x̂0, u0, d0)
x̂0next .+= mpc.estim.f̂op .- mpc.estim.x̂op
x̂0next_Z̃ = @views X̂0_Z̃[(1 + nx̂*(j-1)):(nx̂*j)]
ŝnext = @views geq[(1 + nx̂*(j-1)):(nx̂*j)]
Expand All @@ -1235,5 +1241,5 @@ function con_nonlinprogeq!(
end
return geq
end
con_nonlinprogeq!(geq,_,_,::PredictiveController,::NonLinModel,::SingleShooting, _,_) = geq
con_nonlinprogeq!(geq,_,_,::PredictiveController,::LinModel,::TranscriptionMethod,_,_) = geq
con_nonlinprogeq!(geq,_,_,_,::PredictiveController,::NonLinModel,::SingleShooting, _,_)=geq
con_nonlinprogeq!(geq,_,_,_,::PredictiveController,::LinModel,::TranscriptionMethod,_,_)=geq
8 changes: 5 additions & 3 deletions src/estimator/construct.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
struct StateEstimatorBuffer{NT<:Real}
u ::Vector{NT}
û ::Vector{NT}
k::Vector{NT}
x̂ ::Vector{NT}
P̂ ::Matrix{NT}
Q̂ ::Matrix{NT}
Expand All @@ -13,17 +14,18 @@ struct StateEstimatorBuffer{NT<:Real}
end

@doc raw"""
StateEstimatorBuffer{NT}(nu::Int, nx̂::Int, nym::Int, ny::Int, nd::Int)
StateEstimatorBuffer{NT}(nu::Int, nx̂::Int, nym::Int, ny::Int, nd::Int, nk::Int=0)

Create a buffer for `StateEstimator` objects for estimated states and measured outputs.

The buffer is used to store intermediate results during estimation without allocating.
"""
function StateEstimatorBuffer{NT}(
nu::Int, nx̂::Int, nym::Int, ny::Int, nd::Int
nu::Int, nx̂::Int, nym::Int, ny::Int, nd::Int, nk::Int=0
) where NT <: Real
u = Vector{NT}(undef, nu)
û = Vector{NT}(undef, nu)
k = Vector{NT}(undef, nk)
x̂ = Vector{NT}(undef, nx̂)
P̂ = Matrix{NT}(undef, nx̂, nx̂)
Q̂ = Matrix{NT}(undef, nx̂, nx̂)
Expand All @@ -33,7 +35,7 @@ function StateEstimatorBuffer{NT}(
ŷ = Vector{NT}(undef, ny)
d = Vector{NT}(undef, nd)
empty = Vector{NT}(undef, 0)
return StateEstimatorBuffer{NT}(u, û, x̂, P̂, Q̂, R̂, K̂, ym, ŷ, d, empty)
return StateEstimatorBuffer{NT}(u, û, k, x̂, P̂, Q̂, R̂, K̂, ym, ŷ, d, empty)
end

@doc raw"""
Expand Down
Loading