@@ -136,3 +136,93 @@ p3 = plot(t_data,u_data[1,:],label="cold", linetype=:steppost); ylabel!("flow ra
136136plot!(t_data,u_data[2,:],label="hot", linetype=:steppost); xlabel!("time (s)")
137137p = plot(p1, p2, p3, layout=(3,1), fmt=:svg)
138138```
139+
140+ ### Nonlinear Model
141+
142+ In this example, the goal is to control the angular position `` θ `` of a pendulum
143+ attached to a motor. If the manipulated input is the motor torque `` τ `` , the vectors
144+ are:
145+
146+ ``` math
147+ \begin{aligned}
148+ \mathbf{u} &= \begin{bmatrix} τ \end{bmatrix} \\
149+ \mathbf{y} &= \begin{bmatrix} θ \end{bmatrix}
150+ \end{aligned}
151+ ```
152+
153+ The plant model is nonlinear:
154+
155+ ``` math
156+ \begin{aligned}
157+ \dot{θ}(t) &= ω(t) \\
158+ \dot{ω}(t) &= -\frac{g}{L}\sin\big( θ(t) \big) - \frac{k}{m} ω(t) + \frac{m}{L^2}τ(t)
159+ \end{aligned}
160+ ```
161+
162+ in which `` g `` is the gravitational acceleration, `` L `` , the pendulum length, `` k `` , the
163+ friction coefficient at the pivot point, and `` m `` , the mass attached at the end of the
164+ pendulum. Here, the explicit Euler method discretizes the system to construct a
165+ [ ` NonLinModel ` ] ( @ref ) :
166+
167+ ``` @example 2
168+ using ModelPredictiveControl
169+ function pendulum(par, x, u)
170+ g, L, k, m = par # [m/s], [m], [kg/s], [kg]
171+ θ, ω = x[1], x[2] # [rad], [rad/s]
172+ τ = u[1] # [N m]
173+ dθ = ω
174+ dω = -g/L*sin(θ) - k/m*ω + τ/m/L^2
175+ return [dθ, dω]
176+ end
177+ Ts = 0.1 # [s]
178+ par = (9.8, 0.4, 1.2, 0.3)
179+ f(x, u, _ ) = x + Ts*pendulum(par, x, u) # Euler method
180+ h(x, _ ) = [180/π*x[1]] # [°]
181+ nu, nx, ny = 1, 2, 1
182+ model = NonLinModel(f, h, Ts, nu, nx, ny)
183+ ```
184+
185+ The output function `` \mathbf{h} `` converts the angular position `` θ `` to degrees. It
186+ is good practice to first simulate ` model ` using [ ` sim! ` ] ( @ref ) as a quick sanity check:
187+
188+ ``` @example 2
189+ using Plots
190+ u = [0.5] # τ = 0.5 N m
191+ plot(sim!(model, 60, u), plotu=false)
192+ ```
193+
194+ An [ ` UnscentedKalmanFilter ` ] ( @ref ) estimates the plant state :
195+
196+ ``` @example 2
197+ estim = UnscentedKalmanFilter(model, σQ=[0.5, 2.5], σQ_int=[0.5])
198+ ```
199+
200+ The standard deviation of the angular velocity `` ω `` is higher here (second value of ` σQ ` )
201+ since `` \dot{ω}(t) `` equation includes the friction coefficient `` k `` , an uncertain
202+ parameter. The estimator tuning is tested on a simulated plant with a different `` k `` value:
203+
204+ ``` @example 2
205+ par_plant = (par[1], par[2], par[3] + 0.25, par[4])
206+ f_plant(x, u, _) = x + Ts*pendulum(par_plant, x, u)
207+ plant = NonLinModel(f_plant, h, Ts, nu, nx, ny)
208+ res = sim!(estim, 30, [0.5], plant=plant, y_noise=[0.5]) # τ = 0.5 N m
209+ p2 = plot(res, plotu=false, plotx=true, plotx̂=true)
210+ ```
211+
212+ The Kalman filter performance seems sufficient for control applications. As the motor torque
213+ is limited to -1.5 to 1.5 N m, we incorporate the manipulated input constraints in a
214+ [ ` NonLinMPC ` ] ( @ref ) :
215+
216+ ``` @example 2
217+ mpc = NonLinMPC(estim, Hp=20, Hc=2, Mwt=[0.1], Nwt=[1.0], Cwt=Inf)
218+ mpc = setconstraint!(mpc, umin=[-1.5], umax=[+1.5])
219+ ```
220+
221+ We test ` mpc ` performance on ` plant ` by imposing an angular setpoint of 180° (inverted position):
222+
223+ ``` @example 2
224+ res = sim!(mpc, 30, [180.0], x̂0=zeros(mpc.estim.nx̂), plant=plant, x0=zeros(plant.nx))
225+ plot(res, plotŷ=true)
226+ ```
227+
228+ The controller here seems robust enough to variations on `` k `` coefficients.
0 commit comments