|
| 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 | + |
| 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 | + |
| 126 | + |
| 127 | +```@setup 1 |
| 128 | +global_logger(old_logger); |
| 129 | +``` |
0 commit comments