Adam Obeng

A Javascript Autopilot for Crew Dragon

| Categories: code, ML, space

That title is a truly horrifying combination of words.

SpaceX just released the ISS Docking Simulator, a browser game where the objective is to very slowly fly the Crew Dragon 2 to dock with the International Space Station, just like the real mission is scheduled to next week. Given that a significant portion of my early teens was wasted on just the free demo of Star Wars: X-Wing vs. TIE Fighter, I had to play it. The whole experience is very ponderous, and definitely not as fun as XWvTF — which leads me to believe it is in fact a realistic simulation.

I only needed to dock successfully once to be satisfied with playing the simulator myself. The nex logical step was to move onto the much more interesting challenge of automating it. The result is some hacky Javascript which you can paste into your browser console, and will automatically fly the simulated Crew Dragon to the ISS. Here’s a video of it in action:

And here’s the full code:

How do you fly a spaceship?

A quick Wikipedia browse helped me figure out that the problem to solve here is called ‘attitude control’, which led to some gnarly papers before I realised that the controller is basically a PID controller. Actually, I only had a vague idea what a PID controller was, but by staring at the algorithm and a co-incidental Hacker News post long enough, I realised that the algorithm can be simplified to:

  1. If you’re far away, move closer
  2. If you’re moving too fast, slow down


This chimes with how I was playing the demo too: you realise that you can change your roll but you have to be careful not to overshoot, so when you’re close to zero you have to move more slowly. The UI encourages this by showing not just your current position but the rate of change in your position (i.e. your speed or angular velocity).

The Crew Dragon simulation has six degrees of freedom we can control: roll, pitch, yaw and x, y, z. In a PID algorithm, these are called the process variables, and the difference between the current value of these variables and the desired value is the error. The amount of change you need to effect (here the amount you need to run the thrusters) is a function of the error, the derivative of the error, and some multiplicative constants that describe how those two factors should be combined. The docking is successful when all of those variables are (close to) zero. If they’re not zero, then the autopilot actuates the appropriate control to bring them closer to zero (e.g. roll to the left if currently rolled to the right). I’m treating the controls as decoupled: the thruster which moves you in the x-direction should have no effect on the y-error and so on. I’m not sure if this is true of the real capsule, and in theory it’s not true: it should be the case that moving the forward thruster would move the craft in the z-direction if the pitch wasn’t zero. In practice though, the autopilot achieves level flight very early, so the controls are effectively decoupled.

Implementing the Autopilot in Javascript

The Docking Simulator is nicely written in HTML5 using a WebGL canvas, which makes it really easy to interface with. The autopilot reads the HUD HTML elements to determine the current position of the craft, and controls it by simulating presses on the on-screen controls.

To make it easier to understand what’s happening, I inserted another transparent canvas element on top of the game, which is used to display graphs showing the change over time of the current error on each axis, and the rate of change of that error, as well as the amount the controls are being actuated. The control loop which runs every 500 milliseconds reads all of the process variables, appends them to a running log which is used for the graphs, and then actuates the controls based on the PID output.

There were a couple of other interesting implementation details:

The most difficult part of this process was figuring out what values to set for the gain. The rotational controls worked pretty easily, but it turns out that for the x, y, and z controls to work, the gain for the derivative needs to be 800x larger than the gain for the error! As the video shows, this results in initially very high speeds (especially in the x direction), but a lot of damping so that the final approach is tentatively slow.

I can’t say I can recommend this as an approach for real spacecraft control (there is a reason they call it rocket science), but if the next Mark Watney happens to be a frontend developer stuck on a spaceship controlled via a web browser… you could do worse.