Fly-By-Wire System

Hey all

I’m currently trying to make a space flight sim. One thing I’m particularly keen on is a fly-by-wire system, where thrusters rotate and adjust power levels to achieve velocities and rotations specified. I can’t seem to find anything on the subject though that provides me with some nice maths on how to work it out. Perhaps if I could figure out a way to do it for one ship I could apply a similar method to other ships.

I have a small shuttle with 4 thrusters (shown below). Each thruster can be rotated in the x-axis and their power level varied. At the moment I’m unable to even get the thing hovering stably let alone flying. Of course I’d love a general solution but I don’t think one really exists. How might I go about getting this little guy flying nicely?

*edit: I have an image, how can I get that displayed here?

Perhaps looking at some Inverse Kinematics math will give you some ideas.

EDIT: I’m shocked how hard it is to find any resources on this on the web. Guess not many hobbyists building space shuttles…
Seriously though I’m sure the stuff is covered in detail, just need to find the right wording for the problem.

EDIT: That’s a start: Reaction control system - Wikipedia

EDIT: The solution will probably be complex and I can’t imagine why would you need this? Can’t you fake it?

Call it Star Citizen envy :stuck_out_tongue:

I think I need some sort of PID feedback loop. It just gets mighty complex when you have many thrusters and I was wondering if someone understood the theory a bit better.

I think I have a system working. The thing is horrid to fly so requires a bit of work but it hovers quite nicely and you can fly. I haven’t even implemented the differential or integral parts of the control system yet.

If anyone else wants to implement a similar system in the future here is my messy and un-commented code, enjoy :stuck_out_tongue:

Basically I started getting it hovering by adjusting the power in each thruster proportionally to its tilt (assuming all thrusters were pointing down). I then got it to angle the thrusters forwards if the player is flying forwards and included some clever little bits to allow it to turn.

I would upload some screenshots but that didn’t work too well last time :stuck_out_tongue:

public class Shuttle : MonoBehaviour {
	public GameObject pilot = null;
	public float forwards = 0;
	public float turn = 0;
	public float elevation = 0;
	public float roll = 0;

	void Update(){
		Vector3 normal = Vector3.up;

		Vector3 desiredVelocity = Vector3.Cross(transform.right, normal).normalized*forwards
			+normal*elevation;
		Vector3 force = (desiredVelocity - rigidbody.velocity);
		force -= Physics.gravity*1.7f;
		force*=rigidbody.mass;
		float yForce = Vector3.Dot(transform.up, force);
		float zForce = Vector3.Dot(transform.forward, force);
		float ang = Mathf.Atan2(-yForce,zForce)*180/Mathf.PI;

		float rotSpeed = 100*Time.deltaTime;
		float thrustMid = force.magnitude/4;

		Vector3 tilt = Quaternion.Inverse(transform.rotation)*Vector3.Cross(transform.up, normal);
		float pitch = Mathf.Clamp(-tilt.x*10,-1,1)*0.5f;
		float rolling = Mathf.Clamp(-tilt.z*10,-1,1)*0.5f + roll;

		for(int i=0; i<4; i++){
			Transform thruster = null;
			Quaternion rotation = Quaternion.identity;
			float thrust = thrustMid;
			switch(i){
			case 0:{
				thruster = transform.FindChild("ThrusterFR");
				float dot = Vector3.Dot(thruster.transform.forward, normal);
				rotation = Quaternion.Euler(ang-turn*25*dot,0,0);
				thrust *= 1-turn/4*(1-dot);
				thrust *= 1+pitch*dot;
				thrust *= 1-rolling*dot;
				break;}
			case 1:{
				thruster = transform.FindChild("ThrusterFL");
				float dot = Vector3.Dot(thruster.transform.forward, normal);
				rotation = Quaternion.Euler(ang+turn*25*dot,0,0);
				thrust *= 1+turn/4*(1-dot);
				thrust *= 1+pitch*dot;
				thrust *= 1+rolling*dot;
				break;}
			case 2:{
				thruster = transform.FindChild("ThrusterBR");
				float dot = Vector3.Dot(thruster.transform.forward, normal);
				rotation = Quaternion.Euler(ang-turn*25*dot,0,0);
				thrust *= 1-turn/4*(1-dot);
				thrust *= 1-pitch*dot;
				thrust *= 1-rolling*dot;
				break;}
			case 3:{
				thruster = transform.FindChild("ThrusterBL");
				float dot = Vector3.Dot(thruster.transform.forward, normal);
				rotation = Quaternion.Euler(ang+turn*25*dot,0,0);
				thrust *= 1+turn/4*(1-dot);
				thrust *= 1-pitch*dot;
				thrust *= 1+roll*dot;
				break;}
			}
			thrust = Mathf.Clamp(thrust, 0,100000);
			thruster.localRotation = Quaternion.RotateTowards(thruster.localRotation, rotation, rotSpeed);
			rigidbody.AddForceAtPosition(thruster.forward*thrust, thruster.transform.position);
			thruster.FindChild("Thrust").particleSystem.emissionRate = thrust / 1000;
			thruster.FindChild("Thrust").particleSystem.startSpeed = thrust / 5000;
		}
	}
	
	public void Control(){
		forwards = Input.GetAxis("Vertical")*50;
		turn = Input.GetAxis("Horizontal");
		elevation = Input.GetAxis("Elevation")*20;
		roll = Input.GetAxis("Roll");
	}
}