Howto:Implement a Fly-By-Wire System for Airliners: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
(43 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{Stub}}
{{Stub}}


Objective: Implement a [[Nasal]] based fly-by-wire system for Airliners with fly-by-wire and set configuration properties to get desired results.
Objective: Implement a [[Nasal]] and PID Controller based fly-by-wire system for Airliners with fly-by-wire and set configuration properties to get desired results.


== Background ==
== Background ==
Line 10: Line 10:
* -set.xml
* -set.xml
* fbw.nas
* fbw.nas
* FDM xml file
* fly-by-wire.xml
* FDM xml


Note that as the new 787 uses JSBSim, this tutorial will focus on implementing the FBW to an aircraft running with JSBSim.
Note that as the new 787 uses JSBSim and the FBW has only been tested on JSBSim, but you can also use this on YASim FDMs.
 
== B787-8 Fly-By-Wire ==
 
Another feature in the Boeing 787-8's Fly By Wire is the automatic control of the trailing edge camber which affects lift and drag. The fly-by-wire automatically adjusts that to get maximum efficiency. Redneck and I am working on that but it's not included in the below nasal code as I don't think any other aircraft has that. And ofcourse, it's not yet completed. The rest of the Fly-By-Wire is stable at the moment and has been tested on the new Boeing 787-8.
== Note for Airbus Aircraft Developers ==
About Airbus Fly By Wire : [http://www.apollosoftware.com/products/FlyByWire/FlyByWire_english.pdf]
I've just seen that the airbus Fly By Wire works slightly different. I'll create a new fly-by-wire / modify this one to have it function that way. In Airbus Aircraft, moving the control stick controls angular acceleration, and not angular velocity. That is, when you move the stick to the left, the plane starts turning left, when you bring the stick back to the center, the fly-by-wire asks the plane to maintain that bank angle. It's the same case for pitch, and this is not available in Boeing's Fly-By-Wire.
 
Additional information on Airbus FBW systems can be found [http://www.smartcockpit.com/data/pdfs/plane/airbus/A320/misc/A320_Flight_Deck_and_Systems_Briefing_For_Pilots.pdf in this PDF (section 5.9/p.57+)].
 
== FBW differences between Airbus and Boeing aircraft ==
There's an interesting discussion available here [http://www.askcaptainlim.com/-airplanes-aviation-39/631-how-does-the-fly-by-wire-in-boeing-777-differs-from-the-airbus-system.html].
 
The concept of FBW laws is explained here [http://www.ahrtp.com/RSS-JSfeeds/Fly_By_Wire.htm].


== Functions ==
== Functions ==
Line 22: Line 40:
* 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
* Automatically holds the current aircraft pitch and roll degree when stick is brought back to the center! < NEW!
* ALPHA PROTECTION doesn't allow the plane to pitch up higher than 20 degrees < NEW!




Line 38: Line 61:
<syntaxhighlight lang="php">
<syntaxhighlight lang="php">
#########################################
#########################################
## FLY BY WIRE SYSTEM FOR FLIGHTGEAR  ##
## FLY BY WIRE SYSTEM FOR BOEING 787-8 ##
#########################################
#########################################
## Designed by Omega Pilot and Redneck ##
## Designed by Omega, Hooray & Redneck ##
#########################################
#########################################


Line 47: Line 70:
var RAD2DEG = 57.2957795;
var RAD2DEG = 57.2957795;
var DEG2RAD = 0.0174532925;
var DEG2RAD = 0.0174532925;
var INCREMENT = 0.001;
# helpers:
##
# a wrapper to determine if a value is within a certain range
# usage:in_range(1,[min,max] );
# e.g.: in_range(1, [-1,+1] );
#
var in_range = func(value, range) {
var min=range[0];
var max=range[1];
return ((value <= min) and (value >= max));
}


var fbw = {
var fbw = {
init : func {  
init : func {  
         me.UPDATE_INTERVAL = 0.001;  
         me.UPDATE_INTERVAL = INCREMENT;  
         me.loopid = 0;  
         me.loopid = 0;  
me.throttle = 0;
me.throttle = 0;
me.throttlefix = 0;
me.throttlefix = 0;
me.vsinit = 0;
me.throttleinit = 0;
me.throttleinit = 0;
me.targetthrottle = 0;
me.targetthrottle = 0;
Line 62: Line 98:
me.targetrudder = 0;
me.targetrudder = 0;
me.adjustelevators = 0;
me.adjustelevators = 0;
me.stabilize = 0;
me.stabpitch = 0;
me.stabroll = 0;


me.disconnectannounce = 0;
me.disconnectannounce = 0;
# use a vector of throttles, this can be later on used to support more than
# just two engines
me.throttles = [nil,nil];


## Initialize with FBW Activated
## Initialize with FBW Activated
Line 86: Line 130:
## Fix Damp Rate according to Framerate
## Fix Damp Rate according to Framerate


if (getprop("/sim/frame-rate") != nil) var fpsfix = 25 / getprop("/sim/frame-rate");
me.fpsfix = 1;
else fpsfix = 1;
if (getprop("/sim/frame-rate") != nil) me.fpsfix = 25 / getprop("/sim/frame-rate");


## Bank Limit Setting
## Bank Limit Setting


var banklimit = getprop("/controls/fbw/bank-limit");
me.banklimit = getprop("/controls/fbw/bank-limit");


## Position and Orientation
## Position and Orientation


var altitudeagl = getprop("/position/altitude-agl-ft");
me.altitudeagl = getprop("/position/altitude-agl-ft");


var altitudemsl = getprop("/position/altitude-ft");
var altitudemsl = getprop("/position/altitude-ft");


var pitch = getprop("/orientation/pitch-deg");
var pitch = getprop("/orientation/pitch-deg");
var roll = getprop("/orientation/roll-deg");
me.roll = getprop("/orientation/roll-deg");


var airspeedkt = getprop("/velocities/airspeed-kt");
var airspeedkt = getprop("/velocities/airspeed-kt");


var vsfps = getprop("/velocities/vertical-speed-fps");
## Flight Control System Properties


## Flight Control System Properties
var elevtrim = getprop("/controls/flight/elevator-trim");
var ailtrim = getprop("/controls/flight/aileron-trim");


var aileronin = getprop(fcs~"aileron-cmd-norm");
me.aileronin = getprop(fcs~"aileron-cmd-norm");
var elevatorin =  getprop(fcs~"elevator-cmd-norm");
me.elevatorin =  getprop(fcs~"elevator-cmd-norm");
var rudderin = getprop(fcs~"rudder-cmd-norm");
me.rudderin = getprop(fcs~"rudder-cmd-norm");


## FBW Output (actual surface positions)
## FBW Output (actual surface positions)


var aileronout = getprop(fcs~"aileron-fbw-output");
me.aileronout = getprop(fcs~"aileron-fbw-output");
var elevatorout =  getprop(fcs~"elevator-fbw-output");
me.elevatorout =  getprop(fcs~"elevator-fbw-output");
var rudderout = getprop(fcs~"rudder-fbw-output");
me.rudderout = getprop(fcs~"rudder-fbw-output");


## Engine Throttle Positions
## Engine Throttle Positions


throttle0 = getprop("controls/engines/engine[0]/throttle");
var engines = props.globals.getNode("controls/engines").getChildren("engine");
throttle1 = getprop("controls/engines/engine[1]/throttle");
forindex(var index; engines ) {
me.throttles[index] = engines.getNode("engine",index).getNode("throttle").getValue() );
}


## This is where the FBW actually does it's job ;)
me.throttles[0] = getprop("[0]/throttle");
me.throttles[1] = getprop("controls/engines/engine[1]/throttle");


### 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
## This is where the FBW actually does its job ;)


if (getprop("/systems/electrical/outputs/efis") != nil) {
me.check_if_active();
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;
} } }


if (getprop("/controls/fbw/active")) {
if (getprop("/controls/fbw/active")) {
Line 141: Line 182:
me.disconnectannounce = 0;
me.disconnectannounce = 0;


## AILERON CONTROLS
me.update_ailerons();
me.update_elevator();


### Set Aileron Direction and Roll Direction
## ALPHA PROTECTION


if (roll < 0) var rolldir = -1;
if (pitch > 20) {
if (roll > 0) var rolldir = 1;
setprop("/controls/fbw/alpha-protect", 1);
if (roll == 0) var rolldir = 0;
setprop("/controls/fbw/alpha-limit", 22);
} elsif (pitch < -20) {
setprop("/controls/fbw/alpha-protect", 1);
setprop("/controls/fbw/alpha-limit", -22);
} else setprop("/controls/fbw/alpha-protect", 0);


if (aileronin < 0) var ailerondir = -1;
## PROTECTION END TRIM FIX
if (aileronin > 0) var ailerondir = 1;
if (aileronin == 0) var ailerondir = 0;


if (((roll <= banklimit) and (roll >= -banklimit)) or (rolldir != ailerondir)) {
if ((getprop("/controls/fbw/alpha-limit") == 0) and (getprop("/controls/fbw/autostable") == 0) and (getprop("/autopilot/locks/altitude") == "")) {
 
if (getprop("/controls/flight/elevator-trim") < 0) setprop("/controls/flight/elevator-trim", getprop("/controls/flight/elevator-trim") + 0.03);
if (aileronin > aileronout) aileronout += 0.05 * fpsfix;
if (getprop("/controls/flight/elevator-trim") > 0) setprop("/controls/flight/elevator-trim", getprop("/controls/flight/elevator-trim") - 0.03);
 
}
if (aileronin < aileronout) aileronout -= 0.05 * fpsfix;
 
} else {


### Don't let the plane bank past the bank limit
## AUTO-STABILIZATION


if (roll < -banklimit) me.targetaileron = -(roll + banklimit) * 0.025;
### Get the aircraft to maintain pitch and roll when stick is at the center
if (roll > banklimit) me.targetaileron = -(roll - banklimit) * 0.025;


if (aileronout < me.targetaileron) aileronout += 0.025 * fpsfix;
if ( in_range(me.elevatorin, [-0.1,0.1]) and in_range(me.aileronin, [-0.1,0.1]) ) {
if (aileronout > me.targetaileron) aileronout -= 0.025 * fpsfix;


if (me.stabilize == 0) {
setprop("/controls/fbw/stabpitch-deg", pitch);
setprop("/controls/fbw/stabroll-deg", me.roll);
me.stabilize = 1;
}
}


## ELEVATOR CONTROLS
if ((airspeedkt >= 220) and (me.altitudeagl >= 3500)) {
 
setprop("/controls/fbw/autostable", 1);
if (elevatorin > elevatorout) elevatorout += 0.05 * fpsfix;
} else {
 
setprop("/controls/fbw/autostable", 0);
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 {
} else {
if (me.adjustelevators == 1) {
me.stabilize = 0;
 
setprop("/controls/fbw/autostable", 0);
if (vsfps < me.vsinit) elevatorout -= 0.02;
elsif (vsfps > me.vsinit) elevatorout += 0.02;
else me.adjustelevators = 0;
 
}
}
}
## THROTTLE CONTROLS
## THROTTLE CONTROLS


### Disconnect Throttle fix if manually overridden
### Disconnect Throttle fix if manually overridden


if (throttle0 != me.throttle) {
if (me.throttles[0] != me.throttle) {
me.throttlefix = 0;
me.throttlefix = 0;
me.turnthrottlefix = 0;
me.turnthrottlefix = 0;
Line 214: Line 236:
### Adjust throttle while turning
### Adjust throttle while turning


if ((roll <= -5) or (roll >= 5)) {
if ((me.roll <= -5) or (me.roll >= 5)) {


if (me.turnthrottlefix == 0) {
if (me.turnthrottlefix == 0) {
me.throttleinit = throttle0;
me.throttleinit = me.throttles[0];
me.turnthrottlefix = 1;
me.turnthrottlefix = 1;
}
}


me.targetthrottle = me.throttleinit + (me.throttleinit * math.sin(math.abs(roll * DEG2RAD)))/2;
me.targetthrottle = me.throttleinit + (me.throttleinit * math.sin(math.abs(me.roll * DEG2RAD)))/2;


if (me.targetthrottle > throttle0) {
if (me.targetthrottle > me.throttles[0]) {
throttle0 += 0.001 * fpsfix;
me.inc_throttles();
throttle1 += 0.001 * fpsfix;
} elsif (me.targetthrottle < me.throttles[0]) {
} elsif (me.targetthrottle < throttle0) {
me.dec_throttles();}  
throttle0 -= 0.001 * fpsfix;
throttle1 -= 0.001 * fpsfix;
}  


}
}


if ((roll > -5) and (roll <= 5) and (me.turnthrottlefix == 1)) {
if ( in_range(me.roll,[-5,5]) and (me.turnthrottlefix == 1) ) {


if (throttle0 <= me.throttleinit - 0.05) {
 
throttle0 += 0.001 * fpsfix;
if (me.throttles[0] <= me.throttleinit - 0.05) {
throttle1 += 0.001 * fpsfix;
me.inc_throttles();
} elsif (throttle0 > me.throttleinit + 0.05) {
} elsif (me.throttles[0] > me.throttleinit + 0.05) {
throttle0 -= 0.001 * fpsfix;
me.dec_throttles();
throttle1 -= 0.001 * fpsfix;
} else me.turnthrottlefix = 0;
} else me.turnthrottlefix = 0;
}
}
Line 246: Line 264:
### Reduce throttle if aircraft is faster than 250 KIAS under 10000 ft
### Reduce throttle if aircraft is faster than 250 KIAS under 10000 ft


if ((airspeedkt >= 250) and (altitudemsl <= 10000) and (throttle0 != 0) and (throttle1 != 0)) {
if ((airspeedkt >= 250) and (altitudemsl <= 10000) and me.throttles_not_idle() ) {
throttle0 -= 0.001 * fpsfix;
me.dec_throttles();
throttle1 -= 0.001 * fpsfix;
me.throttlefix = 1;
me.throttlefix = 1;
}
}


if ((me.throttlefix == 1) and (airspeedkt < 245) and (altitudemsl <= 10000) and (throttle0 != 1) and (throttle1 != 1)) {
if ((me.throttlefix == 1) and (airspeedkt < 245) and (altitudemsl <= 10000) and me.throttles_not_maxed() ) {
throttle0 += 0.001 * fpsfix;
me.inc_throttles();
throttle1 += 0.001 * fpsfix;
}
}


### Adjust Throttle to stay under Vne
### Adjust Throttle to stay under Vne


if ((airspeedkt >= 350) and (altitudemsl > 10000) and (throttle0 != 0) and (throttle1 != 0)) {
if ((airspeedkt >= 350) and (altitudemsl > 10000) and me.throttles_not_idle() ) {
throttle0 -= 0.001 * fpsfix;
me.dec_throttles();
throttle1 -= 0.001 * fpsfix;
me.throttlefix = 1;
me.throttlefix = 1;
}
}


if ((me.throttlefix == 1) and (airspeedkt < 340) and (altitudemsl > 10000) and (throttle0 != 1) and (throttle1 != 1)) {
if ((me.throttlefix == 1) and (airspeedkt < 340) and (altitudemsl > 10000) and me.throttles_not_maxed() ) {
throttle0 += 0.001 * fpsfix;
me.inc_throttles();
throttle1 += 0.001 * fpsfix;
}
}


### Adjust Throttle to keep from stalling
### Adjust Throttle to keep from stalling


if ((airspeedkt < 125) and (altitudeagl > 250) and (throttle0 != 1) and (throttle1 != 1)) {
if ((airspeedkt < 125) and (me.altitudeagl > 250) and me.throttles_not_maxed() ) {
throttle0 += 0.001 * fpsfix;
me.inc_throttles();
throttle1 += 0.001 * fpsfix;


### Also help by pushing forward on the stick
### Also help by pushing forward on the stick


elevatorout += 0.02;
me.elevatorout += 0.02;


}
}
Line 286: Line 299:
if (getprop("/controls/fbw/rudder")) {
if (getprop("/controls/fbw/rudder")) {


if ((roll < -5) or (roll > 5)) {
if ((me.roll < -5) or (me.roll > 5)) {
me.targetrudder = aileronout / 2;
me.targetrudder = me.aileronout / 2;


if (me.targetrudder < rudderout) rudderout -= 0.015;
if (me.targetrudder < me.rudderout) me.rudderout -= 0.015;
if (me.targetrudder > rudderout) rudderout += 0.015;
if (me.targetrudder > me.rudderout) me.rudderout += 0.015;


} }
} }


## YAW DAMPER
me.update_yaw_damper();


if (getprop("/controls/fbw/yaw-damper")) {
# Transmit output signals to surfaces


if (rudderin > rudderout) rudderout += 0.05 * fpsfix;
setprop(fcs~"aileron-fbw-output", me.aileronout);
setprop(fcs~"elevator-fbw-output", me.elevatorout);
setprop(fcs~"rudder-fbw-output", me.rudderout);


if (rudderin < rudderout) rudderout -= 0.05 * fpsfix;
setprop("controls/engines/engine[0]/throttle", me.throttles[0]);
setprop("controls/engines/engine[1]/throttle", me.throttles[1]);
 
me.throttle = me.throttles[0]; # This is to find out if the pilot moved the throttle


} else {
} else {


rudderout = rudderin;
# Transmit input signals directly to surfaces
 
setprop(fcs~"aileron-fbw-output", me.aileronin);
setprop(fcs~"elevator-fbw-output", me.elevatorin);
setprop(fcs~"rudder-fbw-output", me.rudderin);


}
}


# Transmit output signals to surfaces
},
    update_elevator: func {
    ## ELEVATOR CONTROLS
    if (me.elevatorin > me.elevatorout) me.elevatorout += 0.05 * me.fpsfix;
    if (me.elevatorin < me.elevatorout) me.elevatorout -= 0.05 * me.fpsfix;
    if ((me.elevatorin - me.elevatorout < 0.05) and (me.elevatorin - me.elevatorout > 0)) me.elevatorout += 0.01;
    if ((me.elevatorout - me.elevatorin < 0.05) and (me.elevatorin - me.elevatorout < 0)) me.elevatorout -= 0.01;
},
 
    update_yaw_damper: func {
    ## YAW DAMPER
 
    if (getprop("/controls/fbw/yaw-damper")) {
 
    if (me.rudderin > me.rudderout) me.rudderout += 0.05 * me.fpsfix;
 
    if (me.rudderin < me.rudderout) me.rudderout -= 0.05 * me.fpsfix;
 
    } else {
 
    me.rudderout = me.rudderin;
 
    }
 
},
    update_ailerons: func {
      ## AILERON CONTROLS
 
      ### Set Aileron Direction and Roll Direction
 
      me.rolldir = 0;
      if (me.roll < 0) me.rolldir = -1;
      if (me.roll > 0) me.rolldir = 1;
 
 
      me.ailerondir = 0;
      if (me.aileronin < 0) me.ailerondir = -1;
      if (me.aileronin > 0) me.ailerondir = 1;
 
 
      if ( in_range(me.roll,[-me.banklimit,me.banklimit]) or (me.rolldir != me.ailerondir)) {
 
 
      if (me.aileronin > me.aileronout) me.aileronout += 0.05 * me.fpsfix;
 
      if (me.aileronin < me.aileronout) me.aileronout -= 0.05 * me.fpsfix;
 
      } else {
 
      ### Don't let the plane bank past the bank limit


setprop(fcs~"aileron-fbw-output", aileronout);
      if (me.roll < -me.banklimit) me.targetaileron = -(me.roll + me.banklimit) * 0.025;
setprop(fcs~"elevator-fbw-output", elevatorout);
      if (me.roll > me.banklimit) me.targetaileron = -(me.roll - me.banklimit) * 0.025;
setprop(fcs~"rudder-fbw-output", rudderout);


setprop("controls/engines/engine[0]/throttle", throttle0);
      if (me.aileronout < me.targetaileron) me.aileronout += 0.025 * me.fpsfix;
setprop("controls/engines/engine[1]/throttle", throttle1);
      if (me.aileronout > me.targetaileron) me.aileronout -= 0.025 * me.fpsfix;


me.throttle = throttle0; # This is to find out if the pilot moved the throttle
      }


} else {
},
    check_if_active : func {
### 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)


# Transmit input signals directly to surfaces
## Turn on Fly By Wire only if we have power


setprop(fcs~"aileron-fbw-output", aileronin);
if (getprop("/systems/electrical/outputs/efis") != nil) {
setprop(fcs~"elevator-fbw-output", elevatorin);
  if ((getprop("/systems/electrical/outputs/efis") < 9) and (me.altitudeagl >= 200)) {
setprop(fcs~"rudder-fbw-output", rudderin);
  setprop("/controls/fbw/active", 0);
  if (me.disconnectannounce == 0) {
    screen.log.write("Fly By Wire Disconnected!", 1, 0, 0);
    me.disconnectannounce = 1;
  }
}
}
},


}
inc_throttles: func {
forindex(var t; me.throttles)
  me.throttles[t] += INCREMENT * me.fpsfix;
},


dec_throttles:func {
forindex(var t; me.throttles)
  me.throttles[t] -= INCREMENT * me.fpsfix;
},
throttles_not_idle: func {
  foreach(var t; me.throttles) {
      if (me.throttles[t] == 0) return 0; # at least one throttle is idle
    }
return 1; # throttles are idle
},
throttles_not_maxed: func {
  foreach(var t; me.throttles) {
      if (me.throttles[t] == 1) return 0; # at least one throttle is maxed
    }
return 1; # throttles are not maxed
},
},
     reset : func {
     reset : func {
Line 347: Line 444:
# *Power-by-wire : corresponds to power steering in cars
# *Power-by-wire : corresponds to power steering in cars
</syntaxhighlight>
</syntaxhighlight>
=== fly-by-wire.xml ===
Open your aircraft's Systems directory and create a file called 'fly-by-wire.xml'. Copy and paste the following pid controller code in that file and save it.
<syntaxhighlight lang="xml">
<?xml version="1.0"?>
<!-- Fly By Wire, Auto Stabilize Pitch and Roll -->
<PropertyList>
<!-- =============================================================== -->
<!-- Roll Deg Hold -->
<!-- =============================================================== -->
<pid-controller>
<name>FBW Roll Deg Hold</name>
<debug type="bool">false</debug>
<enable>
<prop>/controls/fbw/autostable</prop>
</enable>
<input>
<prop>/orientation/roll-deg</prop>
</input>
<reference>
<prop>/controls/fbw/stabroll-deg</prop>
</reference>
<output>
<prop>/controls/flight/aileron-trim</prop>
</output>
<config>
<Kp>0.05</Kp>
<beta>1</beta>
<alpha>0.1</alpha>
<gamma>0</gamma>
<Ti>10</Ti>
<Td>0.00001</Td>
<u_min>-2</u_min>
<u_max>2</u_max>
</config>
</pid-controller>
<!-- =============================================================== -->
<!-- Pitch Hold -->
<!-- =============================================================== -->
<pid-controller>
    <name>Pitch hold</name>
    <debug>false</debug>
    <enable>
<prop>/controls/fbw/autostable</prop>
    </enable>
    <input>
    <prop>/orientation/pitch-deg</prop>
  </input>
  <reference>
    <prop>/controls/fbw/stabpitch-deg</prop>
  </reference>
  <output>
    <prop>/controls/flight/elevator-trim</prop>
  </output>
  <config>
    <Kp>-0.05</Kp>      <!-- proportional gain -->
    <beta>1.0</beta>    <!-- input value weighing factor -->
    <alpha>0.1</alpha>  <!-- low pass filter weighing factor -->
    <gamma>0.0</gamma>  <!-- input value weighing factor for -->
                        <!-- unfiltered derivative error -->
    <Ti>1.0</Ti>        <!-- integrator time -->
    <Td>0.00001</Td>    <!-- derivator time -->
    <u_min>-1.0</u_min> <!-- minimum output clamp -->
    <u_max>1.0</u_max>  <!-- maximum output clamp -->
  </config>
  </pid-controller>
<!-- =============================================================== -->
<!-- Alpha Protection -->
<!-- =============================================================== -->
<pid-controller>
    <name>Alpha Protection</name>
    <debug>false</debug>
    <enable>
<prop>/controls/fbw/alpha-protect</prop>
    </enable>
    <input>
    <prop>/orientation/pitch-deg</prop>
  </input>
  <reference>
    <prop>/controls/fbw/alpha-limit</prop>
  </reference>
  <output>
    <prop>/controls/flight/elevator-trim</prop>
  </output>
  <config>
    <Kp>-0.05</Kp>      <!-- proportional gain -->
    <beta>1.0</beta>    <!-- input value weighing factor -->
    <alpha>0.1</alpha>  <!-- low pass filter weighing factor -->
    <gamma>0.0</gamma>  <!-- input value weighing factor for -->
                        <!-- unfiltered derivative error -->
    <Ti>1.0</Ti>        <!-- integrator time -->
    <Td>0.00001</Td>    <!-- derivator time -->
    <u_min>-1.0</u_min> <!-- minimum output clamp -->
    <u_max>1.0</u_max>  <!-- maximum output clamp -->
  </config>
  </pid-controller>
</PropertyList>
</syntaxhighlight>
Note that the above PID Controller is configured for smooth control of the 787-8. You migh thave to tweak the values inside the <config> .. </config> tags to get to smooth with your aircraft.


=== -set.xml ===
=== -set.xml ===
Line 358: Line 608:
</syntaxhighlight>
</syntaxhighlight>


Replace '787-8' with your aircraft folder name.
And also add the following code under </sim>
 
<syntaxhighlight lang="xml">
<systems>
<autopilot>
<path>Aircraft/787-8/Systems/fly-by-wire.xml</path>
</autopilot>
</systems>
</syntaxhighlight>
 
Replace '787-8' with your aircraft folder name in both instances.


=== JSBSim FDM ===
=== JSBSim FDM ===


Replace 'aileron-cmd-norm' with 'aileron-fbw-output'.
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'.
Replace 'elevator-cmd-norm' with 'elevator-fbw-output'.
 
Finally, replace 'rudder-cmd-norm' with 'rudder-fbw-output'.
For example,
 
<syntaxhighlight lang="xml">
  <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>
</syntaxhighlight>
 
was changed to
 
<syntaxhighlight lang="xml">
  <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>
</syntaxhighlight>
 
== YASim FDM ==
 
Replace '/controls/flight/aileron' with '/fdm/jsbsim/fcs/aileron-fbw-output', replace '/controls/flight/elevator' with '/fdm/jsbsim/fcs/elevator-fbw-output' and finally, replace '/controls/flight/rudder' with '/fdm/jsbsim/fcs/rudder-fbw-output'.
 
For Example, change
 
<syntaxhighlight lang="xml">
<control-input axis="/controls/flight/aileron" control="FLAP1" split="true"/>
</syntaxhighlight>
 
with this
 
<syntaxhighlight lang="xml">
<control-input axis="/fdm/jsbsim/fcs/aileron-fbw-output" control="FLAP1" split="true"/>
</syntaxhighlight>
 
You may be wondering why a /fdm/jsbsim/fcs property is called in a YASim FDM, so here's the answer. That is JUST THE PROPERTY, it has nothing to do with the FDM. As the FBW was built for a JSBSim aircraft, the properties were simply placed there.


== Configuration ==
== Configuration ==
Line 386: Line 688:
</syntaxhighlight>
</syntaxhighlight>


== Related content ==
* [[Autopilot configuration reference]]
* [[Autopilot PID controller tuning resources]]
* [[Howto:Design an autopilot]]


[[Category:Howto]]
[[Category:Aircraft enhancement]]
[[Category:Nasal]]
[[Category:Nasal howto]]

Revision as of 14:22, 8 March 2016

This article is a stub. You can help the wiki by expanding it.

Objective: Implement a Nasal and PID Controller 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
  • fly-by-wire.xml
  • FDM xml

Note that as the new 787 uses JSBSim and the FBW has only been tested on JSBSim, but you can also use this on YASim FDMs.

B787-8 Fly-By-Wire

Another feature in the Boeing 787-8's Fly By Wire is the automatic control of the trailing edge camber which affects lift and drag. The fly-by-wire automatically adjusts that to get maximum efficiency. Redneck and I am working on that but it's not included in the below nasal code as I don't think any other aircraft has that. And ofcourse, it's not yet completed. The rest of the Fly-By-Wire is stable at the moment and has been tested on the new Boeing 787-8.

Note for Airbus Aircraft Developers

About Airbus Fly By Wire : [1]

I've just seen that the airbus Fly By Wire works slightly different. I'll create a new fly-by-wire / modify this one to have it function that way. In Airbus Aircraft, moving the control stick controls angular acceleration, and not angular velocity. That is, when you move the stick to the left, the plane starts turning left, when you bring the stick back to the center, the fly-by-wire asks the plane to maintain that bank angle. It's the same case for pitch, and this is not available in Boeing's Fly-By-Wire.

Additional information on Airbus FBW systems can be found in this PDF (section 5.9/p.57+).

FBW differences between Airbus and Boeing aircraft

There's an interesting discussion available here [2].

The concept of FBW laws is explained here [3].

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
  • Automatically holds the current aircraft pitch and roll degree when stick is brought back to the center! < NEW!
  • ALPHA PROTECTION doesn't allow the plane to pitch up higher than 20 degrees < NEW!


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 BOEING 787-8 ##
#########################################
## Designed by Omega, Hooray & Redneck ##
#########################################

# CONSTANTS

var RAD2DEG = 57.2957795;
var DEG2RAD = 0.0174532925;
var INCREMENT = 0.001;

# helpers:

##
# a wrapper to determine if a value is within a certain range
# usage:in_range(1,[min,max] );
# e.g.: in_range(1, [-1,+1] );
#
var in_range = func(value, range) {
 var min=range[0];
 var max=range[1];
 return ((value <= min) and (value >= max));
}

var fbw = {
	init : func { 
        me.UPDATE_INTERVAL = INCREMENT; 
        me.loopid = 0; 
		me.throttle = 0;
		me.throttlefix = 0;
		me.throttleinit = 0;
		me.targetthrottle = 0;
		me.turnthrottlefix = 0;
		me.targetaileron = 0;
		me.targetelevator = 0;
		me.targetrudder = 0;
		me.adjustelevators = 0;
		me.stabilize = 0;

		me.stabpitch = 0;
		me.stabroll = 0;

		me.disconnectannounce = 0;
		# use a vector of throttles, this can be later on used to support more than
		# just two engines
		me.throttles = [nil,nil]; 


## 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

me.fpsfix = 1;
if (getprop("/sim/frame-rate") != nil) me.fpsfix = 25 / getprop("/sim/frame-rate");

## Bank Limit Setting

me.banklimit = getprop("/controls/fbw/bank-limit");

## Position and Orientation

me.altitudeagl = getprop("/position/altitude-agl-ft");

var altitudemsl = getprop("/position/altitude-ft");

var pitch = getprop("/orientation/pitch-deg");
me.roll = getprop("/orientation/roll-deg");

var airspeedkt = getprop("/velocities/airspeed-kt");

## Flight Control System Properties

var elevtrim = getprop("/controls/flight/elevator-trim");
var ailtrim = getprop("/controls/flight/aileron-trim");

me.aileronin = getprop(fcs~"aileron-cmd-norm");
me.elevatorin =  getprop(fcs~"elevator-cmd-norm");
me.rudderin = getprop(fcs~"rudder-cmd-norm");

## FBW Output (actual surface positions)

me.aileronout = getprop(fcs~"aileron-fbw-output");
me.elevatorout =  getprop(fcs~"elevator-fbw-output");
me.rudderout = getprop(fcs~"rudder-fbw-output");

## Engine Throttle Positions

var engines = props.globals.getNode("controls/engines").getChildren("engine");
forindex(var index; engines ) {
me.throttles[index] = engines.getNode("engine",index).getNode("throttle").getValue() );
}

me.throttles[0] = getprop("[0]/throttle");
me.throttles[1] = getprop("controls/engines/engine[1]/throttle");


## This is where the FBW actually does its job ;)

me.check_if_active();

if (getprop("/controls/fbw/active")) {

me.disconnectannounce = 0;

me.update_ailerons();
me.update_elevator();

## ALPHA PROTECTION

if (pitch > 20) {
setprop("/controls/fbw/alpha-protect", 1);
setprop("/controls/fbw/alpha-limit", 22);
} elsif (pitch < -20) {
setprop("/controls/fbw/alpha-protect", 1);
setprop("/controls/fbw/alpha-limit", -22);
} else setprop("/controls/fbw/alpha-protect", 0);

## PROTECTION END TRIM FIX

if ((getprop("/controls/fbw/alpha-limit") == 0) and (getprop("/controls/fbw/autostable") == 0) and (getprop("/autopilot/locks/altitude") == "")) {
if (getprop("/controls/flight/elevator-trim") < 0) setprop("/controls/flight/elevator-trim", getprop("/controls/flight/elevator-trim") + 0.03);
if (getprop("/controls/flight/elevator-trim") > 0) setprop("/controls/flight/elevator-trim", getprop("/controls/flight/elevator-trim") - 0.03);
}

## AUTO-STABILIZATION

### Get the aircraft to maintain pitch and roll when stick is at the center

if ( in_range(me.elevatorin, [-0.1,0.1]) and in_range(me.aileronin, [-0.1,0.1]) ) {

if (me.stabilize == 0) {
setprop("/controls/fbw/stabpitch-deg", pitch);
setprop("/controls/fbw/stabroll-deg", me.roll);
me.stabilize = 1;
}

if ((airspeedkt >= 220) and (me.altitudeagl >= 3500)) {
setprop("/controls/fbw/autostable", 1);
} else {
setprop("/controls/fbw/autostable", 0);
}

} else {
me.stabilize = 0;
setprop("/controls/fbw/autostable", 0);
}
## THROTTLE CONTROLS

### Disconnect Throttle fix if manually overridden

if (me.throttles[0] != me.throttle) {
me.throttlefix = 0;
me.turnthrottlefix = 0;
}


### Adjust throttle while turning

if ((me.roll <= -5) or (me.roll >= 5)) {

if (me.turnthrottlefix == 0) {
me.throttleinit = me.throttles[0];
me.turnthrottlefix = 1;
}

me.targetthrottle = me.throttleinit + (me.throttleinit * math.sin(math.abs(me.roll * DEG2RAD)))/2;

if (me.targetthrottle > me.throttles[0]) {
me.inc_throttles();
} elsif (me.targetthrottle < me.throttles[0]) {
me.dec_throttles();} 

}

if ( in_range(me.roll,[-5,5]) and (me.turnthrottlefix == 1) ) {


if (me.throttles[0] <= me.throttleinit - 0.05) {
me.inc_throttles();
} elsif (me.throttles[0] > me.throttleinit + 0.05) {
me.dec_throttles();
} else me.turnthrottlefix = 0;
}

### Reduce throttle if aircraft is faster than 250 KIAS under 10000 ft

if ((airspeedkt >= 250) and (altitudemsl <= 10000) and me.throttles_not_idle() ) {
me.dec_throttles();
me.throttlefix = 1;
}

if ((me.throttlefix == 1) and (airspeedkt < 245) and (altitudemsl <= 10000) and me.throttles_not_maxed() ) {
me.inc_throttles();
}

### Adjust Throttle to stay under Vne

if ((airspeedkt >= 350) and (altitudemsl > 10000) and me.throttles_not_idle() ) {
me.dec_throttles();
me.throttlefix = 1;
}

if ((me.throttlefix == 1) and (airspeedkt < 340) and (altitudemsl > 10000) and me.throttles_not_maxed() ) {
me.inc_throttles();
}

### Adjust Throttle to keep from stalling

if ((airspeedkt < 125) and (me.altitudeagl > 250) and me.throttles_not_maxed() ) {
me.inc_throttles();

### Also help by pushing forward on the stick

me.elevatorout += 0.02;

}

## RUDDER CONTROLS

if (getprop("/controls/fbw/rudder")) {

if ((me.roll < -5) or (me.roll > 5)) {
me.targetrudder = me.aileronout / 2;

if (me.targetrudder < me.rudderout) me.rudderout -= 0.015;
if (me.targetrudder > me.rudderout) me.rudderout += 0.015;

} }

me.update_yaw_damper();

# Transmit output signals to surfaces

setprop(fcs~"aileron-fbw-output", me.aileronout);
setprop(fcs~"elevator-fbw-output", me.elevatorout);
setprop(fcs~"rudder-fbw-output", me.rudderout);

setprop("controls/engines/engine[0]/throttle", me.throttles[0]);
setprop("controls/engines/engine[1]/throttle", me.throttles[1]);

me.throttle = me.throttles[0]; # This is to find out if the pilot moved the throttle

} else {

# Transmit input signals directly to surfaces

setprop(fcs~"aileron-fbw-output", me.aileronin);
setprop(fcs~"elevator-fbw-output", me.elevatorin);
setprop(fcs~"rudder-fbw-output", me.rudderin);

}

},
    update_elevator: func {
    ## ELEVATOR CONTROLS
    if (me.elevatorin > me.elevatorout) me.elevatorout += 0.05 * me.fpsfix;
    if (me.elevatorin < me.elevatorout) me.elevatorout -= 0.05 * me.fpsfix;
    if ((me.elevatorin - me.elevatorout < 0.05) and (me.elevatorin - me.elevatorout > 0)) me.elevatorout += 0.01; 
    if ((me.elevatorout - me.elevatorin < 0.05) and (me.elevatorin - me.elevatorout < 0)) me.elevatorout -= 0.01; 
},

    update_yaw_damper: func {
    ## YAW DAMPER

    if (getprop("/controls/fbw/yaw-damper")) {

    if (me.rudderin > me.rudderout) me.rudderout += 0.05 * me.fpsfix;

    if (me.rudderin < me.rudderout) me.rudderout -= 0.05 * me.fpsfix;

    } else {

    me.rudderout = me.rudderin;

    }

},
    update_ailerons: func {
      ## AILERON CONTROLS

      ### Set Aileron Direction and Roll Direction

      me.rolldir = 0;
      if (me.roll < 0) me.rolldir = -1;
      if (me.roll > 0) me.rolldir = 1;


      me.ailerondir = 0;
      if (me.aileronin < 0) me.ailerondir = -1;
      if (me.aileronin > 0) me.ailerondir = 1;


      if ( in_range(me.roll,[-me.banklimit,me.banklimit]) or (me.rolldir != me.ailerondir)) {


      if (me.aileronin > me.aileronout) me.aileronout += 0.05 * me.fpsfix;

      if (me.aileronin < me.aileronout) me.aileronout -= 0.05 * me.fpsfix;

      } else {

      ### Don't let the plane bank past the bank limit

      if (me.roll < -me.banklimit) me.targetaileron = -(me.roll + me.banklimit) * 0.025;
      if (me.roll > me.banklimit) me.targetaileron = -(me.roll - me.banklimit) * 0.025;

      if (me.aileronout < me.targetaileron) me.aileronout += 0.025 * me.fpsfix;
      if (me.aileronout > me.targetaileron) me.aileronout -= 0.025 * me.fpsfix;

      }

},
    check_if_active : func {
	### 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 (me.altitudeagl >= 200)) {
	  setprop("/controls/fbw/active", 0);
	  if (me.disconnectannounce == 0) {
	    screen.log.write("Fly By Wire Disconnected!", 1, 0, 0);
	    me.disconnectannounce = 1;
	  }
	}
	}
},

	inc_throttles: func {
	forindex(var t; me.throttles)
	  me.throttles[t] += INCREMENT * me.fpsfix;
},

	dec_throttles:func {
	forindex(var t; me.throttles)
	  me.throttles[t] -= INCREMENT * me.fpsfix;
},
	throttles_not_idle: func {
	  foreach(var t; me.throttles) {
	      if (me.throttles[t] == 0) return 0; # at least one throttle is idle
	    }
	return 1; # throttles are idle
},
	throttles_not_maxed: func {
	  foreach(var t; me.throttles) {
	      if (me.throttles[t] == 1) return 0; # at least one throttle is maxed
	    }
	return 1; # throttles are not maxed
},
    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

fly-by-wire.xml

Open your aircraft's Systems directory and create a file called 'fly-by-wire.xml'. Copy and paste the following pid controller code in that file and save it.

<?xml version="1.0"?>



<!-- Fly By Wire, Auto Stabilize Pitch and Roll -->



<PropertyList>



<!-- =============================================================== -->

<!-- Roll Deg Hold -->

<!-- =============================================================== -->


	<pid-controller>
		<name>FBW Roll Deg Hold</name>
		<debug type="bool">false</debug>
		<enable>
			<prop>/controls/fbw/autostable</prop>
		</enable>
		<input>
			<prop>/orientation/roll-deg</prop>
		</input>
		<reference>
			<prop>/controls/fbw/stabroll-deg</prop>
		</reference>
		<output>
			<prop>/controls/flight/aileron-trim</prop>
		</output>
		<config>
			<Kp>0.05</Kp>
			<beta>1</beta>
			<alpha>0.1</alpha>
			<gamma>0</gamma>
			<Ti>10</Ti>
			<Td>0.00001</Td>
			<u_min>-2</u_min>
			<u_max>2</u_max>
		</config>
	</pid-controller>


<!-- =============================================================== -->

<!-- Pitch Hold -->

<!-- =============================================================== -->

	 <pid-controller>
	    <name>Pitch hold</name>
	    <debug>false</debug>
	    <enable>
			<prop>/controls/fbw/autostable</prop>
	    </enable>
	    <input>
 	     <prop>/orientation/pitch-deg</prop>
 	   </input>
 	   <reference>
 	     <prop>/controls/fbw/stabpitch-deg</prop>
 	   </reference>
 	   <output>
 	     <prop>/controls/flight/elevator-trim</prop>
 	   </output>
 	   <config>
 	     <Kp>-0.05</Kp>      <!-- proportional gain -->
 	     <beta>1.0</beta>    <!-- input value weighing factor -->
 	     <alpha>0.1</alpha>  <!-- low pass filter weighing factor -->
 	     <gamma>0.0</gamma>  <!-- input value weighing factor for -->
 	                         <!-- unfiltered derivative error -->
 	     <Ti>1.0</Ti>        <!-- integrator time -->
 	     <Td>0.00001</Td>    <!-- derivator time -->
 	     <u_min>-1.0</u_min> <!-- minimum output clamp -->
 	     <u_max>1.0</u_max>  <!-- maximum output clamp -->
 	   </config>
 	  </pid-controller>

<!-- =============================================================== -->

<!-- Alpha Protection -->

<!-- =============================================================== -->

	 <pid-controller>

	    <name>Alpha Protection</name>

	    <debug>false</debug>

	    <enable>

			<prop>/controls/fbw/alpha-protect</prop>

	    </enable>

	    <input>

 	     <prop>/orientation/pitch-deg</prop>

 	   </input>

 	   <reference>

 	     <prop>/controls/fbw/alpha-limit</prop>

 	   </reference>

 	   <output>

 	     <prop>/controls/flight/elevator-trim</prop>

 	   </output>

 	   <config>

 	     <Kp>-0.05</Kp>      <!-- proportional gain -->

 	     <beta>1.0</beta>    <!-- input value weighing factor -->

 	     <alpha>0.1</alpha>  <!-- low pass filter weighing factor -->

 	     <gamma>0.0</gamma>  <!-- input value weighing factor for -->

 	                         <!-- unfiltered derivative error -->

 	     <Ti>1.0</Ti>        <!-- integrator time -->

 	     <Td>0.00001</Td>    <!-- derivator time -->

 	     <u_min>-1.0</u_min> <!-- minimum output clamp -->

 	     <u_max>1.0</u_max>  <!-- maximum output clamp -->

 	   </config>

 	  </pid-controller>



</PropertyList>

Note that the above PID Controller is configured for smooth control of the 787-8. You migh thave to tweak the values inside the <config> .. </config> tags to get to smooth with your aircraft.

-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>

And also add the following code under </sim>

	<systems>
		<autopilot>
			<path>Aircraft/787-8/Systems/fly-by-wire.xml</path>
		</autopilot>
	</systems>

Replace '787-8' with your aircraft folder name in both instances.

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>

YASim FDM

Replace '/controls/flight/aileron' with '/fdm/jsbsim/fcs/aileron-fbw-output', replace '/controls/flight/elevator' with '/fdm/jsbsim/fcs/elevator-fbw-output' and finally, replace '/controls/flight/rudder' with '/fdm/jsbsim/fcs/rudder-fbw-output'.

For Example, change

	<control-input axis="/controls/flight/aileron" control="FLAP1" split="true"/>

with this

	<control-input axis="/fdm/jsbsim/fcs/aileron-fbw-output" control="FLAP1" split="true"/>

You may be wondering why a /fdm/jsbsim/fcs property is called in a YASim FDM, so here's the answer. That is JUST THE PROPERTY, it has nothing to do with the FDM. As the FBW was built for a JSBSim aircraft, the properties were simply placed there.

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>

Related content