|
19 | 19 | const par = (9.8, 0.4, 1.2, 0.3)
|
20 | 20 | f(x, u, _ ) = pendulum(par, x, u)
|
21 | 21 | h(x, _ ) = [180/π*x[1]] # [°]
|
22 |
| -Ts, nu, nx, ny = 0.1, 1, 2, 1 |
23 |
| -model = NonLinModel(f, h, Ts, nu, nx, ny)</code></pre><pre class="documenter-example-output"><code class="nohighlight hljs ansi">NonLinModel with a sample time Ts = 0.1 s, RungeKutta solver and: |
| 22 | +nu, nx, ny, Ts = 1, 2, 1, 0.1 |
| 23 | +vu, vx, vy = ["\$τ\$ (N m)"], ["\$θ\$ (rad)", "\$ω\$ (rad/s)"], ["\$θ\$ (°)"] |
| 24 | +model = setname!(NonLinModel(f, h, Ts, nu, nx, ny); u=vu, x=vx, y=vy)</code></pre><pre class="documenter-example-output"><code class="nohighlight hljs ansi">NonLinModel with a sample time Ts = 0.1 s, RungeKutta solver and: |
24 | 25 | 1 manipulated inputs u
|
25 | 26 | 2 states x
|
26 | 27 | 1 outputs y
|
27 | 28 | 0 measured disturbances d</code></pre><p>The output function <span>$\mathbf{h}$</span> converts the <span>$θ$</span> angle to degrees. Note that special characters like <span>$θ$</span> can be typed in the Julia REPL or VS Code by typing <code>\theta</code> and pressing the <code><TAB></code> key. The tuple <code>par</code> is constant here to improve the <a href="https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-untyped-global-variables">performance</a>. Note that a 4th order <a href="../../public/sim_model/#RungeKutta"><code>RungeKutta</code></a> differential equation solver is used by default. It is good practice to first simulate <code>model</code> using <a href="../../public/generic_func/#ModelPredictiveControl.sim!"><code>sim!</code></a> as a quick sanity check:</p><pre><code class="language-julia hljs">using Plots
|
28 | 29 | u = [0.5]
|
29 | 30 | N = 35
|
30 | 31 | res = sim!(model, N, u)
|
31 |
| -plot(res, plotu=false)</code></pre><p><img src="../plot1_NonLinMPC.svg" alt="plot1_NonLinMPC"/></p><h2 id="Nonlinear-Model-Predictive-Controller"><a class="docs-heading-anchor" href="#Nonlinear-Model-Predictive-Controller">Nonlinear Model Predictive Controller</a><a id="Nonlinear-Model-Predictive-Controller-1"></a><a class="docs-heading-anchor-permalink" href="#Nonlinear-Model-Predictive-Controller" title="Permalink"></a></h2><p>An <a href="../../public/state_estim/#UnscentedKalmanFilter"><code>UnscentedKalmanFilter</code></a> estimates the plant state :</p><pre><code class="language-julia hljs">α=0.01; σQ=[0.1, 0.5]; σR=[0.5]; nint_u=[1]; σQint_u=[0.1] |
| 32 | +plot(res, plotu=false)</code></pre><p>The <a href="../../public/sim_model/#ModelPredictiveControl.setname!"><code>setname!</code></a> function allows customizing the Y-axis labels.</p><p><img src="../plot1_NonLinMPC.svg" alt="plot1_NonLinMPC"/></p><h2 id="Nonlinear-Model-Predictive-Controller"><a class="docs-heading-anchor" href="#Nonlinear-Model-Predictive-Controller">Nonlinear Model Predictive Controller</a><a id="Nonlinear-Model-Predictive-Controller-1"></a><a class="docs-heading-anchor-permalink" href="#Nonlinear-Model-Predictive-Controller" title="Permalink"></a></h2><p>An <a href="../../public/state_estim/#UnscentedKalmanFilter"><code>UnscentedKalmanFilter</code></a> estimates the plant state :</p><pre><code class="language-julia hljs">α=0.01; σQ=[0.1, 0.5]; σR=[0.5]; nint_u=[1]; σQint_u=[0.1] |
32 | 33 | estim = UnscentedKalmanFilter(model; α, σQ, σR, nint_u, σQint_u)</code></pre><pre class="documenter-example-output"><code class="nohighlight hljs ansi">UnscentedKalmanFilter estimator with a sample time Ts = 0.1 s, NonLinModel and:
|
33 | 34 | 1 manipulated inputs u (1 integrating states)
|
34 | 35 | 3 estimated states x̂
|
35 | 36 | 1 measured outputs ym (0 integrating states)
|
36 | 37 | 0 unmeasured outputs yu
|
37 | 38 | 0 measured disturbances d</code></pre><p>The vectors <code>σQ</code> and σR <code>σR</code> are the standard deviations of the process and sensor noises, respectively. The value for the velocity <span>$ω$</span> is higher here (<code>σQ</code> second value) since <span>$\dot{ω}(t)$</span> equation includes an uncertain parameter: the friction coefficient <span>$K$</span>. Also, the argument <code>nint_u</code> explicitly adds one integrating state at the model input, the motor torque <span>$τ$</span>, with an associated standard deviation <code>σQint_u</code> of 0.1 N m. The estimator tuning is tested on a plant with a 25 % larger friction coefficient <span>$K$</span>:</p><pre><code class="language-julia hljs">const par_plant = (par[1], par[2], 1.25*par[3], par[4])
|
38 | 39 | f_plant(x, u, _ ) = pendulum(par_plant, x, u)
|
39 |
| -plant = NonLinModel(f_plant, h, Ts, nu, nx, ny) |
| 40 | +plant = setname!(NonLinModel(f_plant, h, Ts, nu, nx, ny); u=vu, x=vx, y=vy) |
40 | 41 | res = sim!(estim, N, [0.5], plant=plant, y_noise=[0.5])
|
41 | 42 | plot(res, plotu=false, plotxwithx̂=true)</code></pre><p><img src="../plot2_NonLinMPC.svg" alt="plot2_NonLinMPC"/></p><p>The estimate <span>$x̂_3$</span> is the integrating state on the torque <span>$τ$</span> that compensates for static errors. The Kalman filter performance seems sufficient for control.</p><p>As the motor torque is limited to -1.5 to 1.5 N m, we incorporate the input constraints in a <a href="../../public/predictive_control/#NonLinMPC"><code>NonLinMPC</code></a>:</p><pre><code class="language-julia hljs">Hp, Hc, Mwt, Nwt = 20, 2, [0.5], [2.5]
|
42 | 43 | nmpc = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt=Inf)
|
|
53 | 54 | plot(res_ry)</code></pre><p><img src="../plot3_NonLinMPC.svg" alt="plot3_NonLinMPC"/></p><p>The controller seems robust enough to variations on <span>$K$</span> coefficient. Starting from this inverted position, the closed-loop response to a step disturbances of 10° is also satisfactory:</p><pre><code class="language-julia hljs">res_yd = sim!(nmpc, N, [180.0], plant=plant, x_0=[π, 0], x̂_0=[π, 0, 0], y_step=[10])
|
54 | 55 | plot(res_yd)</code></pre><p><img src="../plot4_NonLinMPC.svg" alt="plot4_NonLinMPC"/></p><h2 id="Economic-Model-Predictive-Controller"><a class="docs-heading-anchor" href="#Economic-Model-Predictive-Controller">Economic Model Predictive Controller</a><a id="Economic-Model-Predictive-Controller-1"></a><a class="docs-heading-anchor-permalink" href="#Economic-Model-Predictive-Controller" title="Permalink"></a></h2><p>Economic MPC can achieve the same objective but with lower economical costs. For this case study, the controller will aim to reduce the energy consumed by the motor. The power (W) transmitted by the motor to the pendulum is:</p><p class="math-container">\[Ẇ(t) = τ(t) ω(t)\]</p><p>Thus, the work (J) done by the motor from <span>$t = t_0$</span> to <span>$t_{end}$</span> is:</p><p class="math-container">\[W = \int_{t_0}^{t_{end}} Ẇ(t) \mathrm{d}t = \int_{t_0}^{t_{end}} τ(t) ω(t) \mathrm{d}t\]</p><p>With the sampling time <span>$T_s$</span> in s, the prediction horizon <span>$H_p$</span>, the limits defined as <span>$t_0 = k T_s$</span> and <span>$t_{end} = (k+H_p) T_s$</span>, and the left-endpoint rectangle method for the integral, we get:</p><p class="math-container">\[W ≈ T_s \sum_{j=0}^{H_p-1} τ(k + j) ω(k + j)\]</p><p>The objective function will now include an additive term that penalizes the work done by the motor <span>$W$</span> to reduce the energy consumption. Notice that <span>$W$</span> is a function of the manipulated input <span>$τ$</span> and the angular speed <span>$ω$</span>, a state that is not measured (only the angle <span>$θ$</span> is measured here). As the arguments of <a href="../../public/predictive_control/#NonLinMPC"><code>NonLinMPC</code></a> economic function <code>JE</code> do not include the states, the speed is now defined as an unmeasured output to design a Kalman Filter similar to the previous one (<span>$\mathbf{y^m} = θ$</span> and <span>$\mathbf{y^u} = ω$</span>):</p><pre><code class="language-julia hljs">h2(x, _ ) = [180/π*x[1], x[2]]
|
55 | 56 | nu, nx, ny = 1, 2, 2
|
56 |
| -model2 = NonLinModel(f , h2, Ts, nu, nx, ny) |
57 |
| -plant2 = NonLinModel(f_plant, h2, Ts, nu, nx, ny) |
| 57 | +model2 = setname!(NonLinModel(f , h2, Ts, nu, nx, ny), u=vu, x=vx, y=[vy; vx[2]]) |
| 58 | +plant2 = setname!(NonLinModel(f_plant, h2, Ts, nu, nx, ny), u=vu, x=vx, y=[vy; vx[2]]) |
58 | 59 | estim2 = UnscentedKalmanFilter(model2; σQ, σR, nint_u, σQint_u, i_ym=[1])</code></pre><pre class="documenter-example-output"><code class="nohighlight hljs ansi">UnscentedKalmanFilter estimator with a sample time Ts = 0.1 s, NonLinModel and:
|
59 | 60 | 1 manipulated inputs u (1 integrating states)
|
60 | 61 | 3 estimated states x̂
|
|
142 | 143 | return res
|
143 | 144 | end</code></pre><p>The <a href="../../public/generic_func/#ModelPredictiveControl.setmodel!"><code>setmodel!</code></a> method must be called after solving the optimization problem with <a href="../../public/predictive_control/#ModelPredictiveControl.moveinput!"><code>moveinput!</code></a>, and before updating the state estimate with <a href="../../public/generic_func/#ModelPredictiveControl.updatestate!"><code>updatestate!</code></a>. The <a href="../../public/generic_func/#ModelPredictiveControl.SimResult"><code>SimResult</code></a> object is for plotting purposes only. The adaptive <a href="../../public/predictive_control/#LinMPC"><code>LinMPC</code></a> performances are similar to the nonlinear MPC, both for the 180° setpoint:</p><pre><code class="language-julia hljs">res_slin = test_slmpc(model, mpc3, [180], plant, x_0=[0, 0])
|
144 | 145 | plot(res_slin)</code></pre><p><img src="../plot10_NonLinMPC.svg" alt="plot10_NonLinMPC"/></p><p>and the 10° step disturbance:</p><pre><code class="language-julia hljs">res_slin = test_slmpc(model, mpc3, [180], plant, x_0=[π, 0], y_step=[10])
|
145 |
| -plot(res_slin)</code></pre><p><img src="../plot11_NonLinMPC.svg" alt="plot11_NonLinMPC"/></p><p>The computations of the successive linearization MPC are about 125 times faster than the nonlinear MPC (0.00012 s per time steps versus 0.015 s per time steps, on average), an impressive gain for similar closed-loop performances!</p><section class="footnotes is-size-7"><ul><li class="footnote" id="footnote-1"><a class="tag is-link" href="#citeref-1">1</a>Arnström, D., Bemporad, A., and Axehill, D. (2022). A dual active-set solver for embedded quadratic programming using recursive LDLᵀ updates. IEEE Trans. Autom. Contr., 67(8). <a href="https://doi.org/doi:10.1109/TAC.2022.3176430">https://doi.org/doi:10.1109/TAC.2022.3176430</a>.</li></ul></section></article><nav class="docs-footer"><a class="docs-footer-prevpage" href="../linmpc/">« Linear Design</a><a class="docs-footer-nextpage" href="../../public/sim_model/">Plant Models »</a><div class="flexbox-break"></div><p class="footer-message">Powered by <a href="https://github.com/JuliaDocs/Documenter.jl">Documenter.jl</a> and the <a href="https://julialang.org/">Julia Programming Language</a>.</p></nav></div><div class="modal" id="documenter-settings"><div class="modal-background"></div><div class="modal-card"><header class="modal-card-head"><p class="modal-card-title">Settings</p><button class="delete"></button></header><section class="modal-card-body"><p><label class="label">Theme</label><div class="select"><select id="documenter-themepicker"><option value="auto">Automatic (OS)</option><option value="documenter-light">documenter-light</option><option value="documenter-dark">documenter-dark</option></select></div></p><hr/><p>This document was generated with <a href="https://github.com/JuliaDocs/Documenter.jl">Documenter.jl</a> version 1.4.0 on <span class="colophon-date" title="Wednesday 1 May 2024 15:27">Wednesday 1 May 2024</span>. Using Julia version 1.9.4.</p></section><footer class="modal-card-foot"></footer></div></div></div></body></html> |
| 146 | +plot(res_slin)</code></pre><p><img src="../plot11_NonLinMPC.svg" alt="plot11_NonLinMPC"/></p><p>The computations of the successive linearization MPC are about 125 times faster than the nonlinear MPC (0.00012 s per time steps versus 0.015 s per time steps, on average), an impressive gain for similar closed-loop performances!</p><section class="footnotes is-size-7"><ul><li class="footnote" id="footnote-1"><a class="tag is-link" href="#citeref-1">1</a>Arnström, D., Bemporad, A., and Axehill, D. (2022). A dual active-set solver for embedded quadratic programming using recursive LDLᵀ updates. IEEE Trans. Autom. Contr., 67(8). <a href="https://doi.org/doi:10.1109/TAC.2022.3176430">https://doi.org/doi:10.1109/TAC.2022.3176430</a>.</li></ul></section></article><nav class="docs-footer"><a class="docs-footer-prevpage" href="../linmpc/">« Linear Design</a><a class="docs-footer-nextpage" href="../../public/sim_model/">Plant Models »</a><div class="flexbox-break"></div><p class="footer-message">Powered by <a href="https://github.com/JuliaDocs/Documenter.jl">Documenter.jl</a> and the <a href="https://julialang.org/">Julia Programming Language</a>.</p></nav></div><div class="modal" id="documenter-settings"><div class="modal-background"></div><div class="modal-card"><header class="modal-card-head"><p class="modal-card-title">Settings</p><button class="delete"></button></header><section class="modal-card-body"><p><label class="label">Theme</label><div class="select"><select id="documenter-themepicker"><option value="auto">Automatic (OS)</option><option value="documenter-light">documenter-light</option><option value="documenter-dark">documenter-dark</option></select></div></p><hr/><p>This document was generated with <a href="https://github.com/JuliaDocs/Documenter.jl">Documenter.jl</a> version 1.4.0 on <span class="colophon-date" title="Wednesday 1 May 2024 21:20">Wednesday 1 May 2024</span>. Using Julia version 1.9.4.</p></section><footer class="modal-card-foot"></footer></div></div></div></body></html> |
0 commit comments