Skip to content

Commit d4ff9cb

Browse files
committed
up tuto lqr
1 parent 78d4eaa commit d4ff9cb

File tree

4 files changed

+657
-0
lines changed

4 files changed

+657
-0
lines changed

docs/extras/iss.jl

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
using OptimalControl # to define the optimal control problem and its flow
2+
using OrdinaryDiffEq # to get the Flow function from OptimalControl
3+
using MINPACK # NLE solver: use to solve the shooting equation
4+
using Plots # to plot the solution
5+
6+
const t0 = 0
7+
const tf = 1
8+
const x0 = -1
9+
const xf = 0
10+
const α = 1.5
11+
ocp = @def begin
12+
13+
t [t0, tf], time
14+
x R, state
15+
u R, control
16+
17+
x(t0) == x0
18+
x(tf) == xf
19+
20+
(t) == -x(t) + α * x(t)^2 + u(t)
21+
22+
( 0.5u(t)^2 ) min
23+
24+
end
25+
nothing # hide
26+
27+
u(x, p) = p
28+
φ = Flow(ocp, u)
29+
30+
π((x, p)) = x
31+
32+
S(p0) = π( φ(t0, x0, p0, tf) ) - xf # shooting function
33+
34+
ξ = [0.1] # initial guess
35+
36+
using MINPACK
37+
function fsolve(f, j, x; kwargs...)
38+
try
39+
MINPACK.fsolve(f, j, x; kwargs...)
40+
catch e
41+
println("Erreur using MINPACK")
42+
println(e)
43+
println("hybrj not supported. Replaced by hybrd even if it is not visible on the doc.")
44+
MINPACK.fsolve(f, x; kwargs...)
45+
end
46+
end
47+
48+
using DifferentiationInterface
49+
import ForwardDiff
50+
backend = AutoForwardDiff()
51+
52+
nle! = ( s, ξ) -> s[1] = S(ξ[1]) # auxiliary function
53+
jnle! = (js, ξ) -> jacobian!(nle!, similar(ξ), js, backend, ξ) #
54+
55+
56+
indirect_sol = fsolve(nle!, jnle!, ξ; show_trace=true) # resolution of S(p0) = 0
57+
p0_sol = indirect_sol.x[1] # costate solution
58+
println("\ncostate: p0 = ", p0_sol)
59+
println("shoot: |S(p0)| = ", abs(S(p0_sol)), "\n")
60+
61+
sol = φ((t0, tf), x0, p0_sol)
62+
plot(sol)
63+
64+
using Plots.PlotMeasures
65+
function Plots.plot(S::Function, p0::Float64; Np0=20, kwargs...)
66+
67+
# times for wavefronts
68+
times = range(t0, tf, length=3)
69+
70+
# times for trajectories
71+
tspan = range(t0, tf, length=100)
72+
73+
# interval of initial covector
74+
p0_min = -0.5
75+
p0_max = 2
76+
77+
# covector solution
78+
p0_sol = p0
79+
80+
# plot of the flow in phase space
81+
plt_flow = plot()
82+
p0s = range(p0_min, p0_max, length=Np0)
83+
for i eachindex(p0s)
84+
sol = φ((t0, tf), x0, p0s[i])
85+
x = state(sol).(tspan)
86+
p = costate(sol).(tspan)
87+
label = i==1 ? "extremals" : false
88+
plot!(plt_flow, x, p, color=:blue, label=label)
89+
end
90+
91+
# plot of wavefronts in phase space
92+
p0s = range(p0_min, p0_max, length=200)
93+
xs = zeros(length(p0s), length(times))
94+
ps = zeros(length(p0s), length(times))
95+
for i eachindex(p0s)
96+
sol = φ((t0, tf), x0, p0s[i], saveat=times)
97+
xs[i, :] .= state(sol).(times)
98+
ps[i, :] .= costate(sol).(times)
99+
end
100+
for j eachindex(times)
101+
label = j==1 ? "flow at times" : false
102+
plot!(plt_flow, xs[:, j], ps[:, j], color=:green, linewidth=2, label=label)
103+
end
104+
105+
#
106+
plot!(plt_flow, xlims=(-1.1, 1), ylims=(p0_min, p0_max))
107+
plot!(plt_flow, [0, 0], [p0_min, p0_max], color=:black, xlabel="x", ylabel="p", label="x=xf")
108+
109+
# solution
110+
sol = φ((t0, tf), x0, p0_sol)
111+
x = state(sol).(tspan)
112+
p = costate(sol).(tspan)
113+
plot!(plt_flow, x, p, color=:red, linewidth=2, label="extremal solution")
114+
plot!(plt_flow, [x[end]], [p[end]], seriestype=:scatter, color=:green, label=false)
115+
116+
# plot of the shooting function
117+
p0s = range(p0_min, p0_max, length=200)
118+
plt_shoot = plot(xlims=(p0_min, p0_max), ylims=(-2, 4), xlabel="p₀", ylabel="y")
119+
plot!(plt_shoot, p0s, S, linewidth=2, label="S(p₀)", color=:green)
120+
plot!(plt_shoot, [p0_min, p0_max], [0, 0], color=:black, label="y=0")
121+
plot!(plt_shoot, [p0_sol, p0_sol], [-2, 0], color=:black, label="p₀ solution", linestyle=:dash)
122+
plot!(plt_shoot, [p0_sol], [0], seriestype=:scatter, color=:green, label=false)
123+
124+
# final plot
125+
plot(plt_flow, plt_shoot; layout=(1,2), leftmargin=15px, bottommargin=15px, kwargs...)
126+
127+
end
128+
129+
p0_sol
130+
131+
plot(S, p0_sol; size=(800, 450))

docs/extras/lqr.jl

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using Pkg
2+
Pkg.activate("docs")
3+
4+
using OptimalControl
5+
using NLPModelsIpopt
6+
using Plots
7+
8+
function lqr(tf)
9+
10+
x0 = [ 0
11+
1 ]
12+
13+
A = [ 0 1
14+
-1 0 ]
15+
16+
B = [ 0
17+
1 ]
18+
19+
Q = [ 1 0
20+
0 1 ]
21+
22+
R = 1
23+
24+
ocp = @def begin
25+
t [0, tf], time
26+
x R², state
27+
u R, control
28+
x(0) == x0
29+
(t) == [x₂(t), - x₁(t) + u(t)]
30+
0.5( x₁(t)^2 + x₂(t)^2 + u(t)^2 ) min
31+
end
32+
33+
return ocp
34+
end
35+
36+
solutions = [] # empty list of solutions
37+
tfs = [3, 5, 30]
38+
39+
for tf tfs
40+
solution = solve(lqr(tf); display=false)
41+
push!(solutions, solution)
42+
end
43+
44+
plt = plot(solutions[1], time=:normalize)
45+
for sol solutions[2:end]
46+
plot!(plt, sol, time=:normalize)
47+
end
48+
49+
# we plot only the state and control variables and we add the legend
50+
N = length(tfs)
51+
px1 = plot(plt[1], legend=false, xlabel="s", ylabel="x₁")
52+
px2 = plot(plt[2], label=reshape(["tf = $tf" for tf tfs], (1, N)), xlabel="s", ylabel="x₂")
53+
pu = plot(plt[5], legend=false, xlabel="s", ylabel="u")
54+
55+
using Plots.PlotMeasures # for leftmargin, bottommargin
56+
plot(px1, px2, pu, layout=(1, 3), size=(800, 300), leftmargin=5mm, bottommargin=5mm)
57+
58+
59+
####
60+
61+
plt = plot(solutions[1], :state, :control; time=:normalize, label="tf = $(tfs[1])")
62+
for (tf, sol) zip(tfs[2:end], solutions[2:end])
63+
plot!(plt, sol, :state, :control; time=:normalize, label="tf = $tf")
64+
end
65+
66+
px1 = plot(plt[1], legend=false, xlabel="s", ylabel="x₁")
67+
px2 = plot(plt[2], legend=true, xlabel="s", ylabel="x₂")
68+
pu = plot(plt[3], legend=false, xlabel="s", ylabel="u")
69+
plot(px1, px2, pu, layout=(1, 3), size=(800, 300), leftmargin=5mm, bottommargin=5mm)

0 commit comments

Comments
 (0)