Roy Vegard Ovesen wrote:Let me share my thoughts about the autopilot:
* I would like to see the autopilot move from c++ code into the instrument configuration xml-files.
This is my general plan. Right now I have the autopilot config in a separate .xml file
* The autopilot should get input from other instruments (airspeed indicator, altimeter, attitude indicator, etc.), and not from /position/altitude-ft, /velocities/..., etc. That way the wing-leveler would be unuseable if the attitude indicator was "broken". Failures in the underlying instruments and systems would affect the autopilot.
Yup, each autopilot component will be able to take input from any property, and output to any other property. As long as we have the instrument values modeled and placed in the property tree, we can use them.
And I firmly believe that we _should_ use the instrument values because in my mind using /position/... and /velocities/... would be "cheating", theese perfect values are _not_ available in the real world.
The trick will be for someone who knows a little about autopilots to be able to tell us what inputs drive what functions.
I have a masters-degree in control theory, and I think that an autopilot is just like any other plant that has to be controlled. The most important thing is to have a good knowledge about the process that is to be controlled. So maybe the best consultant for an autopilot would be a good pilot. He/She _is_ the autopilot (think about it!).
I've been hacking things out here a bit this evening and here's what I've come up with for a simple proportional wing leveler. All of this is in a state of flux, is subject to change, and only exists on my local HD.
I'll intersperse some explanatory comments ...
autopilot.xml:
<proportional> <name>Wing Leveler (Turn Coordinator based)</name>
I implemented (in jsbsim) a wing leveler or I should say a roll-angle holder that was roll-angle based. It's all a matter of preference, I think (turn coordinator based or roll-angle based or ...)
<enable>
<!-- enable this component when the defined property equals the
specified value -->
<prop>/autopilot/locks/heading</prop>
<value>wing-leveler</value>
</enable>
<input>
<!-- the input source -->
<prop>/instrumentation/turn-indicator/indicated-turn-rate</prop>
</input>
<target>
<!-- what we want to drive the input value to, this can also be a
property name -->
<value>0.0</value>
</target>
<output>
<!-- the output property name -->
<prop>/controls/flight/aileron</prop>
</output>
<config>
<!-- output = (target - input) * factor + offset -->
<factor>0.5</factor>
<!-- I don't know if it makes sense to offer an offset here, but it's
easy to add and I've seen similar things other places in the
code so I stuck it in. -->
<offset>0.0</offset>
In PID controllers an offset is often to set the working-point. The point where:
offset = output => target - input = 0.
The aileron deflection that results in zero turn rate. This aileron deflection doesn't have to be zero, as we all have experienced :-)
Another application for the offset is to attatch it to the manual actuator, in our case the stick. This means that the pilot can still control the airplane when the autopilot is engaged ;-) , and when the autopilot is disengaged, the output = offset = stick => pilot is flying.
<post>
<!-- I might be better off moving this to the output section, but
this lets us clamp the output result -->
<clamp>
<min>-1.0</min>
<max>1.0</max>
</clamp>
</post>
</config>
</proportional>
So if you set /autopilot/locks/heading = wing-leveler, this component will become active and start driving the turn rate to zero using the ailerons.
A proportional only controller will _not_ be able to drive the turn rate to zero. If the working-point were zero aileron deflection, then this controller would work but, as stated earlier, the working point is moving around.
In general you need at least proportional and intgral components, in a controller, to avoid this static-error.
Here's a more complicated two stage heading bug follower (still using simple proportional control):
This is called cascade control.
<!-- this first stage calculates a target roll degree based on the
difference between our current heading and the heading bug
heading. It writes the result to a temporary property name. This
temp property is the input to the second stage. Both stages use the
same enable property and value. -->
<proportional>
<name>Heading Bug Hold (DG based) Calc Target Roll</name>
<enable>
<prop>/autopilot/locks/heading</prop>
<value>dg-heading-hold</value>
</enable>
<input>
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
</input>
<target>
<prop>/autopilot/settings/heading-bug-deg</prop>
</target>
<output>
<!-- I just made up this property name -->
<prop>/autopilot/internal/target-roll-deg</prop>
</output>
<config>
<!-- this is an obvious hack that I'm not real comfortable with. It
maps the resulting "error" term to +/- 180 by adding or
subtracting 360 until the value get's into that range. -->
<pre>
<one-eighty>true</one-eighty>
</pre>
<factor>1.5</factor>
<offset>0.0</offset>
<post>
<clamp>
<min>-30.0</min>
<max>30.0</max>
</clamp>
</post>
</config>
</proportional>
<!-- this second stage calculates the aileron deflection needed to
achieve the previously calculated target roll degrees. -->
<proportional>
<name>Heading Bug Hold (DG based) (Calc target ailerons)</name>
<enable>
<prop>/autopilot/locks/heading</prop>
<value>dg-heading-hold</value>
</enable>
<input>
<prop>/orientation/roll-deg</prop>
</input>
<target>
<!-- this matches the output property name from the first stage -->
<prop>/autopilot/internal/target-roll-deg</prop>
</target>
<output>
<prop>/controls/flight/aileron</prop>
</output>
<config>
<factor>0.05</factor>
<offset>0.0</offset>
<post>
<clamp>
<min>-1.0</min>
<max>1.0</max>
</clamp>
</post>
</config>
</proportional>
So that's what I've come up with so far. Am I completely nuts? Any suggestions?
In stead of (or in addition to) having <proportional>, <integral> and <derivate> components, I would like to see a <pid-controller> that incorporates all three and in addition features like anti-windup, reduced set point weighing, etc. Here's my suggestion:
(I'm not sure about the propert names)
<pid-controller> <name>Roll Angle Controller</name> <enable> <prop>/autopilot/roll-angle-hold</prop> <value>true</value> </enable> <input> <prop>/instrumentation/attitude-indicator/roll-angle</prop> </input> <setpoint> <prop>/autopilot/roll-angle-setpoint</prop> </setpoint> <output> <prop>/controls/flight/aileron</prop> </output> <config> <proportional> <prop>/autopilot/roll-angle-controller/proportional</prop> </proportional> <integral> <prop>/autopilot/roll-angle-controller/integral</prop> </integral> <derivate> <prop>/autopilot/roll-angle-controller/derivate</prop> </derivate> <!-- clamping is required for the anti-integrator-windup --> <clamp> <min>-15</min> <!-- minimum aileron deflection --> <max>20</max> <!-- maximum aileron deflection --> </clamp> <beta> <!-- set-point weighing --> <prop>/autopilot/roll-angle-controller/beta</prop> </beta> </config> </pid-controller>
The <one-eighty> hack should be moved outside of the controller.
A PID controller is very versitale, it can be a P, PI, PD or a PID controller by setting the proportional, integral and derivate gains, and in many applications a PI controller is good enough.
I have no formal education in control theory, so what I know has been gleaned from here and there so I probably don't know much of the correct terminology and there are certainly tricks and things that I'm not aware of.
I have a PID controller algorithm from one of my textbooks, I could send it to you with lots of comments.
I'm hoping to do a good job of roughing in the flexible xml structure and interfacing to flightgear, then get some help on the control theory side from the experts.
With a PID controlle we should have the tools to build good autopilots. Then comes the art-of-tuning :-)
-- Roy Vegard Ovesen
_______________________________________________ Flightgear-devel mailing list [EMAIL PROTECTED] http://mail.flightgear.org/mailman/listinfo/flightgear-devel