wls-alloc
Weighted Least-Squares (WLS) constrained control allocator for flight controllers.
Solves the box-constrained least-squares problem using an active-set method with incremental QR updates (Givens rotations).
Designed for real-time motor mixing: no_std, fully stack-allocated, const-generic
over problem dimensions.
Use case
Given a desired thrust/torque vector and a motor effectiveness matrix, compute the optimal per-motor throttle commands that best achieve the objective while respecting actuator limits.
Quick start
use ;
// Problem dimensions: NU=4 motors, NV=6 pseudo-controls, NC=NU+NV=10
let g: = /* your effectiveness matrix */;
let wv: = from_column_slice;
let mut wu: = from_column_slice;
// Build the LS problem
let = ;
let v: = /* desired pseudo-control */;
let u_pref: = from_column_slice;
let b = ;
// Solve (ws persists between calls for warmstarting)
let umin: = from_column_slice;
let umax: = from_column_slice;
let mut us: = from_column_slice;
let mut ws = ; // working set: 0=free, +1=at max, -1=at min
let stats = ;
assert_eq!;
// us now contains the optimal motor commands
Features
no_std— no heap allocation, fully stack-based. Runs on bare-metalthumbv7em-none-eabihf.- Const-generic dimensions —
solve::<NU, NV, NC>(...)monomorphizes to exact-sized code. No wasted memory. - Incremental QR — initial factorization via nalgebra's Householder QR, then Givens rotation updates when constraints activate/deactivate. O(n) per constraint change vs O(n³) re-factorization.
- Warmstarting — the working set (
ws) persists between calls. Withimax=1, the solver typically converges in a single iteration during steady flight. - NaN-safe — detects NaN in the QR solution and reports
ExitCode::NanFoundQ/ExitCode::NanFoundUs. No-ffast-mathassumptions.
Benchmarks
Compared against the original C implementation
(ActiveSetCtlAlloc) on 1000 test
cases (n_u=6, n_v=4, cold start, imax=100). Both compiled with maximum
optimization for the same host: GCC -O3 -march=native vs Rust --release.

| Metric | C qr_naive | C qr (incr.) | Rust (incr.) |
|---|---|---|---|
| Median | 1541 ns | 1200 ns | 511 ns |
| P95 | 2321 ns | 1454 ns | 728 ns |
| P99 | 2517 ns | 1567 ns | 1019 ns |
| Throughput | 0.65M/s | 0.83M/s | 1.96M/s |
~2.3x faster than C incremental QR, ~3x faster than C naive QR.
100% of test cases match the C reference within 10⁻⁴ tolerance. Maximum absolute difference: 1.91 × 10⁻⁶.
See the full benchmark report for additional plots.
API
Types
// Convenience aliases (or use nalgebra types directly)
type MatA<NC, NU> = ;
type VecN<N> = ;
Functions
setup_a::<NU, NV, NC>(g, wv, wu, theta, cond_bound) -> (MatA, gamma)— build the A matrixsetup_b::<NU, NV, NC>(v, u_pref, wv, wu, gamma) -> VecN<NC>— build the b vectorsolve::<NU, NV, NC>(a, b, umin, umax, us, ws, imax) -> SolverStats— solve the problem
Exit codes
| Code | Meaning |
|---|---|
Success |
Optimal solution found |
IterLimit |
imax iterations reached (solution is best so far) |
NanFoundQ |
NaN in QR solution (degenerate problem) |
NanFoundUs |
NaN during constraint step |
Origin
This crate is a Rust port of the C ActiveSetCtlAlloc library by Till Blaha (TU Delft MAVLab).