MPC trajectory tracking, MPCC racing line control, and dynamic rollout planning running live on a real Ackermann platform. This runs on the actual car, not in simulation and not from a bag file replay.
I built a full autonomy stack for the RoboRacer F1TENTH platform and ran it on the actual car. I kept planning and control separate on purpose. A fast rollout planner scores trajectory candidates using obstacle distance transforms, then it hands the best one to MPC or MPCC to execute.
I did this because it keeps each layer easy to debug. When something breaks on track, I know where to look.
The planner runs every control tick and produces a scored set of candidate trajectories. It is fast enough to run online because each rollout is only a forward simulation of the bicycle model under a fixed steering command. RK4 keeps the integration accurate even at higher speeds where Euler diverges.
I do the obstacle avoidance with a Euclidean distance transform on the occupancy map. I precompute it once per map update. After that, scoring each rollout is a simple lookup instead of a collision check loop.
This is standard MPC tracking, but with a bicycle model that includes a slip angle. It matters when I push the car faster. A pure kinematic model starts to give wrong predictions. The slip angle gives the controller a better sense of where the car is actually heading versus where the wheels are pointing.
I solve it with CasADi and IPOPT, warm start from the previous solution, and publish the first action each tick. The robot pose comes straight from TF, so there is no separate localisation node.
The difference between MPCC and standard MPC is what you optimise. Standard MPC wants to be at the next waypoint. MPCC wants to move forward along the track as fast as possible while staying near the line. It sounds like a small change, but in practice it changes how the car corners.
I augment the state with an arc length progress variable and add a reward term for advancing it. The contouring error keeps the car on line. The lag error stops it from falling behind the reference. The controller then cuts corners when it helps and stays smooth when it does not.