Problem Formulation
Overview
DirectTrajOpt.jl solves direct trajectory optimization problems using direct transcription, which converts continuous-time optimal control problems into finite-dimensional nonlinear programs (NLPs).
The General Form
A trajectory optimization problem has the form:
\[\begin{align*} \underset{x_{1:N}, u_{1:N}}{\text{minimize}} \quad & J(x_{1:N}, u_{1:N}) \\ \text{subject to} \quad & f(x_{k+1}, x_k, u_k, \Delta t, t_k) = 0, \quad k = 1, \ldots, N-1\\ & c_k(x_k, u_k) \geq 0, \quad k = 1, \ldots, N \\ & x_1 = x_{\text{init}}, \quad x_N = x_{\text{goal}} \\ \end{align*}\]
Let's break down each component:
Decision Variables
States: x₁, x₂, ..., xₖ
The state represents the configuration of your system at each time step.
- For a robot arm: joint angles and velocities
- For a spacecraft: position and velocity
- For a quantum system: state vector or unitary operator
Controls: u₁, u₂, ..., uₖ
The control (or input) represents what you can actuate.
- For a robot: motor torques
- For a spacecraft: thruster forces
- For quantum systems: electromagnetic field amplitudes
Time Steps: Δt₁, Δt₂, ..., Δtₖ
The time step can be:
- Fixed: All Δt are equal and constant
- Free: Each Δt is a decision variable (for minimum time problems)
Cost Function: J(x, u)
The objective or cost function defines what you want to minimize. Common objectives include:
Control Effort
Minimize energy by penalizing large controls:
\[J = \sum_{k=1}^{N} \|u_k\|^2\]
using DirectTrajOpt
using NamedTrajectories
using LinearAlgebraExample:
N = 10
traj = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt, controls=:u
)
obj_effort = QuadraticRegularizer(:u, traj, 1.0)QuadraticRegularizer(:u, [1.0], [0.0 0.0 … 0.0 0.0], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])Minimum Time
Minimize trajectory duration:
\[J = \sum_{k=1}^{N} \Delta t_k\]
obj_time = MinimumTimeObjective(traj, 0.1) # weight = 0.1MinimumTimeObjective(0.1)Terminal Cost
Penalize deviation from goal at final time:
\[J = \|x_N - x_{\text{goal}}\|^2\]
x_goal = [1.0, 0.0]
obj_terminal = TerminalObjective(x -> norm(x - x_goal)^2, :x, traj)KnotPointObjective(DirectTrajOpt.Objectives.var"#14#15"{Main.var"#2#3"}(Main.var"#2#3"()), [:x], [10], [nothing], [1.0])Combined Objectives
You can add multiple objectives together:
obj_combined = obj_effort + 0.1 * obj_time + 10.0 * obj_terminalCompositeObjectiveDynamics Constraints: f(xₖ₊₁, xₖ, uₖ, Δt, t) = 0
The dynamics constraints ensure the trajectory obeys the system's equations of motion. These are encoded via integrators that discretize continuous dynamics.
Continuous Dynamics
A continuous-time system has the form:
\[\dot{x}(t) = g(x(t), u(t), t)\]
Discrete Approximation
Direct transcription approximates this using numerical integration:
\[x_{k+1} \approx \Phi(x_k, u_k, \Delta t)\]
where Φ is an integration scheme (e.g., Euler, RK4, matrix exponential).
Example: Bilinear Dynamics
For control-linear systems:
\[\dot{x} = (G_0 + \sum_i u_i G_i) x\]
The integrator uses matrix exponential:
\[x_{k+1} = \exp(\Delta t \cdot G(u_k)) x_k\]
G_drift = [-0.1 1.0; -1.0 -0.1]
G_drives = [[0.0 1.0; 1.0 0.0]]
G = u -> G_drift + sum(u .* G_drives)
integrator = BilinearIntegrator(G, traj, :x, :u)Path Constraints: c(x, u) ≥ 0
Path constraints restrict states and controls along the trajectory.
Bounds
Simple box constraints on variables:
\[u_{\min} \leq u_k \leq u_{\max}\]
traj_bounded = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
bounds=(u = (-1.0, 1.0),) # -1 ≤ u ≤ 1
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Nonlinear Constraints
More complex constraints (e.g., obstacle avoidance, no-go zones):
Constraint: keep control magnitude bounded
constraint = NonlinearKnotPointConstraint(
u -> [1.0 - norm(u)], # 1 - ||u|| ≥ 0 → ||u|| ≤ 1
:u,
traj;
equality=false
)NonlinearKnotPointConstraint{DirectTrajOpt.Constraints.var"#31#32"{Main.var"#8#9"}}(DirectTrajOpt.Constraints.var"#31#32"{Main.var"#8#9"}(Main.var"#8#9"()), [:u], false, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing], 1, 1, 10)Boundary Conditions
Initial Condition: x₁ = x_init
Fixes the starting state.
Final Condition: xₖ = x_goal
Fixes the ending state (or penalizes deviation via terminal cost).
traj_bc = NamedTrajectory(
(x = randn(2, N), u = randn(1, N), Δt = fill(0.1, N));
timestep=:Δt,
controls=:u,
initial=(x = [0.0, 0.0],), # Fixed initial state
final=(x = [1.0, 0.0],) # Fixed final state
)N = 10, (x = 1:2, u = 3:3, → Δt = 4:4)Direct Transcription
Why Direct Transcription?
- Mature solvers: Leverage powerful NLP solvers (Ipopt, SNOPT)
- Constraint handling: Natural way to include path constraints
- Warm starting: Can initialize with good guesses
- Large problems: Scales well to thousands of variables
The NLP Formulation
After discretization, we have a finite-dimensional problem:
\[\begin{align*} \text{minimize} \quad & J(z) \\ \text{subject to} \quad & h(z) = 0 \\ & g(z) \geq 0 \end{align*}\]
where z = [x₁, u₁, x₂, u₂, ..., xₙ, uₙ, Δt₁, ..., Δtₙ] is the decision vector.
When to Use DirectTrajOpt
DirectTrajOpt.jl is ideal when:
- ✓ You have smooth dynamics
- ✓ You need to handle constraints
- ✓ You want flexibility in cost functions
- ✓ You can provide reasonable initial guesses
It may not be ideal when:
- ✗ Dynamics are highly discontinuous
- ✗ You need guaranteed global optimality
- ✗ Real-time performance is critical (use MPC frameworks)
Summary
| Component | Mathematical Form | Implementation |
|---|---|---|
| Decision Variables | x, u, Δt | NamedTrajectory |
| Objective | J(x, u) | Objective (sum of terms) |
| Dynamics | f(xₖ₊₁, xₖ, uₖ) = 0 | AbstractIntegrator |
| Path Constraints | c(x, u) ≥ 0 | AbstractConstraint |
| Boundary Conditions | x₁ = x_init, xₖ = x_goal | initial, final in trajectory |
Next Steps
- Trajectories: Learn how to construct
NamedTrajectoryobjects - Integrators: Understand how dynamics are discretized
- Objectives: Explore different cost functions
- Constraints: Add complex constraints to your problems
This page was generated using Literate.jl.