Inside TinyMPC
Our 2024 ICRA submission video provides a concise overview of the solver:
Problem formulation
TinyMPC solves convex quadratic model-predictive control programs of the form
where \(x_k \in \mathbb{R}^n\), \(u_k \in \mathbb{R}^m\) are the state and control input at time step \(k\), \(N\) is the number of time steps (also referred to as the horizon), \(A \in \mathbb{R}^{n \times n}\) and \(B \in \mathbb{R}^{n \times m}\) define the system dynamics, \(Q \succeq 0\), \(R \succ 0\), and \(Q_f \succeq 0\) are symmetric cost weight matrices, and \(q_k\), \(r_k\), \(q_f\) are linear cost vectors. The convex sets \(\mathcal{X}\) and \(\mathcal{U}\) represent state and input constraints respectively.
Algorithm
TinyMPC employs the Alternating Direction Method of Multipliers (ADMM) to efficiently solve convex quadratic MPC problems. The algorithm separates dynamics constraints from other convex constraints, enabling the use of specialized techniques for each type.
ADMM Framework
The algorithm reformulates the MPC problem by introducing slack variables and solving three iterative update steps:
Key Innovation: LQR-Based Primal Update
The primal update step is reformulated as a Linear Quadratic Regulator (LQR) problem, which has a closed-form solution through the discrete Riccati recursion. This is the computational bottleneck that TinyMPC optimizes.
The modified cost matrices for the LQR problem become:
The optimal control policy is computed via backward Riccati recursion:
Constraint Handling via Projection
The slack update step handles convex constraints through simple projection operations:
where \(\mathcal{X}\) and \(\mathcal{U}\) are the feasible sets for states and inputs respectively.
Computational Optimization
For long horizons, TinyMPC pre-computes matrices that converge to steady-state values:
This significantly reduces online computation by caching expensive matrix operations.
Algorithm Pseudocode
Algorithm 1: TinyMPC
function TINY_SOLVE(input)
while not converged do
// Primal update
p_{1:N-1}, d_{1:N-1} ← Backward pass via Riccati recursion
x_{1:N}, u_{1:N-1} ← Forward pass via feedback law
// Slack update
z_{1:N}, w_{1:N-1} ← Project to feasible set
// Dual update
y_{1:N}, g_{1:N-1} ← Gradient ascent update
q_{1:N}, r_{1:N-1}, p_N ← Update linear cost terms
end while
return x_{1:N}, u_{1:N-1}
end function
Implementations
The TinyMPC library offers a C++ implementation of the algorithm mentioned above, along with interfaces to several high-level languages. This integration allows these languages to seamlessly solve optimal control problems using TinyMPC.
We have made available Python, MATLAB, and Julia interfaces.
There are also several community-developed implementations of this algorithm: Rust
Numerical benchmarks against other solvers on microcontrollers are available at this repository.
Crazyflie firmware with TinyMPC is available at this repository.