@@ -64,14 +64,16 @@ for this transcription method.
6464struct MultipleShooting <: ShootingMethod end
6565
6666@doc raw """
67- TrapezoidalCollocation()
67+ TrapezoidalCollocation(h::Int=0 )
6868
69- Construct an implicit trapezoidal [`TranscriptionMethod`](@ref).
69+ Construct an implicit trapezoidal [`TranscriptionMethod`](@ref) with `h`th order hold .
7070
7171This is the simplest collocation method. It supports continuous-time [`NonLinModel`](@ref)s
7272only. The decision variables are the same as for [`MultipleShooting`](@ref), hence similar
73- computational costs. It currently assumes piecewise constant manipulated inputs (or zero-
74- order hold) between the samples, but linear interpolation will be added soon.
73+ computational costs. The `h` argument is `0` or `1`, for piecewise constant or linear
74+ manipulated inputs ``\m athbf{u}`` (`h=1` is slightly less expensive). Note that the various
75+ [`DiffSolver`](@ref) here assume zero-order hold, so `h=1` will induce a plant-model
76+ mismatch if the plant is simulated with these solvers.
7577
7678This transcription computes the predictions by calling the continuous-time model in the
7779equality constraint function and by using the implicit trapezoidal rule. It can handle
@@ -92,10 +94,14 @@ transcription method.
9294 for more details.
9395"""
9496struct TrapezoidalCollocation <: CollocationMethod
97+ h:: Int
9598 nc:: Int
96- function TrapezoidalCollocation ()
99+ function TrapezoidalCollocation (h:: Int = 0 )
100+ if ! (h == 0 || h == 1 )
101+ throw (ArgumentError (" h argument must be 0 or 1 for TrapezoidalCollocation." ))
102+ end
97103 nc = 2 # 2 collocation points per interval for trapezoidal rule
98- return new (nc)
104+ return new (h, nc)
99105 end
100106end
101107
@@ -1376,11 +1382,11 @@ extracted from the decision variables `Z̃`. The ``\mathbf{k}`` coefficients are
13761382evaluated from the continuous-time function `model.f!` and:
13771383```math
13781384\b egin{aligned}
1379- \m athbf{k}_1 &= \m athbf{f}\B ig(\m athbf{x_0}(k), \m athbf{û_0}(k), \m athbf{d_0}(k) \B ig) \\
1380- \m athbf{k}_2 &= \m athbf{f}\B ig(\m athbf{x_0}(k+1), \m athbf{û_0}(k), \m athbf{d_0}(k+1)\B ig)
1385+ \m athbf{k}_1 &= \m athbf{f}\B ig(\m athbf{x_0}(k), \m athbf{û_0}(k), \m athbf{d_0}(k) \B ig) \\
1386+ \m athbf{k}_2 &= \m athbf{f}\B ig(\m athbf{x_0}(k+1), \m athbf{û_0}(k+h ), \m athbf{d_0}(k+1)\B ig)
13811387\e nd{aligned}
13821388```
1383- and the input of the augmented model is:
1389+ in which ``h`` is the hold order `transcription.h` and the disturbed input is:
13841390```math
13851391\m athbf{û_0}(k) = \m athbf{u_0}(k) + \m athbf{C_{s_u} x_s}(k)
13861392```
@@ -1391,7 +1397,7 @@ function con_nonlinprogeq!(
13911397 mpc:: PredictiveController , model:: NonLinModel , transcription:: TrapezoidalCollocation ,
13921398 U0, Z̃
13931399)
1394- nu, nx̂, nd, nx = model. nu, mpc. estim. nx̂, model. nd, model. nx
1400+ nu, nx̂, nd, nx, h = model. nu, mpc. estim. nx̂, model. nd, model. nx, transcription . h
13951401 Hp, Hc = mpc. Hp, mpc. Hc
13961402 nΔU, nX̂ = nu* Hc, nx̂* Hp
13971403 Ts, p = model. Ts, model. p
@@ -1401,30 +1407,64 @@ function con_nonlinprogeq!(
14011407 X̂0_Z̃ = @views Z̃[(nΔU+ 1 ): (nΔU+ nX̂)]
14021408 x̂0 = @views mpc. estim. x̂0[1 : nx̂]
14031409 d0 = @views mpc. d0[1 : nd]
1410+ if ! iszero (h)
1411+ k1, u0, û0 = @views K0[1 : nx], U0[1 : nu], Û0[1 : nu]
1412+ x0, xs = @views x̂0[1 : nx], x̂0[nx+ 1 : end ]
1413+ mul! (û0, Cs_u, xs)
1414+ û0 .+ = u0
1415+ model. f! (k1, x0, û0, d0, p)
1416+ lastk2 = k1
1417+ end
14041418 # TODO : allow parallel for loop or threads?
14051419 for j= 1 : Hp
1406- u0 = @views U0[(1 + nu* (j- 1 )): (nu* j)]
1407- û0 = @views Û0[(1 + nu* (j- 1 )): (nu* j)]
14081420 k0 = @views K0[(1 + nk* (j- 1 )): (nk* j)]
14091421 d0next = @views D̂0[(1 + nd* (j- 1 )): (nd* j)]
14101422 x̂0next = @views X̂0[(1 + nx̂* (j- 1 )): (nx̂* j)]
14111423 x̂0next_Z̃ = @views X̂0_Z̃[(1 + nx̂* (j- 1 )): (nx̂* j)]
14121424 ŝnext = @views geq[(1 + nx̂* (j- 1 )): (nx̂* j)]
1413- xd , xs = @views x̂0[1 : nx], x̂0[nx+ 1 : end ]
1414- xdnext_Z̃ , xsnext_Z̃ = @views x̂0next_Z̃[1 : nx], x̂0next_Z̃[nx+ 1 : end ]
1425+ x0 , xs = @views x̂0[1 : nx], x̂0[nx+ 1 : end ]
1426+ x0next_Z̃ , xsnext_Z̃ = @views x̂0next_Z̃[1 : nx], x̂0next_Z̃[nx+ 1 : end ]
14151427 sdnext, ssnext = @views ŝnext[1 : nx], ŝnext[nx+ 1 : end ]
1416- mul! (û0, Cs_u, xs) # ys_u = Cs_u*xs
1417- û0 .+ = u0 # û0 = u0 + ys_u
1418- k1, k2 = @views k0[1 : nx], k0[nx+ 1 : 2 * nx]
1419- model. f! (k1, xd, û0, d0, p)
1420- model. f! (k2, xdnext_Z̃, û0, d0next, p) # assuming ZOH on manipulated inputs u
1428+ k1, k2 = @views k0[1 : nx], k0[nx+ 1 : 2 * nx]
1429+ # ----------------- stochastic defects -----------------------------------------
14211430 xsnext = @views x̂0next[nx+ 1 : end ]
14221431 mul! (xsnext, As, xs)
1423- sdnext .= @. xd - xdnext_Z̃ + (Ts/ 2 )* (k1 + k2)
14241432 ssnext .= @. xsnext - xsnext_Z̃
1433+ # ----------------- deterministic defects --------------------------------------
1434+ if iszero (h) # piecewise constant manipulated inputs u:
1435+ u0 = @views U0[(1 + nu* (j- 1 )): (nu* j)]
1436+ û0 = @views Û0[(1 + nu* (j- 1 )): (nu* j)]
1437+ mul! (û0, Cs_u, xs) # ys_u(k) = Cs_u*xs(k)
1438+ û0 .+ = u0 # û0(k) = u0(k) + ys_u(k)
1439+ model. f! (k1, x0, û0, d0, p)
1440+ model. f! (k2, x0next_Z̃, û0, d0next, p)
1441+ else # piecewise linear manipulated inputs u:
1442+ k1 .= lastk2
1443+ j == Hp && break # special case, treated after the loop
1444+ u0next = @views U0[(1 + nu* j): (nu* (j+ 1 ))]
1445+ û0next = @views Û0[(1 + nu* j): (nu* (j+ 1 ))]
1446+ mul! (û0next, Cs_u, xsnext_Z̃) # ys_u(k+1) = Cs_u*xs(k+1)
1447+ û0next .+ = u0next # û0(k+1) = u0(k+1) + ys_u(k+1)
1448+ model. f! (k2, x0next_Z̃, û0next, d0next, p)
1449+ lastk2 = k2
1450+ end
1451+ sdnext .= @. x0 - x0next_Z̃ + 0.5 * Ts* (k1 + k2)
14251452 x̂0 = x̂0next_Z̃ # using states in Z̃ for next iteration (allow parallel for)
14261453 d0 = d0next
14271454 end
1455+ if ! iszero (h)
1456+ # j = Hp special case: u(k+Hp-1) = u(k+Hp) since Hc ≤ Hp implies Δu(k+Hp)=0
1457+ x̂0, x̂0next_Z̃ = @views X̂0_Z̃[end - 2 nx̂+ 1 : end - nx̂], X̂0_Z̃[end - nx̂+ 1 : end ]
1458+ k1, k2 = @views K0[end - 2 nx+ 1 : end - nx], K0[end - nx+ 1 : end ] # k1 already filled
1459+ d0next = @views D̂0[end - nd+ 1 : end ]
1460+ û0next, u0next = @views Û0[end - nu+ 1 : end ], U0[end - nu+ 1 : end ] # correspond to u(k+Hp-1)
1461+ x0, x0next_Z̃, xsnext_Z̃ = @views x̂0[1 : nx], x̂0next_Z̃[1 : nx], x̂0next_Z̃[nx+ 1 : end ]
1462+ sdnext = @views geq[end - nx̂+ 1 : end - nx̂+ nx] # ssnext already filled
1463+ mul! (û0next, Cs_u, xsnext_Z̃)
1464+ û0next .+ = u0next
1465+ model. f! (k2, x0next_Z̃, û0next, d0next, p)
1466+ sdnext .= @. x0 - x0next_Z̃ + (Ts/ 2 )* (k1 + k2)
1467+ end
14281468 return geq
14291469end
14301470
0 commit comments