Howto:Design an autopilot

From FlightGear wiki
Revision as of 06:52, 4 May 2010 by T3r (Talk | contribs) (Add link to reference page)

Jump to: navigation, search
WIP.png Work in progress
This article or section will be worked on in the upcoming hours or days.
See history for the latest developments.

This howto will guide you through the process of designing a stable and fully functional autopilot in FlightGear. We won't cover every single aspect, because there are hunderts of aircraft out there, with dozens of different autopilot configurations. However, after reading this howto, you should be able to be creative and create unexisting configurations. For a technical reference of the used elements, check Autopilot_Configuration_Reference


Why not make things slightly easier, before we start working on the autopilot? Since we will be updating the autopilot hunderts of times, making tiny adjustments each time, we don't want to reload FlightGear every time. Therefore, we assign an "reload autopilot configuration" action to a key on our keyboard. We've chosen Shift-F5, but it could be any key. Add the code to your $FG_ROOT/keyboard.xml file.

<key n="261">
 <repeatable type="bool">true</repeatable>
  <desc>Reload autopilot configuration</desc>
    fgcommand('reinit',{ subsystem: "xml-autopilot" }));
    print("xml-autopilot reinitialized!");

Creating the autopilot configuration file

Create an autopilot.xml file with the following content, somewhere inside your aircraft's root (eg. Aircraft/747-400/Systems/autopilot.xml).

<?xml version="1.0"?>



A first controller: heading hold

Let's start with a simple wing leveler that keeps your bank angle at zero (or any arbitrary value later). This could be a PID controller with the input from your current roll and a reference of zero. Output should go to your aileron-cmd. Start with:

  • Kp: 0.00
  • Ti: 100.00
  • Td: 0.00

Be sure to add an <enable> element to easily enable/disable your controller if it gets unstable.

<pid-controller> <name>Heading hold</name> <debug>false</debug> <enable> <prop>/autopilot/locks/heading</prop> <value>wing-leveler</value> </enable> <input> <prop>/orientation/roll-deg</prop> </input> <reference>0</reference> <output> <prop>/controls/flight/aileron</prop> </output> <config> <Kp>0.00</Kp> <Ti>100.0</Ti>


<u_min>-1.0</u_min> <u_max>1.0</u_max> </config> </pid-controller>

Since this is our first controller, we will give a short explenation of each part.

  • Name: is only used for debugging.
  • Debug:
  • Enable: when the condition is true, the controller will be activated. In this case that will be when /autopilot/locks/heading=wing-leveler.
  • Input: is the property that will be monitored by the controller. As our aim is to have a roll of zero degree, our input property is our roll orientation.
  • Reference: most of the time this is a prop, like input, but for this one we'll use a static value. The controller will try to get the value of the input to this reference-value.
  • Output: the controller controls flight surfaces, in order to get the input value to the reference one.
  • Config: thit is the tricky part. All these settings affect the behaviour (response time, surface deflection etc.) of the controller.
    • u_min: the minimum output.
    • u_max: the maximum output.

To get an idea of the Kp-value, you need to do a simple calclulation. The PID controller computes "reference - input". If you want wings leveled (zero roll) and your current bank angle is, say 30 degrees, the result of "reference - input" is -30. If you think you need full aileron deflection to get from 30 degrees bank to zero in a reasonable time, you need a factor of 1/30=0.033, because aileron-cmd is in the range of -1 to +1. This is the approximate value for your Kp.

Now enter your aircraft and climb to cruise altitude and cruise speed. Bank your aircraft, to 15-20 degrees. Enable your controller (in our example that is by setting /autopilot/locks/heading=wing-leveler. Look here if you need help on setting properties.) and - nothing happens (because Kp is still zero). Set the value of Kp to 0.01 and hit Shift-F5 to reload your configuration and check what happens. Does the controller tries to level the wings or do things go worse? If the ailerons move in the wrong direction, you need to reverse the sign of Kp.

Once you found the correct sign for Kp, slowly (in steps as little as 0.005) increase the value of Kp to get a fast response. Don't forget to reload the config with Shift-F5 after every edit of your file. There is one pitfall that can drive you crazy: the PID-controller computes delta-values for its correction and it starts with the current setting of your aileron when it is enabled or reloaded. If you have your aileron set to -0.2 when you hit Shift-F5, the controller will apply its corrections to a value of -0.2 which will most certainly fail! Make sure that you have your flight controls neutral (press 5) when (re-)enabling PID-controllers!

You will achive good results by only setting Kp for a roll-computer as a wing-leveler. Slowly increase your Kp until it is just about to become unstable, than divide the current value of Kp by two and use that value. Try several configurations of your aircraft - slow flight, low altitude, fast cruise at higher altitudes. Fly turns with several bank angles, left and right. Whenever you enable your controller, it should level your wings within a few seconds and without bouncing back and forth.

Once you are satisfied with Kp - and not before! - decrease the value for Ti by dividing by a value of 10 and check the response of your controller. When it tends to become unstable, multiply it's Ti by two. Now you should have a fast and stable wing-leveler.

Finally (for the wing-leveler) you can turn it into a roll-hold by setting your target roll as a property for the reference. If you had <reference>0.0</reference>, bind your reference to a property (<reference>autopilot/settings/target-roll-deg</reference>) and your aircraft should roll to any value you set in target-roll-deg. The complete wing-leveler will look similar to the one below (though, the config values may vary and the reference might be replaced by a property).

 <name>Heading hold</name>

This is the base for any lateral hold mode, so try to spend some time on it. It can take some hours if this is your first autopilot.

Pitch hold

So, let's move over to a pitch hold. Our goal is to have a controller that maintains an arbitrary pich. The only difference to the wing leveler is that you are only interested in elevator position changes, not absolute values. To put it in other words: increase elevator for more pitch, decrease for less pitch and maintain elevator position if the pitch is right. This calls for a pid-controller, because it only computes offset values. And you will end up with a relative small value for Kp to avoid rapid imediate movements and a relative small value for Ti to have a greater effect over time.

Start again with a no-op pid-controller (Kp=0, Ti=100.000 and Td=0). Input is your current pitch (/orientation/pitch-deg), reference is a target-pitch property (you wouldn't want a constant zero here, /autopilot/settings/target-pitch-deg) and output goes to elevator-cmd (/autopilot/internal/elevator-cmd).

Again increase you Kp slowly and check each step in FlightGear. I'd expect something like -0.025 resulting in an instanteanous full elevator deflection on a 40 degrees pitch offset. Do not try too large values; it results in instable behaviour very quickly. Now, decrease the value of Ti, probably down to 10 or so. The smaller it gets, the faster the elevator moves and the easier it is to get an unstable loop. Again it is very likely that you won't need Td to get a stable controller.

Simulating servos

Once you have this two axies running, you might want to think about simulating the slow movements of the servo or hydraulic motors that drive the control surfaces. You might have noticed, that the controls react rapidly when enabled which is unrealistic. In a Seneca, the aileron servo takes 12 seconds to move from left to right. The elevator needs 26 seconds and the pitch trim takes 45 seconds for full travel.

There is a filter to simulate that behaviour, it's the noise-spike filter. Here is a sample with 25 seconds transition time from -1 to +1:


The formula for max-rate-of-change is (max-out - min-out)/transition-time. In our example (1--1)/25=2/25=0.08. Try it out, disable your pitch-hold controller and enable the servo-driver. Set your some/internal/elevator-cmd property to +1 and see your elevator moving to fully up or down within 12.5 seconds. Now set your property to -1 and it will take 25 seconds for the elevator to move to the other direction. The feedback-if-disabled property is needed, if this stage is driven from a pid-controller.

We said before that PID-controllers compute offsets to the curren value of it's output. If you disable the servo-driver from your A/P master switch, the value of your elevator is fed back to the internal value of the elevator-cmd so the pid-controller has a reasonable value to start from when it is enabled. Create servo drivers for elevator, aileron and the trim channels if you intend to use them. A good location for the internal driver properties is /autopilot/internal - but that is up to you.Now change your pitch and roll PID-controller so that they no longer directly drive the output properties but the internal properties. You might need to adjust the Kp and Ti values of the PID-controller because you introduced some lag with the driver. But you should end up with perfectly smooth operation of the two main channels.

Spend some time optimizing these pitch and roll channels, they will be used by all other modes and have to be stable in any case!