In this tutorial we explain the different ways to plot a solution of an optimal control problem.
Let us start by importing the necessary package.
Then, we define a simple optimal control problem and solve it.
@def ocp begin t ∈ [ 0, 1 ], time x ∈ R², state u ∈ R, control x(0) == [ -1, 0 ] x(1) == [ 0, 0 ] ẋ(t) == [ x₂(t), u(t) ] ∫( 0.5u(t)^2 ) → min end sol = solve(ocp, display=false)
The simplest way to plot the solution is to use the
plot function with only the solution as argument.
As you can see, it produces a grid of subplots. The left column contains the state trajectories, the right column the costate trajectories, and at the bottom we have the control trajectory.
Plots.jl can be passed to the
- In addition to
solyou can pass attributes to the full
Plot, see the attributes plot documentation from
Plots.jlfor more details. For instance, you can specify the size of the figure.
- You can also pass attributes to the subplots, see the attributes subplot documentation from
Plots.jlfor more details. However, it will affect all the subplots. For instance, you can specify the location of the legend.
- In the same way, you can pass axis attributes to the subplots, see the attributes axis documentation from
Plots.jlfor more details. It will also affect all the subplots. For instance, you can remove the grid.
plot(sol, size=(700, 450), legend=:bottomright, grid=false)
To specify series attributes to a specific subplot, you can use the optional keyword arguments
control_style which correspond respectively to the state, costate and control trajectories. See the attribute series documentation from
Plots.jl for more details. For instance, you can specify the color of the state trajectories and more.
plot(sol, state_style=(color=:blue,), costate_style=(color=:black, linestyle=:dash), control_style=(color=:red, linewidth=2))
If you prefer to get a more compact figure, you can use the
layout optional keyword argument with
:group value. It will group the state, costate and control trajectories in one subplot each.
plot(sol, layout=:group, size=(700, 300))
The default layout value is
:split which corresponds to the grid of subplots presented above.
You can plot the solution of a second optimal control problem on the same figure if it has the same number of states, costates and controls. For instance, consider the same optimal control problem but with a different initial condition.
@def ocp begin t ∈ [ 0, 1 ], time x ∈ R², state u ∈ R, control x(0) == [ -0.5, -0.5 ] x(1) == [ 0, 0 ] ẋ(t) == [ x₂(t), u(t) ] ∫( 0.5u(t)^2 ) → min end sol2 = solve(ocp, display=false)
We first plot the solution of the first optimal control problem, then, we plot the solution of the second optimal control problem on the same figure, but with dashed lines.
# first plot plt = plot(sol, size=(700, 450)) # second plot style = (linestyle=:dash, ) plot!(plt, sol2, state_style=style, costate_style=style, control_style=style)
For some problem, it is interesting to plot the norm of the control. You can do it by using the
control optional keyword argument with
:norm value. The default value is
:components. Let us illustrate this on the consumption minimisation orbital transfer problem from CTProlbems.jl.
using CTProblems prob = Problem(:orbital_transfert, :consumption) sol = prob.solution plot(sol, control=:norm, size=(800, 300), layout=:group)
You can of course create your own plots by getting the
control from the optimal control solution. For instance, let us plot the norm of the control for the orbital transfer problem.
using LinearAlgebra t = sol.times x = sol.state p = sol.costate u = sol.control plot(t, norm∘u, label="‖u‖")
normfunction is from
∘operator is the composition operator. Hence,
norm∘uis the function
t -> norm(u(t)).
sol.controlare functions that return the state, costate and control trajectories at a given time.