Where I should think next : about the convergence, Lossless Convexification and Lars Blackmore's work, comparison with a raw G-FOLD algorithm found somewhere.
Hardware-in-the-loop simulation of the booster catch maneuver
Not affiliated


This project explores the development of autonomous control software for the Super Heavy booster to perform a precise catch maneuver, by reverse-engineering the real system (looking at how it works, at public materials, and proving this solution is plausible). The simulation environment models the booster dynamics, atmospheric effects, and the mechanical behavior of the catch mechanism. Going beyond a simple simulation, the maneuver will happen live in a FANUC robotic cell with human-sized mockups, equipped with noisy sensors and actuators: a hardware-in-the-loop simulation. This makes the simulation susceptible to real noise, drift, and imprecision. It also helps me interest people in the project: it is much more entertaining to see a rocket fly than a cylinder move on a screen.
This is a robotics engineering study project at Polytech Dijon. Robotics is everything that has sensors, actuators, and compute, so even rockets fall into our field. This study project is a perfect fit for me: it brings together passion, advanced theoretical fields, and real-world applications. Crazy as it is, my tutor accepted my project in only a few minutes.
Space exploration is awe-inspiring, and is becoming the obvious next civilization step. The innovations a system like Starship is bringing are crucial for a meaningful adventure into space, for humans and for the economy. Man used to work on cathedrals they would never see completed, and I qualify space exploration as today's cathedral, and I want to be part of it.
Starting at 40 km altitude (~130k ft) on a ballistic trajectory a small distance short of the launch site, the booster will go through 3 phases:
This requires multiple guidance methods (aerodynamic surfaces and thrust vector control), and a solid control system.
I need an environment where the physics simulation happens, and where it can send movements and receive sensor data. This is why I made my own software in C++, using MuJoCo for the physical simulation, and ImGui for the interface. MuJoCo is an open-source simulation engine that is widely used in AI robotics at Nvidia and Google DeepMind, and ImGui is a widely used open-source library for building user interfaces in C++. Applying multiple meganewtons of force on rotating engines and keeping the simulation stable was a difficult job.

The whole goal of "control" is to tell the booster what to do by itself. This problem is unpredictable and it needs to work even if an engine can't start, if there is cross-wind, or if a grid fin is stuck; it is a sort of sentience. As of today, a simple controller (PID) is implemented to hold the booster upright, far from the end goal: Model Predictive Control.
Model Predictive Control (MPC) is a state representation of a system, and an optimization algorithm. Given a state X of the system and its model (A, B, C, D) (a physical model such as mass, inertia, and response to commands), it estimates the future in order to reach a setpoint.
A is the free response of the system (given no command, the booster simply falls). B is how the system behaves in response to commands (torque, force, fuel consumption). C is what is measurable (the position of the booster is measurable, and is a goal/setpoint of the controller). D is often 0 (the command has no direct consequences).
Where I should think next : about the convergence, Lossless Convexification and Lars Blackmore's work, comparison with a raw G-FOLD algorithm found somewhere.
I need an environment where the physics simulation happens, and where it can send movements and receive sensor data. This is why I made my own software in C++, using MuJoCo for the physical simulation, and ImGui for the interface. MuJoCo is an open-source simulation engine that is widely used in AI robotics at Nvidia and Google DeepMind, and ImGui is a widely used open-source library for building user interfaces in C++. Applying multiple meganewtons of force on rotating engines and keeping the simulation stable was a difficult job.

The whole goal of "control" is to tell the booster what to do by itself. This problem is unpredictable and it needs to work even if an engine can't start, if there is cross-wind, or if a grid fin is stuck; it is a sort of sentience. As of today, a simple controller (PID) is implemented to hold the booster upright, far from the end goal: Model Predictive Control.
Model Predictive Control (MPC) is a state representation of a system, and an optimization algorithm. Given a state X of the system and its model (A, B, C, D) (a physical model such as mass, inertia, and response to commands), it estimates the future in order to reach a setpoint.
A is the free response of the system (given no command, the booster simply falls). B is how the system behaves in response to commands (torque, force, fuel consumption). C is what is measurable (the position of the booster is measurable, and is a goal/setpoint of the controller). D is often 0 (the command has no direct consequences).
Where I should think next : about the convergence, Lossless Convexification and Lars Blackmore's work, comparison with a raw G-FOLD algorithm found somewhere.
For this mockup, I need both aesthetic appeal (to be interesting to people) and a solid workhorse. The Super Heavy mockup needs to have 3 actuated grid fins, a way to measure its position relative to the tower mockup, wireless communications, onboard compute, and a mount to the FANUC arm. I made multiple booster mockups; here is the current one. Instead of a pure software simulation, the booster mock-up is physically mounted to the end of a FANUC industrial robotic arm. The arm executes the exact trajectory that the simulated catch maneuver would require, providing realistic inertial loads and kinematics.
Sensor data from the physical setup (encoders, force/torque if available) is fed back into the simulation loop. This closes the loop between the virtual vehicle dynamics and real mechanical hardware, exposing issues that pure simulation would miss.
The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.
For this mockup, I need both aesthetic appeal (to be interesting to people) and a solid workhorse. The Super Heavy mockup needs to have 3 actuated grid fins, a way to measure its position relative to the tower mockup, wireless communications, onboard compute, and a mount to the FANUC arm. I made multiple booster mockups; here is the current one. Instead of a pure software simulation, the booster mock-up is physically mounted to the end of a FANUC industrial robotic arm. The arm executes the exact trajectory that the simulated catch maneuver would require, providing realistic inertial loads and kinematics.
Sensor data from the physical setup (encoders, force/torque if available) is fed back into the simulation loop. This closes the loop between the virtual vehicle dynamics and real mechanical hardware, exposing issues that pure simulation would miss.
The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.
FANUC is a global leader in industrial robotic manipulators, and we have two FANUC arms at Polytech: a small one for educational purposes, and a large one that had no real usage yet. I planned to participate in FANUC's national Olympiads, so I needed experience with their systems anyway. I am working with an R-30iA Mate controller, with an M-10iA/12S arm (V7.70 software). However, I need to make it work so I can stream real-time positions of the Super Heavy booster from ProxSim, following the exact same trajectory, without delay or jerk, and this is not what industrial robots are for: they are made for pre-programmed trajectories and logic.


I found a great library: fanucpy. It provides drivers (KAREL programs) to add to the robot, directions to set up an HTTP server on the controller, and a small Python program to run on a local computer. While it was a very good starting point, multiple features were missing: no continuous options, as all movements were executed individually (and not chained like in a regular FANUC program), commands could start multiple seconds after they were sent, and there was no management of user frames... So I reworked the whole library, from the low-level KAREL for the FANUC to the low-level C++ for the ProxSim library, to add a new spline functionality. The spline functionality is currently working on select test cases, but has yet to prove reliable once integrated into ProxSim; more testing is needed.
Here, I'm sending random positions at random intervals to the robot, and seeing how it handles them. Comparison between the first and the second (latest) version.
The delay of multiple seconds is caused by the FANUC's own motion system. The brakes have to be released first, and then it can move. The brakes automatically activate a few seconds after the last motion concluded. The way fanucpy works by default is with a stack approach: each instruction (position, movement type, speed) has to be processed by the KAREL server and executed from a called TP program before the next can happen. I tried to use SKIP commands, but SKIPs always stop the robot before proceeding. My solution: have a TP program running continuously, waiting for instructions sent from the KAREL program. To manage interrupts (when the server receives a new command while the last one is not completed), I use two memory spaces, so KAREL can work on one while the TP program is looping on the other. Once KAREL has finished processing it, I make the TP program switch memory space.
This is where I ramble about interpolation and splines, and why velocities are important compared to simple geometry, and how I made the FANUC understand what I wanted. If I asked the FANUC to reach each point every millisecond, the server wouldn't handle it. I needed better quality at lower resolution, and that comes down to interpolation: filling the blanks. The movement of Super Heavy is smooth (as it is a system with large inertia), so I thought about curves. Current curve interpolation systems use Bézier curves: they are practical for geometry.
As you can see in the videos above, the results of V1 are not very smooth; at some places, the arm accelerated and decelerated suddenly. This is because Bézier curves were not meant for movement; they are only for geometry. A better alternative: Hermite curves. Instead of joining multiple points, they take the start and end position, as well as the velocities at both points. To ensure the robot catches up with the continuous flow of commands, the position at the beginning of the movement and its speed can be the robot's. If it is late, the interpolation will push the robot faster to catch up. I then calculate, in the robot, the interpolation intermediate positions/speeds, write them to PRs, and send the signal to switch memories.

Finally, here is the KAREL code
FUNCTION dothis(arg: integer):
CONST
var1: string
BEGIN
haha
ENDTP programs are interpreted, which means I can edit PRs at run time. However, FANUC's motion system reads ahead in the program to plan the trajectory. The "ahead" number of positions depends on the movement options: an "ACC" option makes the "ahead" count increase by one, and so does "CNT" when non-zero. So I keep track of the current movement index in memory, and start the next motion as if ahead by ~2 positions. This is the secret sauce to make the trajectory butter-smooth and avoid small hiccups/slowdowns.
FANUC is a global leader in industrial robotic manipulators, and we have two FANUC arms at Polytech: a small one for educational purposes, and a large one that had no real usage yet. I planned to participate in FANUC's national Olympiads, so I needed experience with their systems anyway. I am working with an R-30iA Mate controller, with an M-10iA/12S arm (V7.70 software). However, I need to make it work so I can stream real-time positions of the Super Heavy booster from ProxSim, following the exact same trajectory, without delay or jerk, and this is not what industrial robots are for: they are made for pre-programmed trajectories and logic.


I found a great library: fanucpy. It provides drivers (KAREL programs) to add to the robot, directions to set up an HTTP server on the controller, and a small Python program to run on a local computer. While it was a very good starting point, multiple features were missing: no continuous options, as all movements were executed individually (and not chained like in a regular FANUC program), commands could start multiple seconds after they were sent, and there was no management of user frames... So I reworked the whole library, from the low-level KAREL for the FANUC to the low-level C++ for the ProxSim library, to add a new spline functionality. The spline functionality is currently working on select test cases, but has yet to prove reliable once integrated into ProxSim; more testing is needed.
Here, I'm sending random positions at random intervals to the robot, and seeing how it handles them. Comparison between the first and the second (latest) version.
The delay of multiple seconds is caused by the FANUC's own motion system. The brakes have to be released first, and then it can move. The brakes automatically activate a few seconds after the last motion concluded. The way fanucpy works by default is with a stack approach: each instruction (position, movement type, speed) has to be processed by the KAREL server and executed from a called TP program before the next can happen. I tried to use SKIP commands, but SKIPs always stop the robot before proceeding. My solution: have a TP program running continuously, waiting for instructions sent from the KAREL program. To manage interrupts (when the server receives a new command while the last one is not completed), I use two memory spaces, so KAREL can work on one while the TP program is looping on the other. Once KAREL has finished processing it, I make the TP program switch memory space.
This is where I ramble about interpolation and splines, and why velocities are important compared to simple geometry, and how I made the FANUC understand what I wanted. If I asked the FANUC to reach each point every millisecond, the server wouldn't handle it. I needed better quality at lower resolution, and that comes down to interpolation: filling the blanks. The movement of Super Heavy is smooth (as it is a system with large inertia), so I thought about curves. Current curve interpolation systems use Bézier curves: they are practical for geometry.
As you can see in the videos above, the results of V1 are not very smooth; at some places, the arm accelerated and decelerated suddenly. This is because Bézier curves were not meant for movement; they are only for geometry. A better alternative: Hermite curves. Instead of joining multiple points, they take the start and end position, as well as the velocities at both points. To ensure the robot catches up with the continuous flow of commands, the position at the beginning of the movement and its speed can be the robot's. If it is late, the interpolation will push the robot faster to catch up. I then calculate, in the robot, the interpolation intermediate positions/speeds, write them to PRs, and send the signal to switch memories.

Finally, here is the KAREL code
FUNCTION dothis(arg: integer):
CONST
var1: string
BEGIN
haha
ENDTP programs are interpreted, which means I can edit PRs at run time. However, FANUC's motion system reads ahead in the program to plan the trajectory. The "ahead" number of positions depends on the movement options: an "ACC" option makes the "ahead" count increase by one, and so does "CNT" when non-zero. So I keep track of the current movement index in memory, and start the next motion as if ahead by ~2 positions. This is the secret sauce to make the trajectory butter-smooth and avoid small hiccups/slowdowns.
The goal of this project is to reverse-engineer a complex system, but also to prove to people that the tech exists for revolutionary transport. I need to interest people in my project, for visitors of science fairs, school visits, and so on. This means I have to make a mockup that is practical, easy to 3D-print (this is my only available manufacturing solution), and appealing.
As my project evolved, so did the Super Heavy booster's real design. Accounting for the usual iterations, I made 4 different versions.


It stands ~70 cm high, made in three sections (actuators, logic/comms, and battery). The three parts assemble using a twist-and-lock mechanism after the screws were too ugly and unreliable with heavy parts swinging around for hours.
Features an ESP32 (used both for communications and IMU processing with low-pass filters; Kalman filters might come later). Made to fit and hold a whole breadboard, a large battery for autonomy, and a tight servo holder.
Printed on a BambuLab A1 printer, using 0.2 and 0.4 nozzles, with PLA.
The goal of this project is to reverse-engineer a complex system, but also to prove to people that the tech exists for revolutionary transport. I need to interest people in my project, for visitors of science fairs, school visits, and so on. This means I have to make a mockup that is practical, easy to 3D-print (this is my only available manufacturing solution), and appealing.
As my project evolved, so did the Super Heavy booster's real design. Accounting for the usual iterations, I made 4 different versions.


It stands ~70 cm high, made in three sections (actuators, logic/comms, and battery). The three parts assemble using a twist-and-lock mechanism after the screws were too ugly and unreliable with heavy parts swinging around for hours.
Features an ESP32 (used both for communications and IMU processing with low-pass filters; Kalman filters might come later). Made to fit and hold a whole breadboard, a large battery for autonomy, and a tight servo holder.
Printed on a BambuLab A1 printer, using 0.2 and 0.4 nozzles, with PLA.
Early visuals from the simulation environment, cell programming, and hardware setup. These assets will be replaced or expanded with higher-quality captures, diagrams, and annotated footage in later iterations.



The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.
Early visuals from the simulation environment, cell programming, and hardware setup. These assets will be replaced or expanded with higher-quality captures, diagrams, and annotated footage in later iterations.



The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.
Integrating the simulation timestep with the physical robot’s control loop introduced timing and determinism problems. Small delays in the communication bridge could destabilize the closed-loop behavior.
Mechanical compliance in the mock-up mount, robot joint flexibility, and sensor noise all affect the fidelity of the HIL test. Safety systems and emergency stops had to be designed so that a software fault would not damage the arm or cell.
A simplified excerpt from the real-time communication bridge shows the kind of timing-sensitive C++ that had to be carefully bounded for determinism:
// Real-time bridge loop (simplified, temporary example)
void control_loop() {
while (running) {
auto desired = simulate_descent();
auto start = std::chrono::steady_clock::now();
if (!send_to_fanuc(desired)) {
trigger_emergency_stop();
return;
}
Feedback fb = receive_feedback(); // source of jitter
apply_correction(fb);
auto elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > std::chrono::milliseconds(12)) {
log_determinism_violation(elapsed);
}
std::this_thread::sleep_until(start + std::chrono::milliseconds(10));
}
}The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.
Integrating the simulation timestep with the physical robot’s control loop introduced timing and determinism problems. Small delays in the communication bridge could destabilize the closed-loop behavior.
Mechanical compliance in the mock-up mount, robot joint flexibility, and sensor noise all affect the fidelity of the HIL test. Safety systems and emergency stops had to be designed so that a software fault would not damage the arm or cell.
A simplified excerpt from the real-time communication bridge shows the kind of timing-sensitive C++ that had to be carefully bounded for determinism:
// Real-time bridge loop (simplified, temporary example)
void control_loop() {
while (running) {
auto desired = simulate_descent();
auto start = std::chrono::steady_clock::now();
if (!send_to_fanuc(desired)) {
trigger_emergency_stop();
return;
}
Feedback fb = receive_feedback(); // source of jitter
apply_correction(fb);
auto elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > std::chrono::milliseconds(12)) {
log_determinism_violation(elapsed);
}
std::this_thread::sleep_until(start + std::chrono::milliseconds(10));
}
}The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.
The booster uses a combination of attitude control via grid fins / thrust vectoring and precise vertical positioning during the final meters. Early tests focus on attitude hold and descent rate tracking.
The dynamics of the booster near the catch point can be locally approximated by a linear state-space model. A simplified continuous-time representation is:
Here is the state vector (attitude, rates, position, velocity), contains the control inputs (thrust vectoring, grid fin deflections), and the matrices A and B are identified or derived from the simulation model. Full derivation and discretization for the flight software will be added in a future pass.
The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.
The booster uses a combination of attitude control via grid fins / thrust vectoring and precise vertical positioning during the final meters. Early tests focus on attitude hold and descent rate tracking.
The dynamics of the booster near the catch point can be locally approximated by a linear state-space model. A simplified continuous-time representation is:
Here is the state vector (attitude, rates, position, velocity), contains the control inputs (thrust vectoring, grid fin deflections), and the matrices A and B are identified or derived from the simulation model. Full derivation and discretization for the flight software will be added in a future pass.
The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.
The basic HIL loop is running and the arm can replay representative trajectories. Current work focuses on improving synchronization, adding better instrumentation, and validating that the flight software branch produces stable catch behavior.
Next milestones include logging full state traces, running Monte-Carlo style variations on the physical hardware, and gradually increasing the fidelity of the mock-up and sensor models.
The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.
The basic HIL loop is running and the arm can replay representative trajectories. Current work focuses on improving synchronization, adding better instrumentation, and validating that the flight software branch produces stable catch behavior.
Next milestones include logging full state traces, running Monte-Carlo style variations on the physical hardware, and gradually increasing the fidelity of the mock-up and sensor models.
The Pythagorean theorem for any right triangle with legs a, b and hypotenuse c:
Placeholder technical content. The real depth (equations, code, derivations, etc.) will be provided later.