βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β ββββββββββ ββββ ββββ βββββββ βββββββ β
β ββββββββ βββββ βββββ ββββββββ ββββββββ β
β ββββββ βββββββββββ ββββββββ βββ β
β ββββββ βββββββββββ βββββββ βββ β
β βββββββ βββ βββ βββ βββ ββββββββ β
β βββββββ βββ βββ βββ βββββββ β
β β
β ΟMPC β
β [P]arallel-[i]n-Horizon MPC Solver β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
A Parallel-in-horizon and construction-free MPC solver based on ADMM with GPU acceleration.
- β‘ Parallel Execution: Horizon-wise parallelization on GPU
- π― Construction-Free: Operates directly on system matrices (no MPC-to-QP conversion)
- π‘ Simple Code: Easy deployment on embedded platforms
- π Long-Horizon Efficient: Ideal for large prediction horizons
- π Nesterov Acceleration: Adaptive restart for fast convergence
- π₯ Warm Starting: Efficient trajectory tracking
ΟMPC solves the following MPC problem:
where
using Pkg
Pkg.add("PiMPC")For support or usage-related questions, please contact liangwu2019@gmail.com and bobyang17@163.com.
using PiMPC
using LinearAlgebra
# Define discrete linear system: x_{k+1} = A*x_k + B*u_k
A = [1.0 0.1; 0.0 1.0] # Double integrator
B = reshape([0.005; 0.1], :, 1)
nx, nu = size(B)
# Create and configure model
model = Model()
setup!(model;
A = A, B = B, Np = 20,
Wy = 10.0 * I(nx), # Output weight
Wdu = 0.1 * I(nu), # Input increment weight
umin = [-1.0], # Input lower bound
umax = [1.0], # Input upper bound
rho = 10.0, # ADMM penalty
maxiter = 500,
accel = true # Nesterov acceleration
)
# Solve
x0, u0 = [5.0, 0.0], [0.0] # Initial state and input
yref, uref = zeros(nx), zeros(nu) # Output and input references
w = zeros(nx) # Known disturbance (zeros if none)
results = solve!(model, x0, u0, yref, uref, w; verbose=true)
# Access results
println("Optimal input: ", results.u[:, 1])
println("Iterations: ", results.info.iterations)
# Warm start (automatic for subsequent solves)
x_next = results.x[:, 2]
u_next = results.u[:, 1]
results2 = solve!(model, x_next, u_next, yref, uref, w)Run the AFTI-16 aircraft closed-loop simulation:
julia --project=. examples/AFTI16_example.jl![]() |
![]() |
| AFTI-16 closed-loop trajectory tracking | Convergence iterations at each MPC step |
![]() |
![]() |
| Per-iteration time vs. horizon and dimension | CSTR trajectory tracking with disturbance |
Create a model, configure with setup!(), and solve with solve!().
model = Model()
setup!(model; A, B, Np, kwargs...)
results = solve!(model, x0, u0, yref, uref, w)setup!(model; A, B, Np, kwargs...)Required Arguments:
A: State matrixB: Input matrixNp: Prediction horizon
Optional Problem Arguments:
C: Output matrix (default: I, meaning y = x)e: Affine term in dynamics (default: zeros)Wy: Output weight matrix (default: I)Wu: Input weight matrix (default: I)Wdu: Input increment weight matrix (default: I)Wf: Terminal cost weight matrix (default: Wy)xmin, xmax: State constraints (default: Β±Inf)umin, umax: Input constraints (default: Β±Inf)dumin, dumax: Input increment constraints (default: Β±Inf)
Solver Settings:
rho: ADMM penalty parameter (default: 1.0)tol: Convergence tolerance (default: 1e-4)eta: Acceleration restart factor (default: 0.999)maxiter: Maximum iterations (default: 100)precond: Use preconditioning (default: false)accel: Use Nesterov acceleration (default: false)device: Compute device,:cpuor:gpu(default: :cpu)
results = solve!(model, x0, u0, yref, uref, w; verbose=false)System Model:
x_{k+1} = A * x_k + B * u_k + e + w
y_k = C * x_k
where:
e: Constant affine term (defined insetup!)w: Known/measured disturbance (provided at solve time)
Arguments:
x0: Current state (nx)u0: Previous input (nu)yref: Output reference (ny)uref: Input reference (nu)w: Known disturbance (nx), usezeros(nx)if none
Returns Results:
results.x # State trajectory (nx Γ Np+1)
results.u # Input trajectory (nu Γ Np)
results.du # Input increment (nu Γ Np)
results.info.solve_time # Solve time (seconds)
results.info.iterations # Number of iterations
results.info.converged # Whether convergedmodel = Model()
setup!(model; A=A, B=B, Np=20, accel=true, device=:gpu)
results = solve!(model, x0, u0, yref, uref, w)Falls back to CPU automatically if GPU is not available.
If you use ΟMPC in your research, please cite:
@inproceedings{wu2026piMPC,
title={piMPC: A Parallel-in-horizon and Construction-free NMPC Solver},
author={Liang Wu, Bo Yang, Yilin Mo, Yang Shi, and Jan Drgona},
year={2026},
eprint={2601.14414},
archivePrefix={arXiv},
url={https://arxiv.org/abs/2601.14414},
volume={},
number={},
}


