Skip to content

Commit 4f63d16

Browse files
committed
added: simple ModelingToolkit example in manual
1 parent 4a757cd commit 4f63d16

File tree

5 files changed

+142
-5
lines changed

5 files changed

+142
-5
lines changed

docs/Project.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
55
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
66
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
77
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
8+
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
89
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
910

1011
[compat]
1112
ControlSystemsBase = "1"
13+
DAQP = "0.5"
1214
Documenter = "1"
15+
JuMP = "1"
1316
LinearAlgebra = "1.6"
1417
Logging = "1.6"
18+
ModelingToolkit = "9"
19+
Plots = "1"

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ makedocs(
2727
"Examples" => [
2828
"Linear Design" => "manual/linmpc.md",
2929
"Nonlinear Design" => "manual/nonlinmpc.md",
30+
"MTK Integration" => "manual/mtk.md",
3031
],
3132
],
3233
"Functions" => [

docs/src/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Pages = [
2929
"manual/installation.md",
3030
"manual/linmpc.md",
3131
"manual/nonlinmpc.md",
32+
"manual/mtk.md"
3233
]
3334
```
3435

docs/src/manual/mtk.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# [Manual: ModelingToolkit Integration](@id man_mtk)
2+
3+
```@contents
4+
Pages = ["mtk.md"]
5+
```
6+
7+
```@setup 1
8+
using Logging; errlogger = ConsoleLogger(stderr, Error);
9+
old_logger = global_logger(); global_logger(errlogger);
10+
```
11+
12+
## Pendulum Example
13+
14+
This example integrates the simple pendulum model of the [last section](@man_nonlin) in the
15+
[ModelingToolkit.jl](https://docs.sciml.ai/ModelingToolkit/stable/) (MTK) framework and
16+
extracts appropriate `f!` and `h!` functions to construct a [`NonLinModel`](@ref).
17+
18+
!!! danger "Disclaimer"
19+
This simple example is not an official interface to `ModelingToolkit.jl`. It is provided
20+
as a basic template to combine both packages. There is no guarantee that it will work
21+
for all corner cases.
22+
23+
We first construct and instantiate the pendulum model:
24+
25+
```@example 1
26+
using ModelPredictiveControl, ModelingToolkit
27+
using ModelingToolkit: D_nounits as D, t_nounits as t, varmap_to_vars
28+
@mtkmodel Pendulum begin
29+
@parameters begin
30+
g = 9.8
31+
L = 0.4
32+
K = 1.2
33+
m = 0.3
34+
end
35+
@variables begin
36+
θ(t) # state
37+
ω(t) # state
38+
τ(t) # input
39+
y(t) # output
40+
end
41+
@equations begin
42+
D(θ) ~ ω
43+
D(ω) ~ -g/L*sin(θ) - K/m*ω + τ/m/L^2
44+
y ~ θ * 180 / π
45+
end
46+
end
47+
@named mtk_model = Pendulum()
48+
mtk_model = complete(mtk_model)
49+
```
50+
51+
We than convert the MTK model to an [input-output system](https://docs.sciml.ai/ModelingToolkit/stable/basics/InputOutput/):
52+
53+
```@example 1
54+
function generate_f_h(model, inputs, outputs)
55+
(_, f_ip), dvs, psym, io_sys = ModelingToolkit.generate_control_function(
56+
model, inputs, split=false; outputs
57+
)
58+
if any(ModelingToolkit.is_alg_equation, equations(io_sys))
59+
error("Systems with algebraic equations are not supported")
60+
end
61+
h_ = ModelingToolkit.build_explicit_observed_function(io_sys, outputs; inputs = inputs)
62+
nx = length(dvs)
63+
vx = string.(dvs)
64+
par = varmap_to_vars(defaults(io_sys), psym)
65+
function f!(ẋ, x, u, _ , _ )
66+
f_ip(ẋ, x, u, par, 1)
67+
nothing
68+
end
69+
function h!(y, x, _ , _ )
70+
y .= h_(x, 1, par, 1)
71+
nothing
72+
end
73+
return f!, h!, nx, vx
74+
end
75+
inputs, outputs = [mtk_model.τ], [mtk_model.y]
76+
f!, h!, nx, vx = generate_f_h(mtk_model, inputs, outputs)
77+
nu, ny, Ts = length(inputs), length(outputs), 0.1
78+
vu, vy = ["\$τ\$ (Nm)"], ["\$θ\$ (°)"]
79+
nothing # hide
80+
```
81+
82+
A [`NonLinModel`](@ref) can now be constructed:
83+
84+
```@example 1
85+
model = setname!(NonLinModel(f!, h!, Ts, nu, nx, ny); u=vu, x=vx, y=vy)
86+
```
87+
88+
We also instantiate a plant model with a 25 % larger friction coefficient ``K``:
89+
90+
```@example 1
91+
mtk_model.K = defaults(mtk_model)[mtk_model.K] * 1.25
92+
f_plant, h_plant, _, _ = generate_f_h(mtk_model, inputs, outputs)
93+
plant = setname!(NonLinModel(f_plant, h_plant, Ts, nu, nx, ny); u=vu, x=vx, y=vy)
94+
```
95+
96+
We can than reproduce the Kalman filter and the controller design of the [last section](@man_nonlin):
97+
98+
```@example 1
99+
α=0.01; σQ=[0.1, 1.0]; σR=[5.0]; nint_u=[1]; σQint_u=[0.1]
100+
estim = UnscentedKalmanFilter(model; α, σQ, σR, nint_u, σQint_u)
101+
Hp, Hc, Mwt, Nwt = 20, 2, [0.5], [2.5]
102+
nmpc = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt=Inf)
103+
umin, umax = [-1.5], [+1.5]
104+
nmpc = setconstraint!(nmpc; umin, umax)
105+
```
106+
107+
The angular setpoint response is identical:
108+
109+
```@example 1
110+
res_ry = sim!(nmpc, N, [180.0], plant=plant, x_0=[0, 0], x̂_0=[0, 0, 0])
111+
display(plot(res_ry))
112+
savefig("plot1_MTK.svg"); nothing # hide
113+
```
114+
115+
![plot1_MTK](plot1_MTK.svg)
116+
117+
and also the output disturbance rejection:
118+
119+
```@example 1
120+
res_yd = sim!(nmpc, N, [180.0], plant=plant, x_0=[π, 0], x̂_0=[π, 0, 0], y_step=[10])
121+
display(plot(res_yd))
122+
savefig("plot2_MTK.svg"); nothing # hide
123+
```
124+
125+
![plot2_MTK](plot2_MTK.svg)
126+
127+
```@setup 1
128+
global_logger(old_logger);
129+
```

docs/src/manual/nonlinmpc.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ model = setname!(NonLinModel(f, h, Ts, nu, nx, ny; p=p_model); u=vu, x=vx, y=vy)
6464

6565
The output function ``\mathbf{h}`` converts the ``θ`` angle to degrees. Note that special
6666
characters like ``θ`` can be typed in the Julia REPL or VS Code by typing `\theta` and
67-
pressing the `<TAB>` key. Note that the model parameter `p` can be any
68-
A 4th order [`RungeKutta`](@ref) method solves the differential
69-
equations by default. It is good practice to first simulate `model` using [`sim!`](@ref) as
70-
a quick sanity check:
67+
pressing the `<TAB>` key. Note that the parameter `p` can be of any type but use a mutable
68+
type like a vector of you want to modify it later. A 4th order [`RungeKutta`](@ref) method
69+
solves the differential equations by default. It is good practice to first simulate `model`
70+
using [`sim!`](@ref) as a quick sanity check:
7171

7272
```@example 1
7373
using Plots
@@ -194,7 +194,8 @@ output vector of `plant` argument corresponds to the model output vector in `mpc
194194
We can now define the ``J_E`` function and the `empc` controller:
195195

196196
```@example 1
197-
function JE(UE, ŶE, _ , Ts)
197+
function JE(UE, ŶE, _ , p)
198+
Ts = p
198199
τ, ω = UE[1:end-1], ŶE[2:2:end-1]
199200
return Ts*sum(τ.*ω)
200201
end

0 commit comments

Comments
 (0)