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.
Example 2:
This code implements simultaneous manual rotation and translation control.
Obtaining the manual control inputs in an exercise for the reader
Example 3:
A simple (but very effective) KillRot program.
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
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();
}
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();
}
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 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