|
| 1 | +using Test #src |
| 2 | +# # Example: Single Pendulum solved by [Uniform grid abstraction](https://github.com/dionysos-dev/Dionysos.jl/blob/master/docs/src/manual/manual.md#solvers). |
| 3 | +# |
| 4 | +# A simple pendulum is a typical example of a nonlinear dynamical system. |
| 5 | +# The pendulum's state is represented by its angular position ($x_1$ in radians) and angular velocity ($x_2$ in radians per second), and it is influenced by |
| 6 | +# gravitational forces ($g$ in meter per squared second), the length of the pendulum ($l$ in meter) and an external control input ($u$) that is a torque/force applied to modulate its behavior. |
| 7 | +# The objective is to drive the pendulum from a specified initial state (near the stable downward position) |
| 8 | +# to a target state (near the upright position), respecting constraints on both the state variables and the control input. |
| 9 | +# |
| 10 | +# The dynamics of the pendulum are given by: |
| 11 | +# ```math |
| 12 | +# dot(x_1) = x_2 |
| 13 | +# dot(x_2) = -g/l \sin(x_1) + u |
| 14 | +# ``` |
| 15 | +# |
| 16 | +# Considering this as a reachability problem, we will use it to showcase the capabilities of the Uniform grid abstraction solving typical problem in Dionysos. |
| 17 | +# The initial and target sets are defined as intervals in the state space. |
| 18 | +# x1_initial = [5.0 * pi / 180.0, -5.0 * pi / 180.0] |
| 19 | +# x2_initial = [0.5, -0.5] |
| 20 | +# x1_target = [pi + 5.0 * pi / 180.0, pi - 5.0 * pi / 180.0] |
| 21 | +# x2_target = [1.0, -1.0] |
| 22 | + |
| 23 | +# First, let us import [StaticArrays](https://github.com/JuliaArrays/StaticArrays.jl) and [Plots](https://github.com/JuliaPlots/Plots.jl). |
| 24 | +using StaticArrays, Plots |
| 25 | + |
| 26 | +# At this point, we import Dionysos and JuMP. |
| 27 | +using Dionysos, JuMP |
| 28 | + |
| 29 | +# Define the problem using JuMP |
| 30 | +# We first create a JuMP model: |
| 31 | +model = Model(Dionysos.Optimizer) |
| 32 | + |
| 33 | +# Define the discretization step |
| 34 | +hx = 0.05 |
| 35 | +l = 1.0 |
| 36 | +g = 9.81 |
| 37 | + |
| 38 | +# Define the state variables: x1(t), x2(t) |
| 39 | +x_low, x_upp = [-π, -10.0], [π + pi, 10.0] |
| 40 | +@variable(model, x_low[i] <= x[i = 1:2] <= x_upp[i]) |
| 41 | + |
| 42 | +# Define the control variables: u1(t), u2(t) |
| 43 | +@variable(model, -3.0 <= u <= 3.0) |
| 44 | + |
| 45 | +# Define the dynamics |
| 46 | +@constraint(model, ∂(x[1]) == x[2]) |
| 47 | +@constraint(model, ∂(x[2]) == -(g / l) * sin(x[1]) + u) |
| 48 | + |
| 49 | +# Define the initial and target sets |
| 50 | +x1_initial, x2_initial = (5.0 * pi / 180.0) .* [-1, 1], 0.5 .* [-1, 1] |
| 51 | +x1_target, x2_target = pi .+ (5.0 * pi / 180.0) .* [-1, 1], 1.0 .* [-1, 1] |
| 52 | + |
| 53 | +@constraint(model, start(x[1]) in MOI.Interval(x1_initial...)) |
| 54 | +@constraint(model, start(x[2]) in MOI.Interval(x2_initial...)) |
| 55 | + |
| 56 | +@constraint(model, final(x[1]) in MOI.Interval(x1_target...)) |
| 57 | +@constraint(model, final(x[2]) in MOI.Interval(x2_target...)) |
| 58 | + |
| 59 | +# ### Definition of the abstraction |
| 60 | + |
| 61 | +# Definition of the grid of the state-space on which the abstraction is based (origin `x0` and state-space discretization `h`): |
| 62 | + |
| 63 | +# We define the growth bound function of $f$: |
| 64 | +function jacobian_bound(u) |
| 65 | + return SMatrix{2, 2}(0.0, 1.0, (g / l), 0) |
| 66 | +end |
| 67 | +set_attribute(model, "jacobian_bound", jacobian_bound) |
| 68 | + |
| 69 | +set_attribute(model, "time_step", 0.1) |
| 70 | + |
| 71 | +x0 = SVector(0.0, 0.0); |
| 72 | +h = SVector(hx, hx); |
| 73 | +set_attribute(model, "state_grid", Dionysos.Domain.GridFree(x0, h)) |
| 74 | + |
| 75 | +# Definition of the grid of the input-space on which the abstraction is based (origin `u0` and input-space discretization `h`): |
| 76 | +u0 = SVector(0.0); |
| 77 | +h = SVector(0.3); |
| 78 | +set_attribute(model, "input_grid", Dionysos.Domain.GridFree(u0, h)) |
| 79 | + |
| 80 | +# Solving the problem |
| 81 | +optimize!(model) |
| 82 | + |
| 83 | +# Get the results |
| 84 | +abstract_system = get_attribute(model, "abstract_system"); |
| 85 | +abstract_problem = get_attribute(model, "abstract_problem"); |
| 86 | +abstract_controller = get_attribute(model, "abstract_controller"); |
| 87 | +concrete_controller = get_attribute(model, "concrete_controller") |
| 88 | +concrete_problem = get_attribute(model, "concrete_problem"); |
| 89 | +concrete_system = concrete_problem.system |
| 90 | + |
| 91 | +# ### Trajectory display |
| 92 | +nstep = 100 |
| 93 | +function reached(x) |
| 94 | + if x ∈ concrete_problem.target_set |
| 95 | + return true |
| 96 | + else |
| 97 | + return false |
| 98 | + end |
| 99 | +end |
| 100 | +x0 = SVector(Dionysos.Utils.sample(concrete_problem.initial_set)...) |
| 101 | +control_trajectory = Dionysos.System.get_closed_loop_trajectory( |
| 102 | + get_attribute(model, "discretized_system"), |
| 103 | + concrete_controller, |
| 104 | + x0, |
| 105 | + nstep; |
| 106 | + stopping = reached, |
| 107 | +) |
| 108 | + |
| 109 | +using Plots |
| 110 | + |
| 111 | +# Here we display the coordinate projection on the two first components of the state space along the trajectory. |
| 112 | +fig = plot(; aspect_ratio = :equal); |
| 113 | + |
| 114 | +# We display the concrete domain |
| 115 | +plot!(concrete_system.X); |
| 116 | + |
| 117 | +# We display the abstract domain |
| 118 | +plot!( |
| 119 | + Dionysos.Symbolic.get_domain_from_symbols( |
| 120 | + abstract_system, |
| 121 | + abstract_problem.initial_set, |
| 122 | + ); |
| 123 | + color = :green, |
| 124 | + opacity = 1.0, |
| 125 | +); |
| 126 | +plot!( |
| 127 | + Dionysos.Symbolic.get_domain_from_symbols(abstract_system, abstract_problem.target_set); |
| 128 | + color = :red, |
| 129 | + opacity = 1.0, |
| 130 | +); |
| 131 | +plot!(control_trajectory; markersize = 1, arrows = false) |
| 132 | +display(fig) |
0 commit comments