QpSolverCollection — real‑time optimal‑control toolkit
Work at RWTH IRT – 2
Overview
Fork of isri‑aist/QpSolverCollection with additional solvers, utilities and examples developed during my work at RWTH‑IRT
This fork broadens the original library with Ipopt‑powered nonlinear programming, several MPC and NMPC front‑ends, and a battery of regression tests that guard numerical correctness down to the solver back‑end. Everything – from automatic differentiation to QP factorisation – stays inside C++ templates so you can exploit the full power of modern compilers without run‑time overhead.
What’s new in this fork
- IpoptInterface – full wrapper around COIN‑OR Ipopt for large‑scale, sparse NLPs.
- MpcSolver ➜ ready‑to‑use linear MPC wrapper.
- SQPSolver ➜ first‑order SQP with pluggable QP back‑ends.
- Prototype NMPC module built on multiple shooting and the SQP core.
- Extensive unit tests (HS071, constrained Rosenbrock, drone attitude control, quadrotor MPC …)
Key ideas
- Header‑only, no macros – just add the
include/
folder to your project. - AD everywhere – gradients, Jacobians and Hessians are generated on‑the‑fly from the same expressions that define your problem.
- Dense or sparse – flip a template parameter and every matrix adapts; sparsity patterns are cached once.
- Unified NLP façade – write
cost_impl
,equality_constraints_impl
,inequality_constraints_impl
; the base class synthesises the 15 routines Ipopt (or you) may ask for. - Real‑time focus – every allocation is decided at compile time; run‑time heap hits are avoided completely in dense mode.
How it fits together
user code (struct MyOCP : ProblemBase<…>) // supply cost & constraints
│
├──▶ IpoptInterface<MyOCP> // Newton‑type, second order (new)
│
├──▶ SQPSolver<MyOCP> // first‑order SQP
│
├──▶ MpcSolver // linear MPC (internally QP)
│
└──▶ NMPC::MultipleShooting // nonlinear MPC (experimental)
All solvers accept Eigen vectors as initial guesses and expose the final primal/dual solution without copies.
Typical call‑site
MyOCP ocp;
IpoptInterface<MyOCP> nlp;
nlp.lower_bound_x() << …; // box constraints
nlp.parameters() << …; // static params
nlp.solve(/*x₀*/ VectorXd::Zero(MyOCP::VAR_SIZE),
/*λ₀*/ VectorXd::Zero(MyOCP::DUAL_SIZE));
std::cout << "Optimal cost = " << nlp.cost() << '\n'
<< "‖g‖∞ = " << nlp.constr_violation() << '\n';
Switch to SQP by replacing the two middle lines:
SQPSolver<MyOCP> sqp;
sqp.solve(x0, VectorXd::Zero(MyOCP::NE),
x_min, x_max, g_min, g_max);
Mathematics
Given state $\mathbf x\in\mathbb R^{n}$, control $\mathbf u\in\mathbb R^{m}$ and parameters $\mathbf p$, the toolkit assumes the generic nonlinear program
\[\begin{aligned} \min_{\mathbf x}&\; J(\mathbf x;\mathbf p) \\ \text{s.t.}\quad &\mathbf g(\mathbf x;\mathbf p)=\mathbf 0 \quad(\text{equality})\\ &\mathbf h(\mathbf x;\mathbf p)\le \mathbf 0 \quad(\text{inequality})\\ &\mathbf x_{\min}\le\mathbf x\le\mathbf x_{\max} \,. \end{aligned}\]All partial derivatives are produced automatically.
Need the code? Browse the repository on GitHub – pull requests and issues are very welcome!