Trajectories
What is a NamedTrajectory?
A NamedTrajectory is the central data structure in DirectTrajOpt.jl. It stores:
- States and controls over time
- Time step information (fixed or variable)
- Boundary conditions (initial, final, goal)
- Bounds on variables
- Metadata about which variables are controls, timesteps, etc.
using DirectTrajOpt
using NamedTrajectoriesBasic Construction
Minimal Example
N = 10 # number of time steps10Specify component data as a NamedTuple:
data = (
x = randn(2, N), # 2D state
u = randn(1, N), # 1D control
Δt = fill(0.1, N) # time step
)
traj = NamedTrajectory(
data;
timestep=:Δt, # which variable represents time
controls=:u # which variable(s) are controls
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Access components:
println("State at time 1: ", traj.x[:, 1])
println("Control at time 5: ", traj.u[:, 5])
println("Total time: ", sum(traj.Δt))State at time 1: [0.07518792992328695, -0.13591268969036022]
Control at time 5: [-1.283638949219257]
Total time: 0.9999999999999999Trajectory Components
States
Variables that represent the system configuration. Can be multiple state vectors:
traj_multi = NamedTrajectory(
(
position = randn(3, N), # 3D position
velocity = randn(3, N), # 3D velocity
u = randn(2, N),
Δt = fill(0.1, N)
);
timestep=:Δt,
controls=:u
)N = 10, (position = 1:3, velocity = 4:6, u = 7:8, → Δt = 9:9)Controls
Variables that you can actuate. Can specify multiple control variables:
traj_multi_control = NamedTrajectory(
(
x = randn(2, N),
u1 = randn(1, N),
u2 = randn(1, N),
Δt = fill(0.1, N)
);
timestep=:Δt,
controls=(:u1, :u2)
)N = 10, (x = 1:2, u1 = 3:3, u2 = 4:4, → Δt = 5:5)Time Steps
Fixed time: All time steps equal (constant Δt)
traj_fixed_time = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt, # symbol pointing to timestep component
controls=:u
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Free time: Each time step is a decision variable (with bounds)
traj_free_time = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=(:u, :Δt), # Include Δt in controls for optimization
bounds=(Δt = (0.01, 0.5),) # Set bounds on time steps
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Boundary Conditions
Initial Conditions
Fix the starting state:
traj_initial = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
initial=(x = [0.0, 0.0],) # x₁ = [0, 0]
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Can also fix initial controls:
traj_initial_u = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
initial=(x = [0.0, 0.0], u = [0.0]) # x₁ = [0, 0], u₁ = 0
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Final Conditions
Fix the ending state:
traj_final = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
final=(x = [1.0, 0.0],) # xₖ = [1, 0]
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Goal Conditions
Similar to final, but typically used with terminal cost instead of hard constraint:
traj_goal = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
goal=(x = [1.0, 0.0],) # target: xₖ → [1, 0]
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Complete Example
traj_complete = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
initial=(x = [0.0, 0.0], u = [0.0]),
final=(u = [0.0],),
goal=(x = [1.0, 0.0],)
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Bounds on Variables
Bounds constrain variables to lie within specified ranges.
Scalar Bounds (Symmetric)
A single number creates symmetric bounds: -bound ≤ var ≤ bound
traj_scalar_bound = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
bounds=(u = 1.0,) # -1 ≤ u ≤ 1 for all components
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Applies to all components of the variable:
println("u bounds: ", traj_scalar_bound.bounds.u)u bounds: ([-1.0], [1.0])Output: ([-1.0], [1.0])
Tuple Bounds (Asymmetric)
A tuple (lower, upper) creates asymmetric bounds:
traj_tuple_bound = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
bounds=(u = (-2.0, 1.0),) # -2 ≤ u ≤ 1
)
println("u bounds: ", traj_tuple_bound.bounds.u)u bounds: ([-2.0], [1.0])Output: ([-2.0], [1.0])
Vector Bounds (Component-wise Symmetric)
A vector creates component-specific symmetric bounds:
traj_vector_bound = NamedTrajectory(
(x = randn(2, N), u = randn(2, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
bounds=(u = [1.0, 2.0],) # -1 ≤ u₁ ≤ 1, -2 ≤ u₂ ≤ 2
)
println("u bounds: ", traj_vector_bound.bounds.u)u bounds: ([-1.0, -2.0], [1.0, 2.0])Output: ([-1.0, -2.0], [1.0, 2.0])
Tuple of Vectors (Component-wise Asymmetric)
The most general form - specify lower and upper for each component:
traj_full_bound = NamedTrajectory(
(x = randn(2, N), u = randn(2, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
bounds=(u = ([-2.0, -1.0], [1.0, 3.0]),) # -2 ≤ u₁ ≤ 1, -1 ≤ u₂ ≤ 3
)
println("u bounds: ", traj_full_bound.bounds.u)u bounds: ([-2.0, -1.0], [1.0, 3.0])Output: ([-2.0, -1.0], [1.0, 3.0])
Multiple Variable Bounds
traj_multi_bounds = NamedTrajectory(
(x = randn(2, N), u = randn(2, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
bounds=(
x = 5.0, # -5 ≤ x ≤ 5 (both components)
u = [1.0, 2.0], # component-specific
Δt = (0.05, 0.15) # 0.05 ≤ Δt ≤ 0.15
)
)N = 10, (x = 1:2, u = 3:4, → Δt = 5:5)Time Step Bounds
For free-time problems, bound the time steps:
traj_time_bounds = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
bounds=(
u = 1.0,
Δt = (0.01, 0.2) # 0.01 ≤ Δt ≤ 0.2
)
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Accessing Trajectory Data
Direct Access
x_data = traj.x # Get all states (2 × N matrix)
u_data = traj.u # Get all controls (1 × N matrix)
x_final = traj.x[:, end] # Get final state2-element Vector{Float64}:
-0.23309388948949253
-0.8488999917117233Metadata
println("Number of time steps: ", traj.N)
println("State dimension: ", traj.dims.x)
println("Control dimension: ", traj.dims.u)
println("Total dimension: ", traj.dim)Number of time steps: 10
State dimension: 2
Control dimension: 1
Total dimension: 4Time Information
times = get_times(traj) # Cumulative time at each knot point
total_time = sum(traj.Δt)0.9999999999999999Building Trajectories for Optimization
Good Initialization Matters
Start with a reasonable guess:
- Linear interpolation between initial and final states
- Zero or small random controls
- Uniform time steps
x_init = [0.0, 0.0]
x_goal = [1.0, 1.0]2-element Vector{Float64}:
1.0
1.0Linear interpolation
x_guess = hcat([x_init + (x_goal - x_init) * (t / (N-1)) for t in 0:N-1]...)
traj_good_init = NamedTrajectory(
(
x = x_guess,
u = zeros(1, N),
Δt = fill(0.1, N)
);
timestep=:Δt,
controls=:u,
initial=(x = x_init,),
final=(x = x_goal,)
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Common Patterns
State Transfer Problem
Drive from initial to final state with bounded controls
traj_transfer = NamedTrajectory(
(x = randn(3, 50), u = randn(2, 50), Δt = fill(0.1, 50));
timestep=:Δt,
controls=:u,
initial=(x = zeros(3),),
final=(x = ones(3),),
bounds=(u = 1.0,)
)N = 50, (x = 1:3, u = 4:5, → Δt = 6:6)Minimum Time Problem
Free time steps, bounded, with time regularization
traj_mintime = NamedTrajectory(
(x = randn(2, 30), u = randn(1, 30), Δt = fill(0.1, 30));
timestep=:Δt,
controls=:u,
initial=(x = [0.0, 0.0],),
final=(x = [1.0, 0.0],),
bounds=(
u = 1.0,
Δt = (0.01, 0.5)
)
)N = 30, (x = 1:2, u = 3:3, → Δt = 4:4)Smooth Control Problem
Include control derivatives for smoothness
traj_smooth = NamedTrajectory(
(
x = randn(2, 40),
u = randn(2, 40),
du = zeros(2, 40), # control derivative
Δt = fill(0.1, 40)
);
timestep=:Δt,
controls=:u,
initial=(x = [0.0, 0.0], u = [0.0, 0.0]),
final=(x = [1.0, 0.0], u = [0.0, 0.0]),
bounds=(u = 1.0,)
)N = 40, (x = 1:2, u = 3:4, du = 5:6, → Δt = 7:7)Summary
| Concept | Syntax | Example |
|---|---|---|
| Fixed time | timestep=0.1 | timestep=0.1 |
| Free time | timestep=:Δt | timestep=:Δt |
| Initial condition | initial=(x = [...],) | initial=(x = [0, 0],) |
| Final condition | final=(x = [...],) | final=(x = [1, 0],) |
| Scalar bound | bounds=(u = 1.0,) | -1 ≤ u ≤ 1 |
| Tuple bound | bounds=(u = (-2, 1),) | -2 ≤ u ≤ 1 |
| Vector bound | bounds=(u = [1, 2],) | -1 ≤ u₁ ≤ 1, -2 ≤ u₂ ≤ 2 |
| Full bound | bounds=(u = ([-2,-1], [1,3]),) | -2 ≤ u₁ ≤ 1, -1 ≤ u₂ ≤ 3 |
Next Steps
- Integrators: Learn how dynamics are encoded
- Objectives: Define cost functions on trajectories
- Tutorials: See complete examples using trajectories
This page was generated using Literate.jl.