Skip to content

Commit b320658

Browse files
authored
Merge pull request #96 from JuliaControl/mhe_direct_initial_posteriori
added: MovingHorizonEstimator support for `direct=true`, initialized with `P̂(-1|-1)`
2 parents 1e4d3f8 + 0f161ba commit b320658

18 files changed

+967
-528
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ModelPredictiveControl"
22
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
33
authors = ["Francis Gagnon"]
4-
version = "0.23.1"
4+
version = "0.24.0"
55

66
[deps]
77
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"

docs/src/assets/plot_controller.svg

Lines changed: 92 additions & 86 deletions
Loading

docs/src/assets/plot_estimator.svg

Lines changed: 87 additions & 91 deletions
Loading

docs/src/internals/predictive_control.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@ ModelPredictiveControl.linconstraint!(::PredictiveController, ::LinModel)
3333
## Solve Optimization Problem
3434

3535
```@docs
36-
ModelPredictiveControl.optim_objective!
36+
ModelPredictiveControl.optim_objective!(::PredictiveController)
3737
```

docs/src/internals/state_estim.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ ModelPredictiveControl.initpred!(::MovingHorizonEstimator, ::LinModel)
3434
ModelPredictiveControl.linconstraint!(::MovingHorizonEstimator, ::LinModel)
3535
```
3636

37+
## Solve Optimization Problem
38+
39+
```@docs
40+
ModelPredictiveControl.optim_objective!(::MovingHorizonEstimator)
41+
```
42+
3743
## Evaluate Estimated Output
3844

3945
```@docs

docs/src/manual/linmpc.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ mpc_mhe = LinMPC(estim, Hp=10, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1])
203203
mpc_mhe = setconstraint!(mpc_mhe, ymin=[45, -Inf])
204204
```
205205

206-
The rejection is not improved here:
206+
The rejection is indeed improved:
207207

208208
```@example 1
209209
setstate!(model, zeros(model.nx))
@@ -216,10 +216,6 @@ savefig("plot3_LinMPC.svg"); nothing # hide
216216

217217
![plot3_LinMPC](plot3_LinMPC.svg)
218218

219-
This is because the more performant `direct=true` version of the [`MovingHorizonEstimator`](@ref)
220-
is not not implemented yet. The rejection will be improved with the `direct=true` version
221-
(coming soon).
222-
223219
## Adding Feedforward Compensation
224220

225221
Suppose that the load disturbance ``u_l`` of the last section is in fact caused by a

src/controller/construct.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ The predictive controllers support both soft and hard constraints, defined by:
4444
\mathbf{u_{min} - c_{u_{min}}} ϵ ≤&&\ \mathbf{u}(k+j) &≤ \mathbf{u_{max} + c_{u_{max}}} ϵ &&\qquad j = 0, 1 ,..., H_p - 1 \\
4545
\mathbf{Δu_{min} - c_{Δu_{min}}} ϵ ≤&&\ \mathbf{Δu}(k+j) &≤ \mathbf{Δu_{max} + c_{Δu_{max}}} ϵ &&\qquad j = 0, 1 ,..., H_c - 1 \\
4646
\mathbf{y_{min} - c_{y_{min}}} ϵ ≤&&\ \mathbf{ŷ}(k+j) &≤ \mathbf{y_{max} + c_{y_{max}}} ϵ &&\qquad j = 1, 2 ,..., H_p \\
47-
\mathbf{x̂_{min} - c_{x̂_{min}}} ϵ ≤&&\ \mathbf{x̂}_{i}(k+j) &≤ \mathbf{x̂_{max} + c_{x̂_{max}}} ϵ &&\qquad j = H_p
47+
\mathbf{x̂_{min} - c_{x̂_{min}}} ϵ ≤&&\ \mathbf{x̂}_i(k+j) &≤ \mathbf{x̂_{max} + c_{x̂_{max}}} ϵ &&\qquad j = H_p
4848
\end{alignat*}
4949
```
5050
and also ``ϵ ≥ 0``. The last line is the terminal constraints applied on the states at the
@@ -435,7 +435,7 @@ The model predictions are evaluated from the deviation vectors (see [`setop!`](@
435435
&= \mathbf{E ΔU} + \mathbf{F}
436436
\end{aligned}
437437
```
438-
in which ``\mathbf{x̂_0}(k) = \mathbf{x̂}_{i}(k) - \mathbf{x̂_{op}}``, with ``i = k`` if
438+
in which ``\mathbf{x̂_0}(k) = \mathbf{x̂}_i(k) - \mathbf{x̂_{op}}``, with ``i = k`` if
439439
`estim.direct==true`, otherwise ``i = k - 1``. The predicted outputs ``\mathbf{Ŷ_0}`` and
440440
measured disturbances ``\mathbf{D̂_0}`` respectively include ``\mathbf{ŷ_0}(k+j)`` and
441441
``\mathbf{d̂_0}(k+j)`` values with ``j=1`` to ``H_p``, and input increments ``\mathbf{ΔU}``,
@@ -453,8 +453,9 @@ terminal states at ``k+H_p``:
453453
&= \mathbf{e_x̂ ΔU} + \mathbf{f_x̂}
454454
\end{aligned}
455455
```
456-
The ``\mathbf{F}`` and ``\mathbf{f_x̂}`` vectors are recalculated at each control period
457-
``k``, see [`initpred!`](@ref) and [`linconstraint!`](@ref).
456+
The matrices ``\mathbf{E, G, J, K, V, B, e_x̂, g_x̂, j_x̂, k_x̂, v_x̂, b_x̂}`` are defined in the
457+
Extended Help section. The ``\mathbf{F}`` and ``\mathbf{f_x̂}`` vectors are recalculated at
458+
each control period ``k``, see [`initpred!`](@ref) and [`linconstraint!`](@ref).
458459
459460
# Extended Help
460461
!!! details "Extended Help"
@@ -529,7 +530,7 @@ function init_predmat(estim::StateEstimator{NT}, model::LinModel, Hp, Hc) where
529530
# Apow_csum 3D array : Apow_csum[:,:,1] = A^0, Apow_csum[:,:,2] = A^1 + A^0, ...
530531
Âpow_csum = cumsum(Âpow, dims=3)
531532
# helper function to improve code clarity and be similar to eqs. in docstring:
532-
getpower(array3D, power) = array3D[:,:, power+1]
533+
getpower(array3D, power) = @views array3D[:,:, power+1]
533534
# --- state estimates x̂ ---
534535
kx̂ = getpower(Âpow, Hp)
535536
K = Matrix{NT}(undef, Hp*ny, nx̂)

src/controller/execute.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ Solve the optimization problem of `mpc` [`PredictiveController`](@ref) and retur
1717
results ``\mathbf{u}(k)``. Following the receding horizon principle, the algorithm discards
1818
the optimal future manipulated inputs ``\mathbf{u}(k+1), \mathbf{u}(k+2), ...`` Note that
1919
the method mutates `mpc` internal data but it does not modifies `mpc.estim` states. Call
20-
[`updatestate!(mpc, u, ym, d)`](@ref) to update `mpc` state estimates.
20+
[`preparestate!(mpc, ym, d)`](@ref) before `moveinput!`, and [`updatestate!(mpc, u, ym, d)`](@ref)
21+
after, to update `mpc` state estimates.
2122
2223
Calling a [`PredictiveController`](@ref) object calls this method.
2324

src/estimator/construct.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,9 @@ end
197197
198198
One integrator on each measured output by default if `model` is not a [`LinModel`](@ref).
199199
200-
If the integrator quantity per manipulated input `nint_u ≠ 0`, the method returns zero
201-
integrator on each measured output.
200+
Theres is no verification the augmented model remains observable. If the integrator quantity
201+
per manipulated input `nint_u ≠ 0`, the method returns zero integrator on each measured
202+
output.
202203
"""
203204
function default_nint(model::SimModel, i_ym=1:model.ny, nint_u=0)
204205
validate_ym(model, i_ym)

src/estimator/execute.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ end
8686
8787
Init `estim.x̂0` states from current inputs `u`, measured outputs `ym` and disturbances `d`.
8888
89-
The method tries to find a good steady-state for the initial estimate ``\mathbf{x̂}(0)``. It
89+
The method tries to find a good steady-state for the initial estimate ``\mathbf{x̂}``. It
9090
removes the operating points with [`remove_op!`](@ref) and call [`init_estimate!`](@ref):
9191
9292
- If `estim.model` is a [`LinModel`](@ref), it finds the steady-state of the augmented model
@@ -203,7 +203,7 @@ Prepare `estim.x̂0` estimate with meas. outputs `ym` and dist. `d` for the curr
203203
204204
This function should be called at the beginning of each discrete time step. Its behavior
205205
depends if `estim` is a [`StateEstimator`](@ref) in the current/filter (1.) or
206-
delayed/predictor (2.) form:
206+
delayed/predictor (2.) formulation:
207207
208208
1. If `estim.direct` is `true`, it removes the operating points with [`remove_op!`](@ref),
209209
calls [`correct_estimate!`](@ref), and returns the corrected state estimate
@@ -246,7 +246,9 @@ This function should be called at the end of each discrete time step. It removes
246246
operating points with [`remove_op!`](@ref), calls [`update_estimate!`](@ref) and returns the
247247
state estimate for the next time step ``\mathbf{x̂}_k(k+1)``. The method [`preparestate!`](@ref)
248248
should be called prior to this one to correct the estimate when applicable (if
249-
`estim.direct == true`).
249+
`estim.direct == true`). Note that the [`MovingHorizonEstimator`](@ref) with the default
250+
`direct=true` option is not able to estimate ``\mathbf{x̂}_k(k+1)``, the returned value
251+
is therefore the current corrected state ``\mathbf{x̂}_k(k)``.
250252
251253
# Examples
252254
```jldoctest

0 commit comments

Comments
 (0)