@@ -18,16 +18,19 @@ struct Luenberger <: StateEstimator
1818 Ĉm :: Matrix{Float64}
1919 D̂dm :: Matrix{Float64}
2020 K:: Matrix{Float64}
21- function Luenberger (model, i_ym, nint_ym, Asm, Csm, L )
21+ function Luenberger (model, i_ym, nint_ym, Asm, Csm, p )
2222 nu, nx, ny = model. nu, model. nx, model. ny
2323 nym, nyu = length (i_ym), ny - length (i_ym)
2424 nxs = size (Asm,1 )
2525 nx̂ = nx + nxs
26- validate_kfcov (nym, nx̂, Q̂, R̂)
2726 As, _ , Cs, _ = stoch_ym2y (model, i_ym, Asm, [], Csm, [])
28- f̂, ĥ, Â, B̂u, Ĉ, B̂d, D̂d = augment_model (model, As, Cs)
27+ Â, B̂u, Ĉ, B̂d, D̂d = augment_model (model, As, Cs)
2928 Ĉm, D̂dm = Ĉ[i_ym, :], D̂d[i_ym, :] # measured outputs ym only
30- K = L
29+ K = try
30+ place (Â, Ĉ, p, :o )
31+ catch
32+ error (" Cannot compute the Luenberger gain L with specified poles p." )
33+ end
3134 i_ym = collect (i_ym)
3235 lastu0 = zeros (nu)
3336 x̂ = [copy (model. x); zeros (nxs)]
@@ -38,8 +41,69 @@ struct Luenberger <: StateEstimator
3841 As, Cs, nint_ym,
3942 Â, B̂u, B̂d, Ĉ, D̂d,
4043 Ĉm, D̂dm,
41- Q̂, R̂,
4244 K
4345 )
4446 end
47+ end
48+
49+ @doc raw """
50+ Luenberger(
51+ model::LinModel;
52+ i_ym = 1:model.ny,
53+ nint_ym = fill(1, length(i_ym)),
54+ p̂ = 1e-3*(0:(model.nx + sum(nint_ym)-1)) .+ 0.5)
55+ )
56+
57+ Construct a Luenberger observer with the [`LinModel`](@ref) `model`.
58+
59+ `i_ym` provides the `model` output indices that are measured ``\m athbf{y^m}``, the rest are
60+ unmeasured ``\m athbf{y^u}``. `model` matrices are augmented with the stochastic model, which
61+ is specified by the numbers of output integrator `nint_ym` (see [`SteadyKalmanFilter`](@ref)
62+ Extended Help). The argument `p̂` is a vector of `model.nx + sum(nint_ym)` elements
63+ specifying the observer poles/eigenvalues (near ``z=0.5`` by default). The method computes
64+ the observer gain ``\m athbf{K}`` with [`place`](https://juliacontrol.github.io/ControlSystems.jl/stable/lib/synthesis/#ControlSystemsBase.place).
65+
66+ # Examples
67+ ```jldoctest
68+ julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);
69+
70+ julia> lo = Luenberger(model, nint_ym=[1, 1], p̂=[0.61, 0.62, 0.63, 0.64])
71+ Luenberger estimator with a sample time Ts = 0.5 s, LinModel and:
72+ 1 manipulated inputs u
73+ 4 states x̂
74+ 2 measured outputs ym
75+ 0 unmeasured outputs yu
76+ 0 measured disturbances d
77+ ```
78+ """
79+ function Luenberger (
80+ model:: LinModel ;
81+ i_ym:: IntRangeOrVector = 1 : model. ny,
82+ nint_ym:: IntVectorOrInt = fill (1 , length (i_ym)),
83+ p̂ = 1e-3 * (0 : (model. nx + sum (nint_ym)- 1 )) .+ 0.5
84+ )
85+ if nint_ym == 0 # alias for no output integrator at all :
86+ nint_ym = fill (0 , length (i_ym));
87+ end
88+ Asm, Csm = init_estimstoch (i_ym, nint_ym)
89+ nx = model. nx
90+ if length (p̂) ≠ model. nx + sum (nint_ym)
91+ error (" p̂ length ($(length (p̂)) ) ≠ nx ($nx ) + integrator quantity ($(sum (nint_ym)) )" )
92+ end
93+ any (abs .(p̂) .≥ 1 ) && error (" Observer poles p̂ should be inside the unit circles." )
94+ return Luenberger (model, i_ym, nint_ym, Asm, Csm, p̂)
95+ end
96+
97+
98+ """
99+ updatestate!(estim::Luenberger, u, ym, d=Float64[])
100+
101+ Same `SteadyKalmanFilter` but using the Luenberger observer.
102+ """
103+ function updatestate! (estim:: Luenberger , u, ym, d= Float64[])
104+ u, d, ym = remove_op! (estim, u, d, ym)
105+ Â, B̂u, B̂d, Ĉm, D̂dm = estim. Â, estim. B̂u, estim. B̂d, estim. Ĉm, estim. D̂dm
106+ x̂, K = estim. x̂, estim. K
107+ x̂[:] = Â* x̂ + B̂u* u + B̂d* d + K* (ym - Ĉm* x̂ - D̂dm* d)
108+ return x̂
45109end
0 commit comments