Constraint Interface
All constraints inherit from AbstractConstraint.
TrajectoryOptimization.AbstractConstraint — TypeAbstractConstraintAbstract vector-valued constraint for a trajectory optimization problem. May be either inequality or equality (specified by sense(::AbstractConstraint)::ConstraintSense), and be function of single or adjacent knotpoints.
Interface: Any constraint type must implement the following interface:
n = state_dim(::MyCon)
m = control_dim(::MyCon)
p = Base.length(::MyCon)
sense(::MyCon)::ConstraintSense
c = evaluate(::MyCon, args...)
jacobian!(∇c, ::MyCon, args...)All constraints are categorized into the following type tree:
AbstractConstraint
↙ ↘
StageConstraint CoupledConstraint
↙ ↘ ↙ ↘
StageConstraint ControlConstraint CoupledStateConstraint CoupledControlConstraintThe state and control dimensions (where applicable) can be queried using state_dim(::AbstractConstraint) and control_dim(::AbstractConstraint). The dimensions of a constraint can be verified using check_dims. The width of the constraint Jacobian is given by get_inds or widths.
The number of constraint values associated with the constraint (length of the constraint vector) is given with length(::AbstractConstraint).
Evaluation methods
Refer to the doc strings for the following methods for more information on the required signatures.
Constraint Sense
TrajectoryOptimization.jl assumes equality constraints are of the form $g(x) = 0$, and that all other constraints are constrained to lie with a specified cone. This is referred to as the ConstraintSense. The following are currently implemented:
TrajectoryOptimization.ConstraintSense — Type" Specifies whether the constraint is an equality or inequality constraint. Valid subtypes are Equality, Inequality ⟺ NegativeOrthant, and SecondOrderCone.
The sense of a constraint can be queried using sense(::AbstractConstraint)
If sense(con) <: Conic (i.e. not Equality), then the following operations are supported:
Base.in(::Conic, x::StaticVector). i.e.x ∈ coneprojection(::Conic, x::StaticVector)∇projection(::Conic, J, x::StaticVector)∇²projection(::Conic, J, x::StaticVector, b::StaticVector)
TrajectoryOptimization.Equality — TypeEquality constraints of the form `g(x) = 0. Type singleton, so it is created with Equality().
TrajectoryOptimization.NegativeOrthant — TypeInequality constraints of the form $h(x) \leq 0$. Type singleton, so it is created with Inequality(). Equivalent to NegativeOrthant.
TrajectoryOptimization.SecondOrderCone — TypeThe second-order cone is defined as $\|x\| \leq t$ where $x$ and $t$ are both part of the cone. TrajectoryOptimization assumes the scalar part $t$ is the last element in the vector.
Evaluating Constraints
The following methods are used to evaluate a constraint:
TrajectoryOptimization.evaluate — FunctionReturn the constraint value
evaluate(con::AbstractConstraint, z) # stage constraint
evaluate(con::AbstractConstraint, z1, z2) # coupled constraintEvaluate the constraint con at knot point z. By default, this method will attempt to call
evaluate(con, x)if con is a StateConstraint,
evaluate(con, u)if con is a ControlConstraint, or
evaluate(con, x, u)if con is a StageConstraint. If con is a CoupledConstraint the constraint should define
evaluate(con, z1, z2)TrajectoryOptimization.evaluate! — Functionevaluate!(vals, con::AbstractConstraint, Z, [inds])Evaluate constraints for entire trajectory. This is the most general method used to evaluate constraints along the trajectory Z, and should be the one used in other functions. The inds argument determines at which knot points the constraint is evaluated.
If con is a StageConstraint, this will call evaluate(con, z) by default, or evaluate(con, z1, z2) if con is a CoupledConstraint.
RobotDynamics.jacobian! — Functionjacobian!(∇f, model, z::AbstractKnotPoint, [cache])Compute the n × (n + m) Jacobian ∇f of the continuous-time dynamics. Only accepts an AbstractKnotPoint as input in order to avoid potential allocations associated with concatenation.
This method can use either ForwardDiff or FiniteDiff, based on the result of RobotDynamics.diffmethod(model). When using FiniteDiff, the cache should be passed in for best performance. The cache can be generated using either of the following:
RobotDynamics.gen_cache(model)
FiniteDiff.JacobianCache(model)jacobian!(∇c, con::AbstractConstraint, Z, [inds, is_const])Evaluate constraints for entire trajectory. This is the most general method used to evaluate constraints along the trajectory Z, and should be the one used in other functions. The inds argument determines at which knot points the constraint is evaluated. The optional is_const argument is a BitArray of the same size as ∇c, and captures the output of jacobian!, which should return a Boolean specifying if the Jacobian is constant or not.
The values are stored in ∇c, which should be a matrix of matrices. If con is a StageConstraint, size(∇c,2) = 1, and size(∇c,2) = 2 if con is a CoupledConstraint.
If con is a StageConstraint, this will call jacobian!(∇c, con, z) by default, or jacobian!(∇c, con, z1, z2, i) if con is a CoupledConstraint.
jacobian!(∇c, con::AbstractConstraint, z, i=1) # stage constraint
jacobian!(∇c, con::AbstractConstraint, z1, z2, i=1) # coupled constraintEvaluate the constraint con at knot point z. By default, this method will attempt to call
jacobian!(∇c, con, x)if con is a StateConstraint,
jacobian!(∇c, con, u)if con is a ControlConstraint, or
jacobian!(∇c, con, x, u)if con is a StageConstraint. If con is a CoupledConstraint the constraint should define
jacobian!(∇c, con, z, i)where i determines which Jacobian should be evaluated. E.g. if i = 1, the Jacobian with respect to the first knot point's stage and controls is calculated.
Automatic Differentiation
If con is a StateConstraint or ControlConstraint then this method is automatically defined using ForwardDiff.
TrajectoryOptimization.∇jacobian! — Function∇jacobian!(G, con::AbstractConstraint, Z, λ, inds, is_const, init)
∇jacobian!(G, con::AbstractConstraint, Z::AbstractKnotPoint, λ::AbstractVector)Evaluate the second-order expansion of the constraint con along the trajectory Z after multiplying by the lagrange multiplier λ. The optional is_const argument is a BitArray of the same size as ∇c, and captures the output of jacobian!, which should return a Boolean specifying if the Jacobian is constant or not. The init flag will force re-calculation of constant Jacobians when true.
The method for each constraint should calculate the Jacobian of the vector-Jacobian product, and therefore should be of size n × n if the input dimension is n.
Importantly, this method should ADD and not overwrite the contents of G, since this term is dependent upon all the constraints acting at that time step.
Methods
The following methods are defined for all AbstractConstraints
RobotDynamics.state_dim — FunctionDimension of the state vector
RobotDynamics.control_dim — FunctionDimension of the control vector
TrajectoryOptimization.sense — FunctionGet constraint sense (Inequality vs Equality)
TrajectoryOptimization.widths — Functionwidths(::AbstractConstraint)
widths(::AbstractConstraint, n, m)Return a tuple of the widths of the Jacobians for a constraint. If n and m are not passed in, they are assumed to be consistent with those returned by state_dim and control_dim.
TrajectoryOptimization.upper_bound — FunctionUpper bound of the constraint, as a vector, which is 0 for all constraints (except bound constraints)
TrajectoryOptimization.lower_bound — FunctionUpper bound of the constraint, as a vector, which is 0 equality and -Inf for inequality (except bound constraints)
TrajectoryOptimization.is_bound — FunctionIs the constraint a bound constraint or not
TrajectoryOptimization.check_dims — FunctionCheck whether the constraint is consistent with the specified state and control dimensions
TrajectoryOptimization.get_inds — Functionget_inds(con::AbstractConstraint)Get the indices of the joint state-control vector that are used to calculate the constraint. If the constraint depends on more than one time step, the indices start from the beginning of the first one.
Adding a New Constraint
See interface description in documentation for AbstractConstraint. The interface allows for a lot of flexibility, but let's do a simple example. Let's say we have a 2-norm constraint on the controls at each time step, e.g. $||u|| \leq a$. We can do this with just a few lines of code:
struct ControlNorm{T} <: ControlConstraint
m::Int
val::T
function ControlNorm(m::Int, val::T) where T
@assert val ≥ 0 "Value must be greater than or equal to zero"
new{T}(m,val,sense,inds)
end
end
control_dim(con::ControlNorm) = con.m
sense(::ControlNorm) = Inequality()
Base.length(::ControlNorm) = 1
evaluate(con::ControlNorm, u::SVector) = SA[norm(u) - con.a] # needs to be a vector output
jacobian(con::ControlNorm, u::SVector) = u'/norm(u) # optionalImportantly, note that the inheritance specifies the constraint applies only to individual controls.
Let's say the bound $a$ varied by time-step. We could handle this easily by instead defining the methods operating on the entire trajectory:
struct ControlNorm2{T} <: ControlConstraint
m::Int
val::Vector{T}
function ControlNorm2(m::Int, val::T) where T
@assert val ≥ 0 "Value must be greater than or equal to zero"
new{T}(m,val,sense,inds)
end
end
control_dim(con::ControlNorm2) = con.m
sense(::ControlNorm2) = Inequality()
Base.length(::ControlNorm2) = 1
function evaluate!(vals, con::ControlNorm2, Z::AbstractTrajectory, inds=1:length(Z))
for (i,k) in enumerate(inds)
u = control(Z[k])
vals[i] = SA[norm(u) - con.a[i]]
end
end
function jacobian!(∇c, con::ControlNorm2, Z::AbstractTrajectory, inds=1:length(Z),
is_const = BitArray(undef, size(∇c)))
for (i,k) in enumerate(inds)
u = control(Z[k])
∇c[i] = u'/norm(u)
is_const[i] = false
end
endConstraint Types
The ConstraintType defines the "bandedness" of the constraint, or the number of adjacent state or constraint values needed to calculate the constraint.
TrajectoryOptimization.StageConstraint — TypeOnly a function of states and controls at a single knotpoint
TrajectoryOptimization.StateConstraint — TypeOnly a function of states at a single knotpoint
TrajectoryOptimization.ControlConstraint — TypeOnly a function of controls at a single knotpoint
TrajectoryOptimization.CoupledConstraint — TypeOnly a function of states and controls at two adjacent knotpoints
TrajectoryOptimization.CoupledStateConstraint — TypeOnly a function of states at adjacent knotpoints
TrajectoryOptimization.CoupledControlConstraint — TypeOnly a function of controls at adjacent knotpoints