Quickstart Guide
Getting set up
To install NamedTrajectories simply enter the package manager in the Julia REPL with ] and run
pkg> add NamedTrajectoriesThen just use the package as usual with
using NamedTrajectoriesFor the following examples let's work with a simple trajectory
\[\qty{z_t = \mqty(x_t \\ u_t)}_{t=1:T}\]
where $x_t$ is the state and $u_t$ is the control at a time indexed by $t$. Together $z_t$ is referred to as a knot point and a NamedTrajectory essentially just stores a collection of knot points and makes it easy to access the state and control variables.
Creating a variable-timestep NamedTrajectory
Here we will create a NamedTrajectory with a variable timestep.
# define the number of timesteps
T = 10
Δt = 0.1
# define the knot point data as a NamedTuple of matrices
data = (
x = rand(3, T),
u = rand(2, T),
Δt = fill(Δt, T),
)
# we must specify a timestep and control variable for the NamedTrajectory.
timestep = :Δt
control = :u
# we can now create a `NamedTrajectory` object
traj = NamedTrajectory(data; timestep=timestep, controls=control)
# we can return the names of the stored variables
traj.names(:x, :u, :Δt)Adding more problem data
In many settings we will want to specify the problem data of our NamedTrajectory – e.g. bounds, initial values, final values, and goal values.
# define the number of timesteps
T = 10
# define the knot point data as a NamedTuple of matrices
data = (
x = rand(3, T),
u = rand(2, T),
Δt = rand(T),
)
# define initial values
initial = (
x = [1.0, 0.0, 0.0],
u = [0.0, 0.0],
)
# define final value, here just on the control
final = (
u = [0.0, 0.0],
)
# define bounds
bounds = (
x = 1.0,
u = 1.0
)
# set a goal for the state
goal = (
x = [0.0, 0.0, 1.0],
)
# we must specify a timestep and control variable for the NamedTrajectory
timestep = :Δt
control = :u
# we can now create a `NamedTrajectory` object
traj = NamedTrajectory(
data;
timestep=timestep,
controls=control,
initial=initial,
final=final,
bounds=bounds,
goal=goal
)
# we can then show the bounds
traj.bounds(x = ([-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]), u = ([-1.0, -1.0], [1.0, 1.0]))Retrieving data
There are a number of ways to access data, for example
traj.x3×10 view(reshape(view(::Vector{Float64}, :), 6, 10), 1:3, :) with eltype Float64:
0.062621 0.683595 0.0188711 0.775151 … 0.143919 0.774154 0.682823
0.884868 0.785242 0.758395 0.567504 0.23007 0.447008 0.575099
0.429379 0.616285 0.621487 0.582538 0.812058 0.831208 0.513353returns the data matrix associated with the state variable x.
traj.data6×10 reshape(view(::Vector{Float64}, :), 6, 10) with eltype Float64:
0.062621 0.683595 0.0188711 0.775151 … 0.143919 0.774154 0.682823
0.884868 0.785242 0.758395 0.567504 0.23007 0.447008 0.575099
0.429379 0.616285 0.621487 0.582538 0.812058 0.831208 0.513353
0.587826 0.28598 0.950745 0.137184 0.896353 0.819744 0.225621
0.114243 0.307589 0.975214 0.714748 0.822505 0.878333 0.244404
0.725331 0.56313 0.829107 0.35452 … 0.658809 0.547748 0.757093returns the all of the data as a matrix where each column is a knot point.
traj.datavec60-element Vector{Float64}:
0.06262099524346576
0.8848678735789626
0.42937946294245677
0.5878258153546183
0.11424265685968238
0.7253306763262346
0.68359461381518
0.7852419447611848
0.6162847299083707
0.28598018507275613
⋮
0.8197442349881201
0.8783330403397912
0.5477478124617645
0.6828229767207316
0.5750992198570385
0.513353164729202
0.2256208373992138
0.24440352127471532
0.7570934189785193returns the all of the data as a view of the data matrix as a vector – useful for passing data to solvers.
traj[1]KnotPoint{Float64, (:x, :u, :Δt), Tuple{UnitRange{Int64}, UnitRange{Int64}, UnitRange{Int64}}, Tuple{Symbol, Symbol, Symbol}, Tuple{Symbol, Symbol}}(1, [0.06262099524346576, 0.8848678735789626, 0.42937946294245677, 0.5878258153546183, 0.11424265685968238, 0.7253306763262346], 0.7253306763262346, (x = 1:3, u = 4:5, Δt = 6:6), (:x, :u, :Δt), (:u, :Δt))returns a KnotPoint.
traj[1].x3-element view(reshape(view(::Vector{Float64}, :), 6, 10), 1:3, 1) with eltype Float64:
0.06262099524346576
0.8848678735789626
0.42937946294245677returns the state at the first knot point.
get_times(traj)10-element Vector{Float64}:
0.0
0.7253306763262346
1.288460717598963
2.1175680766918323
2.4720884125110842
3.25633676839012
3.4889586199927343
3.9839081318642693
4.642716712252917
5.190464524714682returns the times of the knot points.
get_timesteps(traj)10-element reshape(view(reshape(view(::Vector{Float64}, :), 6, 10), 6:6, :), 10) with eltype Float64:
0.7253306763262346
0.5631300412727286
0.8291073590928691
0.35452033581925213
0.7842483558790359
0.23262185160261428
0.49494951187153524
0.6588085803886482
0.5477478124617645
0.7570934189785193returns the timesteps of the knot points, as vector.
Retrieving metadata
We can also retrieve metadata about the trajectory, for example
traj.names(:x, :u, :Δt)returns the names of the variables stored in the trajectory.
traj.dims(x = 3, u = 2, Δt = 1)returns the dimensions of the variables stored in the trajectory.
traj.T10returns the number of knot points in the trajectory.
traj.components(x = 1:3, u = 4:5, Δt = 6:6)returns the components of the trajectory.
Updating problem data
The NamedTrajectory can be updated by accessing fields and replacing the data.
We also have update! to update trajectory components, and update_bound!, which allows you to pass in the same kinds of bounds available at construction (e.g., an Int or Tuple). The bound will get shaped to match the trajectory component dimensions just like at construction. These methods cannot be used to update non-existent bounds or components.
For efficiency, a trajectory cannot add new data after it is constructed. However, we have convenience methods like add_component that build a new trajectory with added data.
update_bound!(traj, :x, 2.) # TODO: consider fleshing out this section with more examples of updating trajectory components, knot points, globals, bounds, etc.This page was generated using Literate.jl.