Skip to main content

Constraints

Struct Constraints 

Source
pub struct Constraints { /* private fields */ }
Expand description

Constraint storage and query object used by TOPP/COPP solvers.

§Mathematical symbols (with code mapping)

For each path station $s_k$:

  • $a_k = \dot{s}_k^2$ (squared path speed), mapped to code symbol a[k].
  • $b_k = \ddot{s}_k = \frac{1}{2}\frac{\mathrm{d}a}{\mathrm{d}s}(s_k)$ (path acceleration), mapped to b[k].
  • $c_k = \frac{\dddot{s}_k}{\dot{s}_k} = \frac{\mathrm{d}b}{\mathrm{d}s}(s_k)$ (normalized jerk term), mapped to c[k].

§Constraint families

  • First-order: 0 <= a[k] <= amax[k]
  • Second-order: acc_a[k]*a[k] + acc_b[k]*b[k] <= acc_max[k]
  • Third-order (nonlinear): sqrt(a[k])*(jerk_a[k]*a[k] + jerk_b[k]*b[k] + jerk_c[k]*c[k] + jerk_d[k]) <= jerk_max[k]
  • Third-order (linearized): jerk_a_linear[k]*a[k] + jerk_b[k]*b[k] + jerk_c[k]*c[k] <= jerk_max_linear[k]

§Storage model

All station-wise arrays are stored as circular column-major matrices. Logical station index range is [idx_s, idx_s + len), and logical column i maps to physical column (head_col + i) % capacity_col.

§API contract

  • get_* methods are safe public accessors and return Result<_, ConstraintError>.
  • *_unchecked methods are internal fast-path helpers. Callers must satisfy preconditions; debug builds assert them.

§Example

The example below constructs low-level constraints directly, without going through Robot.

use copp::constraints::Constraints;
use nalgebra::DMatrix;

let mut constraints = Constraints::with_capacity(2, 8);

let s = [0.0, 0.5, 1.0];
constraints.with_s(s.as_slice())?;

let amax = [1.0, 0.8, 1.0];
constraints.with_constraint_1order(amax.as_slice(), 0)?;

let acc_a = DMatrix::from_row_slice(2, 3, &[
    0.0, 0.0, 0.0,
    0.0, 0.0, 0.0,
]);
let acc_b = DMatrix::from_row_slice(2, 3, &[
    1.0, 1.0, 1.0,
    -1.0, -1.0, -1.0,
]);
let acc_max = DMatrix::from_row_slice(2, 3, &[
    2.0, 2.0, 2.0,
    2.0, 2.0, 2.0,
]);

constraints.with_constraint_2order(
    &acc_a.as_view(),
    &acc_b.as_view(),
    &acc_max.as_view(),
    0,
    false,
)?;

assert_eq!(constraints.len(), 3);
assert_eq!(constraints.get_s(1)?, 0.5);

Implementations§

Source§

impl Constraints

Source

pub const DEFAULT_CAPACITY: usize = 1000

Default number of constraint stations preallocated for a new container.

Source

pub fn new(dim: usize) -> Self

Construct a new container with default column capacity.

§Parameters
  • dim: path dimension / DoF.
§Notes

Equivalent to with_capacity(dim, DEFAULT_CAPACITY).

Source

pub fn with_capacity(dim: usize, capacity_col: usize) -> Self

Construct a new container with explicit column capacity.

§Parameters
  • dim: path dimension / DoF.
  • capacity_col: initial number of allocated columns.
§Initialization policy
  • Bound matrices are initialized to neutral values (infinity where applicable).
  • Valid-row maps start empty and are progressively populated by with_s().
Source

pub fn len(&self) -> usize

Current number of logical stations stored in the buffer.

Source

pub fn is_empty(&self) -> bool

Whether no logical stations are currently stored.

Source

pub fn get_s(&self, idx_s: usize) -> Result<f64, ConstraintError>

Get station value s[idx_s] with bounds validation.

§Errors

Returns ConstraintError::OutOfSBounds if idx_s is outside [idx_s_start(), idx_s_end()).

Source

pub fn amax_rows(&self) -> usize

Number of rows currently allocated for first-order constraints.

Normally this is 1, but the method is intentionally generic.

Source

pub fn acc_rows(&self) -> usize

Number of rows currently allocated for second-order constraints.

Source

pub fn jerk_rows(&self) -> usize

Number of rows currently allocated for third-order constraints.

Source

pub fn s_vec( &self, idx_s_from: usize, idx_s_to: usize, ) -> Result<Vec<f64>, ConstraintError>

Export station values in half-open interval [idx_s_from, idx_s_to).

§Parameters
  • idx_s_from: global start station id (inclusive).
  • idx_s_to: global end station id (exclusive).
§Returns

A contiguous vector of station values with length idx_s_to - idx_s_from.

§Errors
Source

pub fn amax_vec( &self, idx_s_from: usize, idx_s_to: usize, ) -> Result<Vec<f64>, ConstraintError>

Export first-order upper bounds in [idx_s_from, idx_s_to).

Semantics and error behavior are identical to s_vec().

Source

pub fn get_amax(&self, idx_s: usize) -> Result<f64, ConstraintError>

Get first-order upper bound amax[idx_s] with bounds validation.

§Errors

Returns ConstraintError::OutOfSBounds if idx_s is invalid.

Source

pub fn idx_s_start(&self) -> usize

Global start station id (inclusive) of current logical window.

Source

pub fn idx_s_end(&self) -> usize

Global end station id (exclusive) of current logical window.

Source

pub fn get_acc_constraints<'a>( &'a self, idx_s: usize, ) -> Result<(InputMatrix<'a>, InputMatrix<'a>, InputMatrix<'a>), ConstraintError>

Get second-order row views at station idx_s.

§Returns

(acc_a_col, acc_b_col, acc_max_col) where each matrix is a valid_rows x 1 view into internal storage.

§Errors

Returns ConstraintError::OutOfSBounds if idx_s is invalid.

Source

pub fn get_jerk_constraints<'a>( &'a self, idx_s: usize, ) -> Result<(InputMatrix<'a>, InputMatrix<'a>, InputMatrix<'a>, InputMatrix<'a>, InputMatrix<'a>), ConstraintError>

Get nonlinear third-order row views at station idx_s.

§Returns

(jerk_a, jerk_b, jerk_c, jerk_d, jerk_max), each a valid_rows x 1 view.

§Errors

Returns ConstraintError::OutOfSBounds if idx_s is invalid.

Source

pub fn get_jerk_linear_constraints<'a>( &'a self, idx_s: usize, ) -> Result<(InputMatrix<'a>, InputMatrix<'a>, InputMatrix<'a>, InputMatrix<'a>), ConstraintError>

Get linearized third-order row views at station idx_s.

The linearized rows are prepared by Topp3ProblemBuilder::build_with_linearization or Copp3ProblemBuilder::build_with_linearization. Call this accessor when solver-side code needs to inspect the affine third-order rows generated from the latest reference profile.

§Returns

(jerk_a_linear, jerk_b, jerk_c, jerk_max_linear), each a valid_rows x 1 view into internal storage.

§Errors
Source

pub fn expand_capacity(&mut self, new_capacity: usize)

Ensure buffer capacity is at least new_capacity columns.

§Growth strategy

If expansion is required, target capacity is max(new_capacity, 2 * current_capacity + 1).

§Guarantees
  • Logical order of existing data is preserved.
  • head_col is reset to 0 after re-layout.
  • All backing matrices (s, derivative buffers, and constraint buffers) are expanded consistently.
Source

pub fn with_s<T: AsInputMatrix1D + ?Sized>( &mut self, s_new: &T, ) -> Result<&mut Self, ConstraintError>

Append strictly increasing station samples to the logical tail.

§Parameters

For the higher-level robot wrapper, Robot::with_s delegates to this method.

§Behavior
  • Rejects non-increasing input.
  • Rejects overlap with existing tail station (s_new[0] must be greater than current last station when the buffer is non-empty).
  • Expands capacity proactively.
  • Appends zero-valid-row segments into all validity maps so downstream with_q / with_constraint_* calls can progressively fill data.
§Errors

Returns ConstraintError::NonIncreasingS on monotonicity violations.

§Returns

Returns &mut Self for chaining on success.

Source

pub fn with_q( &mut self, q_new: &InputMatrix<'_>, dq_new: &InputMatrix<'_>, ddq_new: &InputMatrix<'_>, dddq_new: Option<&InputMatrix<'_>>, idx_s: usize, ) -> Result<&mut Self, ConstraintError>

Write path geometry derivatives on a station interval.

This is the low-level entry point for users who populate Constraints directly. Robot-centric workflows usually call Robot::with_q, Robot::with_q_from_path_2nd, or Robot::with_q_from_path_3rd, which forward into this storage layer.

§Parameters
  • q_new: configuration values (dim x N).
  • dq_new: first derivatives (dim x N).
  • ddq_new: second derivatives (dim x N).
  • dddq_new: optional third derivatives (dim x N).
  • idx_s: global start station id (inclusive).
§Behavior
  • Performs shape checks and bounds checks.
  • Overwrites corresponding circular-buffer ranges.
  • Marks second-order derivative data as fully valid (n_rows = dim) over the updated range.
  • If dddq_new is provided, writes it and marks third-order derivative data as valid over the updated range.
  • If dddq_new is None, clears third-order derivative availability over the updated range.
§Errors
§Returns

Returns &mut Self for chaining on success.

§Example

The example below writes one-dimensional path geometry into a directly constructed constraint container.

use copp::constraints::Constraints;
use nalgebra::DMatrix;

let mut constraints = Constraints::with_capacity(2, 3);
let s = [0.0, 0.5, 1.0];
constraints.with_s(s.as_slice())?;

let q = DMatrix::from_row_slice(2, 3, &[
    0.0, 0.125, 0.5,
    1.0, 1.0, 1.0,
]);
let dq = DMatrix::from_row_slice(2, 3, &[
    0.0, 0.5, 1.0,
    0.0, 0.0, 0.0,
]);
let ddq = DMatrix::from_row_slice(2, 3, &[
    1.0, 1.0, 1.0,
    0.0, 0.0, 0.0,
]);

constraints.with_q(&q.as_view(), &dq.as_view(), &ddq.as_view(), None, 0)?;
assert_eq!(constraints.len(), 3);
Source

pub fn with_constraint_1order<T: AsInputMatrix1D + ?Sized>( &mut self, amax_new: &T, idx_s: usize, ) -> Result<&mut Self, ConstraintError>

Add / tighten first-order bound amax over an interval.

§Parameters
  • amax_new: candidate upper bounds as R x N; each column is reduced to its minimum before being fused into storage.
  • idx_s: global start station id.
§Fusion rule

Stored value is updated as self.amax = min(self.amax, amax_new_reduced).

§Errors
§Returns

Returns &mut Self for chaining on success.

Source

pub fn with_constraint_2order( &mut self, acc_a_new: &InputMatrix<'_>, acc_b_new: &InputMatrix<'_>, acc_max_new: &InputMatrix<'_>, idx_s: usize, is_negative: bool, ) -> Result<&mut Self, ConstraintError>

Append second-order inequality rows over station interval starting at idx_s.

§Model

For each station column, rows satisfy: acc_a * a + acc_b * b <= acc_max.

§Parameters
  • acc_a_new, acc_b_new, acc_max_new: same-shape matrices (R x N).
  • idx_s: global start station id.
  • is_negative: whether to negate inserted rows (used to build symmetric upper/lower bounds from one physical expression).
§Behavior
  • Increases row counts by R on affected stations.
  • Appends new rows below existing rows per station.
  • Merges adjacent validity intervals when row counts match.
§Errors
§Returns

Returns &mut Self for chaining on success.

Source

pub fn with_constraint_3order( &mut self, jerk_a_new: &InputMatrix<'_>, jerk_b_new: &InputMatrix<'_>, jerk_c_new: &InputMatrix<'_>, jerk_d_new: &InputMatrix<'_>, jerk_max_new: &InputMatrix<'_>, idx_s: usize, is_negative: bool, ) -> Result<&mut Self, ConstraintError>

Append third-order nonlinear inequality rows over station interval.

§Model

sqrt(a) * (jerk_a*a + jerk_b*b + jerk_c*c + jerk_d) <= jerk_max

§Parameters
  • jerk_*_new: same-shape matrices (R x N).
  • idx_s: global start station id.
  • is_negative: if true, inserted rows are sign-flipped.
§Side effects

If the inserted interval overlaps current valid_ids_linear_jerk, the linearization-valid interval is cleared because source nonlinear rows changed.

§Errors
§Returns

Returns &mut Self for chaining on success.

§Example

The example below installs a single jerk upper-bound row at three stations. After changing third-order rows, build a TOPP3/COPP3 problem with build_with_linearization() before reading linearized jerk rows.

use copp::constraints::Constraints;
use nalgebra::DMatrix;

let mut constraints = Constraints::with_capacity(2, 3);
let s = [0.0, 0.5, 1.0];
constraints.with_s(s.as_slice())?;

let zero = DMatrix::from_row_slice(2, 3, &[
    0.0, 0.0, 0.0,
    0.0, 0.0, 0.0,
]);
let jerk_c = DMatrix::from_row_slice(2, 3, &[
    1.0, 1.0, 1.0,
    -1.0, -1.0, -1.0,
]);
let jerk_max = DMatrix::from_row_slice(2, 3, &[
    5.0, 5.0, 5.0,
    5.0, 5.0, 5.0,
]);

constraints.with_constraint_3order(
    &zero.as_view(),
    &zero.as_view(),
    &jerk_c.as_view(),
    &zero.as_view(),
    &jerk_max.as_view(),
    0,
    false,
)?;
assert_eq!(constraints.get_jerk_constraints(1)?.0.nrows(), 2);
Source

pub fn pop_front(&mut self, mode: ModePopConstraints)

Remove a prefix of logical stations from the front.

§Modes
§Notes
  • amax values in removed columns are reset to +inf.
  • Valid-row maps are trimmed and re-anchored.
  • idx_s increases and head_col advances accordingly.
Source

pub fn pop_back(&mut self, mode: ModePopConstraints)

Remove a suffix of logical stations from the back.

§Modes
§Notes
  • amax values in removed columns are reset to +inf.
  • Valid-row maps are trimmed to new right boundary.
  • idx_s is unchanged; only len shrinks.
Source

pub fn clear(&mut self, keep_idx_s: bool)

Reset logical content and validity maps.

§Parameters
  • keep_idx_s: when true, preserve current global station origin; otherwise reset it to 0.
§Notes

amax is reinitialized to +inf; other matrices are kept allocated and may retain old values outside the active logical window.

Source

pub fn amax_substitute( &mut self, amax_new: &[f64], idx_from: usize, ) -> Result<(), ConstraintError>

Overwrite first-order bounds in [idx_from, idx_from + amax_new.len()).

§Parameters
  • amax_new: replacement values.
  • idx_from: global start station id.
§Errors

Returns ConstraintError::OutOfSBounds if target range is invalid.

Source

pub fn exceed_topp2(&self, idx_s_start: usize, a_profile: &[f64]) -> (f64, f64)

Evaluate maximum violation magnitudes of the TOPP2 constraints for a profile.

a_profile holds a = s_dot^2 on the stations starting at idx_s_start. The path acceleration of each interval is reconstructed from it by the TOPP2 relation b[k] = (a[k+1] - a[k]) / (2 * ds[k]), and the acceleration rows of both endpoint stations are checked against that interval’s b.

§Returns

(exceed_1order, exceed_2order), each <= 0 when fully feasible and positive when violated. The first-order term covers 0 <= a[k] <= amax[k]. Returns NaNs if the station range is unavailable or b cannot be reconstructed from a_profile, e.g. because it contains a non-finite value.

Source

pub fn exceed_topp3( &self, idx_s_start: usize, a_profile: &[f64], b_profile: &[f64], num_stationary: (usize, usize), ) -> (f64, f64, f64)

Evaluate maximum violation magnitudes of the TOPP3 constraints for a profile.

The third-order term uses the original sqrt(a) form, not the linearized one, so this certifies the profile that is actually delivered rather than the model the solver optimized. Run it after any post-processing such as force_positive_a, which rewrites a and b without knowing about the acceleration and jerk limits.

The third-order term covers only the intervals the solver models with a constant c, i.e. it skips the two stationary blocks. There b follows the sdddot-constant law and c = 2a/(9 d^2), so a finite difference of b across a block edge overestimates c by a factor of three and would report a violation that the model never claimed.

§Returns

(exceed_1order, exceed_2order, exceed_3order), each <= 0 when fully feasible and positive when violated. Returns NaNs if the station range is unavailable, the two profiles disagree in length, or either profile contains a non-finite value.

Trait Implementations§

Source§

impl Clone for Constraints

Source§

fn clone(&self) -> Constraints

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.