Howto:Implement a Fly-By-Wire System for Airliners: Difference between revisions
m (→/Nasal/fbw.nas: formatting) |
No edit summary |
||
Line 22: | Line 22: | ||
* Applies thrust and slightly moves the elevators down when closing in on stall speed | * Applies thrust and slightly moves the elevators down when closing in on stall speed | ||
* Reduces thrust when exceeding 250 knots under 10000 ft MSL and when reaching Vne above 10000 ft MSL | * Reduces thrust when exceeding 250 knots under 10000 ft MSL and when reaching Vne above 10000 ft MSL | ||
* Dampens the controls to prevent sudden movements | |||
Revision as of 09:47, 7 February 2012
This article is a stub. You can help the wiki by expanding it. |
Objective: Implement a Nasal based fly-by-wire system for Airliners with fly-by-wire and set configuration properties to get desired results.
Background
The Fly-By-Wire system was developed for the Boeing 787-8, but can be used for other aircraft too. As Boeing wanted to give pilots the upper hand, the fly-by-wire systems CAN be disabled and overridden by pilot inputs. If you would like to use this fly-by-wire script for an Airbus Aircraft, you might want to make a few changes to give the fly-by-wire more power.
The whole idea here is to get the pilot's control inputs through a nasal script, and creating custom output properties which are then read by the FDM. To implement this system, you'll need to work with the following files:
- -set.xml
- fbw.nas
- FDM xml file
Note that as the new 787 uses JSBSim, this tutorial will focus on implementing the FBW to an aircraft running with JSBSim.
Functions
The Boeing 787-8 Fly-By-Wire system executes the following functions which help stabilize the aircraft:
- Accordingly adjusts thrust and elevators to maintain lift during turns
- Helps during turns with rudder movement
- Doesn't allow the plane to turn beyond the specified bank limit (customizable)
- Applies thrust and slightly moves the elevators down when closing in on stall speed
- Reduces thrust when exceeding 250 knots under 10000 ft MSL and when reaching Vne above 10000 ft MSL
- Dampens the controls to prevent sudden movements
The Fly-By-Wire Configs can be set/edit from the property tree. In the Boeing 787-8 (new one coming up soon), you can edit the following FBW Configurations from the FBW CONFIG menu in the CDU:
- Fly-By-Wire Status (ACTIVE / DISABLED)
- Rudder Control (ALLOWED / DENIED)
- YAW DAMPER (ACTIVE / DISABLED)
- BANK LIMIT (CYCLE BETWEEN 15, 20, 25, 30, 35, 40)
Implementation
/Nasal/fbw.nas
Open your aircraft's nasal directory and create a file called 'fbw.nas'. Copy and paste the following code in that file and save it.
#########################################
## FLY BY WIRE SYSTEM FOR FLIGHTGEAR ##
#########################################
## Designed by Omega Pilot and Redneck ##
#########################################
# CONSTANTS
var RAD2DEG = 57.2957795;
var DEG2RAD = 0.0174532925;
var fbw = {
init : func {
me.UPDATE_INTERVAL = 0.001;
me.loopid = 0;
me.throttle = 0;
me.throttlefix = 0;
me.vsinit = 0;
me.throttleinit = 0;
me.targetthrottle = 0;
me.turnthrottlefix = 0;
me.targetaileron = 0;
me.targetelevator = 0;
me.targetrudder = 0;
me.adjustelevators = 0;
me.disconnectannounce = 0;
## Initialize with FBW Activated
setprop("/controls/fbw/active", 1);
setprop("/controls/fbw/rudder", 1);
setprop("/controls/fbw/yaw-damper", 1);
setprop("/controls/fbw/bank-limit", 35);
## Initialize Control Surfaces
setprop("/fdm/jsbsim/fcs/aileron-fbw-output", 0);
setprop("/fdm/jsbsim/fcs/rudder-fbw-output", 0);
setprop("/fdm/jsbsim/fcs/elevator-fbw-output", 0);
me.reset();
},
update : func {
var fcs = "/fdm/jsbsim/fcs/";
## Fix Damp Rate according to Framerate
var fpsfix=1;
if (getprop("/sim/frame-rate") != nil) fpsfix = 25 / getprop("/sim/frame-rate");
## Bank Limit Setting
var banklimit = getprop("/controls/fbw/bank-limit");
## Position and Orientation
var altitudeagl = getprop("/position/altitude-agl-ft");
var altitudemsl = getprop("/position/altitude-ft");
var pitch = getprop("/orientation/pitch-deg");
var roll = getprop("/orientation/roll-deg");
var airspeedkt = getprop("/velocities/airspeed-kt");
var vsfps = getprop("/velocities/vertical-speed-fps");
## Flight Control System Properties
var aileronin = getprop(fcs~"aileron-cmd-norm");
var elevatorin = getprop(fcs~"elevator-cmd-norm");
var rudderin = getprop(fcs~"rudder-cmd-norm");
## FBW Output (actual surface positions)
var aileronout = getprop(fcs~"aileron-fbw-output");
var elevatorout = getprop(fcs~"elevator-fbw-output");
var rudderout = getprop(fcs~"rudder-fbw-output");
## Engine Throttle Positions
throttle0 = getprop("controls/engines/engine[0]/throttle");
throttle1 = getprop("controls/engines/engine[1]/throttle");
## This is where the FBW actually does it's job ;)
### The Fly-by--wire only works when it is active. In the Boeing 787, pilots have the option to disable fly-by-wire and use power-by-wire* in case of emergencies. The Fly By Wire Configuration includes: On/Off, Bank Limit and Rudder Control. The FBW Configs can be set in the FBW CONFIG Page in the CDU(s)
## Turn on Fly By Wire only if we have power
if (getprop("/systems/electrical/outputs/efis") != nil) {
if ((getprop("/systems/electrical/outputs/efis") < 9) and (altitudeagl >= 200)) {
setprop("/controls/fbw/active", 0);
if (me.disconnectannounce == 0) {
screen.log.write("Fly By Wire Disconnected!", 1, 0, 0);
me.disconnectannounce = 1;
} # end of disconnect announce
} # end of efis/agl check
}
if (getprop("/controls/fbw/active")) {
me.disconnectannounce = 0;
## AILERON CONTROLS
### Set Aileron Direction and Roll Direction
if (roll < 0) var rolldir = -1;
if (roll > 0) var rolldir = 1;
if (roll == 0) var rolldir = 0;
if (aileronin < 0) var ailerondir = -1;
if (aileronin > 0) var ailerondir = 1;
if (aileronin == 0) var ailerondir = 0;
if (((roll <= banklimit) and (roll >= -banklimit)) or (rolldir != ailerondir)) {
if (aileronin > aileronout) aileronout += 0.05 * fpsfix;
if (aileronin < aileronout) aileronout -= 0.05 * fpsfix;
} else {
### Don't let the plane bank past the bank limit
if (roll < -banklimit) me.targetaileron = -(roll + banklimit) * 0.025;
if (roll > banklimit) me.targetaileron = -(roll - banklimit) * 0.025;
if (aileronout < me.targetaileron) aileronout += 0.025 * fpsfix;
if (aileronout > me.targetaileron) aileronout -= 0.025 * fpsfix;
}
## ELEVATOR CONTROLS
if (elevatorin > elevatorout) elevatorout += 0.05 * fpsfix;
if (elevatorin < elevatorout) elevatorout -= 0.05 * fpsfix;
if ((elevatorin - elevatorout < 0.05) and (elevatorin - elevatorout > 0)) elevatorout += 0.01;
if ((elevatorout - elevatorin < 0.05) and (elevatorin - elevatorout < 0)) elevatorout -= 0.01;
### Use elevator to stabilize VS on turns
if ((roll <= -5) or (roll >= 5)) {
if (me.adjustelevators == 0) {
me.adjustelevators = 1;
me.vsinit = vsfps;
}
if (vsfps < me.vsinit) elevatorout -= 0.02;
if (vsfps > me.vsinit) elevatorout += 0.02;
} else {
if (me.adjustelevators == 1) {
if (vsfps < me.vsinit) elevatorout -= 0.02;
elsif (vsfps > me.vsinit) elevatorout += 0.02;
else me.adjustelevators = 0;
}
}
## THROTTLE CONTROLS
### Disconnect Throttle fix if manually overridden
if (throttle0 != me.throttle) {
me.throttlefix = 0;
me.turnthrottlefix = 0;
}
### Adjust throttle while turning
if ((roll <= -5) or (roll >= 5)) {
if (me.turnthrottlefix == 0) {
me.throttleinit = throttle0;
me.turnthrottlefix = 1;
}
me.targetthrottle = me.throttleinit + (me.throttleinit * math.sin(math.abs(roll * DEG2RAD)))/2;
if (me.targetthrottle > throttle0) {
throttle0 += 0.001 * fpsfix;
throttle1 += 0.001 * fpsfix;
} elsif (me.targetthrottle < throttle0) {
throttle0 -= 0.001 * fpsfix;
throttle1 -= 0.001 * fpsfix;
}
}
if ((roll > -5) and (roll <= 5) and (me.turnthrottlefix == 1)) {
if (throttle0 <= me.throttleinit - 0.05) {
throttle0 += 0.001 * fpsfix;
throttle1 += 0.001 * fpsfix;
} elsif (throttle0 > me.throttleinit + 0.05) {
throttle0 -= 0.001 * fpsfix;
throttle1 -= 0.001 * fpsfix;
} else me.turnthrottlefix = 0;
}
### Reduce throttle if aircraft is faster than 250 KIAS under 10000 ft
if ((airspeedkt >= 250) and (altitudemsl <= 10000) and (throttle0 != 0) and (throttle1 != 0)) {
throttle0 -= 0.001 * fpsfix;
throttle1 -= 0.001 * fpsfix;
me.throttlefix = 1;
}
if ((me.throttlefix == 1) and (airspeedkt < 245) and (altitudemsl <= 10000) and (throttle0 != 1) and (throttle1 != 1)) {
throttle0 += 0.001 * fpsfix;
throttle1 += 0.001 * fpsfix;
}
### Adjust Throttle to stay under Vne
if ((airspeedkt >= 350) and (altitudemsl > 10000) and (throttle0 != 0) and (throttle1 != 0)) {
throttle0 -= 0.001 * fpsfix;
throttle1 -= 0.001 * fpsfix;
me.throttlefix = 1;
}
if ((me.throttlefix == 1) and (airspeedkt < 340) and (altitudemsl > 10000) and (throttle0 != 1) and (throttle1 != 1)) {
throttle0 += 0.001 * fpsfix;
throttle1 += 0.001 * fpsfix;
}
### Adjust Throttle to keep from stalling
if ((airspeedkt < 125) and (altitudeagl > 250) and (throttle0 != 1) and (throttle1 != 1)) {
throttle0 += 0.001 * fpsfix;
throttle1 += 0.001 * fpsfix;
### Also help by pushing forward on the stick
elevatorout += 0.02;
}
## RUDDER CONTROLS
if (getprop("/controls/fbw/rudder")) {
if ((roll < -5) or (roll > 5)) {
me.targetrudder = aileronout / 2;
if (me.targetrudder < rudderout) rudderout -= 0.015;
if (me.targetrudder > rudderout) rudderout += 0.015;
} }
## YAW DAMPER
if (getprop("/controls/fbw/yaw-damper")) {
if (rudderin > rudderout) rudderout += 0.05 * fpsfix;
if (rudderin < rudderout) rudderout -= 0.05 * fpsfix;
} else {
rudderout = rudderin;
}
# Transmit output signals to surfaces
setprop(fcs~"aileron-fbw-output", aileronout);
setprop(fcs~"elevator-fbw-output", elevatorout);
setprop(fcs~"rudder-fbw-output", rudderout);
setprop("controls/engines/engine[0]/throttle", throttle0);
setprop("controls/engines/engine[1]/throttle", throttle1);
me.throttle = throttle0; # This is to find out if the pilot moved the throttle
} else {
# Transmit input signals directly to surfaces
setprop(fcs~"aileron-fbw-output", aileronin);
setprop(fcs~"elevator-fbw-output", elevatorin);
setprop(fcs~"rudder-fbw-output", rudderin);
}
},
reset : func {
me.loopid += 1;
me._loop_(me.loopid);
},
_loop_ : func(id) {
id == me.loopid or return;
me.update();
settimer(func { me._loop_(id); }, me.UPDATE_INTERVAL);
}
};
fbw.init();
print("Fly-By-Wire ......... Initialized");
# *Power-by-wire : corresponds to power steering in cars
-set.xml
Call the Nasal file in your aircraft's -set.xml file by adding the following code inside <nasal></nasal>
<fbw>
<file>Aircraft/787-8/Nasal/fbw.nas</file>
</fbw>
Replace '787-8' with your aircraft folder name.
JSBSim FDM
Replace 'aileron-cmd-norm' with 'aileron-fbw-output', replace 'elevator-cmd-norm' with 'elevator-fbw-output' and finally, replace 'rudder-cmd-norm' with 'rudder-fbw-output'.
For example,
<summer name="Pitch Trim Sum">
<input>fcs/elevator-cmd-norm</input>
<input>fcs/pitch-trim-cmd-norm</input>
<clipto>
<min> -1 </min>
<max> 1 </max>
</clipto>
</summer>
was changed to
<summer name="Pitch Trim Sum">
<input>fcs/elevator-fbw-output</input>
<input>fcs/pitch-trim-cmd-norm</input>
<clipto>
<min> -1 </min>
<max> 1 </max>
</clipto>
</summer>
Configuration
Add the following switches anywhere (preferably the CDU) to be able to configure the CDU in FlightGear:
- /controls/fbw/active (controls whether the fly-by-wire system is active or not)
- /controls/fbw/yaw-damper (controls the yaw damper)
- /controls/fbw/rudder (controls whether the fly-by-wire controls rudder or not)
- /controls/fbw/bank-limit (controls the aircraft's bank limit)
If you don't want any of the configurations to be edited in Flightgear, but configured generally, add the following under </sim> to your -set.xml for the appropriate properties you want to configure:
<controls>
<fbw>
<yaw-damper type="int">1</yawdamper>
<rudder type="int">1</rudder>
<bank-limit type="int">30</bank-limit> <!-- 30 degrees bank permitted -->
</fbw>
</controls>