You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In this tutorial we explain the different ways to plot a solution of an optimal control problem.
3
+
In this tutorial, we explain the different options for plotting the solution of an optimal control problem using the `plot` and `plot!` functions, which are extensions of the [Plots.jl](https://docs.juliaplots.org) package. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
4
4
5
-
Let us start by importing the package to define the problem and solve it.
5
+
```julia
6
+
plot(args...; kw...) # creates a new Plot, and set it to be the `current`
7
+
plot!(args...; kw...) # modifies Plot `current()`
8
+
plot!(plt, args...; kw...) # modifies Plot `plt`
9
+
```
10
+
11
+
More precisely, the signature of `plot` is as follows.
12
+
13
+
```julia
14
+
functionplot(
15
+
sol; # optimal control solution
16
+
layout, # layout of the subplots
17
+
control, # plot the norm or components of the control
18
+
time, # normalise the time or not
19
+
size, # size of the figure
20
+
solution_label, # suffix for the labels
21
+
state_style, # style for the state trajectory
22
+
costate_style, # style for the costate trajectory
23
+
control_style, # style for the control trajectory
24
+
kwargs..., # attributes from Plots
25
+
)
26
+
```
27
+
28
+
In the following, we detail the roles of the arguments.
|[Add a plot](@ref tutorial-plot-add) |`solution_label`|
37
+
38
+
You can plot a solution obtained from the `solve` function, as well as from the flow computed using an optimal control problem and a control law. See, respectively, [Basic Concepts](@ref tutorial-plot-basic) and [From Flow](@ref tutorial-plot-flow) sections for more details.
39
+
40
+
You can also retrieve the state, the costate and the control to create your own plots, see [Custom plot](@ref tutorial-plot-custom) section.
41
+
42
+
## The problem and the solution
43
+
44
+
Let us start by importing the packages needed to define and solve the problem.
6
45
7
46
```@example main
8
47
using OptimalControl
9
48
using NLPModelsIpopt
10
49
```
11
50
12
-
Then, we define a simple optimal control problem and solve it.
51
+
We consider the simple optimal control problem from the [basic example tutorial](@ref tutorial-double-integrator-energy).
13
52
14
53
```@example main
54
+
const t0 = 0 # initial time
55
+
const tf = 1 # final time
56
+
const x0 = [ -1, 0 ] # initial condition
57
+
const xf = [ 0, 0 ] # final condition
58
+
15
59
ocp = @def begin
16
60
17
-
t ∈ [0, 1], time
61
+
t ∈ [t0, tf], time
18
62
x ∈ R², state
19
63
u ∈ R, control
20
64
21
-
x(0) == [-1, 0]
22
-
x(1) == [0, 0]
65
+
x(t0) == x0
66
+
x(tf) == xf
23
67
24
68
ẋ(t) == [x₂(t), u(t)]
25
69
@@ -31,92 +75,122 @@ sol = solve(ocp, display=false)
31
75
nothing # hide
32
76
```
33
77
34
-
## First ways to plot
78
+
## [Basic concepts](@id tutorial-plot-basic)
35
79
36
-
The simplest way to plot the solution is to use the `plot` function with only the solution as argument.
80
+
The simplest way to plot the solution is to use the `plot` function with the solution as the only argument.
37
81
38
-
!!! note "The plot function"
82
+
!!! warning
39
83
40
-
The plot function on a solution of an optimal control problem is an extension of the plot function from the package Plots.jl. Hence, we need to import this package to plot a solution.
84
+
The `plot` function for a solution of an optimal control problem extends the `plot` function from Plots. Therefore, you need to import this package in order to plot a solution.
41
85
42
86
```@example main
43
87
using Plots
44
88
plot(sol)
45
89
```
46
90
47
-
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.
91
+
In the figure above, we have a grid of subplots: the left column displays the state component trajectories, the right column shows the costate component trajectories, and the bottom row contains the control component trajectories.
92
+
93
+
As in Plots, input data is passed positionally (for example, `sol` in `plot(sol)`), and attributes are passed as keyword arguments (for example, `plot(sol; color = :blue)`). After executing `using Plots` in the REPL, you can use the `plotattr()` function to print a list of all available attributes for series, plots, subplots, or axes.
94
+
95
+
```julia
96
+
# Valid Operations
97
+
plotattr(:Plot)
98
+
plotattr(:Series)
99
+
plotattr(:Subplot)
100
+
plotattr(:Axis)
101
+
```
102
+
103
+
Once you have the list of attributes, you can either use the aliases of a specific attribute or inspect a specific attribute to display its aliases and description.
104
+
105
+
```@repl main
106
+
plotattr("color") # Specific Attribute Example
107
+
```
108
+
109
+
!!! warning
110
+
111
+
Some attributes have different default values in OptimalControl compared to Plots. For instance, the default figure size is 600x400 in Plots, while in OptimalControl, it depends on the number of states and controls.
48
112
49
-
Attributes from [Plots.jl](https://docs.juliaplots.org) can be passed to the `plot` function:
113
+
You can also visit the Plot documentation online to get the descriptions of the attributes:
114
+
115
+
- To pass attributes to the plot, see the [attributes plot](https://docs.juliaplots.org/latest/generated/attributes_plot/) documentation. For instance, you can specify the size of the figure.
116
+
- You can pass attributes to all subplots at once by referring to the [attributes subplot](https://docs.juliaplots.org/latest/generated/attributes_subplot/) documentation. For example, you can specify the location of the legends.
117
+
- Similarly, you can pass axis attributes to all subplots. See the [attributes axis](https://docs.juliaplots.org/latest/generated/attributes_axis/) documentation. For example, you can remove the grid from every subplot.
118
+
- Finally, you can pass series attributes to all subplots. Refer to the [attributes series](https://docs.juliaplots.org/latest/generated/attributes_series/) documentation. For instance, you can set the width of the curves using `linewidth`.
50
119
51
-
- In addition to `sol` you can pass attributes to the full plot, see the [attributes plot documentation](https://docs.juliaplots.org/latest/generated/attributes_plot/) from Plots.jl for more details. For instance, you can specify the size of the figure.
52
-
- You can also pass attributes to the subplots, see the [attributes subplot documentation](https://docs.juliaplots.org/latest/generated/attributes_subplot/) from Plots.jl for more details. However, it will affect all the subplots. For instance, you can specify the location of the legend.
53
-
- In the same way, you can pass axis attributes to the subplots, see the [attributes axis documentation](https://docs.juliaplots.org/latest/generated/attributes_axis/) from Plots.jl for more details. It will also affect all the subplots. For instance, you can remove the grid.
54
-
- In the same way, you can pass series attributes to the all the subplots, see the [attributes series documentation](https://docs.juliaplots.org/latest/generated/attributes_series/) from Plots.jl for more details. It will also affect all the subplots. For instance, you can set the width of the curves with `linewidth`.
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](https://docs.juliaplots.org/latest/generated/attributes_series/) from Plots.jl for more details. For instance, you can specify the color of the state trajectories and more.
125
+
To specify series attributes for a specific group of subplots (state, costate or control), you can use the optional keyword arguments `state_style`, `costate_style`, and `control_style`, which correspond to the state, costate, and control trajectories, respectively.
61
126
62
127
```@example main
63
128
plot(sol;
64
-
state_style = (color=:blue,),
65
-
costate_style = (color=:black, linestyle=:dash),
66
-
control_style = (color=:red, linewidth=2))
129
+
state_style = (color=:blue,), # style of the state trajectory
130
+
costate_style = (color=:black, linestyle=:dash), # style of the costate trajectory
131
+
control_style = (color=:red, linewidth=2)) # style of the control trajectory
67
132
```
68
133
69
-
## From Flow
134
+
## [From Flow](@id tutorial-plot-flow)
70
135
71
-
The previous resolution of the optimal control problem was done with the `solve` function. If you use an indirect shooting method and solve shooting equations, you may want to plot the associated solution. To do so, you need to use the `Flow` function to reconstruct the solution. See the [Indirect Simple Shooting](@reftutorial-indirect-simple-shooting) tutorial for an example. In our example, you must provide the maximising control $(x, p) \mapsto p_2$ together with the optimal control problem.
136
+
The previous solution of the optimal control problem was obtained using the `solve` function. If you prefer using an indirect shooting method and solving shooting equations, you may also want to plot the associated solution. To do this, you need to use the `Flow` function to reconstruct the solution. See the manual on [how to compute flows](@refmanual-flow) for more details. In our case, you must provide the maximizing control $(x, p) \mapsto p_2$ along with the optimal control problem. For an introduction to simple indirect shooting, see the [indirect simple shooting](@ref tutorial-indirect-simple-shooting) tutorial for an example.
72
137
73
138
!!! tip "Interactions with an optimal control solution"
74
139
75
-
Please check [`state`](@ref), [`costate`](@ref), [`control`](@ref) and [`variable`](@ref) to get data from the solution. The functions `state`, `costate` and `control` return functions of time and `variable` returns a vector.
140
+
Please check [`state`](@ref), [`costate`](@ref), [`control`](@ref), and [`variable`](@ref) to retrieve data from the solution. The functions `state`, `costate`, and `control` return functions of time, while `variable` returns a vector.
76
141
77
142
```@example main
78
143
using OrdinaryDiffEq
79
-
t0 = 0
80
-
tf = 1
81
-
x0 = [ -1, 0 ]
82
-
p0 = costate(sol)(t0)
83
-
f = Flow(ocp, (x, p) -> p[2])
84
-
sol_flow = f( (t0, tf), x0, p0 )
85
-
plot(sol_flow)
144
+
145
+
p = costate(sol) # costate as a function of time
146
+
p0 = p(t0) # costate solution at the initial time
147
+
f = Flow(ocp, (x, p) -> p[2]) # flow from an ocp and a control law
You can notice that the time grid has very few points. To have a better visualisation (the accuracy won't change), you can give a finer grid.
153
+
We may notice that the time grid contains very few points. This is evident from the subplot of $x_2$, or by retrieving the time grid directly from the solution.
## [Split versus group layout](@id tutorial-plot-layout)
96
168
97
169
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 for each.
98
170
99
171
```@example main
100
172
plot(sol; layout=:group, size=(800, 300))
101
173
```
174
+
175
+
The default layout value is `:split` which corresponds to the grid of subplots presented above.
102
176
103
-
!!! note "Default layout value"
104
-
105
-
The default layout value is `:split` which corresponds to the grid of subplots presented above.
177
+
```@example main
178
+
plot(sol; layout=:split)
179
+
```
106
180
107
-
## Additional plots
181
+
## [Add a plot](@id tutorial-plot-add)
108
182
109
183
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.
110
184
111
185
```@example main
112
186
ocp = @def begin
113
187
114
-
t ∈ [0, 1], time
188
+
t ∈ [t0, tf], time
115
189
x ∈ R², state
116
190
u ∈ R, control
117
191
118
-
x(0) == [-0.5, -0.5]
119
-
x(1) == [0, 0]
192
+
x(t0) == [-0.5, -0.5]
193
+
x(tf) == xf
120
194
121
195
ẋ(t) == [x₂(t), u(t)]
122
196
@@ -130,28 +204,27 @@ nothing # hide
130
204
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.
## [Plot the norm of the control](@id tutorial-plot-control)
141
212
142
-
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`.
213
+
For some problem, it is interesting to plot the (Euclidean) norm of the control. You can do it by using the `control` optional keyword argument with `:norm` value.
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.
!!! tip "Interactions with an optimal control solution"
225
+
## [Custom plot](@id tutorial-plot-custom)
153
226
154
-
Additionally to [`state`](@ref), [`costate`](@ref), [`control`](@ref) and [`variable`](@ref), the function [`time_grid`](@ref) returns the discretized time grid returned by the solver.
227
+
You can, of course, create your own plots by extracting the `state`, `costate`, and `control` from the optimal control solution. For instance, let us plot the norm of the control.
- The `∘` operator is the composition operator. Hence, `norm∘u` is the function `t -> norm(u(t))`.
169
242
170
-
## Normalized time
243
+
## [Normalised time](@id tutorial-plot-time)
171
244
172
-
We consider a [LQR example](@ref) and solve the problem for different values of the final time `tf`. Then, we plot the solutions on the same figure considering a normalized time $s=(t-t_0)/(t_f-t_0)$, thanks to the keyword argument `time=:normalize`of the `plot` function.
245
+
We consider a [LQR example](@ref) and solve the problem for different values of the final time `tf`. Then, we plot the solutions on the same figure using a normalized time $s = (t - t_0) / (t_f - t_0)$, enabled by the keyword argument `time = :normalize`(or `:normalise`) in the `plot` function.
173
246
174
247
```@example main
175
-
176
-
# parameters
177
-
x0 = [ 0
178
-
1 ]
179
-
180
-
# definition
248
+
# definition of the problem, parameterised by the final time
181
249
function lqr(tf)
182
250
183
251
ocp = @def begin
184
252
t ∈ [0, tf], time
185
253
x ∈ R², state
186
254
u ∈ R, control
187
-
x(0) == x0
255
+
x(0) == [0, 1]
188
256
ẋ(t) == [x₂(t), - x₁(t) + u(t)]
189
257
∫( 0.5(x₁(t)^2 + x₂(t)^2 + u(t)^2) ) → min
190
258
end
191
259
192
260
return ocp
193
261
end;
194
262
195
-
# solve
263
+
# solve the problems and store them
196
264
solutions = []
197
265
tfs = [3, 5, 30]
198
266
for tf ∈ tfs
@@ -206,7 +274,7 @@ for sol ∈ solutions[2:end]
206
274
plot!(plt, sol; time=:normalize)
207
275
end
208
276
209
-
# make a custom plot from created plots: only state and control are plotted
0 commit comments