My Own Space Program

Just want the code? Here it is. But it's not brilliant code. It is best used as the basis for your own work.


Kerbal Space Program is a game in development currently available as a commercial Alpha offering on Steam. Thus far it has no real story, you just get to build vehicles like planes and spacecraft. Most players try to build a space station or get to the Moon, called Mün in the game(perhaps because of trademark issues? :) I missed a guidance system to make a rocket go in a given direction as I always ended up in a new orbit every time I launched a new rocket. As has a good plug-in system I figured I'd make my own guidance system as an add-on. There exist very good add-on for this purpose already but I wanted to make one myself in C#.

KSP's plug-in system requires that you import some libraries into a development environment like Visual Studio. Then you can use most of the classes and methods used in the actual game. Your module is finally compiled into a DLL file which is placed in KSP's Plugin-folder. There are some in and outs explained on the wiki and along those lines I copied the configuration file for the sas module to create a new part.cfg file. Next I created an empty class extending PartModule.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Guidance;

namespace guidance
    public class GuidanceModule : PartModule

As we extend PartModule we can directly reference variables like vessel and various utility functions. It's also enormously useful to be able to visualize many vectors you tinker with while launching rockets. I declared a number of LineRenderers at the top of my GuidanceModule:

private LineRenderer line1 = null;

The basic process of initializing is explained on this page from the KSP wiki. It demonstrates how to draw lines relative to the rockets own coordinate system but I have gone over to drawing them in absolute space. To do this we change line.useWorldSpace = false to line.useWorldSpace = true. This also requires that the starting point of the vector is set to a point near the rocket. The following code is executed in my module every time fly(FlightCtrlState s) is called by the simulator:

Vector3d worldPos = vessel.GetWorldPos3D();
Vector3d ref1 = worldPos + Vector3d.right * 5;
line1.SetPosition(0, ref1);
line1.SetPosition(1, ref1 + (vec * 3));

This will visualize a vector called vec a few units of distance to the side of the rocket, increased in size by a factor of 3.

Control Theory

Control theory is a very intricate approach to controlling dynamic systems pretty much without consideration for what makes the system tick. We only look at how a change in what influences the system causes the system to change in itself. The basic principle is negative feedback. If we want a valve to put out 2 liters of water per second at it only puts out 1.5 liters per second, the error -0.5 l/s is fed back with a negative sign to the tap so we turn the tap on by some measure 0.5. If we the output is actually 2.3 l/s then we turn the tap off by some measure 0.3.

To make things work smoothly we not only scale the value we feed back by some amount we determine to be useful, we also include the derivative of the error and the sum of all errors recorded by the control system. So we have a Proportional, Integrating and Derivative part in our controller, hence it is called a PID controller. In KSP the rocket accepts three steering inputs: pitch, yaw and roll. So I have one PID-controller for yaw, one for pitch and one for roll. Currently I don't use the one for roll but that's actually the easiest to control, by and large.


How then do we give the guidance system its target? I give it a set of vectors, like so:

Heading 0 0.0 0.0 -1.0
Heading 1000 -0.3 0.0 -1.0
Heading 5000 -0.5 0.0 -1.0

This means that between altitudes 0 and 1000 we should be heading along the vector 0.0,0.0,-1.0 which in KSP means we head straight up from the surface of the planet. Between 1000 and 5000 units of distance from sea level we now tilt slightly to the west. After we pass an altitude of 5000 units we should tilt even more to the west. It was not designed to be user friendly, it was designed to be quick and easy for me to program.

By visualizating various vectors available to the programmer it turns out that the call vessel.transform.up gives us a vector representing the direction in which the rocket is pointing. To make sure we are just comparing angles we normalize both the target vector and the vector representing the rockets heading and then subtract the one from the other:

Vector3d diff = target.normalized - vec.normalized;

Normally we would just feed the errors to the respective PID controllers by calling their steer(err) method and put the values returned into FlightCtrlState s :

s.yaw = yaw.steer(diff.x);
s.pitch = pitch.steer(diff.y);

This unfortunately doesn't work because yaw doesn't always control how the rocket tilts in the x-plane, nor does pitch always control how the rocket tilts in the y-plane. Yaw and pitch are relative to the rockets own sense of orientation so when the rocket rotates around it own axis yaw and pitch can end up affecting both the rockets orientation in the x- and y-plane! So we can't just take the values our PID controllers give us and feed them to the FlightCtrlState. We have to figure out some way to translate absolute x and y changes into the appropriate yaw and pitch changes.

Thankfully there is a vector available called the forward vector: vessel.GetFwdVector() which tells us how a positive pitch input changes the rockets orientation. When we start at the launch pad the forward vector is 0.0,1.0,0.0, that is to say positive pitch makes the rockets vector have a bigger y-component. By using some linear algebra we can also get a vector that defines what impact a yaw input will have, which I call the yaw vector. If we assume that there is always some way for us to turn in the right direction we can argue that some combination of yaw and pitch will yield the changes to te rocket's vector's x and y components as recommended by the PID controllers. If we call the forward vector fwdVec and the yaw vector yawVec we can express the relationship like so: A*yawVec+B*fwdVec=diff.

Here we have two black vectors, one pointing straight up, just like the rocket. To the side is an angled one representing the target vector. The red line shows the difference between the two vectors. The blue and green vectors represent the yaw vector and the forward vector.

Here we see how the yaw and forward vectors change their position as we have rotated the rocket.

This turns out to be kind of awkward because we have a z-component as well and I have just chosen to ignore it, which seems to work... After tinkering with the equations for a while I ended up with the following code:

Vector3d fwdVec = vessel.GetFwdVector();
Vector3d yawVec = Vector3d.Cross(fwdVec, vessel.transform.up);
Vector3d vec = vessel.transform.up;
double altitude = vessel.mainBody.GetAltitude(position);
Vector3d target = traj.getTarget((int)altitude);

Vector3d diff = target.normalized - vec.normalized;
float xInput = yaw.steer(diff.x);
float yInput = pitch.steer(diff.y);

double B = (yInput * yawVec.x + xInput * yawVec.y) / (fwdVec.y * yawVec.x - fwdVec.x * yawVec.y);
double A = (xInput - B * fwdVec.x) / yawVec.x;

s.yaw = (float) -A;
s.pitch = (float) -B;

This works! It makes sense that A should be negated because of how yawVec is created but I don't quite understand why B has to be negated. I just figured it out by trial and error. The system doesn't respond at all well when large changes have to be made. If the differential between the target x-value and the actual x-value is greater than say 0.5 the system locks into a wild oscillation around a horizontal heading.


Next I'm going to try to establish a space station but I might also go into Wiremod for Garry's Mod. I'm glad that people have made games like KSP and Wiremod that are engineering simulators and hope that we'll be seeing more of that. An architecture thing like the old Pontifex only for skyscrapers would be really neat. I'd be thrilled about a sort of Manhattan project thing where one develops atomic reactors and weapons. It's hard to simulate an implosion-trigger for a plutonium based device but I think it can be done. Maybe it's a bit too politically sensitive? It could be argued that furnishing some countries with even an approximating simulator of nuclear warhead detonation isn't in the best interest of world peace. But I think something similar can be said for KSP to be honest. It's a brilliant primer in intercontinental ballistic missiles construction...


Early attempt that didn't handle rotation very well.

This is a later attempt that could even handle the target being changed in flight(hence the jumps in the printout).

And here I've graduated to using a two-stage rocket with the guidance system.

And the rocket ended up mostly oriented in the same plane as the Mün.