Plot a solution

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.

using OptimalControl

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)

First ways to plot

The simplest way to plot the solution is to use the plot function with only the solution as argument.

plot(sol)
Example block output

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.

Attributes from Plots.jl can be passed to the plot function:

  • In addition to sol you can pass attributes to the full Plot, see the attributes plot documentation from Plots.jl for 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.jl for 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.jl for 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)
Example block output

To specify series attributes to a specific subplot, you can use the optional keyword arguments state_style, costate_style and 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))
Example block output

Split versus group layout

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))
Example block output
Default layout value

The default layout value is :split which corresponds to the grid of subplots presented above.

Additional plots

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)
Example block output

Plot the norm of the control

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)
Example block output

Custom plot

You can of course create your own plots by getting the state, costate and 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‖")
Example block output
Nota bene
  • The norm function is from LinearAlgebra.jl.
  • The operator is the composition operator. Hence, norm∘u is the function t -> norm(u(t)).
  • The sol.state, sol.costate and sol.control are functions that return the state, costate and control trajectories at a given time.