Skip to content

Commit e0c9c5d

Browse files
authored
Merge pull request #303 from JuliaControl/doc_test_correction
doc and test: clearer `jℓ` notation and comparing `ExplicitMPC` to others move-blocked controllers
2 parents c156223 + 444a9c8 commit e0c9c5d

4 files changed

Lines changed: 52 additions & 40 deletions

File tree

src/controller/construct.jl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -499,14 +499,18 @@ strictly positive integers):
499499
```math
500500
\mathbf{n_b} = \begin{bmatrix} n_1 & n_2 & \cdots & n_{H_c} \end{bmatrix}'
501501
```
502-
Introducing the notation ``j_ℓ = ∑_{i=1}^{ℓ} n_i`` to convert from block lengths to discrete
503-
time steps, the vector that includes all the free moves of the manipulated input is then
504-
defined as:
502+
Introducing the notation:
503+
```math
504+
j_ℓ = \begin{cases}
505+
0 & ℓ = 0 \\
506+
∑_{i=1}^{ℓ} n_i & ℓ > 0 \end{cases}
507+
```
508+
to convert from block lengths to discrete time steps, the vector that includes all the free
509+
moves of the manipulated input is then defined as:
505510
```math
506511
\mathbf{ΔU} = \begin{bmatrix}
507-
\mathbf{Δu}(k + 0) \\
512+
\mathbf{Δu}(k + j_0) \\
508513
\mathbf{Δu}(k + j_1) \\
509-
\mathbf{Δu}(k + j_2) \\
510514
\vdots \\
511515
\mathbf{Δu}(k + j_{H_c-1})
512516
\end{bmatrix}

src/controller/linmpc.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,11 @@ Time-varying and non-diagonal weights are also supported. Modify the last block
137137
``\mathbf{Z}`` depends on the chosen [`TranscriptionMethod`](@ref) (default to
138138
[`SingleShooting`](@ref), hence ``\mathbf{Z = ΔU}``). The ``\mathbf{ΔU}`` includes the input
139139
increments ``\mathbf{Δu}(k+j) = \mathbf{u}(k+j) - \mathbf{u}(k+j-1)`` from ``j=0`` to
140-
``H_c-1``, the ``\mathbf{Ŷ}`` vector, the output predictions ``\mathbf{ŷ}(k+j)`` from
141-
``j=1`` to ``H_p``, and the ``\mathbf{U}`` vector, the manipulated inputs ``\mathbf{u}(k+j)``
142-
from ``j=0`` to ``H_p-1``. The slack variable ``ϵ`` relaxes the constraints, as described
143-
in [`setconstraint!`](@ref) documentation. See Extended Help for a detailed nomenclature.
140+
``H_c-1`` (without any custom move blocking), the ``\mathbf{Ŷ}`` vector, the output
141+
predictions ``\mathbf{ŷ}(k+j)`` from ``j=1`` to ``H_p``, and the ``\mathbf{U}`` vector, the
142+
manipulated inputs ``\mathbf{u}(k+j)`` from ``j=0`` to ``H_p-1``. The slack variable ``ϵ``
143+
relaxes the constraints, as described in [`setconstraint!`](@ref) documentation. See
144+
Extended Help for a detailed nomenclature.
144145
145146
This method uses the default state estimator, a [`SteadyKalmanFilter`](@ref) with default
146147
arguments. This controller allocates memory at each time step for the optimization.

src/controller/transcription.jl

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ end
261261
Construct the prediction matrices for [`LinModel`](@ref) and [`SingleShooting`](@ref).
262262
263263
The model predictions are evaluated from the deviation vectors (see [`setop!`](@ref)), the
264-
decision variable ``\mathbf{Z}`` (see [`TranscriptionMethod`](@ref)), and:
264+
decision variable ``\mathbf{Z = ΔU}`` (with a [`SingleShooting`](@ref) transcription), and:
265265
```math
266266
\begin{aligned}
267267
\mathbf{Ŷ_0} &= \mathbf{E Z} + \mathbf{G d_0}(k) + \mathbf{J D̂_0}
@@ -274,7 +274,7 @@ in which ``\mathbf{x̂_0}(k) = \mathbf{x̂}_i(k) - \mathbf{x̂_{op}}``, with ``i
274274
`estim.direct==true`, otherwise ``i = k - 1``. The predicted outputs ``\mathbf{Ŷ_0}`` and
275275
measured disturbances ``\mathbf{D̂_0}`` respectively include ``\mathbf{ŷ_0}(k+j)`` and
276276
``\mathbf{d̂_0}(k+j)`` values with ``j=1`` to ``H_p``, and input increments ``\mathbf{ΔU}``,
277-
``\mathbf{Δu}(k+j)`` from ``j=0`` to ``H_c-1``. The vector ``\mathbf{B}`` contains the
277+
``\mathbf{Δu}(k+j_ℓ)`` from ``=0`` to ``H_c-1``. The vector ``\mathbf{B}`` contains the
278278
contribution for non-zero state ``\mathbf{x̂_{op}}`` and state update ``\mathbf{f̂_{op}}``
279279
operating points (for linearization at non-equilibrium point, see [`linearize`](@ref)). The
280280
stochastic predictions ``\mathbf{Ŷ_s=0}`` if `estim` is not a [`InternalModel`](@ref), see
@@ -312,10 +312,10 @@ each control period ``k``, see [`initpred!`](@ref) and [`linconstraint!`](@ref).
312312
```math
313313
\begin{aligned}
314314
\mathbf{E} &= \begin{bmatrix}
315-
\mathbf{Q}(0, j_1, 0) & \mathbf{0} & \cdots & \mathbf{0} \\
316-
\mathbf{Q}(j_1, j_2, 0) & \mathbf{Q}(j_1, j_2, j_1) & \cdots & \mathbf{0} \\
317-
\vdots & \vdots & \ddots & \vdots \\
318-
\mathbf{Q}(j_{H_c-1}, j_{H_c}, 0) & \mathbf{Q}(j_{H_c-1}, j_{H_c}, j_1) & \cdots & \mathbf{Q}(j_{H_c-1}, j_{H_c}, j_{H_c-1}) \end{bmatrix} \\
315+
\mathbf{Q}(j_0, j_1, j_0) & \mathbf{0} & \cdots & \mathbf{0} \\
316+
\mathbf{Q}(j_1, j_2, j_0) & \mathbf{Q}(j_1, j_2, j_1) & \cdots & \mathbf{0} \\
317+
\vdots & \vdots & \ddots & \vdots \\
318+
\mathbf{Q}(j_{H_c-1}, j_{H_c}, j_0) & \mathbf{Q}(j_{H_c-1}, j_{H_c}, j_1) & \cdots & \mathbf{Q}(j_{H_c-1}, j_{H_c}, j_{H_c-1}) \end{bmatrix} \\
319319
\mathbf{G} &= \begin{bmatrix}
320320
\mathbf{Ĉ}\mathbf{Â}^{0} \mathbf{B̂_d} \\
321321
\mathbf{Ĉ}\mathbf{Â}^{1} \mathbf{B̂_d} \\
@@ -343,7 +343,7 @@ each control period ``k``, see [`initpred!`](@ref) and [`linconstraint!`](@ref).
343343
```math
344344
\begin{aligned}
345345
\mathbf{e_x̂} &= \begin{bmatrix}
346-
\mathbf{W}(H_p-1)\mathbf{B̂_u} & \mathbf{W}(H_p-j_1-1)\mathbf{B̂_u} & \cdots & \mathbf{W}(H_p-j_{H_c-1}-1)\mathbf{B̂_u} \end{bmatrix} \\
346+
\mathbf{W}(H_p-j_0-1)\mathbf{B̂_u} & \mathbf{W}(H_p-j_1-1)\mathbf{B̂_u} & \cdots & \mathbf{W}(H_p-j_{H_c-1}-1)\mathbf{B̂_u} \end{bmatrix} \\
347347
\mathbf{g_x̂} &= \mathbf{Â}^{H_p-1} \mathbf{B̂_d} \\
348348
\mathbf{j_x̂} &= \begin{bmatrix}
349349
\mathbf{Â}^{H_p-2}\mathbf{B̂_d} & \mathbf{Â}^{H_p-3}\mathbf{B̂_d} & \cdots & \mathbf{0} \end{bmatrix} \\
@@ -370,10 +370,11 @@ function init_predmat(
370370
end
371371
# Apow_csum 3D array : Apow_csum[:,:,1] = A^0, Apow_csum[:,:,2] = A^1 + A^0, ...
372372
Âpow_csum = cumsum(Âpow, dims=3)
373-
jℓ = cumsum(nb) # introduced in move_blocking docstring
374-
# three helper functions to improve code clarity and be similar to eqs. in docstring:
373+
jℓ_data = [0; cumsum(nb)] # introduced in move_blocking docstring
374+
# four helper functions to improve code clarity and be similar to eqs. in docstring:
375375
getpower(array3D, power) = @views array3D[:,:, power+1]
376-
W(m) = @views Âpow_csum[:,:, m+1]
376+
W(m) = @views Âpow_csum[:,:, m+1]
377+
jℓ(ℓ) = jℓ_data[ℓ+1]
377378
function Q!(Q, i, m, b)
378379
for=0:m-i-1
379380
iRows = (1:ny) .+ ny*
@@ -396,18 +397,15 @@ function init_predmat(
396397
nZ = get_nZ(estim, transcription, Hp, Hc)
397398
ex̂ = Matrix{NT}(undef, nx̂, nZ)
398399
E = zeros(NT, Hp*ny, nZ)
399-
for j=1:Hc
400-
iCol = (1:nu) .+ nu*(j-1)
401-
for i=j:Hc
402-
i_Q = (i == 1 && j == 1) ? 0 : jℓ[i-1]
403-
m_Q = jℓ[i]
404-
b_Q = (j == 1) ? 0 : jℓ[j-1]
405-
iRow = (1:ny*nb[i]) .+ ny*i_Q
400+
for j=0:Hc-1
401+
iCol = (1:nu) .+ nu*j
402+
for i=j:Hc-1
403+
i_Q, m_Q, b_Q = jℓ(i), jℓ(i+1), jℓ(j)
404+
iRow = (1:ny*nb[i+1]) .+ ny*i_Q
406405
Q = @views E[iRow, iCol]
407406
Q!(Q, i_Q, m_Q, b_Q)
408407
end
409-
j_ex̂ = (j == 1) ? 0 : jℓ[j-1]
410-
ex̂[:, iCol] = W(Hp - j_ex̂ - 1)*B̂u
408+
ex̂[:, iCol] = W(Hp - jℓ(j) - 1)*B̂u
411409
end
412410
# --- current measured disturbances d0 and predictions D̂0 ---
413411
gx̂ = getpower(Âpow, Hp-1)*B̂d
@@ -453,10 +451,10 @@ They are defined in the Extended Help section.
453451
They are all appropriately sized zero matrices ``\mathbf{0}``, except for:
454452
```math
455453
\begin{aligned}
456-
\mathbf{E} &= [\begin{smallmatrix}\mathbf{0} & \mathbf{E^†} \end{smallmatrix}] \\
457-
\mathbf{E^} &= \text{diag}\mathbf{(Ĉ,Ĉ,...,Ĉ)} \\
458-
\mathbf{J} &= \text{diag}\mathbf{(D̂_d,D̂_d,...,D̂_d)} \\
459-
\mathbf{e_x̂} &= [\begin{smallmatrix}\mathbf{0} & \mathbf{I}\end{smallmatrix}]
454+
\mathbf{E} &= [\begin{smallmatrix}\mathbf{0} & \mathbf{E^{x̂}} \end{smallmatrix}] \\
455+
\mathbf{E^{x̂}} &= \text{diag}\mathbf{(Ĉ,Ĉ,...,Ĉ)} \\
456+
\mathbf{J} &= \text{diag}\mathbf{(D̂_d,D̂_d,...,D̂_d)} \\
457+
\mathbf{e_x̂} &= [\begin{smallmatrix}\mathbf{0} & \mathbf{I}\end{smallmatrix}]
460458
\end{aligned}
461459
```
462460
"""
@@ -543,8 +541,10 @@ end
543541
544542
Init the matrices for computing the defects over the predicted states.
545543
546-
An equation similar to the prediction matrices (see
547-
[`init_predmat`](@ref)) computes the defects over the predicted states:
544+
Knowing that the decision vector ``\mathbf{Z}`` contains both ``\mathbf{ΔU}`` and
545+
``\mathbf{X̂_0}`` vectors (with a [`MultipleShooting`](@ref) transcription), an equation
546+
similar to the prediction matrices (see [`init_predmat`](@ref)) computes the defects over
547+
the predicted states:
548548
```math
549549
\begin{aligned}
550550
\mathbf{Ŝ} &= \mathbf{E_ŝ Z} + \mathbf{G_ŝ d_0}(k) + \mathbf{J_ŝ D̂_0}

test/3_test_predictive_control.jl

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,7 +1473,7 @@ end
14731473
@test U_nonlinmpc1 U_nonlinmpc2 rtol=1e-3 atol=1e-3
14741474
end
14751475

1476-
@testitem "LinMPC v.s. NonLinMPC with move blocking" setup=[SetupMPCtests] begin
1476+
@testitem "All MPCs with move blocking" setup=[SetupMPCtests] begin
14771477
using .SetupMPCtests, ControlSystemsBase, LinearAlgebra
14781478
G = tf( 10, [400, 1])
14791479
linmodel = LinModel(G, 100.0)
@@ -1482,30 +1482,37 @@ end
14821482
nonlinmodel = NonLinModel(f, h, 100.0, 1, 1, 1, p=linmodel, solver=nothing)
14831483
Mwt, Nwt, Hp, Hc = [1], [0], 30, [2, 3, 4, 21]
14841484
N = 25
1485+
mpc0 = ExplicitMPC(linmodel; Mwt, Nwt, Hp, Hc)
14851486
mpc1 = LinMPC(linmodel; Mwt, Nwt, Hp, Hc, transcription=SingleShooting())
14861487
mpc2 = LinMPC(linmodel; Mwt, Nwt, Hp, Hc, transcription=MultipleShooting())
14871488
mpc3 = NonLinMPC(linmodel; Mwt, Nwt, Hp, Hc, transcription=SingleShooting())
14881489
mpc4 = NonLinMPC(linmodel; Mwt, Nwt, Hp, Hc, transcription=MultipleShooting())
14891490
mpc5 = NonLinMPC(nonlinmodel; Mwt, Nwt, Hp, Hc, transcription=SingleShooting())
14901491
mpc6 = NonLinMPC(nonlinmodel; Mwt, Nwt, Hp, Hc, transcription=MultipleShooting())
1492+
U0 = zeros(1, N)
14911493
U1, U2, U3 = zeros(1, N), zeros(1, N), zeros(1, N)
14921494
U4, U5, U6 = zeros(1, N), zeros(1, N), zeros(1, N)
14931495
for i=1:N
14941496
r = [5.0]
14951497
y = linmodel()
1498+
preparestate!(mpc0, y);
14961499
preparestate!(mpc1, y); preparestate!(mpc2, y); preparestate!(mpc3, y);
14971500
preparestate!(mpc4, y); preparestate!(mpc5, y); preparestate!(mpc6, y);
1501+
u0 = moveinput!(mpc0, r);
14981502
u1 = moveinput!(mpc1, r); u2 = moveinput!(mpc2, r); u3 = moveinput!(mpc3, r);
14991503
u4 = moveinput!(mpc4, r); u5 = moveinput!(mpc5, r); u6 = moveinput!(mpc6, r);
1504+
U0[:, i] = u0;
15001505
U1[:, i] = u1; U2[:, i] = u2; U3[:, i] = u3;
15011506
U4[:, i] = u4; U5[:, i] = u5; U6[:, i] = u6;
1507+
updatestate!(mpc0, u0, y);
15021508
updatestate!(mpc1, u1, y); updatestate!(mpc2, u2, y); updatestate!(mpc3, u3, y);
15031509
updatestate!(mpc4, u4, y); updatestate!(mpc5, u5, y); updatestate!(mpc6, u6, y);
15041510
updatestate!(linmodel, u1);
15051511
end
1506-
@test U1 U2 rtol=1e-3 atol=1e-3
1507-
@test U1 U3 rtol=1e-3 atol=1e-3
1508-
@test U1 U4 rtol=1e-3 atol=1e-3
1509-
@test U1 U5 rtol=1e-3 atol=1e-3
1510-
@test U1 U6 rtol=1e-3 atol=1e-3
1512+
@test U0 U1 rtol=1e-3 atol=1e-3
1513+
@test U0 U2 rtol=1e-3 atol=1e-3
1514+
@test U0 U3 rtol=1e-3 atol=1e-3
1515+
@test U0 U4 rtol=1e-3 atol=1e-3
1516+
@test U0 U5 rtol=1e-3 atol=1e-3
1517+
@test U0 U6 rtol=1e-3 atol=1e-3
15111518
end

0 commit comments

Comments
 (0)