Posted on by

In a previous post we covered the software to perform detumbling – the first function of an Attitude Determination + Control System (ADCS). We now move onto the second (and more interesting) function of an ADCS – to point yourself in a certain direction. This functionality is critical when you have any direction-specific equipment on your satellite – whether you have a parabolic antenna for providing the internet or a space laser for destroying the internet, you need your equipment to be pointing in the right direction for it to work properly.

Now in order to point, you must be able to work out what angle you’re currently sitting at – this is normally done by using a magnetometer (an electronic compass). However, in our setup there was a lot of magnetic interference from the motor, making the magnetometer very inaccurate in calculating the platform’s angle. Thus we have to make use of the other sensors on board – a gyro and an accelerometer (the latter being fairly useless for measuring rotation).

The Problem

Imagine that you’re driving a windowless tram, and someone tells you that there’s five workers chained to the tracks exactly 100m ahead. Now it just so happens that you’ve brought along your favourite pair of bolt-cutters, but you also happen to be super lazy and would rather drive the tram to them instead of walking.

Bonus points if you use a bang-bang controller
The Trolley Problem for Engineers

 

As you look down at your odometer, you remember your old physics teacher going on about how to calculate your distance from your velocity (remember: distance = velocity ⨉ time). Unfortunately, this formula only works for constant velocities, and the accelerator is way too touchy to keep a constant speed. What do you do?

The Solution

Just like dealing with incriminating evidence, this problem can be solved by chopping it into tiny pieces. Let’s say for the first second of our journey, we recorded our average velocity – say 2m/s. Then we know that we’ve travelled 2m down the road (using our handy formula d = v ⨉ t). Similarly for the next second if we measure our average velocity to be 3m/s, then we know we’ve travelled 3m. So in total we’ve travelled 2m + 3m = 5m. It turns out we can calculate our position by repeating this process until we arrive at the workers.

Now in the case of our ADCS, our trusty gyro measures angular velocity. Angular velocity formulas work the same as linear ones, so we can actually use the same approach to work out the angle of our platform with ease. For example, if after 0.1s we measured our angular velocity to be 2°/s, then 0.1s later we measured it to be 3°/s, then our current angle would be (2⨉0.1 + 3⨉0.1)° = 0.5° anticlockwise from where we started (positive angles are anticlockwise by convention).

In code, this process (also known as ‘integration’) is simple. If every 5ms a new gyro measurement is taken, then the following line can be used to calculate the new platform angle.

currAng = oldAng + angVel * 0.005;
    //where angVel is the latest unbiased gyro measurement

The Controller

Now that we have a way to calculate the angle of our ADCS, we can reuse our proportional controller from our detumbling code:

Output = K ⨉ error

(where K is some tuned constant, and error was the difference between the current value and the target value.)

Now it’s possible to use this same controller to control our angle, but variety is the spice of life so let’s go for something a bit fancier – a ‘proportional derivative’ (PD) controller. In math-speak, a PD controller looks something like this:

Output = K ⨉ error + C ⨉ error’

(where K and C are two constants.)

The little apostrophe indicates a derivative (rate of change) – in this case it’s the derivative of the error, or how fast the error is changing. Remember that the error = current angle – target angle. The target angle is usually constant, so the rate of change of the target angle is 0 (because it’s not changing). Thus

error’ = current angle’

Now we’re just left with the rate of change of the current angle (how fast the angle of the platform is changing) – sound familiar? The rate of change of the current angle is the same as the angular velocity, i.e. what we originally got from the gyro!

Putting this all together, in code-speak our PD controller is given by:

output = k * posnErr + c * angVel;
    //where posnErr = targetAng - currAng
    //and angVel = angular velocity from gyro 

Tuning K and C is simply a matter of trial and error, finding the pair of values that minimises the time to reach the target angle without overshooting it.

The Radio

Now it’s a bit boring if your satellite can only point in a predetermined direction – what’s really fun is being able to point it wherever you want while it’s in operation. A simple potentiometer on a separate Arduino allows us to digitise angles:

angle = analogRead(A0)/1024.0*(2*PI);
    //assuming your pot rotates a full 360deg

So now all we need to do is somehow transmit this angle to the Arduino on-board the reaction wheel system – to do this we need to utilise RF (Radio Frequency), the black magic of electrical engineering.
In the realm of RF, things behave in strange ways. Radio signals vary in range depending on the time of day, straight wires transfer more power than bendy ones, and you can even use funny-shaped wires to improve your signal quality. It takes mad skills to truly harness the power of RF, and many believe that RF engineers are actually wizards.

Luckily for us, these wizards also sell radio modules that can interface easily with our Arduinos, such as the NRF24L01 chip. After wiring up a module to both the on-board and remote Arduinos (example), we can transmit data using TMRh20’s RF24 library and the following code:

On the transmitter side:

RF24 radio(9, 10);              //set pins 9 and 10 to be CE and SCN respectively
const byte rxAddr[6] = "00001"; //set address of radio
radio.begin();                  //initialise radio
radio.setRetries(15, 15);       //if message not received, wait 4ms ((15+1)*250us) before retrying, retry 15x before giving up
radio.openWritingPipe(rxAddr);  //open a pipe for writing
radio.stopListening();          //stop listening for RF messages, switch to transmit mode

float angle = analogRead(A0)/1024.0*(2*PI); //read in angle of potentiometer
radio.write(&angle, sizeof(angle));         //transmit angle as a radio message

On the receiver side:

RF24 radio(18, 19);
const byte rxAddr[6] = "00001";
radio.begin();
radio.openReadingPipe(0, rxAddr);  //open a pipe for reading
radio.startListening();            //switch to receive mode, start listening for messages
if (radio.available()){            //if there's an incoming message,
    float rx;
    radio.read(&rx, sizeof(rx));   //store it in the variable rx
}

These code stumps transmit the angle of the potentiometer from the transmitting Arduino to the receiving Arduino (the one on the ADCS). This allows us to update the target angle on the on-board Arduino, thus giving us run-time control of its position.

Here’s a demonstration of this whole thing in action:

 

 

(As always, code is available here)