Dense linear algebra on plain arrays: a row-major Mat, a flat Vec, and the factorisations the control solvers are built on.
Types
structEigh#
pub struct Eigh {
values : Vec
vectors : Mat
}
- values
- Vec
- vectors
- Mat
The eigenvalues and eigenvectors of a symmetric matrix.
values is ascending and vectors has the matching eigenvector in its matching column, so a * v[:, i] = values[i] * v[:, i].
fnEigh::to_repr#
pub fn Eigh::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structLu#
pub struct Lu {
n : Int32
lower : Mat
upper : Mat
perm : Array[Int32]
sign : Double
}
- n
- Int32
- lower
- Mat
- upper
- Mat
- perm
- Array[Int32]
- sign
- Double
An LU factorisation with partial pivoting, perm * A = lower * upper, where lower is unit lower triangular and upper is upper triangular.
sign is the parity of the row swaps, so det = sign * product(upper). The factorisation is kept so that several right-hand sides can reuse it, which is what the estimators do inside a filter loop.
fnLu::det#
pub fn Lu::det(self : Lu) -> Double
The determinant of the factorised matrix.
fnLu::solve#
pub fn Lu::solve(self : Lu, b : Mat) -> Mat raise Refused
Solve a * x = b from a factorisation. b has one column per right-hand side; the result has the same width.
fnLu::to_repr#
pub fn Lu::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structMat#
pub struct Mat {
rows : Int32
cols : Int32
data : FixedArray[Double]
}
- rows
- Int32
- cols
- Int32
- data
- FixedArray[Double]
A dense matrix of Double in row-major order.
Element (i, j) lives at data[i * cols + j]. The arithmetic methods return new matrices; set is the only writer.
fnMat::add#
pub fn Mat::add(self : Mat, other : Mat) -> Mat
The elementwise sum. Both matrices must have the same shape.
fnMat::add_to#
pub fn Mat::add_to(self : Mat, i : Int32, j : Int32, value : Double) -> Unit
Add value to the entry at (i, j).
fnMat::apply#
pub fn Mat::apply(self : Mat, v : Vec) -> Vec
fnMat::apply_transposed#
pub fn Mat::apply_transposed(self : Mat, v : Vec) -> Vec
The product v^T * self, a row vector times the matrix.
fnMat::approx#
pub fn Mat::approx(self : Mat, other : Mat, tolerance? : Double) -> Bool
Whether self and other agree entry by entry within tolerance.
fnMat::at#
pub fn Mat::at(self : Mat, i : Int32, j : Int32) -> Double?
The entry at (i, j), or None when the index is outside the matrix.
fnMat::augment#
pub fn Mat::augment(self : Mat, right : Mat) -> Mat
self with right appended on the right, so the result is rows x (cols + right.cols).
fnMat::block#
pub fn Mat::block(
self : Mat,
row? : Int32,
col? : Int32,
height? : Int32,
width? : Int32,
) -> Mat
The height x width block whose top-left corner is (row, col).
fnMat::col#
pub fn Mat::col(self : Mat, j : Int32) -> Vec
The j-th column, copied out.
fnMat::diagonal#
pub fn Mat::diagonal(values : Vec) -> Mat
A diagonal matrix with values on the diagonal.
fnMat::filled#
pub fn Mat::filled(rows : Int32, cols : Int32, value : Double) -> Mat
A rows x cols matrix with every entry value.
fnMat::from_fixed#
pub fn Mat::from_fixed(
rows : Int32,
cols : Int32,
data : FixedArray[Double],
) -> Mat
A matrix that takes over data without copying it. data holds the entries row by row.
fnMat::get#
pub fn Mat::get(self : Mat, i : Int32, j : Int32) -> Double
The entry at (i, j). An index outside the matrix aborts.
fnMat::identity#
pub fn Mat::identity(n : Int32) -> Mat
fnMat::is_finite#
pub fn Mat::is_finite(self : Mat) -> Bool
Whether every entry is finite.
fnMat::mul#
pub fn Mat::mul(self : Mat, other : Mat) -> Mat
The product self * other.
fnMat::negate#
pub fn Mat::negate(self : Mat) -> Mat
fnMat::of#
pub fn Mat::of(values : Array[Array[Double]]) -> Mat
A matrix whose rows are values; every row must have the same length.
fnMat::row#
pub fn Mat::row(self : Mat, i : Int32) -> Vec
The i-th row, copied out.
fnMat::scale#
pub fn Mat::scale(self : Mat, factor : Double) -> Mat
Every entry multiplied by factor.
fnMat::set#
pub fn Mat::set(self : Mat, i : Int32, j : Int32, value : Double) -> Unit
Overwrite the entry at (i, j).
fnMat::set_col#
pub fn Mat::set_col(self : Mat, j : Int32, values : Vec) -> Unit
Overwrite the j-th column with values.
fnMat::set_row#
pub fn Mat::set_row(self : Mat, i : Int32, values : Vec) -> Unit
Overwrite the i-th row with values.
fnMat::stack#
pub fn Mat::stack(self : Mat, below : Mat) -> Mat
self with below appended underneath, so the result is (rows + below.rows) x cols.
fnMat::sub#
pub fn Mat::sub(self : Mat, other : Mat) -> Mat
The elementwise difference. Both matrices must have the same shape.
fnMat::to_array_rows#
pub fn Mat::to_array_rows(self : Mat) -> Array[Array[Double]]
The rows copied into growable arrays, for output and test expectations.
fnMat::to_repr#
pub fn Mat::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
fnMat::to_string#
pub fn Mat::to_string(self : Mat) -> String
One line per row, each formatted like a Vec.
fnMat::trace#
pub fn Mat::trace(self : Mat) -> Double
The sum of the diagonal entries.
fnMat::transpose#
pub fn Mat::transpose(self : Mat) -> Mat
fnMat::zeros#
pub fn Mat::zeros(rows : Int32, cols : Int32) -> Mat
An all-zero rows x cols matrix.
structQr#
pub struct Qr {
q : Mat
r : Mat
}
- q
- Mat
- r
- Mat
An economy QR factorisation, a = q * r, with q of orthonormal columns and r upper triangular.
q has the same shape as a and r is square, which is the form the least-squares step and the UKF's square root need.
fnQr::to_repr#
pub fn Qr::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structVec#
pub struct Vec {
data : FixedArray[Double]
}
- data
- FixedArray[Double]
A dense vector of Double.
The storage is a fixed array, so a vector is cheap to pass around and a set writes through to it. Every arithmetic method returns a new vector; none of them mutate the receiver.
fnVec::add#
pub fn Vec::add(self : Vec, other : Vec) -> Vec
The elementwise sum. The two vectors must have the same length.
fnVec::approx#
pub fn Vec::approx(self : Vec, other : Vec, tolerance? : Double) -> Bool
Whether self and other agree entry by entry within tolerance.
fnVec::at#
pub fn Vec::at(self : Vec, i : Int32) -> Double?
The entry at i, or None when i is outside the vector.
fnVec::axpy#
pub fn Vec::axpy(self : Vec, factor : Double, other : Vec) -> Vec
self + factor * other, the operation a matrix-vector product accumulates with.
fnVec::clone#
pub fn Vec::clone(self : Vec) -> Vec
fnVec::dot#
pub fn Vec::dot(self : Vec, other : Vec) -> Double
The inner product. The two vectors must have the same length.
fnVec::filled#
pub fn Vec::filled(len : Int32, value : Double) -> Vec
A vector of len copies of value.
fnVec::from_fixed#
pub fn Vec::from_fixed(data : FixedArray[Double]) -> Vec
A vector that takes over data without copying it.
fnVec::get#
pub fn Vec::get(self : Vec, i : Int32) -> Double
The entry at i. An index outside the vector aborts: it is a programming error, not input data.
fnVec::hadamard#
pub fn Vec::hadamard(self : Vec, other : Vec) -> Vec
The elementwise product. The two vectors must have the same length.
fnVec::is_empty#
pub fn Vec::is_empty(self : Vec) -> Bool
Whether the vector has no entries.
fnVec::is_finite#
pub fn Vec::is_finite(self : Vec) -> Bool
Whether every entry is finite.
fnVec::len#
pub fn Vec::len(self : Vec) -> Int32
fnVec::linspace#
pub fn Vec::linspace(start : Double, stop : Double, len : Int32) -> Vec
A vector of len samples evenly spaced from start to stop inclusive. A single sample is start.
fnVec::max_abs#
pub fn Vec::max_abs(self : Vec) -> Double
The largest absolute entry, or zero for an empty vector.
fnVec::negate#
pub fn Vec::negate(self : Vec) -> Vec
fnVec::norm#
pub fn Vec::norm(self : Vec) -> Double
fnVec::norm_squared#
pub fn Vec::norm_squared(self : Vec) -> Double
The sum of the squared entries.
fnVec::normalized#
pub fn Vec::normalized(self : Vec) -> Vec
The unit vector in the same direction; a zero vector is returned as it is.
fnVec::of#
pub fn Vec::of(values : ArrayView[Double]) -> Vec
fnVec::scale#
pub fn Vec::scale(self : Vec, factor : Double) -> Vec
Every entry multiplied by factor.
fnVec::set#
pub fn Vec::set(self : Vec, i : Int32, value : Double) -> Unit
Overwrite the entry at i.
fnVec::sub#
pub fn Vec::sub(self : Vec, other : Vec) -> Vec
The elementwise difference. The two vectors must have the same length.
fnVec::sum#
pub fn Vec::sum(self : Vec) -> Double
fnVec::to_array#
pub fn Vec::to_array(self : Vec) -> Array[Double]
The entries copied into a growable array.
fnVec::to_repr#
pub fn Vec::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
fnVec::to_string#
pub fn Vec::to_string(self : Vec) -> String
The entries in [ a, b, c ] form, for output and test expectations.
fnVec::zeros#
pub fn Vec::zeros(len : Int32) -> Vec
Values
fncholesky#
pub fn cholesky(a : Mat) -> Mat raise Refused
The lower Cholesky factor of a symmetric positive-definite matrix a, so that a = l * l^T.
The factor is what the Kalman gain is solved with: a Cholesky solve costs half an LU solve and never needs pivoting for a covariance.
fncholesky_solve#
pub fn cholesky_solve(l : Mat, b : Mat) -> Mat raise Refused
Solve a * x = b given the lower Cholesky factor l of a. b has one column per right-hand side.
fndet#
pub fn det(a : Mat) -> Double raise Refused
The determinant of a square matrix.
fneigh#
pub fn eigh(a : Mat, sweeps? : Int32) -> Eigh raise Refused
The symmetric eigendecomposition of a by cyclic Jacobi rotations.
The rotations are stable for the covariance-shaped matrices this library hands it, and they need no shift strategy. Only the symmetric part of a is used, so a small skew from rounding does not matter. Raises Refused::Dimension when a is not square and Refused::Exceeded when the sweep limit is reached.
fnexpm#
pub fn expm(a : Mat, terms? : Int32) -> Mat raise Refused
The matrix exponential of a square matrix, by scaling and squaring followed by a truncated Taylor series.
This is what turns a continuous state matrix into a discrete one; the scaling keeps the series inside the range where twenty terms reach machine precision.
fninv#
pub fn inv(a : Mat) -> Mat raise Refused
The inverse of a square matrix.
fnlu#
pub fn lu(a : Mat) -> Lu raise Refused
Factorise a, a square matrix, into perm * a = lower * upper.
Raises Refused::Singular at the first column whose pivot is zero; Refused::Dimension when a is not square.
fnnorm#
pub fn norm(a : Mat) -> Double
The Frobenius norm of a matrix.
fnqr#
pub fn qr(a : Mat) -> Qr raise Refused
Factorise a as q * r with Householder reflections.
Raises Refused::Dimension when a has fewer rows than columns: an economy factorisation cannot represent that shape.
fnsolve#
pub fn solve(a : Mat, b : Mat) -> Mat raise Refused
Solve a * x = b for a square a.
fnsolve_vec#
pub fn solve_vec(a : Mat, b : Vec) -> Vec raise Refused
Solve a * x = b for a square a and a single right-hand side.
The traits every algorithm is written against — Model, Controller, Estimator, Trajectory, Kinematics, Space, Validity and Planner — and the shared records that go with them.
Types
structBudget#
pub struct Budget {
iterations : Int32
seconds : Double?
}
- iterations
- Int32/ / The number of samples or iterations to draw before giving up.
- seconds
- Double?/ / A wall-clock limit in seconds, when the caller has a clock.
How long a planner may search.
fnBudget::to_repr#
pub fn Budget::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
enumDiscretize#
pub enum Discretize {
Zoh
ForwardEuler
Tustin
}
Which rule turns a continuous state matrix into a discrete one.
fnDiscretize::to_repr#
pub fn Discretize::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structDynamics#
pub struct Dynamics {
f : (@linear.Vec, @linear.Vec, Double) -> @linear.Vec
h : (@linear.Vec) -> @linear.Vec
integrator : Integrator
}
- f
- (@linear.Vec, @linear.Vec, Double) -> @linear.Vec
- h
- (@linear.Vec) -> @linear.Vec
- integrator
- Integrator
A nonlinear plant given as a derivative and an output map, integrated with a named rule.
The derivative is f(x, u, dt), taking the period as well because a plant may be defined for a fixed control period; the rule treats it as the vector field at x.
fnDynamics::new#
pub fn Dynamics::new(
f? : (@linear.Vec, @linear.Vec, Double) -> @linear.Vec,
h? : (@linear.Vec) -> @linear.Vec,
integrator? : Integrator,
) -> Dynamics
A nonlinear plant from its derivative and its output map, integrated with RK4 unless told otherwise.
fnDynamics::observe#
pub fn Dynamics::observe(Self, @linear.Vec) -> @linear.Vec
fnDynamics::observe_jacobian#
pub fn Dynamics::observe_jacobian(Self, @linear.Vec) -> @linear.Mat
fnDynamics::step#
pub fn Dynamics::step(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
fnDynamics::step_jacobian#
pub fn Dynamics::step_jacobian(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Mat
fnDynamics::to_repr#
pub fn Dynamics::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
enumIntegrator#
pub enum Integrator {
Euler
Midpoint
Rk4
}
fnIntegrator::to_repr#
pub fn Integrator::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structSample#
pub struct Sample {
position : Double
velocity : Double
acceleration : Double
jerk : Double
}
- position
- Double/ / Position, in metres for a linear axis and radians for a joint.
- velocity
- Double/ / First derivative of the position.
- acceleration
- Double/ / Second derivative of the position.
- jerk
- Double/ / Third derivative of the position.
One instant of a single-axis motion, with every derivative a drive may need.
The jerk is what separates a smooth profile from a trapezoid: a drive that bounds it never excites the resonance the way a step in acceleration does.
fnSample::new#
pub fn Sample::new(
position? : Double,
velocity? : Double,
acceleration? : Double,
jerk? : Double,
) -> Sample
A sample from its four derivatives.
fnSample::still#
pub fn Sample::still(position : Double) -> Sample
A sample that is not moving.
fnSample::to_repr#
pub fn Sample::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structStateSpace#
pub struct StateSpace {
a : @linear.Mat
b : @linear.Mat
c : @linear.Mat
d : @linear.Mat
dt : Double?
}
- a
- @linear.Mat
- b
- @linear.Mat
- c
- @linear.Mat
- d
- @linear.Mat
- dt
- Double?
A linear time-invariant plant, x' = a x + b u, y = c x + d u.
With dt set the record is already discrete and step applies a x + b u once; without it the record is continuous and step integrates over the period it is given. A discrete record is what the Kalman filter and the model-predictive controller want; a continuous one is what a plant model in physical units looks like.
fnStateSpace::continuous#
pub fn StateSpace::continuous(
a? : @linear.Mat,
b? : @linear.Mat,
c? : @linear.Mat,
d? : @linear.Mat,
) -> StateSpace raise @linear.Refused
fnStateSpace::discrete#
pub fn StateSpace::discrete(
a? : @linear.Mat,
b? : @linear.Mat,
c? : @linear.Mat,
d? : @linear.Mat,
dt? : Double,
) -> StateSpace raise @linear.Refused
A plant already discretised at dt seconds.
fnStateSpace::discretize#
pub fn StateSpace::discretize(
self : StateSpace,
dt? : Double,
discretize? : Discretize,
) -> StateSpace raise @linear.Refused
The same plant discretised at dt seconds.
A record that is already discrete is returned unchanged when the periods agree and refused when they do not: resampling would need the original continuous plant, which the record no longer carries.
fnStateSpace::is_discrete#
pub fn StateSpace::is_discrete(self : StateSpace) -> Bool
Whether the record is already discrete.
fnStateSpace::observe#
pub fn StateSpace::observe(Self, @linear.Vec) -> @linear.Vec
fnStateSpace::observe_jacobian#
pub fn StateSpace::observe_jacobian(Self, @linear.Vec) -> @linear.Mat
fnStateSpace::step#
pub fn StateSpace::step(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
fnStateSpace::step_jacobian#
pub fn StateSpace::step_jacobian(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Mat
fnStateSpace::to_repr#
pub fn StateSpace::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
Traits
traitController#
pub(open) trait Controller {
fn update(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
fn reset(Self) -> Unit
}
- update
- fn update(The input for one control period of
dt seconds.
- reset
- fn reset(Self) -> UnitDrop the accumulated state (an integrator, a stored derivative) and start again from the next measurement.
A feedback law: the input to apply for a reference and a measurement.
The combinators in control (cascade, saturation, rate limiting) take any &Controller, so a controller written outside this library composes the same way the ones here do.
traitEstimator#
pub(open) trait Estimator {
fn predict(Self, @linear.Vec, Double) -> Unit
fn correct(Self, @linear.Vec) -> Unit raise @linear.Refused
fn state(Self) -> @linear.Vec
fn covariance(Self) -> @linear.Mat
}
- predict
- fn predict(Self, u : @linear.Vec, dt : Double) -> UnitPropagate the estimate and its covariance by
dt seconds under u.
- correct
- fn correct(Self, z : @linear.Vec) -> Unit raise @linear.RefusedFold in the measurement
z.
- state
- fn state(Self) -> @linear.VecThe current state estimate.
- covariance
- fn covariance(Self) -> @linear.MatThe current state covariance.
A state estimator: propagate on the input, then fold in a measurement.
The two steps are separate because a sensor does not always deliver on every control period; a caller that has no measurement that cycle calls predict alone.
traitKinematics#
pub(open) trait Kinematics {
fn forward(Self, @linear.Vec) -> @linear.Vec
fn inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fn jacobian(Self, @linear.Vec) -> @linear.Mat = _
}
- forward
- fn forward(Self, q : @linear.Vec) -> @linear.VecThe task-space pose for the joint values
q.
- inverse
- fn inverse(Self, x : @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.RefusedThe joint values that reach the pose
x. seed starts an iterative solve; without it the model picks its own starting point.
- jacobian
- fn jacobian(Self, q : @linear.Vec) -> @linear.Mat = _
d forward / d q, the task-space rows by the joint columns.
A mechanism: joint values to a task-space pose, and back.
Both spaces are plain Vecs so that one contract covers a two-wheel base and a six-axis arm; a model that has typed joint values offers them alongside.
traitModel#
pub(open) trait Model {
fn step(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
fn observe(Self, @linear.Vec) -> @linear.Vec
fn step_jacobian(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Mat = _
fn observe_jacobian(Self, @linear.Vec) -> @linear.Mat = _
}
- step
- fn step(Self, x : @linear.Vec, u : @linear.Vec, dt : Double) -> @linear.VecThe state after
dt seconds from x under the held input u.
- observe
- fn observe(Self, x : @linear.Vec) -> @linear.VecThe measurement a sensor produces for the state
x.
- step_jacobian
- fn step_jacobian(Self, x : @linear.Vec, u : @linear.Vec, dt : Double) -> @linear.Mat = _
d step / d x, an n x n matrix for an n-state.
- observe_jacobian
- fn observe_jacobian(Self, x : @linear.Vec) -> @linear.Mat = _
d observe / d x, an m x n matrix for an n-state and an m-output.
A plant: how the state moves under an input, and what a sensor sees.
step and observe are the model. step_jacobian and observe_jacobian have default implementations built on them by central differences, so a plant that gives only the two functions works with the extended Kalman filter, the unscented Kalman filter and the particle filter as it is.
traitPlanner#
pub(open) trait Planner {
fn plan(Self, @linear.Vec, @linear.Vec, Budget) -> Array[@linear.Vec] raise @linear.Refused
}
- plan
- fn plan(Self, start : @linear.Vec, goal : @linear.Vec, budget : Budget) -> Array[The path, including both ends, or
Refused::Exceeded when the budget runs out and Refused::Unreachable when the search space is exhausted.
A planner: a start, a goal and a budget, out comes a path of states from the start to the goal.
traitSpace#
pub(open) trait Space {
fn sample(Self, &@moonbitstack/moonrand/contract.Rng) -> @linear.Vec
fn distance(Self, @linear.Vec, @linear.Vec) -> Double
fn interpolate(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
}
- sample
- fn sample(Self, rng : &@rand.Rng) -> @linear.VecA state drawn from the space's own bounds, using
rng.
- distance
- fn distance(Self, a : @linear.Vec, b : @linear.Vec) -> DoubleThe distance between two states, in the space's own metric.
- interpolate
- fn interpolate(Self, a : @linear.Vec, b : @linear.Vec, t : Double) -> @linear.VecThe state
t of the way from a to b, with t in [0, 1]. A planner relies on it staying on a feasible connection, not merely on a straight line.
A state space a planner can search: where to draw random states, how far apart two are, and how to walk between them.
A vehicle model implements this with its (x, y, heading) states and its own steering primitives, which is what lets a sampling planner plan a Dubins path without knowing what Dubins is.
traitTrajectory#
pub(open) trait Trajectory {
fn duration(Self) -> Double
fn sample(Self, Double) -> Sample
}
- duration
- fn duration(Self) -> DoubleThe length of the motion in seconds.
- sample
- fn sample(Self, t : Double) -> SampleThe state at
t seconds from the start.
A single-axis motion profile: a duration, and the state at any time in it.
Sampling outside [0, duration] is defined rather than refused: before the start the motion is held at its first sample and after the end at its last, so a caller can run a clock past the end without a branch.
traitValidity#
pub(open) trait Validity {
fn valid(Self, @linear.Vec) -> Bool
fn motion(Self, &Space, @linear.Vec, @linear.Vec) -> Bool = _
}
- valid
- fn valid(Self, x : @linear.Vec) -> BoolWhether the state
x is free.
- motion
- fn motion(Self, space : &Space, a : @linear.Vec, b : @linear.Vec) -> Bool = _Whether the motion from
a to b stays free. The default walks the space's own interpolation in motion_samples steps and checks each point, which is right for a space whose interpolation cannot cut a corner.
Which states and motions a motion planner may use.
Collision checking lives here, so the planners stay free of geometry.
Feedback: the PID family and its combinators, LQR and its integral form, the iterative LQR, a box-constrained MPC, the two geometric path followers, computed torque, impedance and a barrier filter.
Types
enumAntiWindup#
pub enum AntiWindup {
Off
Conditional
BackCalculation
}
How a PID stops its integrator while the output is saturated.
fnAntiWindup::to_repr#
pub fn AntiWindup::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structCascade#
pub struct Cascade {
stages : Array[&@contract.Controller]
}
- stages
- Array[&@contract.Controller]
Controllers in series: the output of one stage is the reference of the next.
A cascade is how a current loop inside a velocity loop inside a position loop is written. The measurement vector carries one entry per stage, so stage i is handed measurement[i].
fnCascade::new#
pub fn Cascade::new(stages? : Array[&@contract.Controller]) -> Cascade
A cascade of the given stages, outermost first.
fnCascade::reset#
pub fn Cascade::reset(Self) -> Unit
fnCascade::update#
pub fn Cascade::update(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
structCbfConfig#
pub struct CbfConfig {
iterations : Int32
tolerance : Double
}
- iterations
- Int32
- tolerance
- Double
The knobs of the control-barrier-function filter.
iterations bounds the alternating projection. The filter is exact when it converges, and the bound is there so a pathological gradient cannot stall a control period.
fnCbfConfig::to_repr#
pub fn CbfConfig::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structComputedTorque#
pub struct ComputedTorque {
inertia : (@linear.Vec) -> @linear.Mat
gravity : (@linear.Vec) -> @linear.Vec
kp : Double
kd : Double
}
- inertia
- (@linear.Vec) -> @linear.Mat
- gravity
- (@linear.Vec) -> @linear.Vec
- kp
- Double
- kd
- Double
A computed-torque law: the inertia and gravity of the mechanism plus a proportional-derivative correction in joint space.
inertia and gravity are supplied by the caller because the mass matrix of a mechanism belongs to the mechanism, not to the controller; a model that has them can pass them straight in, and a model that does not can pass an identity and get plain joint-space feedback.
fnComputedTorque::effort#
pub fn ComputedTorque::effort(
self : ComputedTorque,
q? : @linear.Vec,
qd? : @linear.Vec,
q_desired? : @linear.Vec,
qd_desired? : @linear.Vec,
qdd_desired? : @linear.Vec,
) -> @linear.Vec raise @linear.Refused
The effort that tracks the desired joint motion.
fnComputedTorque::inertia_of#
pub fn ComputedTorque::inertia_of(
self : ComputedTorque,
q? : @linear.Vec,
) -> @linear.Mat
The inertia alone, for a caller that wants the model part without the loop.
fnComputedTorque::new#
pub fn ComputedTorque::new(
inertia? : (@linear.Vec) -> @linear.Mat,
gravity? : (@linear.Vec) -> @linear.Vec,
kp? : Double,
kd? : Double,
) -> ComputedTorque raise @linear.Refused
A computed-torque law with the given gains.
fnComputedTorque::to_repr#
pub fn ComputedTorque::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structFeedforward#
pub struct Feedforward {
velocity : Double
acceleration : Double
}
- velocity
- Double
- acceleration
- Double
A velocity- and acceleration-feedforward term on the reference trajectory.
A feedforward does not close a loop: it removes the lag the feedback would otherwise have to work off by itself. On a servo that follows a planned trajectory the velocity gain dominates and the acceleration gain covers the inertia.
fnFeedforward::command#
pub fn Feedforward::command(
self : Feedforward,
desired : @contract.Sample,
) -> Double
The command for a reference sample: the gains applied to its derivatives.
fnFeedforward::new#
pub fn Feedforward::new(
velocity? : Double,
acceleration? : Double,
) -> Feedforward raise @linear.Refused
A feedforward from its two gains. A negative gain is refused.
fnFeedforward::to_repr#
pub fn Feedforward::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structFeedforwardRunner#
pub struct FeedforwardRunner {
feedforward : Feedforward
mut previous_reference : Double
mut previous_velocity : Double
mut primed : Bool
}
- feedforward
- Feedforward
A feedforward driven by the reference sequence: the velocity and the acceleration are differenced from the references it is handed.
fnFeedforwardRunner::new#
pub fn FeedforwardRunner::new(feedforward? : Feedforward) -> FeedforwardRunner
A runner for a feedforward.
fnFeedforwardRunner::reset#
pub fn FeedforwardRunner::reset(Self) -> Unit
fnFeedforwardRunner::update#
pub fn FeedforwardRunner::update(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
structIlqrConfig#
pub struct IlqrConfig {
iterations : Int32
tolerance : Double
control : Output
regularisation : Double
state_weight : Double
input_weight : Double
}
- iterations
- Int32
- tolerance
- Double
- control
- Output
- regularisation
- Double
- state_weight
- Double
- input_weight
- Double
The knobs of the iterative LQR.
state_weight and input_weight are the diagonal weights of the quadratic cost: every state deviation costs state_weight / 2 per unit squared and every input input_weight / 2. regularisation is added to the input Hessian so a direction the model cannot move stays solvable.
fnIlqrConfig::to_repr#
pub fn IlqrConfig::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structImpedance#
pub struct Impedance {
mass : Double
damping : Double
stiffness : Double
mut position : @linear.Vec
mut velocity : @linear.Vec
}
- mass
- Double
- damping
- Double
- stiffness
- Double
An impedance controller: a mass-spring-damper between the tool and the target, with the contact force measured rather than modelled.
The rendered position follows the admittance m x'' = f_ext - d x' - k (x - x_d) and the effort returned is the spring-damper wrench at that position, so a tool pushed off its target yields and then returns.
fnImpedance::effort#
pub fn Impedance::effort(
self : Impedance,
target? : @linear.Vec,
external? : @linear.Vec,
dt? : Double,
) -> @linear.Vec
The wrench to command for one period.
fnImpedance::new#
pub fn Impedance::new(
mass? : Double,
damping? : Double,
stiffness? : Double,
start? : @linear.Vec,
) -> Impedance raise @linear.Refused
An impedance from its three parameters and the starting position.
fnImpedance::position#
pub fn Impedance::position(self : Impedance) -> @linear.Vec
The rendered position, which is where the tool actually is under a contact force.
fnImpedance::to_repr#
pub fn Impedance::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structIntegralLqr#
pub struct IntegralLqr {
gain : @linear.Mat
state_dim : Int32
output : @linear.Mat
dt : Double
mut integral : @linear.Vec
}
- gain
- @linear.Mat
- state_dim
- Int32
- output
- @linear.Mat
- dt
- Double
An LQR with an integral of the output error appended to the state.
The extra state is what removes a constant disturbance's offset: the proportional part of an LQR leaves a standing error when the plant is pushed by a bias, and the integrator integrates that error away. The integral acts on the output c x, not on the whole state, because integrating a state the input cannot reach independently would leave the augmented plant unstabilisable.
fnIntegralLqr::gain#
pub fn IntegralLqr::gain(self : IntegralLqr) -> @linear.Mat
The gain matrix over [state; integral].
fnIntegralLqr::reset#
pub fn IntegralLqr::reset(Self) -> Unit
fnIntegralLqr::update#
pub fn IntegralLqr::update(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
structLqr#
pub struct Lqr {
gain : @linear.Mat
}
- gain
- @linear.Mat
A linear-quadratic regulator: the infinite-horizon gain for a discrete plant and quadratic weights.
The control law is u = -k (x - reference), so a regulator that drives the plant to a setpoint is the same object as the one that drives it to zero.
fnLqr::gain#
pub fn Lqr::gain(self : Lqr) -> @linear.Mat
The gain matrix, for a caller that applies it itself.
fnLqr::reset#
pub fn Lqr::reset(Self) -> Unit
fnLqr::update#
pub fn Lqr::update(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
structMpc#
pub struct Mpc {
model : @contract.StateSpace
state_weight : @linear.Mat
input_weight : @linear.Mat
config : MpcConfig
mut previous : @linear.Vec
}
- model
- @contract.StateSpace
- state_weight
- @linear.Mat
- input_weight
- @linear.Mat
- config
- MpcConfig
A receding-horizon controller for a discrete linear plant with box limits on the input and on its rate of change.
The prediction is condensed into one quadratic program over the stacked inputs, which is what makes the period predictable: the matrices are built and factorised once per call, and the solve is a fixed number of ADMM iterations rather than a search whose length depends on the active set.
fnMpc::control#
pub fn Mpc::control(
self : Mpc,
state? : @linear.Vec,
target? : @linear.Vec,
) -> @linear.Vec raise @linear.Refused
The command for this period, after which the horizon shifts by one.
fnMpc::new#
pub fn Mpc::new(
model? : @contract.StateSpace,
state_weight? : @linear.Mat,
input_weight? : @linear.Mat,
config? : MpcConfig,
) -> Mpc raise @linear.Refused
A model-predictive controller for model at the given weights.
The plant must be discrete: a continuous record would need the period it is to be discretised at, which the controller cannot guess.
structMpcConfig#
pub struct MpcConfig {
horizon : Int32
iterations : Int32
penalty : Double
input : Output
rate : Output
}
- horizon
- Int32
- iterations
- Int32
- penalty
- Double
- input
- Output
- rate
- Output
The knobs of the model-predictive controller.
penalty is the ADMM step of the quadratic program, input the box on the command and rate the box on its change per second. iterations bounds the solve, so a period can never take longer than the budget says.
fnMpcConfig::to_repr#
pub fn MpcConfig::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structOutput#
pub struct Output {
low : Double
high : Double
}
- low
- Double
- high
- Double
An inclusive band [low, high] for one scalar.
fnOutput::to_repr#
pub fn Output::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structPid#
pub struct Pid {
config : PidConfig
mut integral : Double
mut previous_input : Double
mut derivative : Double
mut primed : Bool
}
- config
- PidConfig
A proportional-integral-derivative controller.
The integrator, the previous derivative input and the filtered derivative are the only state; reset clears them so a loop can start from rest.
fnPid::command#
pub fn Pid::command(
self : Pid,
reference? : Double,
measurement? : Double,
dt? : Double,
) -> Double
The output for one period of a scalar loop.
fnPid::integral#
pub fn Pid::integral(self : Pid) -> Double
The integrator value, which is what an anti-windup scheme acts on and what a test asserts when the loop sits in saturation.
fnPid::new#
pub fn Pid::new(config? : PidConfig) -> Pid
A PID from a configuration.
fnPid::reset#
pub fn Pid::reset(Self) -> Unit
fnPid::update#
pub fn Pid::update(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
structPidConfig#
pub struct PidConfig {
kp : Double
ki : Double
kd : Double
weight_p : Double
weight_d : Double
derivative_on_measurement : Bool
anti_windup : AntiWindup
tracking_time : Double
filter : Double
output : Output
}
- kp
- Double
- ki
- Double
- kd
- Double
- weight_p
- Double
- weight_d
- Double
- derivative_on_measurement
- Bool
- anti_windup
- AntiWindup
- tracking_time
- Double
- filter
- Double
- output
- Output
Every number a PID uses that is not one of its three gains.
weight_p and weight_d are the setpoint weights of the standard form: the proportional term acts on weight_p * reference - measurement and the derivative on weight_d * reference - measurement. A weight of zero takes the reference out of that term, which is what "derivative on measurement" means; derivative_on_measurement does exactly that for the derivative without the caller having to reason about the weight.
filter is the fraction of the raw derivative kept each period: 1 is no filtering, 0.1 smooths heavily. Values outside (0, 1] are read as 1.
fnPidConfig::to_repr#
pub fn PidConfig::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structPursuit#
pub struct Pursuit {
lookahead : Double
wheelbase : Double
}
- lookahead
- Double
- wheelbase
- Double
The steering a pure-pursuit follower asks for.
The follower looks a fixed distance ahead on the path, which is what makes it stable at speed: a short lookahead cuts corners and oscillates, a long one turns early. The returned angle is the one that puts the rear axle on an arc through the lookahead point.
fnPursuit::steering#
pub fn Pursuit::steering(
self : Pursuit,
pose? : @linear.Vec,
path? : ArrayView[@linear.Vec],
) -> Double raise @linear.Refused
The steering angle that puts the vehicle on the lookahead point.
fnPursuit::to_repr#
pub fn Pursuit::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structQpConfig#
pub struct QpConfig {
iterations : Int32
penalty : Double
tolerance : Double
}
- iterations
- Int32
- penalty
- Double
- tolerance
- Double
The knobs of the box-constrained quadratic program.
penalty is the ADMM step; too small and the dual converges slowly, too large and the primal oscillates. Twice the largest diagonal of h is the usual choice for a well-scaled problem.
fnQpConfig::to_repr#
pub fn QpConfig::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structRateLimited#
pub struct RateLimited {
inner : &@contract.Controller
rate : Double
mut previous : Double
}
- inner
- &@contract.Controller
- rate
- Double
A controller whose output may not change faster than rate per second.
The limit applies to the first entry of the command, which is the one a single-drive loop carries; a higher-dimensional command passes through the slowest component.
fnRateLimited::new#
pub fn RateLimited::new(
inner? : &@contract.Controller,
rate? : Double,
) -> RateLimited raise @linear.Refused
A rate-limit wrapper. A rate that is not positive is refused.
fnRateLimited::reset#
pub fn RateLimited::reset(Self) -> Unit
fnRateLimited::update#
pub fn RateLimited::update(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
structRiccati#
pub struct Riccati {
gain : @linear.Mat
cost : @linear.Mat
}
- gain
- @linear.Mat
- cost
- @linear.Mat
The solution of a discrete algebraic Riccati equation: the stabilising gain and the cost-to-go matrix it came with.
fnRiccati::to_repr#
pub fn Riccati::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structSaturating#
pub struct Saturating {
inner : &@contract.Controller
output : Output
}
- inner
- &@contract.Controller
- output
- Output
A controller whose output is clamped into a band.
fnSaturating::new#
pub fn Saturating::new(
inner? : &@contract.Controller,
output? : Output,
) -> Saturating
fnSaturating::output#
pub fn Saturating::output(self : Saturating) -> Output
The band, exposed so a caller can read back the limit it set.
fnSaturating::reset#
pub fn Saturating::reset(Self) -> Unit
fnSaturating::update#
pub fn Saturating::update(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
structStanley#
pub struct Stanley {
gain : Double
wheelbase : Double
soft : Double
}
- gain
- Double
- wheelbase
- Double
- soft
- Double
The steering a Stanley follower asks for.
The law is the heading error plus a term that grows with the distance from the path: the first brings the vehicle parallel, the second brings it back on. soft stands in for the speed in the usual atan(k e / v), so the follower does not need a speed input.
fnStanley::steering#
pub fn Stanley::steering(
self : Stanley,
pose? : @linear.Vec,
path? : ArrayView[@linear.Vec],
) -> Double raise @linear.Refused
The steering angle from the heading error and the cross-track error.
fnStanley::to_repr#
pub fn Stanley::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
Values
fnbox_qp#
pub fn box_qp(
h? : @linear.Mat,
f? : @linear.Vec,
lower? : @linear.Vec,
upper? : @linear.Vec,
config? : QpConfig,
) -> @linear.Vec raise @linear.Refused
The minimiser of 0.5 z' h z + f' z inside the box [lower, upper].
ADMM rather than an active-set method: the horizon of a predictive controller is re-solved every period with the same h, so a method whose cost per iteration is one triangular solve and one clamp keeps the period short, and there is no active set to warm start.
fncbf_filter#
pub fn cbf_filter(
nominal? : @linear.Vec,
gradient? : @linear.Vec,
drift? : Double,
barrier? : Double,
alpha? : Double,
lower? : @linear.Vec,
upper? : @linear.Vec,
config? : CbfConfig,
) -> @linear.Vec
The safe input closest to the nominal one.
The barrier h is the one the caller computed for the current state; gradient is d h / d u, drift is d h / d t without the input, and alpha is the class-K gain that decides how hard the filter pushes towards a larger barrier. The result satisfies drift + gradient * u + alpha * barrier >= 0 inside the box, or the closest point to that set when the box and the half-space do not meet.
Dykstra's alternating projection rather than plain alternation: plain alternation lands on a point of the intersection but not on the nearest one, and the whole point of a safety filter is to change the command as little as possible.
fnclamp_scalar#
pub fn clamp_scalar(value : Double, low : Double, high : Double) -> Double
Clamp into an inclusive band.
fndare#
pub fn dare(
a? : @linear.Mat,
b? : @linear.Mat,
q? : @linear.Mat,
r? : @linear.Mat,
iterations? : Int32,
tolerance? : Double,
) -> Riccati raise @linear.Refused
Solve the discrete algebraic Riccati equation for (a, b) at the weights (q, r) by iterating the Riccati recursion to a fixed point.
The recursion is monotone from p = q, so it converges to the stabilising solution without a Schur decomposition. A system that cannot be stabilised does not converge, and the iteration says so with Refused::Exceeded rather than returning a gain that would drive it further away.
fnilqr#
pub fn ilqr(
model? : &@contract.Model,
start? : @linear.Vec,
goal? : @linear.Vec,
controls? : Array[@linear.Vec],
dt? : Double,
config? : IlqrConfig,
) -> Array[@linear.Vec] raise @linear.Refused
The input sequence that drives start to goal over the horizon the initial guess fixes.
The initial guess gives the horizon, the control dimension and the region the solve starts in; the returned sequence has the same length. The backward pass uses the model's state Jacobian and a central difference for the input one, because a plant contract only promises the former.
fnintegral_lqr#
pub fn integral_lqr(
a? : @linear.Mat,
b? : @linear.Mat,
c? : @linear.Mat,
q? : @linear.Mat,
r? : @linear.Mat,
integral_weight? : @linear.Mat,
dt? : Double,
iterations? : Int32,
tolerance? : Double,
) -> IntegralLqr raise @linear.Refused
The integral LQR for a discrete (a, b) pair with outputs c, accumulating the output error at dt seconds.
fnlqr#
pub fn lqr(
a? : @linear.Mat,
b? : @linear.Mat,
q? : @linear.Mat,
r? : @linear.Mat,
iterations? : Int32,
tolerance? : Double,
) -> Lqr raise @linear.Refused
The infinite-horizon LQR for a discrete (a, b) pair. An element of r that is not positive is refused: it would let the input be free.
fnpid_gains#
pub fn pid_gains(
kp? : Double,
ki? : Double,
kd? : Double,
) -> PidConfig raise @linear.Refused
A PID configuration from the three gains. A negative gain is refused: a loop with a negative proportional gain is unstable by construction, and refusing it here names the mistake where it is made.
State estimation: the linear Kalman filter, its extended and unscented forms over any plant, a particle filter, the one-dimensional trackers and the backward smoothing pass.
Types
structAlphaBetaGamma#
pub struct AlphaBetaGamma {
alpha : Double
beta : Double
gamma : Double
mut position : Double
mut velocity : Double
mut acceleration : Double
}
- alpha
- Double
- beta
- Double
- gamma
- Double
An alpha-beta-gamma tracker: position, velocity and acceleration of one axis from a position measurement.
The three gains trade noise against lag the way a Kalman filter does with a fixed model: alpha corrects the position, beta the velocity and gamma the acceleration. The preset is the critically damped set for a steady-state model of a constant acceleration.
fnAlphaBetaGamma::new#
pub fn AlphaBetaGamma::new(
alpha? : Double,
beta? : Double,
gamma? : Double,
position? : Double,
) -> AlphaBetaGamma raise @linear.Refused
A tracker with the given gains and starting position.
fnAlphaBetaGamma::to_repr#
pub fn AlphaBetaGamma::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
fnAlphaBetaGamma::update#
pub fn AlphaBetaGamma::update(
self : AlphaBetaGamma,
measurement? : Double,
dt? : Double,
) -> @contract.Sample
The state after folding in one position measurement.
structComplementary#
pub struct Complementary {
alpha : Double
mut angle : Double
}
- alpha
- Double
A complementary filter for one angle: the gyro is trusted for the fast motion, the measurement for the slow drift.
alpha is the fraction of the integrated rate kept each period. At 0.98 with a period of a hundredth of a second the time constant is about half a second, which is the usual balance for a hobby-grade inertial sensor.
fnComplementary::angle#
pub fn Complementary::angle(self : Complementary) -> Double
fnComplementary::new#
pub fn Complementary::new(
alpha? : Double,
angle? : Double,
) -> Complementary raise @linear.Refused
A complementary filter with the given blend and starting angle.
fnComplementary::to_repr#
pub fn Complementary::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
fnComplementary::update#
pub fn Complementary::update(
self : Complementary,
rate? : Double,
measurement? : Double,
dt? : Double,
) -> Double
The corrected angle after one period.
structExtendedKalman#
pub struct ExtendedKalman {
model : &@moonbitstack/moonmcl/contract.Model
process : @linear.Mat
measurement : @linear.Mat
mut state : @linear.Vec
mut covariance : @linear.Mat
}
- model
- &@contract.Model
- process
- @linear.Mat
- measurement
- @linear.Mat
An extended Kalman filter: the linear recursion with the Jacobians the model supplies, whether analytically or by the contract's default differences.
fnExtendedKalman::correct#
pub fn ExtendedKalman::correct(Self, @linear.Vec) -> Unit raise @linear.Refused
fnExtendedKalman::covariance#
pub fn ExtendedKalman::covariance(Self) -> @linear.Mat
fnExtendedKalman::new#
pub fn ExtendedKalman::new(
model? : &@contract.Model,
process? : @linear.Mat,
measurement? : @linear.Mat,
state? : @linear.Vec,
covariance? : @linear.Mat,
) -> ExtendedKalman raise @linear.Refused
An extended Kalman filter for any plant that implements the model contract.
fnExtendedKalman::predict#
pub fn ExtendedKalman::predict(Self, @linear.Vec, Double) -> Unit
fnExtendedKalman::state#
pub fn ExtendedKalman::state(Self) -> @linear.Vec
fnExtendedKalman::to_repr#
pub fn ExtendedKalman::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structKalman#
pub struct Kalman {
model : @moonbitstack/moonmcl/contract.StateSpace
process : @linear.Mat
measurement : @linear.Mat
mut state : @linear.Vec
mut covariance : @linear.Mat
}
- model
- @contract.StateSpace
- process
- @linear.Mat
- measurement
- @linear.Mat
A linear Kalman filter for a discrete state-space plant.
fnKalman::correct#
pub fn Kalman::correct(Self, @linear.Vec) -> Unit raise @linear.Refused
fnKalman::covariance#
pub fn Kalman::covariance(Self) -> @linear.Mat
fnKalman::new#
pub fn Kalman::new(
model? : @contract.StateSpace,
process? : @linear.Mat,
measurement? : @linear.Mat,
state? : @linear.Vec,
covariance? : @linear.Mat,
) -> Kalman raise @linear.Refused
A Kalman filter for model with the given noise covariances and prior.
fnKalman::predict#
pub fn Kalman::predict(Self, @linear.Vec, Double) -> Unit
fnKalman::state#
pub fn Kalman::state(Self) -> @linear.Vec
structParticleFilter#
pub struct ParticleFilter {
model : &@moonbitstack/moonmcl/contract.Model
process : @linear.Mat
measurement : @linear.Mat
mut particles : Array[@linear.Vec]
mut weights : Array[Double]
rng : &@moonbitstack/moonrand/contract.Rng
}
- model
- &@contract.Model
- process
- @linear.Mat
- measurement
- @linear.Mat
- rng
- &@rand_contract.Rng
A particle filter over any plant that implements the model contract.
The particles are drawn around the prior at construction and then carried by the model itself, so the filter never assumes the state distribution stays Gaussian. Resampling is systematic and only runs when the effective sample size falls below half the cloud, which keeps the diversity a plain resample-every-step filter loses.
fnParticleFilter::correct#
pub fn ParticleFilter::correct(Self, @linear.Vec) -> Unit raise @linear.Refused
fnParticleFilter::covariance#
pub fn ParticleFilter::covariance(Self) -> @linear.Mat
fnParticleFilter::new#
pub fn ParticleFilter::new(
model? : &@contract.Model,
process? : @linear.Mat,
measurement? : @linear.Mat,
state? : @linear.Vec,
covariance? : @linear.Mat,
rng? : &@rand_contract.Rng,
samples? : Int32,
) -> ParticleFilter raise @linear.Refused
A particle filter with samples particles drawn around state.
fnParticleFilter::particles#
pub fn ParticleFilter::particles(self : ParticleFilter) -> Array[@linear.Vec]
The particles, for a caller that plots the cloud.
fnParticleFilter::predict#
pub fn ParticleFilter::predict(Self, @linear.Vec, Double) -> Unit
fnParticleFilter::state#
pub fn ParticleFilter::state(Self) -> @linear.Vec
fnParticleFilter::to_repr#
pub fn ParticleFilter::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
fnParticleFilter::weights#
pub fn ParticleFilter::weights(self : ParticleFilter) -> Array[Double]
The weight of each particle.
structUnscentedConfig#
pub struct UnscentedConfig {
alpha : Double
beta : Double
kappa : Double
}
- alpha
- Double
- beta
- Double
- kappa
- Double
The spread of the sigma points in the unscented transform.
alpha sets how far the points sit from the mean, beta folds in the knowledge that the distribution is Gaussian, and kappa is the usual secondary scaling. The preset is the one the reference implementations use.
fnUnscentedConfig::to_repr#
pub fn UnscentedConfig::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structUnscentedKalman#
pub struct UnscentedKalman {
model : &@moonbitstack/moonmcl/contract.Model
process : @linear.Mat
measurement : @linear.Mat
mut state : @linear.Vec
mut covariance : @linear.Mat
config : UnscentedConfig
}
- model
- &@contract.Model
- process
- @linear.Mat
- measurement
- @linear.Mat
- config
- UnscentedConfig
An unscented Kalman filter.
fnUnscentedKalman::correct#
pub fn UnscentedKalman::correct(Self, @linear.Vec) -> Unit raise @linear.Refused
fnUnscentedKalman::covariance#
pub fn UnscentedKalman::covariance(Self) -> @linear.Mat
fnUnscentedKalman::new#
pub fn UnscentedKalman::new(
model? : &@contract.Model,
process? : @linear.Mat,
measurement? : @linear.Mat,
state? : @linear.Vec,
covariance? : @linear.Mat,
config? : UnscentedConfig,
) -> UnscentedKalman raise @linear.Refused
An unscented Kalman filter for any plant that implements the model contract.
fnUnscentedKalman::predict#
pub fn UnscentedKalman::predict(Self, @linear.Vec, Double) -> Unit
fnUnscentedKalman::state#
pub fn UnscentedKalman::state(Self) -> @linear.Vec
fnUnscentedKalman::to_repr#
pub fn UnscentedKalman::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
Values
fnrts#
pub fn rts(
states? : Array[@linear.Vec],
covariances? : Array[@linear.Mat],
transitions? : Array[@linear.Mat],
process? : @linear.Mat,
) -> (Array[@linear.Vec], Array[@linear.Mat]) raise @linear.Refused
The Rauch-Tung-Striebel smoother: the forward filter's estimates refined by a backward pass over the same record.
states and covariances are the filtered estimates, transitions the state matrices between consecutive steps, and process the process covariance. The result is the smoothed state and covariance at every step, whose covariances are never larger than the filtered ones.
Single-axis motion: the trapezoid and the jerk-limited profile, polynomials, a natural cubic spline, a clamped B-spline, and the synchroniser that runs several axes on one clock.
Types
structBSpline#
pub struct BSpline {
degree : Int32
control : Array[Double]
knots : Array[Double]
duration : Double
}
- degree
- Int32
- control
- Array[Double]
- knots
- Array[Double]
- duration
- Double
A clamped uniform B-spline over a control polygon.
The curve is pulled towards the control points without passing through them, which is what makes it the profile for a path that only has to be smooth. The first and last control points are interpolated, so it starts and ends where the polygon does.
fnBSpline::duration#
pub fn BSpline::duration(Self) -> Double
fnBSpline::sample#
pub fn BSpline::sample(Self, Double) -> @contract.Sample
fnBSpline::to_repr#
pub fn BSpline::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structCubicSpline#
pub struct CubicSpline {
times : Array[Double]
positions : Array[Double]
curvatures : Array[Double]
}
- times
- Array[Double]
- positions
- Array[Double]
- curvatures
- Array[Double]
A natural cubic spline through time-stamped positions, continuous in velocity and acceleration, with zero curvature at both ends.
fnCubicSpline::duration#
pub fn CubicSpline::duration(Self) -> Double
fnCubicSpline::sample#
pub fn CubicSpline::sample(Self, Double) -> @contract.Sample
fnCubicSpline::to_repr#
pub fn CubicSpline::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structPolynomial#
pub struct Polynomial {
coefficients : Array[Double]
duration : Double
}
- coefficients
- Array[Double]
- duration
- Double
A polynomial in time, position = sum(coefficients[i] * t^i).
A polynomial is the profile to reach for when the boundary conditions are the whole requirement: it matches a position, a velocity and an acceleration at both ends exactly, and it does not care about a jerk limit on the way.
fnPolynomial::duration#
pub fn Polynomial::duration(Self) -> Double
fnPolynomial::sample#
pub fn Polynomial::sample(Self, Double) -> @contract.Sample
fnPolynomial::to_repr#
pub fn Polynomial::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structScurve#
pub struct Scurve {
distance : Double
sign : Double
duration : Double
phases : Array[Double]
jerks : Array[Double]
starts : Array[(Double, Double, Double)]
}
- distance
- Double
- sign
- Double
- duration
- Double
- phases
- Array[Double]
- jerks
- Array[Double]
- starts
- Array[(Double, Double, Double)]
fnScurve::duration#
pub fn Scurve::duration(Self) -> Double
fnScurve::sample#
pub fn Scurve::sample(Self, Double) -> @contract.Sample
structSynced#
pub struct Synced {
axes : Array[&@contract.Trajectory]
duration : Double
}
- axes
- Array[&@contract.Trajectory]
- duration
- Double
Several single-axis profiles run together.
Axes are slowed by sampling them on a stretched clock: axis i is sampled at t * duration_i / duration, and its velocity and acceleration are scaled by the same factor and its square. The path is preserved, the timing is not.
fnSynced::axis_sample#
pub fn Synced::axis_sample(
self : Synced,
index : Int32,
t : Double,
) -> @contract.Sample
One axis sampled on the common clock, with its derivatives scaled back to the axis's own time.
fnSynced::duration#
pub fn Synced::duration(Self) -> Double
fnSynced::new#
pub fn Synced::new(
axes? : Array[&@contract.Trajectory],
) -> Synced raise @linear.Refused
A synchronised set from axes that already have a duration.
fnSynced::sample#
pub fn Synced::sample(Self, Double) -> @contract.Sample
fnSynced::sample_all#
pub fn Synced::sample_all(
self : Synced,
t : Double,
) -> Array[@contract.Sample]
Every axis sampled at the same instant.
fnSynced::to_repr#
pub fn Synced::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structTrapezoid#
pub struct Trapezoid {
distance : Double
v_max : Double
a_max : Double
start_velocity : Double
end_velocity : Double
duration : Double
ramp_time : Double
cruise_time : Double
peak_velocity : Double
sign : Double
}
- distance
- Double
- v_max
- Double
- a_max
- Double
- start_velocity
- Double
- end_velocity
- Double
- duration
- Double
- ramp_time
- Double
- cruise_time
- Double
- peak_velocity
- Double
- sign
- Double
A trapezoidal velocity profile: accelerate, cruise, decelerate.
It is the cheapest profile that respects a velocity and an acceleration limit, and the one that puts a step in the acceleration at each corner. The boundary velocities may be anything up to the peak the distance allows.
fnTrapezoid::duration#
pub fn Trapezoid::duration(Self) -> Double
fnTrapezoid::sample#
pub fn Trapezoid::sample(Self, Double) -> @contract.Sample
fnTrapezoid::to_repr#
pub fn Trapezoid::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
Values
fnb_spline#
pub fn b_spline(
control? : ArrayView[Double],
degree? : Int32,
duration? : Double,
) -> BSpline raise @linear.Refused
A clamped B-spline of degree over control, running for duration seconds.
fncubic#
pub fn cubic(
start? : Double,
end? : Double,
start_velocity? : Double,
end_velocity? : Double,
duration? : Double,
) -> Polynomial raise @linear.Refused
A cubic from one boundary state to another over duration.
A cubic matches the two positions and the two velocities exactly; its acceleration jumps at both ends, which is what a drive without a jerk limit does anyway.
fncubic_spline#
pub fn cubic_spline(
knots? : ArrayView[(Double, Double)],
) -> CubicSpline raise @linear.Refused
A natural cubic spline through (time, position) knots, which must be ordered in time.
fnquintic#
pub fn quintic(
start? : Double,
end? : Double,
start_velocity? : Double,
start_acceleration? : Double,
end_velocity? : Double,
end_acceleration? : Double,
duration? : Double,
) -> Polynomial raise @linear.Refused
A quintic from one boundary state to another over duration.
A quintic also matches the two accelerations, so the acceleration is continuous with whatever profile came before and whatever comes after.
fnscurve#
pub fn scurve(
distance? : Double,
v_max? : Double,
a_max? : Double,
j_max? : Double,
start_velocity? : Double,
start_acceleration? : Double,
end_velocity? : Double,
end_acceleration? : Double,
) -> Scurve raise @linear.Refused
An S-curve over distance with the given limits and boundary conditions.
The start and end accelerations are folded into the first and last ramp, so a motion that is already accelerating keeps its acceleration instead of spending time on coming back to zero. A boundary acceleration outside the limit is refused, and so is a boundary velocity against the direction of travel or above the velocity limit: none of those has a jerk-limited profile that starts from it.
fntrapezoid#
pub fn trapezoid(
distance? : Double,
v_max? : Double,
a_max? : Double,
start_velocity? : Double,
end_velocity? : Double,
) -> Trapezoid raise @linear.Refused
A trapezoid over distance with the given limits and boundary velocities.
The boundary velocities are read along the direction of travel: a negative distance with a start velocity of -0.5 means the axis is already moving the way it will go. A velocity against the direction of travel is refused, since the profile would have to reverse inside itself.
Motion planning: the sampling planners over any state space, the Dubins and Reeds-Shepp car spaces, the two local planners, and the bridge to the graph package's grid searches.
Types
structDubins#
pub struct Dubins {
turning_radius : Double
extent : Double
}
- turning_radius
- Double
- extent
- Double
fnDubins::distance#
pub fn Dubins::distance(Self, @linear.Vec, @linear.Vec) -> Double
fnDubins::interpolate#
pub fn Dubins::interpolate(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
fnDubins::new#
pub fn Dubins::new(
turning_radius? : Double,
extent? : Double,
) -> Dubins raise @linear.Refused
A Dubins car with the given radius. extent is the half width of the box the space samples from.
fnDubins::path#
pub fn Dubins::path(
self : Dubins,
start? : @linear.Vec,
goal? : @linear.Vec,
) -> DubinsPath raise @linear.Refused
The shortest Dubins path between two (x, y, heading) poses.
fnDubins::paths#
pub fn Dubins::paths(
self : Dubins,
start? : @linear.Vec,
goal? : @linear.Vec,
) -> Array[DubinsPath] raise @linear.Refused
Every word that joins the two poses, in word order.
A car whose words are all drawn is a car the reader can compare; the callers that only want to drive one take path.
fnDubins::sample#
pub fn Dubins::sample(Self, &@moonbitstack/moonrand/contract.Rng) -> @linear.Vec
fnDubins::to_repr#
pub fn Dubins::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structDubinsPath#
pub struct DubinsPath {
word : DubinsWord
lengths : (Double, Double, Double)
length : Double
origin : @linear.Vec
radius : Double
}
- word
- DubinsWord
- lengths
- (Double, Double, Double)
- length
- Double
- origin
- @linear.Vec
- radius
- Double
The Dubins path between two poses: the word and the length of each of its three pieces.
fnDubinsPath::at#
pub fn DubinsPath::at(self : DubinsPath, distance : Double) -> @linear.Vec
The pose at distance travelled along the path.
fnDubinsPath::to_repr#
pub fn DubinsPath::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
enumDubinsWord#
pub enum DubinsWord {
Lsl
Lsr
Rsl
Rsr
Rlr
Lrl
}
One of the six Dubins words: three pieces, each a left arc, a right arc or a straight line.
fnDubinsWord::to_repr#
pub fn DubinsWord::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structDynamicWindow#
pub struct DynamicWindow {
max_velocity : Double
min_velocity : Double
max_rotation : Double
max_acceleration : Double
max_rotation_acceleration : Double
resolution_velocity : Double
resolution_rotation : Double
predict : Double
clearance : Double
margin : Double
heading_weight : Double
clearance_weight : Double
velocity_weight : Double
}
- max_velocity
- Double
- min_velocity
- Double
- max_rotation
- Double
- max_acceleration
- Double
- max_rotation_acceleration
- Double
- resolution_velocity
- Double
- resolution_rotation
- Double
- predict
- Double
- clearance
- Double
- margin
- Double
- heading_weight
- Double
- clearance_weight
- Double
- velocity_weight
- Double
The dynamic window approach: sample the velocities the drives can reach in one period, roll each one forward, and keep the best by heading, clearance and speed.
fnDynamicWindow::command#
pub fn DynamicWindow::command(
self : DynamicWindow,
pose? : @linear.Vec,
velocity? : @linear.Vec,
goal? : @linear.Vec,
obstacles? : ArrayView[@linear.Vec],
dt? : Double,
) -> @linear.Vec raise @linear.Refused
The best velocity pair for the current pose towards goal, avoiding obstacles, which are points.
fnDynamicWindow::to_repr#
pub fn DynamicWindow::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
enumGridSearch#
pub enum GridSearch {
Dijkstra
AStar
Jps
ThetaStar
}
Which search a grid query runs.
fnGridSearch::to_repr#
pub fn GridSearch::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structGridSpace#
pub struct GridSpace {
grid : @grid.Grid
resolution : Double
}
- grid
- @graph_grid.Grid
- resolution
- Double
A grid used as a planning space, so a sampling planner can search a lattice.
fnGridSpace::distance#
pub fn GridSpace::distance(Self, @linear.Vec, @linear.Vec) -> Double
fnGridSpace::interpolate#
pub fn GridSpace::interpolate(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
fnGridSpace::motion#
pub fn GridSpace::motion(Self, &@moonbitstack/moonmcl/contract.Space, @linear.Vec, @linear.Vec) -> Bool
fnGridSpace::new#
pub fn GridSpace::new(
grid : @graph_grid.Grid,
resolution? : Double,
) -> GridSpace raise @linear.Refused
A space whose states are grid cells scaled by resolution.
fnGridSpace::sample#
pub fn GridSpace::sample(Self, &@moonbitstack/moonrand/contract.Rng) -> @linear.Vec
fnGridSpace::valid#
pub fn GridSpace::valid(Self, @linear.Vec) -> Bool
structInformedRrtStar#
pub struct InformedRrtStar {
space : &@moonbitstack/moonmcl/contract.Space
validity : &@moonbitstack/moonmcl/contract.Validity
step : Double
goal_bias : Double
neighbourhood : Double
rng : &@moonbitstack/moonrand/contract.Rng
}
- space
- &@contract.Space
- validity
- &@contract.Validity
- step
- Double
- goal_bias
- Double
- neighbourhood
- Double
- rng
- &@rand_contract.Rng
An RRT* that only samples where a path shorter than the best one could still run: the ellipse whose foci are the start and the goal.
fnInformedRrtStar::new#
pub fn InformedRrtStar::new(
space? : &@contract.Space,
validity? : &@contract.Validity,
step? : Double,
goal_bias? : Double,
neighbourhood? : Double,
seed? : Int32,
) -> InformedRrtStar raise @linear.Refused
An informed RRT* seeded from the best path found so far.
fnInformedRrtStar::plan#
pub fn InformedRrtStar::plan(Self, @linear.Vec, @linear.Vec, @moonbitstack/moonmcl/contract.Budget) -> Array[@linear.Vec] raise @linear.Refused
structPotentialField#
pub struct PotentialField {
attraction : Double
repulsion : Double
influence : Double
step : Double
}
- attraction
- Double
- repulsion
- Double
- influence
- Double
- step
- Double
An artificial potential field: attraction to the goal, repulsion from the obstacles.
fnPotentialField::command#
pub fn PotentialField::command(
self : PotentialField,
position? : @linear.Vec,
goal? : @linear.Vec,
obstacles? : ArrayView[@linear.Vec],
) -> @linear.Vec
The velocity command for this step, as a two-dimensional motion.
fnPotentialField::to_repr#
pub fn PotentialField::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structPrm#
pub struct Prm {
space : &@moonbitstack/moonmcl/contract.Space
validity : &@moonbitstack/moonmcl/contract.Validity
samples : Int32
neighbours : Int32
connection : Double
rng : &@moonbitstack/moonrand/contract.Rng
}
- space
- &@contract.Space
- validity
- &@contract.Validity
- samples
- Int32
- neighbours
- Int32
- connection
- Double
- rng
- &@rand_contract.Rng
fnPrm::new#
pub fn Prm::new(
space? : &@contract.Space,
validity? : &@contract.Validity,
samples? : Int32,
neighbours? : Int32,
connection? : Double,
seed? : Int32,
) -> Prm raise @linear.Refused
A roadmap with the given sample count and connection radius.
fnPrm::plan#
pub fn Prm::plan(Self, @linear.Vec, @linear.Vec, @moonbitstack/moonmcl/contract.Budget) -> Array[@linear.Vec] raise @linear.Refused
structReedsShepp#
pub struct ReedsShepp {
turning_radius : Double
extent : Double
}
- turning_radius
- Double
- extent
- Double
fnReedsShepp::candidates#
pub fn ReedsShepp::candidates(
self : ReedsShepp,
start? : @linear.Vec,
goal? : @linear.Vec,
) -> Array[ReedsSheppPath] raise @linear.Refused
Every word that joins the two poses, shortest first.
A word that cannot reach the goal returns no parameters at all, so the list holds only the ones that do; the shortest of them shares its length with any other candidate of the same total.
fnReedsShepp::distance#
pub fn ReedsShepp::distance(Self, @linear.Vec, @linear.Vec) -> Double
fnReedsShepp::interpolate#
pub fn ReedsShepp::interpolate(Self, @linear.Vec, @linear.Vec, Double) -> @linear.Vec
fnReedsShepp::new#
pub fn ReedsShepp::new(
turning_radius? : Double,
extent? : Double,
) -> ReedsShepp raise @linear.Refused
A Reeds-Shepp car with the given radius. extent is the half width of the box the space samples from.
fnReedsShepp::path#
pub fn ReedsShepp::path(
self : ReedsShepp,
start? : @linear.Vec,
goal? : @linear.Vec,
) -> ReedsSheppPath raise @linear.Refused
The shortest path between two (x, y, heading) poses, reversing as needed.
fnReedsShepp::sample#
pub fn ReedsShepp::sample(Self, &@moonbitstack/moonrand/contract.Rng) -> @linear.Vec
fnReedsShepp::to_repr#
pub fn ReedsShepp::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structReedsSheppPath#
pub struct ReedsSheppPath {
word : ReedsSheppWord
segments : Array[ReedsSheppSegment]
length : Double
origin : @linear.Vec
radius : Double
}
- word
- ReedsSheppWord/ / The word the path spells.
- segments
- Array[ReedsSheppSegment]/ / The pieces, in the order the car drives them.
- length
- Double/ / The total length, in metres.
- origin
- @linear.Vec
- radius
- Double
A Reeds-Shepp path: the word it spells, its pieces, and the pose it starts from.
fnReedsSheppPath::at#
pub fn ReedsSheppPath::at(
self : ReedsSheppPath,
distance : Double,
) -> @linear.Vec
The pose at distance travelled along the path: an arc turns around its own centre, a straight runs along the heading, and backing up moves the car the other way without changing which way it faces.
fnReedsSheppPath::cusps#
pub fn ReedsSheppPath::cusps(self : ReedsSheppPath) -> Int32
How many times the path changes direction.
fnReedsSheppPath::to_repr#
pub fn ReedsSheppPath::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structReedsSheppSegment#
pub struct ReedsSheppSegment {
turn : Double
forward : Double
length : Double
}
- turn
- Double/ /
1 for a left arc, -1 for a right one, 0 for a straight.
- forward
- Double/ /
1 when the car drives forward along the piece, -1 when it backs up.
- length
- Double/ / The length of the piece, in metres.
One straight or curved piece of a Reeds-Shepp path.
fnReedsSheppSegment::to_repr#
pub fn ReedsSheppSegment::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
enumReedsSheppWord#
pub enum ReedsSheppWord {
LpSpLp
RpSpRp
LmSmLm
RmSmRm
LpSpRp
RpSpLp
LmSmRm
RmSmLm
LpRmLp
RpLmRp
LmRpLm
RmLpRm
LpRmLm
RpLmRm
LmRpLp
RmLpRp
LpRpLm
RpLpRm
LmRmLp
RmRmLp
LpRpLmRm
RpLpRmLm
LmRmLpRp
RmRmLpLp
LpRmLmRp
RpLmRmLp
LmRpLpRm
RmLpRpLm
LpRmSmLm
RpLmSmRm
LmRpSpLp
RmLpSpRp
LpRmSmRm
RpLmSmLm
LmRpSpRp
RmLpSpLp
LpSpRmLm
RpSpLmRm
LmSmRpLp
RmSmLpRp
LpSpLpRm
RpSpRpLm
LmSmLmRp
RmSmRmLp
LpRmSmLmRp
RpLmSmRmLp
LmRpSpLpRm
RmLpSpRpLm
}
One of the forty-eight reversable-car path words.
fnReedsSheppWord::code#
pub fn ReedsSheppWord::code(self : ReedsSheppWord) -> Int32
The position of a word in the enumeration: family times four plus variant.
A family is one set of segment formulas and a variant is one of the four frames it is applied in, so the pair is all the table a word needs.
fnReedsSheppWord::of_code#
pub fn ReedsSheppWord::of_code(code : Int32) -> ReedsSheppWord?
The word a code names, or None outside the forty-eight.
fnReedsSheppWord::to_repr#
pub fn ReedsSheppWord::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structRrt#
pub struct Rrt {
space : &@moonbitstack/moonmcl/contract.Space
validity : &@moonbitstack/moonmcl/contract.Validity
step : Double
goal_bias : Double
rng : &@moonbitstack/moonrand/contract.Rng
}
- space
- &@contract.Space
- validity
- &@contract.Validity
- step
- Double
- goal_bias
- Double
- rng
- &@rand_contract.Rng
A rapidly-exploring random tree.
step is the length of the edge a new sample is allowed to reach, and goal_bias the chance that a sample is the goal itself rather than a random state. The generator is seeded from the planner, so a run replays exactly.
fnRrt::new#
pub fn Rrt::new(
space? : &@contract.Space,
validity? : &@contract.Validity,
step? : Double,
goal_bias? : Double,
seed? : Int32,
) -> Rrt raise @linear.Refused
An RRT that extends by step and steers to the goal once every 1 / goal_bias samples.
fnRrt::plan#
pub fn Rrt::plan(Self, @linear.Vec, @linear.Vec, @moonbitstack/moonmcl/contract.Budget) -> Array[@linear.Vec] raise @linear.Refused
structRrtConnect#
pub struct RrtConnect {
space : &@moonbitstack/moonmcl/contract.Space
validity : &@moonbitstack/moonmcl/contract.Validity
step : Double
rng : &@moonbitstack/moonrand/contract.Rng
}
- space
- &@contract.Space
- validity
- &@contract.Validity
- step
- Double
- rng
- &@rand_contract.Rng
An RRT grown from both ends at once: the two trees take turns reaching for each other, which closes a narrow passage far faster than one tree alone.
fnRrtConnect::new#
pub fn RrtConnect::new(
space? : &@contract.Space,
validity? : &@contract.Validity,
step? : Double,
seed? : Int32,
) -> RrtConnect raise @linear.Refused
fnRrtConnect::plan#
pub fn RrtConnect::plan(Self, @linear.Vec, @linear.Vec, @moonbitstack/moonmcl/contract.Budget) -> Array[@linear.Vec] raise @linear.Refused
structRrtStar#
pub struct RrtStar {
space : &@moonbitstack/moonmcl/contract.Space
validity : &@moonbitstack/moonmcl/contract.Validity
step : Double
goal_bias : Double
neighbourhood : Double
rng : &@moonbitstack/moonrand/contract.Rng
}
- space
- &@contract.Space
- validity
- &@contract.Validity
- step
- Double
- goal_bias
- Double
- neighbourhood
- Double
- rng
- &@rand_contract.Rng
An RRT that rewires the tree as it grows, so the path converges on the shortest one inside the budget.
fnRrtStar::new#
pub fn RrtStar::new(
space? : &@contract.Space,
validity? : &@contract.Validity,
step? : Double,
goal_bias? : Double,
neighbourhood? : Double,
seed? : Int32,
) -> RrtStar raise @linear.Refused
An RRT* with a rewiring radius.
fnRrtStar::plan#
pub fn RrtStar::plan(Self, @linear.Vec, @linear.Vec, @moonbitstack/moonmcl/contract.Budget) -> Array[@linear.Vec] raise @linear.Refused
Values
fnfamily_words#
pub fn family_words(family : Int32) -> Array[ReedsSheppWord]
The words of the family family, in variant order.
fngrid_path#
pub fn grid_path(
grid : @graph_grid.Grid,
start? : Int32,
goal? : Int32,
search? : GridSearch,
) -> Array[Int32] raise @linear.Refused
The shortest path between two grid cells, as node indices, from the grid package's own search.
Mechanisms: the wheeled bases and their steering, the n-link planar arm with a damped inverse, the delta cell, the SCARA and the gantry.
Types
structAckermann#
pub struct Ackermann {
wheelbase : Double
track : Double
}
- wheelbase
- Double
- track
- Double
An Ackermann steering base: the inner and outer front wheels turn by different angles about a common centre.
fnAckermann::angles#
pub fn Ackermann::angles(
self : Ackermann,
curvature? : Double,
) -> (Double,
Double,
) -> Unit
The inner and outer steering angles for a curvature: the wheel on the inside of the turn has to turn further, by the angle its shorter radius demands.
fnAckermann::forward#
pub fn Ackermann::forward(Self, @linear.Vec) -> @linear.Vec
fnAckermann::inverse#
pub fn Ackermann::inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fnAckermann::jacobian#
pub fn Ackermann::jacobian(Self, @linear.Vec) -> @linear.Mat
fnAckermann::new#
pub fn Ackermann::new(
wheelbase? : Double,
track? : Double,
) -> Ackermann raise @linear.Refused
An Ackermann base with the given wheelbase and track.
fnAckermann::to_repr#
pub fn Ackermann::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structBicycle#
pub struct Bicycle {
wheelbase : Double
}
- wheelbase
- Double
A bicycle model: the joint space is (speed, steering angle) and the task space the body twist (vx, vy, omega).
fnBicycle::curvature#
pub fn Bicycle::curvature(self : Bicycle, steering? : Double) -> Double
The curvature a steering angle gives.
fnBicycle::forward#
pub fn Bicycle::forward(Self, @linear.Vec) -> @linear.Vec
fnBicycle::inverse#
pub fn Bicycle::inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fnBicycle::jacobian#
pub fn Bicycle::jacobian(Self, @linear.Vec) -> @linear.Mat
fnBicycle::new#
pub fn Bicycle::new(wheelbase? : Double) -> Bicycle raise @linear.Refused
A bicycle with the given wheelbase.
fnBicycle::steering#
pub fn Bicycle::steering(self : Bicycle, curvature? : Double) -> Double
The steering angle a curvature needs.
fnBicycle::to_repr#
pub fn Bicycle::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structDelta#
pub struct Delta {
base_radius : Double
platform_radius : Double
upper_arm : Double
lower_arm : Double
}
- base_radius
- Double
- platform_radius
- Double
- upper_arm
- Double
- lower_arm
- Double
A delta robot: three parallel arms from a fixed base to a moving platform.
The joint space is the three upper-arm angles measured from horizontal and the task space the platform centre (x, y, z) under the base.
fnDelta::elbows#
pub fn Delta::elbows(self : Delta, q : @linear.Vec) -> Array[@linear.Vec]
The three elbows, in the base frame, for the upper-arm angles q.
A drawing of the cell needs the elbows as well as the platform, and the cell already computes them to solve its own forward map.
fnDelta::forward#
pub fn Delta::forward(Self, @linear.Vec) -> @linear.Vec
fnDelta::inverse#
pub fn Delta::inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fnDelta::jacobian#
pub fn Delta::jacobian(Self, @linear.Vec) -> @linear.Mat
fnDelta::new#
pub fn Delta::new(
base_radius? : Double,
platform_radius? : Double,
upper_arm? : Double,
lower_arm? : Double,
) -> Delta raise @linear.Refused
A delta cell with the given geometry.
fnDelta::to_repr#
pub fn Delta::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structDifferential#
pub struct Differential {
wheel_radius : Double
track : Double
}
- wheel_radius
- Double
- track
- Double
A two-wheel differential drive.
track is the distance between the wheels and wheel_radius the radius of each. The joint space is (omega_left, omega_right) in radians per second and the task space (v, omega) in metres and radians per second.
fnDifferential::advance#
pub fn Differential::advance(
self : Differential,
pose? : @linear.Vec,
twist? : @linear.Vec,
dt? : Double,
) -> @linear.Vec
The pose after driving twist for dt seconds.
The arc is integrated exactly: a straight chord would cut the corner of every turn, and the error grows with the square of the period.
fnDifferential::forward#
pub fn Differential::forward(Self, @linear.Vec) -> @linear.Vec
fnDifferential::inverse#
pub fn Differential::inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fnDifferential::jacobian#
pub fn Differential::jacobian(Self, @linear.Vec) -> @linear.Mat
fnDifferential::new#
pub fn Differential::new(
wheel_radius? : Double,
track? : Double,
) -> Differential raise @linear.Refused
A differential drive with the given wheel radius and track width.
fnDifferential::to_repr#
pub fn Differential::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structGantry#
pub struct Gantry {
track : Double
}
- track
- Double
A gantry: two motors drive one gantry axis from either end, so the pair sets the position and the tilt of the beam.
The joint space is (x, y_left, y_right, z) and the task space (x, y, z, tilt), where y is the middle of the beam and tilt the angle the difference between its ends makes.
fnGantry::forward#
pub fn Gantry::forward(Self, @linear.Vec) -> @linear.Vec
fnGantry::inverse#
pub fn Gantry::inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fnGantry::jacobian#
pub fn Gantry::jacobian(Self, @linear.Vec) -> @linear.Mat
fnGantry::new#
pub fn Gantry::new(track? : Double) -> Gantry raise @linear.Refused
A gantry with the given beam length.
fnGantry::to_repr#
pub fn Gantry::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structMecanum#
pub struct Mecanum {
wheel_radius : Double
half_length : Double
half_width : Double
}
- wheel_radius
- Double
- half_length
- Double
- half_width
- Double
A four-wheel mecanum base. The joint space is the four wheel speeds in the order front left, front right, rear left, rear right, and the task space the body twist (vx, vy, omega).
fnMecanum::forward#
pub fn Mecanum::forward(Self, @linear.Vec) -> @linear.Vec
fnMecanum::inverse#
pub fn Mecanum::inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fnMecanum::jacobian#
pub fn Mecanum::jacobian(Self, @linear.Vec) -> @linear.Mat
fnMecanum::new#
pub fn Mecanum::new(
wheel_radius? : Double,
half_length? : Double,
half_width? : Double,
) -> Mecanum raise @linear.Refused
A mecanum base with the given wheel radius and half extents.
fnMecanum::to_repr#
pub fn Mecanum::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structOmnidirectional#
pub struct Omnidirectional {
wheel_radius : Double
radius : Double
angles : Array[Double]
}
- wheel_radius
- Double
- radius
- Double
- angles
- Array[Double]
An omnidirectional base with three or more driven wheels on a circle.
The wheels are mounted tangentially, so each one drives along theta and the map from twist to wheel speeds is one row per wheel. With three wheels it inverts exactly; with more the inverse is the least-squares one, which spreads the effort over the wheels that can help.
fnOmnidirectional::forward#
pub fn Omnidirectional::forward(Self, @linear.Vec) -> @linear.Vec
fnOmnidirectional::inverse#
pub fn Omnidirectional::inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fnOmnidirectional::jacobian#
pub fn Omnidirectional::jacobian(Self, @linear.Vec) -> @linear.Mat
fnOmnidirectional::new#
pub fn Omnidirectional::new(
wheel_radius? : Double,
radius? : Double,
angles? : ArrayView[Double],
) -> Omnidirectional raise @linear.Refused
An omnidirectional base whose wheel i sits at angles[i] radians on a circle of radius.
fnOmnidirectional::to_repr#
pub fn Omnidirectional::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structPlanarArm#
pub struct PlanarArm {
lengths : Array[Double]
}
- lengths
- Array[Double]
A planar chain of n links. The joint space is the joint angles and the task space the (x, y) of the tip.
fnPlanarArm::forward#
pub fn PlanarArm::forward(Self, @linear.Vec) -> @linear.Vec
fnPlanarArm::inverse#
pub fn PlanarArm::inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fnPlanarArm::jacobian#
pub fn PlanarArm::jacobian(Self, @linear.Vec) -> @linear.Mat
fnPlanarArm::jacobian_analytic#
pub fn PlanarArm::jacobian_analytic(
self : PlanarArm,
q : @linear.Vec,
) -> @linear.Mat
The analytic Jacobian of the arm at the joint angles q: how far the tip moves for a small change in each joint.
Link k and everything outboard of it turn with joint k, so column k is that joint's direction rotated by the total angle up to k, scaled by the reach still in front of it. Kinematics::jacobian defaults to a central difference; this is the same matrix in closed form.
fnPlanarArm::new#
pub fn PlanarArm::new(
lengths? : ArrayView[Double],
) -> PlanarArm raise @linear.Refused
A planar arm with the given link lengths.
fnPlanarArm::solve#
pub fn PlanarArm::solve(
self : PlanarArm,
target? : @linear.Vec,
seed? : @linear.Vec,
iterations? : Int32,
damping? : Double,
tolerance? : Double,
) -> @linear.Vec raise @linear.Refused
The joint angles that put the tip on target, by damped least squares.
The damping is what makes the solve usable near a singularity: without it the Jacobian's inverse blows up and the joint velocities with it, which on a real arm is a torque the drive cannot deliver. damping is the square of the smallest singular value the solve allows.
fnPlanarArm::to_repr#
pub fn PlanarArm::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.
structScara#
pub struct Scara {
upper_arm : Double
forearm : Double
vertical : Double
}
- upper_arm
- Double
- forearm
- Double
- vertical
- Double
A SCARA arm: two revolute joints about the vertical axis and one prismatic joint along it.
fnScara::forward#
pub fn Scara::forward(Self, @linear.Vec) -> @linear.Vec
fnScara::inverse#
pub fn Scara::inverse(Self, @linear.Vec, seed? : @linear.Vec) -> @linear.Vec raise @linear.Refused
fnScara::jacobian#
pub fn Scara::jacobian(Self, @linear.Vec) -> @linear.Mat
fnScara::new#
pub fn Scara::new(
upper_arm? : Double,
forearm? : Double,
vertical? : Double,
) -> Scara raise @linear.Refused
A SCARA with the two link lengths and the vertical stroke.
fnScara::to_repr#
pub fn Scara::to_repr(self) -> @debug.Repr
The @debug.Debug representation of this value.