MOONMCL motion control

MoonBit · pure standard library

Plan the path.
Hold the pose.

Trajectory generation, feedback control, state estimation, grid planning and drive kinematics in one library. Five packages, one flat surface, and nothing outside the standard library.

7
modules
390
public items
core
only dependency
portal · 12 × 8cartesian 3-axis x ±6.00 · z ±4.00
progress
0%
feed
0.00 m/s
segment
1/1
route
—

01 / Modules

Seven packages, one surface

Each package is a separate MoonBit package with its own moon.pkg, so a project that only needs a filter takes only the filter. The root package re-exports all seven, which is how the examples address them.

  1. 01

    Linearlinear

    Dense vectors and matrices over Double: products, transpose, LU, QR, Cholesky and the symmetric eigendecomposition.

    80 items
  2. 02

    Contractcontract

    The traits every algorithm is written against, and the records they pass around: a plant, a controller, an estimator, a trajectory, a mechanism, a state space with its validity oracle, and a planner.

    35 items
  3. 03

    Controlcontrol

    PID with conditional-integral anti-windup, feedforward, LQR and integral LQR, iterative LQR, linear MPC over a box-constrained program, pure pursuit, Stanley, computed torque, impedance and a barrier filter.

    78 items
  4. 04

    Estimationestimation

    A linear Kalman filter, an extended filter and an unscented one over any plant, a particle filter, a complementary filter, an alpha-beta-gamma tracker and a backward smoothing pass.

    41 items
  5. 05

    Trajectorytrajectory

    Trapezoidal and jerk-limited profiles that are time optimal from any boundary state, cubic and quintic polynomials, a natural cubic spline, a clamped B-spline and a synchroniser for several axes.

    32 items
  6. 06

    Planningplanning

    Sampling planners over any state space, the Dubins and Reeds-Shepp car spaces with every one of their words, dynamic-window and potential-field local planners, and a bridge to the graph package's grid searches.

    63 items
  7. 07

    Kinematicskinematics

    Differential, bicycle and Ackermann bases, mecanum and omnidirectional wheels, an n-link planar arm with a damped inverse, a delta cell, a SCARA and a gantry.

    61 items

02 / Capabilities

What the machine does

The shapes below are the library's own output: a jerk-limited move, a Riccati gain settling, a filter pulling an estimate out of noise, and a shortest route through a grid.

Jerk-limited motion

A rest-to-rest profile for a distance and a peak velocity, acceleration and jerk. at(t) returns position, velocity, acceleration and jerk at any instant, and the profile is the minimal-time one for those limits.

Optimal feedback

lqr iterates the discrete Riccati equation, with the tolerance and the iteration cap optional. The gain drives the closed loop; integral LQR adds the states that remove a standing error.

Estimation

Predict and update in place. The extended filter differentiates the models numerically, which keeps a nonlinear plant to two closures and one cone of covariance.

Grid planning

grid_path returns the route as cell indices through the graph package's own search, or refuses when there is none when the goal is walled off.

03 / Surface

Import once, address everything

The root package re-exports the public types and free functions of all five modules. Import it under an alias — the examples use mctl — and every name is one level deep. The module packages stay importable on their own.

Angles are radians, lengths are metres, time is seconds, and every scalar is a Double.

Start the guide
import {
  "moonbitstack/moonmcl" @mctl,
}

// A jerk-limited move over twelve metres.
let profile = @mctl.scurve(distance=12.0, v_max=2.0, a_max=1.0, j_max=4.0)
let end = profile.sample(profile.duration())
assert_eq((end.position - 12.0).abs() < 1.0e-6, true)
assert_eq(end.velocity.abs() < 1.0e-6, true)

// A route across an eight by eight grid with a wall in it.
let grid = @mctl.Grid::new(8, 8)
for row in 0..<6 {
  grid.block(4, row)
}
let route = @mctl.grid_path(grid, start=grid.node(0, 0), goal=grid.node(7, 7))
assert_eq(route.length() > 8, true)

04 / Where it fits

Small enough for a controller, exact enough for a tool

The library carries no allocation-heavy machinery and no numerical dependencies: it is a few thousand lines of MoonBit that compile to wasm, JavaScript and native code alike.

Machine and robot motion

Profile a move, run it through a drive's inverse kinematics, and close the loop with a gain from the Riccati equation. The planner and the controller share one notion of time.

Games and simulation

Deterministic, allocation-light stepping suits a fixed-timestep loop; the same code runs in the browser through the JavaScript backend.

Teaching and prototyping

Every algorithm is a page of readable MoonBit with the equations in the doc comments, so a controller can be swapped for another without a rewrite.

Embedded paths

No I/O, no threads and no global state: the filters and controllers hold their state in the value you own, which keeps them easy to place on a fixed budget.