Project Simplex-based thruster control

darrenc

New member
Joined
Nov 22, 2009
Messages
22
Reaction score
0
Points
0
Greets, Forum!

It's been a while since I've done much development. My last Orbiter project (Kliper spacecraft) ground to a halt when I discovered Orbiter didn't handle unbalanced RCS configurations too well. I didn't like the idea of "faking it" with virtual thrusters, so I left it while I worked on other projects.

The good news is that I have finally taken a step towards solving the RCS problem, and would like to put out there for comment and critique the RCS class I have developed, which uses the standard simplex method to solve the thruster firing problem.

The main advantages of this system over the thruster group method are:

* No need for thruster groups - works with arbitrary thruster configurations.
* Command rotations and translations by specifying torque and force vectors.
* Simultaneous rotations and translations are possible.
* Optimal propellant usage.
* Linear scaling of force and torque vectors if they exceed the RCS capability.
* Error tolerances can be used for greater command flexibility.

The disadvantages:

* Time. The simplex method involves a lot of number chrunching.
* Numerical instabilities can cause occasional problems.

To the first issue, time. On my system with a 22-thruster spacecraft, and timing using win32 GetTickCount(), I get results between 0 and 15 milliseconds per solution. YMMV.

The numerical instabilities I have only noticed when trying to generate unrealistically large torques during a killrot program (see below).

I am not very good at explaining how things work in English - code is my first language. So I will give you a couple of examples of how to use the package.

Example 1:

This example commands a torque of 1 kNm around the +Z axis. The RCS class is de-coupled from Orbiter, and requires an object of the RCS::ENUMERATOR class to act as intermediary. RCS::ATTITUDE_ENUMERATOR is a derivation of this class, and will "enumerate" all the attitude control thrusters of the provided vessel.

Code:
/* command a rotation of 1000 Nm around the +Z axis */

void ZRot1000(VESSEL2 * pVessel)
{
    RCS::ATTITUDE_ENUMERATOR    enumerator(pVessel);
    RCS                            rcs(&enumerator);

    rcs.SetTorqueCommand(_V(0, 0, 1000));
    rcs.FindSolution();
}
Example 2:

This code implements simultaneous manual rotation and translation control.
Obtaining the manual control inputs in an exercise for the reader ;)

Code:
/**
 * Example: Set manual rotational and linear force control. The components
 * of each vector should be in the range (-1 .. 1). Each vector must also
 * be correctly oriented for Orbiters co-ordinate system.
 */
void SetManualControl(RCS * rcs, const VECTOR3 & F, const VECTOR3 & T)
{
    /* find the maximum absolute axis component (must not exceed 1.0) */
    double max_abs = 0;
    for (int i = 0; i < 3; ++i) max_abs = max(abs(F.data[i]), max_abs);
    for (int i = 0; i < 3; ++i) max_abs = max(abs(T.data[i]), max_abs);

    /* set input commands */
    rcs->ClearInputs();
    rcs->SetTorqueCommand(T);
    rcs->SetForceCommand(F);

    /* Here, a negative values means (100 * -n) percent of the largest
     * possible response scale for the input vectors */
    rcs->SetResponseScaleLimit(-max_abs);

    /* solve and implement */
    rcs->FindSolution();
}
Example 3:

A simple (but very effective) KillRot program.


Code:
/*
 * A simple KillRot program. Returns true when we have stopped rotating.
 */
bool KillRot(VESSEL2 * pVessel, double sim_dt)
{
    /* it would be more efficient to make the RCS and ENUMERATOR members of
     * the VESSEL so we don't create/destroy them all the time */

    RCS::ATTITUDE_ENUMERATOR enumerator(pVessel);
    RCS rcs(&enumerator);

    VECTOR3 w;        /* angular velocity */
    VECTOR3 L;        /* angular momentum */
    VECTOR3 T;        /* torque */
    VECTOR3 PMI;    /* principle moments of inertia */

    /* fetch angular velocity and PMI */
    pVessel->GetAngularVel(w);
    pVessel->GetPMI(PMI);
    
    /* mass de-normalise PMI */
    PMI *= pVessel->GetMass();

    /* determine angular momentum from velocity and PMI */
    L.x = w.x * PMI.x;    
    L.y = w.y * PMI.y;
    L.z = w.z * PMI.z;

    /* torque is angular momentum over time - how much torque would we need
     * to kill our angular momentum over the next time step? */
    T = -L / sim_dt;

    /* unless we are almost already stopped, it is highly unlikely the RCS
     * will be able to produce this torque - in that case it will scale the
     * torque command to the largest value it can produce along the same
     * vector. */
    rcs.SetTorqueCommand(T);

    /* here we sacrafice translational force for better propellent usage */
    rcs.SetForceTolerance(RCS::UNLIMITED);
    rcs.SetResponsePriority(RCS::PROPELLANT);

    /* find (and implement) the solution */
    rcs.FindSolution();

    /* return true when the response scale is close enough to 1.0 - this means
     * the produced torque was equal to the commanded torque which, in turn,
     * means our rotation has been arrested.
     */
    return abs(rcs.GetResponseScale() - 1.0) < 1E-5;
}
The best way to learn more is to play around with it! Or read the documentation - whichever tickles your fancy :thumbup:

The KillRot example above should be "ready to go". Just call it from your clbkPreStep() when appropriate. It's fun to watch with "body force vectors" turned on - in my tests the vector hardly even wobbles :)

The ZIP (source & docs) is attached to this post, and also downloadable from:

http://www.freefilehosting.net/rcs
 

Attachments

  • rcs.zip
    72.1 KB · Views: 49

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
This looks very interesting but I'm a little confused about what it intends to accomplish.

Unbalanced thruster groups can be quite manageable as long as their positions are consistant within the vessel's frame of reference. It takes a little bit of math (usually a 'void' routine in either clbkPrestep or poststep) but calculating (and thus countering) the amount of torque or linear velocity produced by a given thruster configuration is pretty straight forward.

I encountred this problem while working on my space-tug and trying to account for the effects of a docked vessel and associated CG shift on it's RCS controls. Look into the dragonfly's SDK code if you want an example of how to impliment it.
 

darrenc

New member
Joined
Nov 22, 2009
Messages
22
Reaction score
0
Points
0
This looks very interesting but I'm a little confused about what it intends to accomplish.

Unbalanced thruster groups can be quite manageable as long as their positions are consistant within the vessel's frame of reference. It takes a little bit of math (usually a 'void' routine in either clbkPrestep or poststep) but calculating (and thus countering) the amount of torque or linear velocity produced by a given thruster configuration is pretty straight forward.

By "unbalanced", I wasn't just referring to a "standard" thruster configuration with a shifting CG. The Kliper spacecraft which inspired this has a highly non-orthodox configuration, which doesn't at all fit into the thruster group idiom. (ie: a pitch command involves all the thrusters).

Say you want to pitch the (Kliper) spacecraft up. You start by firing the pitch thrusters, but this induces an unwanted translation (up and back). Nulling out the back translation is straight-foward (introduces a small pitch-down), but nulling out the up translation involves firing thrusters which will increase the back translation and pitch the spacecraft down a lot! And compensating for the unwanted pitch-down introduces more up translation... and so on. It quickly becomes a non-trivial problem. Also, a problem which needs to be re-solved anew for each new thruster configuration.

The RCS class uses the same technique (simplex method) which has been historically used in real-life spacecraft to calculate thruster firings (though some more efficient methods are now available - but beyond my comprehension!). It works for any thruster configuration and degrades gracefully when it is asked to do things the thruster configuration can't do.

On top of that, you (optionally) get optimal propellant usage :)

If you code works, by all means - stick with it! If it ain't broke and all that... (although I will point out that your code may not necessarily be producing propellant-optimal solutions). This code lays a foundation to allow people to use accurate (for vessels based on RL spacecraft) thruster layouts, rather than choosing between accurate layouts, controllability, and rolling-their-own thruster manager.

The fly-by-wire proposal thread (which I keep meaning to respond to - sorry lassombra!) looks interesting as a way of integrating this code into existing spacecraft, specifically so the default auto-pilots can take advantage of it.

Can you tell I'm a little passionate about this? :lol:
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,664
Reaction score
2,385
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
If I remember correctly, the RCS of Klipr was pretty balanced and not that complex at all.

Small translations or rotations because of the CoG being off the design reference is the norm in spaceflight, you usually compensate it with much simpler math by doing closed loop attitude control.
 

darrenc

New member
Joined
Nov 22, 2009
Messages
22
Reaction score
0
Points
0
If I remember correctly, the RCS of Klipr was pretty balanced and not that complex at all.

Really? I was going off all the information I could find, which largely consisted of pictures of mock-ups. I noticed the configuration changed several times; perhaps I used one of the duds.

Small translations or rotations because of the CoG being off the design reference is the norm in spaceflight, you usually compensate it with much simpler math by doing closed loop attitude control.

...which Orbiter doesn't do - leaving the original problem that the default auto-pilots do not handle unbalanced RCS configurations very well. This is intended to be a generic "drop-in" solution.
 

lassombra

New member
Joined
Aug 12, 2010
Messages
26
Reaction score
0
Points
0
A drop in solution which I'm very interested in using in my UFBW development. I asked in that thread, and I'll ask here as well if you'd be interested in collaborating on that project?

It has to go on a couple of week hiatus at the moment as my monitor managed to short itself out, but details...
 

whitewatcher

New member
Joined
Jun 2, 2008
Messages
23
Reaction score
0
Points
0
Darrenc, I like your code very much.
There are vessels which really need it.

Sometimes, ideas in this forum are months ahead of their time. ;-)

Thank you!
 

whitewatcher

New member
Joined
Jun 2, 2008
Messages
23
Reaction score
0
Points
0
I just put the RCS code into a very specific vessel and it works fine.
Even my framerate doubled, because the your Simplex implementation is faster than the Gauss-Jordan which I had.
 
Top