GPU quadratic programming for real-time MPC

FAME‑QP

A GPU-accelerated quadratic-programming solver for real-time model predictive control

Santosh Mohan RajkumarDebdipta GoswamiStephanie Stockar
Department of Mechanical & Aerospace Engineering, The Ohio State University
Unitree G1 recovering from an 80 N push under MPC solved by FAME-QP
Unitree G1 recovering from an 80 N push. Its 290-variable MPC QP is solved by FAME‑QP every 2 ms.
Skydio X2 tracking a lemniscate with KQ-LMPC solved by FAME-QP
Skydio X2 tracking a lemniscate with KQ‑LMPC. The QP's matrices change at every 20 ms step; red is the reference, yellow the flown path.
25.9×
faster than DAQP, the fastest CPU solver, on dense random QPs (n = 1400)
7.3×
faster closed-loop MPC steps than ReLU‑QP on random linear systems
2×
longer MPC horizon for the G1 humanoid within its 2 ms control period
4.7×
faster than CuPIQP for quadrotor MPC with matrices that change every step

Abstract

Dense convex QPs of linear MPC, solved on the GPU.

We present FAME‑QP, a GPU-accelerated solver for dense quadratic programs arising in linear model predictive control. FAME‑QP combines constraint-dependent operator representations with fused single- and two-kernel iterations replayed through CUDA graphs, automatically selecting the execution configuration through device calibration and problem-specific timing. It probes a cached operator bank to select the initial penalty and updates operators lazily when problem matrices change. Numerical benchmarks and simulated humanoid and quadrotor control experiments demonstrate reduced latency relative to competing GPU solvers while maintaining solution accuracy and control performance.

Four ideas

1

Constraint-dependent operators

Rows that bound single variables, such as input limits, are handled element-wise instead of as dense rows: up to 4× less operator storage and up to 3× faster iterations.

2

Fused kernels, replayed as CUDA graphs

A full ADMM iteration runs in one or two kernels, and blocks of iterations with the stopping test are replayed with a single host call.

3

Automatic selection

A one-time device calibration and a short timing test per problem shape pick the execution mode, its tile sizes and, optionally, the precision.

4

Penalty probing and lazy updates

A bank of pre-inverted operators gives penalty adaptation without refactorisation and a probed starting penalty; changed matrices re-invert only the operator in use.

Use cases

FAME‑QP solves the QP at the heart of every MPC step whose optimization problem is a convex quadratic program, and keeps it on the GPU next to the model.

L

Linear MPC (LMPC)

Condensed linear MPC with input and state bounds, optionally LQR pre-stabilized, from a few hundred to thousands of decision variables. fame_qp.mpc.LinearMPC builds the QP from \(A, B, Q, R\).

K

Koopman MPC

Koopman-lifted linear models whose matrices change at every step, as in KQ‑LMPC for quadrotors: the QP's matrices are swapped in place and only the active operator is re-inverted.

W

World-model and learned-dynamics MPC

When a learned world model has linear or locally linear latent dynamics, or is linearized along a trajectory, each MPC step is a QP. FAME‑QP solves it on the same GPU as the model, without a round trip to a CPU solver.

R

Fast MPC for robots

Real-time control at kilohertz rates: humanoid balance with a 2 ms control period and quadrotor tracking with changing models, in GPU pipelines for simulation, learning and inference.

Method

FAME‑QP runs the ADMM iteration of OSQP with pre-inverted linear systems, and is built around how that iteration executes on a GPU.

$$\min_{z}\ \tfrac12 z^\top P z + q^\top z \quad \text{s.t.}\quad l \le G z \le h$$ $$\tilde z = M\big(\sigma z - q + G^\top(\boldsymbol\rho\, s - \lambda)\big),\quad M = \big(P + \sigma I + G^\top \boldsymbol\rho\, G\big)^{-1}$$ $$z \leftarrow \alpha \tilde z + (1-\alpha) z,\qquad \zeta = \alpha G\tilde z + (1-\alpha) s + \boldsymbol\rho^{-1}\lambda,\qquad s \leftarrow \Pi_{[l,h]}(\zeta),\qquad \lambda \leftarrow \boldsymbol\rho(\zeta - s)$$
Setuponce per problem
  1. Detect the constraint structurebox, split (diagonal block + dense rows) or general
  2. Ruiz-scale the problem data
  3. Select the execution modefrom a cached device calibration and a short timing test
  4. Invert a bank of five operators in float64one per penalty, spaced by factors of 10
  5. Probe the starting penalty and capture the CUDA graph
Solveevery QP, e.g. every MPC step
  1. Update \(q, l, h\) (and \(P, G\) if they change)only the active operator is re-inverted in lazy mode
  2. Warm start, shifted one stage along the horizon
  3. Replay the graph: fused iterations + on-device stopping test
  4. One host read per block of iterationsterminate, detect stalls or switch to another operator in the bank

Execution modes

ModeOperatorKernels per iterationBest when
Single kernelstacked \(S M S^\top\), \((n+m_c)^2\) entries1few dense rows: launch overhead dominates
Two kernelsfactored \([M,\ MC^\top]\) and \(C\): \(m_c^2\) fewer entries2many dense rows: memory traffic dominates
Persistent\(M\) held in registersone launch per solvesmall box-constrained problems

The mode is chosen automatically from a per-device calibration and a timing test on placeholder operators of the problem's shape, and cached for later runs.

Results

All timings on an NVIDIA RTX 3080 Laptop GPU in float64, against CPU and GPU solvers and GPU MPC methods, using their original implementations.

Random dense QPs

Solve time against problem size on random dense QPs
Cold-solve times at matched accuracy, excluding setup: medians with min–max bands over five instances. All solvers in float64 except cuOSQP (float32).
  • Fastest GPU solver at every size from n = 20 to 1400, against ReLU‑QP, CuPIQP, qpax and cuOSQP.
  • Faster than every CPU solver for n ≥ 220, including OSQP, PIQP, ProxQP, Clarabel and DAQP.
  • Over n = 220–1400: 1.2–25.9× faster than DAQP and 1.5–4.3× faster than ReLU‑QP.
  • In float32, 1.95 ms at n = 1400, against 326 ms for cuOSQP.

Closed-loop MPC on random linear systems

Methodn = 400800120016002000
QP solvers, LQR-preconditioned condensed QP
FAME‑QP0.658 ± 0.0661.19 ± 0.042.05 ± 0.452.92 ± 0.094.23 ± 0.21
ReLU‑QP2.10 ± 0.376.06 ± 1.0211.6 ± 1.920.0 ± 3.930.9 ± 6.1
CuPIQP23.7 ± 7.058.9 ± 11.3178 ± 42248 ± 63449 ± 120
GPU MPC methods, original OCP
CUDA MPC22.7 ± 0.159.9 ± 0.582.8 ± 0.8103 ± 0138 ± 3
πMPC9.06 ± 12.398.90 ± 12.886.78 ± 5.6715.4 ± 23.913.4 ± 18.1

MPC step time [ms], mean ± standard deviation over 50 closed-loop steps, including data updates, warm-start shifts and the transfer of the first input to the host. Closed-loop costs of all methods agree within 0.06 %.

Robots in MuJoCo

Closed-loop control of a humanoid and a quadrotor, with the MPC QP solved by FAME‑QP at every control step.

Unitree G1 push recovery

  • 29 position-controlled joints, 70 states, a 2 ms control period.
  • Condensed QP with \(n = m = 29N\), general constraint structure, against ReLU‑QP.
  • 2.3–3.1× lower mean QP time, within the control period at every horizon tested; ReLU‑QP exceeds it from N = 15.
  • The largest horizon within the budget doubles, from N = 10 to N = 20, with comparable balance recovery.
N (n)FAME‑QP [ms]ReLU‑QP [ms]CoM RMS [mm]
FAME‑QP / ReLU‑QP
10 (290)0.650 ± 0.0911.47 ± 0.0518.7 / 18.7
15 (435)0.763 ± 0.1162.26 ± 0.1219.1 / 19.2
20 (580)1.04 ± 0.203.25 ± 0.1320.5 / 19.5
Unitree G1 push recovery frames
Push, largest lean, return and final stance, with FAME‑QP at N = 10.

Skydio X2 trajectory tracking with KQ‑LMPC

Skydio X2 lemniscate tracking
20 s of lemniscate tracking at 50 Hz with a 1.4 s horizon.
  • Koopman-lifted linear MPC: both the Hessian and the constraint matrix change at every step.
  • Condensed QP with n = 280 (N = 70), against CuPIQP.
  • 12.5 ± 3.2 ms per step against 59.2 ± 4.8 ms: 4.7× faster, including matrix updates, warm starts and the host transfer.
  • 99th percentile 17.6 ms, within the 20 ms control period (CuPIQP: 72.1 ms).
  • Position RMSE 2.7 mm (CuPIQP: 3.4 mm).

Get started

Linux, an NVIDIA GPU supported by Triton, Python 3.9+ and PyTorch with CUDA.

# install
pip install fame-qp

# or from source
git clone https://github.com/santoshrajkumar/fame-qp.git
cd fame-qp && pip install -e .
from fame_qp import QP, Solver

qp = QP.create(P, q, G, l, h, device="cuda")
solver = Solver(qp, precision="float64")
res = solver.solve()
res.z, res.lam, res.status

solver.update(q=q2, l=l2, h=h2)   # warm, same setup
solver.update_matrices(P=P2)      # new matrices

FAQ

Common questions about GPU MPC with FAME‑QP.

What is FAME‑QP?

FAME‑QP is an open-source GPU solver for convex quadratic programs, built for the dense QPs of real-time model predictive control. It runs the ADMM iteration of OSQP in fused GPU kernels replayed through CUDA graphs and tunes itself to the GPU it runs on.

Which MPC problems can FAME‑QP solve?

Any MPC problem whose optimization step is a convex QP: linear MPC (LMPC) in condensed form with input and state bounds, LQR-pre-stabilized MPC, and MPC whose model matrices change at every step, such as Koopman-lifted or linearized models.

Can FAME‑QP be used for world-model MPC?

Yes, when the world model provides linear or locally linear latent dynamics, or is linearized along a trajectory, the resulting MPC step is a QP that FAME‑QP solves on the GPU, next to the model itself. Its matrix updates are designed for models that change at every step.

How fast is FAME‑QP?

On an RTX 3080 Laptop GPU it was the fastest GPU solver at every tested size of random dense QPs, up to 25.9 times faster than DAQP, 3.2 to 7.3 times faster than ReLU‑QP in closed-loop MPC, and 4.7 times faster than CuPIQP for quadrotor MPC with changing matrices.

How do I install FAME‑QP?

pip install fame-qp. It needs Linux, an NVIDIA GPU supported by Triton, Python 3.9 or newer and PyTorch with CUDA.

Citation

If you use FAME‑QP, please cite the paper.

@misc{rajkumar2026fameqp,
  title         = {{FAME-QP}: A {GPU}-Accelerated Quadratic-Programming Solver for Real-Time Model Predictive Control},
  author        = {Rajkumar, Santosh Mohan and Goswami, Debdipta and Stockar, Stephanie},
  year          = {2026},
  eprint        = {2611.13766},
  archivePrefix = {arXiv},
  primaryClass  = {cs.RO},
  url           = {https://arxiv.org/abs/2611.13766}
}