An open source, GPU-accelerated trajectory optimization solver for batched model predictive control.
GATO solves batches of nonlinear trajectory optimization problems using a GPU-accelerated Sequential Quadratic Programming (SQP) approach co-designed across algorithm, software, and hardware. At each SQP iteration, it:
This co-design fills the critical gap between single-solve real-time methods and large-batch offline solvers, enabling scalable edge MPC for manipulation and other robotics applications.
These figures report the paper's experiments, not benchmarks of the latest software revision. See the reproduction protocols and current limitations.
(Left) Solve times for 6-DoF manipulator motions while varying the batch size (M) and underlying solver. N=64 for all solves. GATO shows far improved scalability as compared to state-of-the-art CPU and GPU solutions. (Right) A heat map of solve times while varying both batch size (M) and time horizon (N). GATO is able to reach kHz control rates for real-time iterations of large batches (512) of short horizon (N=8) trajectories, as well as smaller batches (32) of longer horizon trajectories (N=128), showing the flexibility of the design.
Figure-8 tracking task, with an external disturbance applied at the end effector. (Left) Bar chart shows tracking error, scatter plot shows average total joint velocities. Increasing GATO's batch size enables increased disturbance rejection, lowering tracking error and joint velocities until the increased latency from a larger batch size outweighs the optimality gains. (Right) End-effector trajectories realized during this experiment when 50N of external force is applied at the end effector, again showing that modest batch sizes lead to the best performance.
(Left) Cumulative density function of the solve times for a trajectory length N=16 for GATO across different batch sizes and varied pendulum configurations. For each batch size, the solver accounts for 100 disturbance scenarios. We see how larger batch sizes enable more accurate unmodeled disturbance rejection. (Right) Hardware experiment showing the solver successfully (M=32) and unsuccessfully (M=1) accounting for the time-varying unmodeled disturbance and reach the targets.
October 2026 experiments use an NVIDIA RTX 5090 with CUDA 13.2. The October 1 iiwa14 figures below use GATO b893083; the Indy7 comparison was collected October 3. These are dated research measurements, not a new release or a reproduction of every paper result. The reproduction protocols specify the differences from the paper.
Internal solver time per batched solve on a matched iiwa14 figure-eight benchmark. GATO is 4.4–7.2× faster than the multi-threaded QDLDL-based CPU solver and is faster than the October 1 MPCGPU baseline at batch sizes of four and above. This MPCGPU baseline predates its October 3 graph-driver update. The paper used Indy7, a different timing boundary and different hardware; these ratios are not directly comparable.
An October 6 check on source 813c6e3 measured controller-call overhead of approximately 42–209 µs across the two arms and batches 1–128. The call excludes sensing, reference preparation and actuator I/O; it is not a whole robot-cycle deadline measurement. Timing boundaries and source details.
October 19–20 measurements cover the current constraint defaults and all five fixed-base arms. At N64 with default ADMM limits, one-iteration NumPy controller-call medians span 0.926–1.284 ms for batches 1 and 8. PyTorch and JAX device inputs preserve the plans but do not reduce call latency in this replay. Host pauses remain visible, including a 43 ms call with limits off; iteration budgets are not hard deadlines. These prescribed-input replays do not establish closed-loop feasibility or replace the paper comparisons. Protocol, all-arm results and raw samples.
URDF limit rows are enabled by default for fixed-base controllers without existing rows. They are soft numerical constraints, not actuator clipping or a hardware safety layer. New-arm holdouts and sustained-saturation studies still show effort or velocity excursions. Constraint behavior and measured limitations.
On the same figure-eight problem at N = 64, GATO is 5.2–10.6× faster than the CPU baseline across batches 1–128. Selected October 3 cells are below; all values are internal solver medians in milliseconds. Single-run CPU variability limits the precision of these ratios.
| Batch | GATO | CPU | Speedup |
|---|---|---|---|
| 1 | 0.266 | 2.498 | 9.4× |
| 4 | 0.338 | 3.169 | 9.4× |
| 16 | 0.633 | 3.307 | 5.2× |
| 64 | 1.875 | 12.002 | 6.4× |
| 128 | 3.621 | 21.961 | 6.1× |
The six-joint arm narrows, but does not close, the gap to the published 18–21× CPU comparison. Hardware, baseline implementation and timing boundaries also differ. Bundled values and source hashes accompany the experiment report.
Batching improves arm-arrival success in both tested settings. The stop task uses a 1.5 s reference ramp and 100 ms dwell; pass-through uses step references and an instantaneous gate. Both require EE error below 5 cm and joint-velocity norm below 1 rad/s, with a simulated 15 kg swinging payload. These seed-0 scenarios were reused during tuning; the stop artifact records a dirty source tree. Neither task enforces actuator limits or tests payload placement. Clean-source, held-out feasibility validation is a separate experiment, not implied by these curves. Re-render from the bundled evidence.
Held-out feasibility check, October 4: a separate frozen pool completed 1,200 episodes. Both stop variants, including applied-torque clamping, reached all five goals in 100/100 scenarios at batches 8, 32 and 128. However, zero episodes satisfied all measured effort, velocity and position limits. Torque clamping alone did not make the trajectories feasible. These are simulated arm-arrival results, not evidence of hardware-safe payload handling. Complete held-out summary.
(Left) Figure-eight tracking under an end-effector disturbance at fixed simulation pacing. Larger batches keep tracking error low as the force grows; fixed pacing measures control quality without the batch-latency effect shown in the paper. (Right) Relative merit versus SQP iteration, averaged over 50 random goals and 24 cost settings, when each batch member uses a different regularization value. The paper averaged a larger cost grid.
GATO ships with five fixed-base arm models and experimental Go2 floating-base examples.
Register your own URDF with gato.register_robot(...); its solver compiles once on first use. See the
feature-status page
for tested configurations: Go2 standing is demonstrated; closed-loop walking remains in development.
| Robot | DOF | Notes |
|---|---|---|
| KUKA iiwa14 | 7 | Industrial manipulator; grid dynamics pre-generated via GRiD |
| Neuromeka Indy7 | 6 | Collaborative robot; grid dynamics pre-generated via GRiD |
| Franka Research 3 | 7 | Pre-generated dynamics; N64 correctness and replay coverage |
| Kinova Gen3 | 7 | Pre-generated dynamics; N64 correctness and replay coverage |
| Universal Robots UR5e | 6 | Pre-generated dynamics; N64 correctness and replay coverage |
| Unitree Go2 | 12 + floating base | N16 experimental standing and contact-force variants; walking remains in development |
Solver modules are compiled per (robot, knot points, variant) and cached per machine. The signed receipt covers the listed module set, not every buildable horizon or variant combination. Indy7 and iiwa14 have broader horizon coverage; the new arms are tested at N64 and Go2 at N16. See the receipt scope.
The pip command installs the published package. Features listed as unreleased in the changelog require the corresponding source checkout.
pip install gato-mpc # the import name is gato
import numpy as np
import gato
solver = gato.BSQP(gato.robot_urdf("indy7"), batch_size=8,
N=32, dt=0.01, plant_type="indy7") # compiles once
x0 = np.zeros((8, solver.nx), dtype=np.float32)
goals = np.zeros((8, 32 * 6), dtype=np.float32)
goals[:, 0::6], goals[:, 2::6] = 0.35, 0.5
res = solver.solve(x0, goals)
res = solver.solve(x0, goals, xu_warm=res.xu) # explicit warm start
print(res.u0(0))
git clone https://github.com/A2R-Lab/GATO.git && cd GATO ./tools/install.sh --test MODULES="indy7:64" JOBS=2 ./tools/build.sh source .venv/bin/activate python examples/01_single_solve.py # numbered examples 01 through 14
cmake -S examples/cpp -B build-cpp -DGATO_ROBOT=indy7 -DGATO_N=32 # includes + defines via gato.paths cmake --build build-cpp --parallel 2 && ./build-cpp/batched_solve # BSQP<T>::solve on your device buffers
The CUDA module for each (robot, horizon) is compiled on your machine the first time it is used — a few
minutes, logged when it starts — and cached under ~/.cache/gato; GRiD and GLASS arrive through the
pinned grid-rbd wheel, so no submodule is needed. Wheels never contain compiled modules.
See the quick start and requirements,
documentation, and
numbered examples.
Printed demo durations on a shared GPU are not performance measurements.
The checkout command builds only Indy7 N64; other example modules compile on first use.
Optional examples require their documented dependencies. Use the receipt build profile to prepare the full test module set.
@inproceedings{du2026gato,
title={GATO: GPU-Accelerated and Batched Trajectory Optimization for Scalable Edge Model Predictive Control},
author={Alexander Du and Emre Adabag and Gabriel Bravo and Brian Plancher},
booktitle={IEEE International Conference on Robotics and Automation (ICRA)},
year={2026},
month={June}
}