moonbitstack/moonmcl/control does not have a README file

    AntiWindup

    pub(all) enum AntiWindup {
    Off
    Conditional
    BackCalculation
    } derive(
    Debug
    )

    How a PID stops its integrator while the output is saturated.

    Cascade

    pub(all) struct Cascade {
    stages : Array[&
    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].

    Cascade::new

    A cascade of the given stages, outermost first.

    Cascade::reset

    fn Cascade::reset(self : Cascade) -> Unit

    CbfConfig

    pub(all) struct CbfConfig {
    iterations : Int
    tolerance : Double
    } derive(
    Debug
    )

    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.

    ComputedTorque

    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.

    ComputedTorque::inertia_of

    The inertia alone, for a caller that wants the model part without the loop.

    ComputedTorque::new

    A computed-torque law with the given gains.

    ComputedTorque::to_repr

    Feedforward

    pub(all) struct Feedforward {
    velocity : Double
    acceleration : Double
    } derive(
    Debug
    )

    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.

    Feedforward::command

    The command for a reference sample: the gains applied to its derivatives.

    Feedforward::new

    fn Feedforward::new(velocity? : Double, acceleration? : Double) -> Feedforward raise
    Refused

    A feedforward from its two gains. A negative gain is refused.

    FeedforwardRunner

    pub(all) struct FeedforwardRunner {
    feedforward : Feedforward
    previous_reference : Double
    previous_velocity : Double
    primed : Bool
    }

    A feedforward driven by the reference sequence: the velocity and the acceleration are differenced from the references it is handed.

    FeedforwardRunner::new

    A runner for a feedforward.

    FeedforwardRunner::reset

    fn FeedforwardRunner::reset(self : FeedforwardRunner) -> Unit

    IlqrConfig

    pub(all) struct IlqrConfig {
    iterations : Int
    tolerance : Double
    control : Output
    regularisation : Double
    state_weight : Double
    input_weight : Double
    } derive(
    Debug
    )

    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.

    Impedance

    pub(all) struct Impedance {
    mass : Double
    damping : Double
    stiffness : Double
    position :
    Vec

    velocity :
    Vec

    } derive(
    Debug
    )

    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.

    Impedance::effort

    The wrench to command for one period.

    Impedance::new

    fn Impedance::new(mass~ : Double, damping~ : Double, stiffness~ : Double, start~ :
    Vec
    ) -> Impedance raise
    Refused

    An impedance from its three parameters and the starting position.

    Impedance::position

    The rendered position, which is where the tool actually is under a contact force.

    IntegralLqr

    pub(all) struct IntegralLqr {
    gain :
    Mat

    state_dim : Int
    output :
    Mat

    dt : Double
    integral :
    Vec

    }

    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.

    IntegralLqr::gain

    The gain matrix over [state; integral].

    IntegralLqr::reset

    fn IntegralLqr::reset(self : IntegralLqr) -> Unit

    Lqr

    pub(all) struct Lqr {
    gain :
    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.
    impl Controller for Lqr

    Lqr::gain

    The gain matrix, for a caller that applies it itself.

    Lqr::reset

    fn Lqr::reset(self : Lqr) -> Unit

    Lqr::update

    Mpc

    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.

    Mpc::control

    The command for this period, after which the horizon shifts by one.

    Mpc::new

    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.

    MpcConfig

    pub(all) struct MpcConfig {
    horizon : Int
    iterations : Int
    penalty : Double
    input : Output
    rate : Output
    } derive(
    Debug
    )

    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.

    Output

    pub(all) struct Output {
    low : Double
    high : Double
    } derive(
    Debug
    )

    An inclusive band [low, high] for one scalar.

    Output::to_repr

    Pid

    pub(all) struct Pid {
    config : PidConfig
    integral : Double
    previous_input : Double
    derivative : Double
    primed : Bool
    }

    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.
    impl Controller for Pid

    Pid::command

    fn Pid::command(self : Pid, reference~ : Double, measurement~ : Double, dt~ : Double) -> Double

    The output for one period of a scalar loop.

    Pid::integral

    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.

    Pid::new

    fn Pid::new(config? : PidConfig) -> Pid

    A PID from a configuration.

    Pid::reset

    fn Pid::reset(self : Pid) -> Unit

    Pid::update

    PidConfig

    pub(all) 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
    } derive(
    Debug
    )

    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.

    Pursuit

    pub(all) struct Pursuit {
    lookahead : Double
    wheelbase : Double
    } derive(
    Debug
    )

    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.

    Pursuit::steering

    The steering angle that puts the vehicle on the lookahead point.

    Pursuit::to_repr

    QpConfig

    pub(all) struct QpConfig {
    iterations : Int
    penalty : Double
    tolerance : Double
    } derive(
    Debug
    )

    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.

    QpConfig::to_repr

    RateLimited

    pub(all) struct RateLimited {
    inner : &
    Controller

    rate : Double
    previous : 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.

    RateLimited::new

    A rate-limit wrapper. A rate that is not positive is refused.

    RateLimited::reset

    fn RateLimited::reset(self : RateLimited) -> Unit

    Riccati

    The solution of a discrete algebraic Riccati equation: the stabilising gain and the cost-to-go matrix it came with.

    Riccati::to_repr

    Saturating

    pub(all) struct Saturating {
    inner : &
    Controller

    output : Output
    }

    A controller whose output is clamped into a band.

    Saturating::new

    A saturation wrapper.

    Saturating::output

    fn Saturating::output(self : Saturating) -> Output

    The band, exposed so a caller can read back the limit it set.

    Saturating::reset

    fn Saturating::reset(self : Saturating) -> Unit

    Stanley

    pub(all) struct Stanley {
    gain : Double
    wheelbase : Double
    soft : Double
    } derive(
    Debug
    )

    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.

    Stanley::steering

    The steering angle from the heading error and the cross-track error.

    Stanley::to_repr

    box_qp

    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.

    cbf_config

    let cbf_config : CbfConfig

    The preset the barrier filter runs with.

    cbf_filter

    fn cbf_filter(nominal~ :
    Vec
    , gradient~ :
    Vec
    , drift~ : Double, barrier~ : Double, alpha~ : Double, lower~ :
    Vec
    , upper~ :
    Vec
    , config? : CbfConfig) ->
    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.

    clamp_scalar

    fn clamp_scalar(value : Double, low : Double, high : Double) -> Double

    Clamp into an inclusive band.

    dare

    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.

    feedforward

    let feedforward : Feedforward

    A feedforward with unit velocity gain and no acceleration gain.

    ilqr

    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.

    ilqr_config

    let ilqr_config : IlqrConfig

    The preset the iterative LQR runs with.

    integral_lqr

    The integral LQR for a discrete (a, b) pair with outputs c, accumulating the output error at dt seconds.

    lqr

    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.

    mpc_config

    let mpc_config : MpcConfig

    The preset the model-predictive controller runs with: a horizon of twenty periods, no limit on the command and no limit on its rate.

    output_unbounded

    let output_unbounded : Output

    A band that never binds.

    pid_config

    let pid_config : PidConfig

    A PID with no gain and no limit: the preset the constructors start from.

    Conditional anti-windup, no setpoint weighting, the derivative on the error and no filtering are the textbook defaults; a drive with a noisy encoder switches derivative_on_measurement on and sets filter.

    pid_gains

    fn pid_gains(kp~ : Double, ki? : Double, kd? : Double) -> PidConfig raise
    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.

    pursuit

    let pursuit : Pursuit

    The preset pure-pursuit follower: one metre of lookahead on a one-metre wheelbase.

    qp_config

    let qp_config : QpConfig

    The preset the quadratic program runs with.

    stanley

    let stanley : Stanley

    The preset Stanley follower.