This project is a compact C++17 demonstration of Model Predictive Control (MPC) for trajectory optimization. It predicts vehicle motion over a finite horizon, evaluates tracking and obstacle-avoidance costs, and optimizes acceleration and steering commands. The prediction dynamics can be switched between bicycle, Ackermann, and unicycle models without rebuilding. Python scripts are included for plotting and animating the resulting trajectory.
The MPC parameters are defined in config/mpc_configs.yaml and loaded when
the demo starts. Configuration-only changes do not require a C++ rebuild.
Select the control solver with control_solver. The built-in
OSQP is the default solver and is downloaded automatically by CMake. The
built-in optimizeControls solver remains available; IPOPT and ACADO retain
dispatch points that require their libraries and adapters before use.
Select the prediction model with kinematic_model:
bicycleuses a centre-of-gravity kinematic bicycle with a slip angle.ackermannuses a rear-axle, constant-curvature Ackermann model.unicycleuses a unicycle model;steeringis interpreted as yaw rate in radians per second for this model.
Each model implements the common VehicleModel interface, so the selected
model is used for both MPC cost evaluation and final trajectory rollout.
- CMake 3.18 or newer
- A C++17 compiler
- Windows: Visual Studio Build Tools with the C++ workload
- Linux: GCC or Clang
- macOS: Apple Clang/Xcode Command Line Tools
- Python 3 for visualization
- Python packages:
pandasandmatplotlib
Install the visualization dependencies with:
python -m pip install pandas matplotlibRun all commands from the project root—the directory containing
CMakeLists.txt.
Configure and build the project:
cmake -S . -B build
cmake --build build --config DebugRun the demo:
.\build\Debug\trajectory_optimization_demo.exeAnimate the generated trajectory:
python .\visualization\animate_trajectory.pyAfter the initial CMake configuration, run_demo.bat can build, run, and
launch the animation in one step:
.\run_demo.batConfigure, build, and run:
cmake -S . -B build
cmake --build build
./build/trajectory_optimization_demo
python visualization/animate_trajectory.pyTo display a static plot instead of the animation, run:
python visualization/plot_trajectory.pyThe executable writes these files to output/:
reference_trajectory.csv— desired pathmpc_trajectory.csv— trajectory generated by the MPC planner
Model Predictive Control is an optimization-based, receding-horizon control method. At each planning update, MPC predicts how the system will move over a finite horizon and selects the control sequence that minimizes a cost function while respecting control limits.
At a high level, one planning update works as follows:
- Begin with a sequence of acceleration and steering controls over a finite prediction horizon.
- Roll the controls forward through the configured kinematic model.
- Score the predicted trajectory using path tracking, heading, velocity, control effort, and obstacle-proximity costs.
- Use the configured control solver to update acceleration and steering. The built-in optimizer uses central-difference gradients; OSQP solves bounded local quadratic updates.
- Clamp controls to their permitted limits.
- Repeat the optimization and roll out the final controls to produce the MPC trajectory.
For a control value u, this implementation estimates the numerical gradient
with a central difference:
dJ/du = (J(u + epsilon) - J(u - epsilon)) / (2 * epsilon)
It then performs a gradient-descent update:
u = clamp(u - learning_rate * dJ/du, minimum_control, maximum_control)
The default configuration uses:
| Parameter | Value | Meaning |
|---|---|---|
| Time step | 0.1 s | Time between predicted states |
| Horizon | 30 steps | Number of future controls and states |
| Kinematic model | bicycle |
Dynamics used during prediction |
| Wheelbase | 2.7 m | Vehicle-model wheelbase |
| Optimization iterations | 50 | Gradient-descent passes per plan |
| Learning rate | 0.01 | Size of each control update |
| Gradient epsilon | 0.001 | Perturbation used for numerical gradients |
Select a solver in config/mpc_configs.yaml:
control_solver: OSQP # optimizeControls, IPOPT, OSQP, or ACADOSelect a vehicle model in the same file:
kinematic_model: bicycle # bicycle, ackermann, or unicycle
wheelbase: 2.7 # used by bicycle and ackermannFor bicycle and ackermann, Control::steering is a steering angle in
radians. For unicycle, it is a yaw rate in radians per second, and
wheelbase is not used.
Acceleration is limited to [-2.0, 2.0] m/s², and steering is limited to
[-0.5, 0.5] rad.
Each candidate control sequence is scored using:
- Position tracking error
- Heading tracking error
- Velocity tracking error
- Acceleration and steering effort
- A strong penalty for entering an obstacle safety region
This repository contains a simplified MPC implementation. In a full receding-horizon controller, the planner runs repeatedly: apply the first optimized control, measure the new state, shift the control horizon, and optimize again. Production MPC systems also commonly use dedicated solvers such as IPOPT, OSQP, ACADO, or CasADi.
.
|-- CMakeLists.txt
|-- README.md
|-- config/
| `-- mpc_configs.yaml
|-- include/
| |-- ackermann_model.h
| |-- bicycle_model.h
| |-- common_types.h
| |-- mpc_config.h
| |-- mpc_planner.h
| |-- unicycle_model.h
| `-- vehicle_model.h
|-- src/
| |-- ackermann_model.cpp
| |-- bicycle_model.cpp
| |-- main.cpp
| |-- mpc_config.cpp
| |-- mpc_control_solvers.cpp
| |-- mpc_planner.cpp
| `-- unicycle_model.cpp
|-- output/
|-- visualization/
| |-- animate_trajectory.py
| `-- plot_trajectory.py
`-- run_demo.bat
- Change the initial state, reference path, obstacle, model, solver, or planner
settings in
config/mpc_configs.yaml. - Increase the horizon to plan farther ahead at the cost of more computation.
- Increase the optimization iterations for additional refinement at the cost of a longer planning time.
- Adjust the learning rate carefully. A value that is too large can make the optimization unstable, while a very small value can converge slowly.
- Rebuild after changing C++ code.