Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 383
  • Last Modified:

Flight Control for an Autopilot

Ok here goes:

I am trying to create a Flight Control Actuator for an Autopilot. The FCA simply gets the desired course, and sensor info including alititude, speed, and current movements for the aileron, rudder, and elevator from an autopilot class, which gets the info from the Sensor Controller Unit. It is packaged as one object with methods for extracting all the pertinent info. My FCA class does not have to give out any info to any other class, it only receives info from the autopilot class, period.

Here is my unfinished interface:

public class FlightControlActuator implements FlightControlActuatorInterface {
    public FlightControlActuator() {
    }

 private int aileron, elevator, rudder;

    // This method would return the current aileron movements in degrees using the computeMovement class..
    public int getAileron() {
        return 0;
    }

    // This method would return the current elevator movements in degrees using the computeMovement class..
    public int getElevator() {
        return 0;
    }

    // This method would return the current rudder movements in degrees using the computeMovement class.
    public int getRudder() {
        return 0;
    }

    // This method would actually establish the desired movement of the aileron in certain number of degrees.
    public void setAileron(int aileron) {
        this.aileron = aileron;
    }

    // This method would actually establish the desired movement of the elevator in certain number of degrees.
    public void setElevator(int elevator) {
        this.elevator = elevator;
    }

    // This method would actually establish the desired movement of the aileron in certain number of degrees.
    public void setRudder(int rudder) {
        this.rudder = rudder;
    }

    /* This class would have to compute some sort of elapsed time or a time frame in which each movement actually takes place in order that it be done in a safe and timely fashion, and simply that it keep the plane on course in a suitable manner. It would return an integer.  Further, it would set some sort of minimum and maximum possible degrees of movement. Using the current position, desired position, time frame and elapsed time, this class would do all of the above successfully.
     * Time time is the current time for the current position
     * int maxMovement is the maximum movement possible in degrees (a minimum movement is obviously 0)
     * int currentPosition is the current position of the aircraft
     * int desiredPosition is the desired position of the aircraft obtained from the Autopilot class
     */
    public int computeMovement(Time time, int maxMovement, int currentPosition,
                               int desiredPosition) {
        return 0;
    }
}

My questions are:

1) I am at a loss for the computeMovement class. Other than defining variable, I do not know where to go from there.
2) How do I return the current values for each of the three parts of the plane i.e. aileron, rudder, elevator?
3) Any suggestions/questions?

Thanks guys!
0
TheTechGuysNYC
Asked:
TheTechGuysNYC
  • 5
  • 4
  • 2
2 Solutions
 
jrschererCommented:
I an just curious to know how you want to run your autopilot.
Will it be attached to a flight simulator software.
Autopilots are not stand-alone devices. They only run with an airplane they are mounted on, and it is this airplane (or its simulator) which
A) supplies the flight data, such as airspeed, heading, rate of climb, accelereations, and many other relevant data.
B) the autopilot influences the flight pad in a desired way by deflecting control surfaces, which in turn changes the flight data as a feedback. These deflections will stabilize when the dynamic feedback of the flight data equals to the control input. In other words, when the aircraft flies to the dstination you want it to fly, despite of disturbancesm such as wind changes and fuel burn.

If you want to run the autopilot class stand alone, you must al least have a bare bone flight formula which simulates the airplane characteristics.

Unless you have some knowledge in aeronattical engineering you may not be in a position to make a meaningful model. Just to give you an idea of the complexity.

The most important force vector of an airplane is the Lift vector. This has to be bigger than the weight of the airplane to start flying. Here is a simplified formula:

L = (1/2) d v2 s CL
L = Lift, which must equal the airplane's weight in pounds  for level flifgt
d = density of the air. This will change due to altitude. These values can be found in a I.C.A.O. Standard Atmosphere Table.
v = velocity of an aircraft expressed in feet per second
s = the wing area of an aircraft in square feet
CL = Coefficient of lift , which is determined by the type of airfoil and angle of attack.

If you feel it is doable for you, go to the NASA learning pages at
http://www.grc.nasa.gov/WWW/K-12/airplane/
and teach yourself some aeronautics.

Simulators work that way that they use a fixed time raster to recalculate the aircraft formula. This time may be for example 100 milliseconds. Every 100 msec the flight data are updated by the changes which did occur within the past 100 msec. This will give a pretty good approximation to a real airplane.

jack.net, (Aeronautical engineer, programmer, pilot and CFI)
0
 
cookreCommented:
First off, I'd simplify the problem by not worrying about control surfaces for now, but instead, concentrating on the results in terms of basic actions turn left, turn right, climb, and descend, and the current climb, descent, and turn states.

For example, if the current heading is 330 and the desired heading is 030, what do you do? If you're not turning, turn right.  If you're already in a right turn, do nothing.  If you're in a left turn, turn right.

Likewise, if ytou're on the desired heading, you have to stop turning if you're in a turning state.

The same thing goes for altitude changes.

Once you have the those basics established, you can work on how to implement those actions.  And, as above, we don't worry about control surfaces yet, but instead we use basic actions needed to perform the higher level actions above.  For example, to initiate a right turn , we roll the aircraft into a 30 degree right bank and maintain that bank angle.  To stop the right turn, we roll the aircraft to the left until the wings are level.  Similarly, to initiate a climb or descent, add or remove a standard amount of power to achieve a standard climb or descent rate.  To level off, return the power to the previous setting.

NOW you can worry about control surfaces.  
Well, the ailerons.  
Maybe.

To initiate a roll right, raise the right aileron and lower the left aileron by some standard amount.  To stop the roll, center the ailerons.  In real life, the ailerons are cross-linked so that turning the yoke causes appropriate, simultaneous aileron deflection, so you may want to consider a yoke controller instead of an aileron controller.

You can make it a bit more realistic by realizing that a rolling back level from a given bank angle takes a palpable amount of time.  So, in order to roll out level on a given heading, you have to start the roll back x many degrees ahead of the desired heading, x depending bank angle and roll rate.

===

The primary function of the rudder is to keep the nose pointed properly during the turn.  The amount of yaw needed depends on the rate of turn, which is a function of bank angle.  Insufficient yaw, i.e., the nose is pointing outside the turn, may cause the up wing to lose lift or stall, thereby causing the aircraft to flop back level.  Too much yaw, i.e., the nose is pointing to the inside of the turn, may cause the down wing to lose lift or stall, thereby causing the bank angle to suddenly increase, possibly inverting the aircraft.

===

The elevator is primarily an airspeed control.  Assuming level flight and constant airspeed, if you add or remove power, you'll climb or descend at that same airspeed.  If raise or lower the nose keeping the power constant, you'll have a momentary altitude change, but then return to level flight at a lower or higher airspeed.

===



0
 
jrschererCommented:
cookre,
A good simplification of flying, but probably too much simplified. It will be very tough to build an autopilot based on your observations. You mix up cause and effect.
It is right, most of your statements appear to the pilot the way you describe them, but are technically wrong.

For example:
====
The elevator is primarily an airspeed control.  Assuming level flight and constant airspeed, if you add or remove power, you'll climb or descend at that same airspeed.  If raise or lower the nose keeping the power constant, you'll have a momentary altitude change, but then return to level flight at a lower or higher airspeed.
====
If you add power, the aircraft in the first place accelerates its forward velocity, since the power vector is higher than the total drag. Speed will slightly increase, as a result total drag increases in about a square function of the speed increase. Drag and power vectors will balance out, but we have a higher resulting speed. This in turn will increase the lift vector which opposes the aircraft's weight. Since the weight is stable at this point, the aircraft starts to climb with a higher lift.

Unless you respect such fundamentals of aircraft control, your autopilot will never be a "Automatic Pilot", but just a useless piece of code.
Or can you show me an autopilot which uses pimarly Autothrottle to do Altitude Hold? Most of the GA planes do not have autothrottle, but they may have a perfect working altitude hold.

So my recommendation is: If someone wants to program an Autopilot which deserves this name, you have to start with the fundamentals of flight the way they physically work. It is not enough to use the training talk your CFI tells you when you try your first 360.

Happy landings.. Jack.net


0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
cookreCommented:
Absolutely correct.  Nobody, human or mechanical, flies that way.  Since this seems more of a programming exercise for someone with little understanding of flight, I thought it best to keep things simple.
0
 
TheTechGuysNYCAuthor Commented:
And you were right cookre :) Absolutely right. I am not creating a real autopilot, persay. It is more of a programming exercise. I appreciate the suggestions.

The autopilot is a very very basic one, and I am not even worried about the actual computeMovement class, but any suggestions for an algorithm would be appreciated, though not 100% needed. I am more worried about the framework of an autopilot, rather than the intricate movements, etc.
0
 
cookreCommented:
Let's assume a turn state variable TState whose value indicates a turn in degrees per second, negative being left and positive right.  For example, a value of -3 indicates the craft is in a 3 degree per second left turn.

Similarly, a climb state variable CState indicates climb rate in feet per second.  For example, -10 indicates a 10 foot per second descent.

We also use variables CurrentHeading, DesiredHeading, CurrentAltitude, and DesiredAltitude as expected.

Once per second update the Current* variables based on the values of *State, then modify the *State variables as appropriate.

For example, in a one second timer handler:

// Update heading and altitude
CurrentHeading+=TState;
CurentAltitude+=CState;

// Decide if we need to start or stop a turn
if (CurrentHeading==DesiredHeading)
   {
   TState=0;  //  Stop turn, if we were
   }
else
   {
   // Look at DesiredHeading and CurrentHeading to decide which direction to turn
   // then set TState accordingly (3 degrees per second is a common rate).
   // (Note that by always setting TState, we don't care what it was)
   }

//  Do same sort of thing for altitude.

// Display updated states


0
 
TheTechGuysNYCAuthor Commented:
OK, great!!

1) Now, do I have to adjust each individual part of the plane i.e. rudder, elevator, aileron?
2) Also, how should I go about getting each idividual setting for the three parts? They are going to be sent to me in an object with methods of extracting included I am fairly certain.
3) And are my set* methods ok as is?

I've written a Timer class (basically a stopwatch) which I think might be useful as far as helping the FCA make its calculations. Where do you think I should utilize it (if, of course, you think I should, and how?
0
 
TheTechGuysNYCAuthor Commented:
Simply:

// Creates the timer
Timer timer = new Timer ();

// Start timer
timer.start();
0
 
TheTechGuysNYCAuthor Commented:
Again, i am not worried about the calculations. Let's put it this way, I want to make this FCA so that if I did have the proper calculations, it would work the right way. Hehe
0
 
TheTechGuysNYCAuthor Commented:
I also have one more question.... when the desired course is met, the turn is stopped. Is there any way to actually find out when the desired course is about to be met, and then gradually slow it down?
0
 
cookreCommented:
Sorry for the delay, I've been a bit under the weather.

Here's something I did this evening.  Note the slowing of turns and altitude changes as the target is approached.



using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Timers;


namespace AutoPilot {
    partial class Form1 {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
double DesHdg=0;
double DesAlt=10000;
double CurHdg=0;
double CurAlt=10000;
double TState=0;
double CState=0;
double Epsilon=0.4;
System.Timers.Timer APTimer;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing) {
        if(disposing && (components != null)) {
        components.Dispose();
        }
        base.Dispose(disposing);
        }
protected override void OnActivated(EventArgs e)
{
this.tbCmdAlt.Text=DesAlt.ToString();
this.tbCmdHdg.Text=DesHdg.ToString();
this.lbCurHdg.Text="Cur Hdg:  "+String.Format("{0:###}",CurHdg);
this.lbCurAlt.Text="Cur Alt:  "+String.Format("{0:##,###}",CurAlt);
}        

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent() {
        this.tbCmdHdg = new System.Windows.Forms.TextBox();
        this.tbCmdAlt = new System.Windows.Forms.TextBox();
        this.label1 = new System.Windows.Forms.Label();
        this.label2 = new System.Windows.Forms.Label();
        this.groupBox1 = new System.Windows.Forms.GroupBox();
        this.cbSet = new System.Windows.Forms.Button();
        this.lbCurHdg = new System.Windows.Forms.Label();
        this.lbCurAlt = new System.Windows.Forms.Label();
        this.APTimer = new System.Timers.Timer();
        this.groupBox1.SuspendLayout();
        ((System.ComponentModel.ISupportInitialize)(this.APTimer)).BeginInit();
        this.SuspendLayout();
        //
        // tbCmdHdg
        //
        this.tbCmdHdg.Font = new System.Drawing.Font("Arial",12F,System.Drawing.FontStyle.Regular,System.Drawing.GraphicsUnit.Point,((byte)(0)));
        this.tbCmdHdg.Location = new System.Drawing.Point(11,37);
        this.tbCmdHdg.Name = "tbCmdHdg";
        this.tbCmdHdg.Size = new System.Drawing.Size(109,26);
        this.tbCmdHdg.TabIndex = 0;
        //
        // tbCmdAlt
        //
        this.tbCmdAlt.Font = new System.Drawing.Font("Arial",12F,System.Drawing.FontStyle.Regular,System.Drawing.GraphicsUnit.Point,((byte)(0)));
        this.tbCmdAlt.Location = new System.Drawing.Point(144,37);
        this.tbCmdAlt.Name = "tbCmdAlt";
        this.tbCmdAlt.Size = new System.Drawing.Size(109,26);
        this.tbCmdAlt.TabIndex = 1;
        //
        // label1
        //
        this.label1.AutoSize = true;
        this.label1.Font = new System.Drawing.Font("Arial",12F,System.Drawing.FontStyle.Regular,System.Drawing.GraphicsUnit.Point,((byte)(0)));
        this.label1.Location = new System.Drawing.Point(38,21);
        this.label1.Name = "label1";
        this.label1.Size = new System.Drawing.Size(43,18);
        this.label1.TabIndex = 2;
        this.label1.Text = "HDG";
        //
        // label2
        //
        this.label2.AutoSize = true;
        this.label2.Font = new System.Drawing.Font("Arial",12F,System.Drawing.FontStyle.Regular,System.Drawing.GraphicsUnit.Point,((byte)(0)));
        this.label2.Location = new System.Drawing.Point(184,21);
        this.label2.Name = "label2";
        this.label2.Size = new System.Drawing.Size(37,18);
        this.label2.TabIndex = 3;
        this.label2.Text = "ALT";
        //
        // groupBox1
        //
        this.groupBox1.Controls.Add(this.cbSet);
        this.groupBox1.Controls.Add(this.tbCmdAlt);
        this.groupBox1.Controls.Add(this.label2);
        this.groupBox1.Controls.Add(this.tbCmdHdg);
        this.groupBox1.Controls.Add(this.label1);
        this.groupBox1.Font = new System.Drawing.Font("Arial",12F,System.Drawing.FontStyle.Regular,System.Drawing.GraphicsUnit.Point,((byte)(0)));
        this.groupBox1.Location = new System.Drawing.Point(12,12);
        this.groupBox1.Name = "groupBox1";
        this.groupBox1.Size = new System.Drawing.Size(274,107);
        this.groupBox1.TabIndex = 4;
        this.groupBox1.TabStop = false;
        this.groupBox1.Text = "CMD";
        //
        // cbSet
        //
        this.cbSet.Font = new System.Drawing.Font("Arial",12F,System.Drawing.FontStyle.Regular,System.Drawing.GraphicsUnit.Point,((byte)(0)));
        this.cbSet.Location = new System.Drawing.Point(91,69);
        this.cbSet.Name = "cbSet";
        this.cbSet.Size = new System.Drawing.Size(85,30);
        this.cbSet.TabIndex = 4;
        this.cbSet.Text = "SET";
        this.cbSet.UseVisualStyleBackColor = true;
        this.cbSet.Click += new System.EventHandler(this.Set_Click);
        //
        // lbCurHdg
        //
        this.lbCurHdg.AutoSize = true;
        this.lbCurHdg.Font = new System.Drawing.Font("Arial",14.25F,System.Drawing.FontStyle.Regular,System.Drawing.GraphicsUnit.Point,((byte)(0)));
        this.lbCurHdg.Location = new System.Drawing.Point(19,182);
        this.lbCurHdg.Name = "lbCurHdg";
        this.lbCurHdg.Size = new System.Drawing.Size(129,22);
        this.lbCurHdg.TabIndex = 5;
        this.lbCurHdg.Text = "Curr Hdg: 000";
        //
        // lbCurAlt
        //
        this.lbCurAlt.AutoSize = true;
        this.lbCurAlt.Font = new System.Drawing.Font("Arial",14.25F,System.Drawing.FontStyle.Regular,System.Drawing.GraphicsUnit.Point,((byte)(0)));
        this.lbCurAlt.Location = new System.Drawing.Point(19,204);
        this.lbCurAlt.Name = "lbCurAlt";
        this.lbCurAlt.Size = new System.Drawing.Size(143,22);
        this.lbCurAlt.TabIndex = 6;
        this.lbCurAlt.Text = "Curr Alt: 10,000";
        //
        // APTimer
        //
        this.APTimer.Enabled = true;
        this.APTimer.Interval = 333;
        this.APTimer.SynchronizingObject = this;
        this.APTimer.Elapsed += new System.Timers.ElapsedEventHandler(this.APTimer_Elapsed);
        //
        // Form1
        //
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F,13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(309,248);
        this.Controls.Add(this.lbCurAlt);
        this.Controls.Add(this.lbCurHdg);
        this.Controls.Add(this.groupBox1);
        this.Name = "Form1";
        this.Text = "Form1";
        this.groupBox1.ResumeLayout(false);
        this.groupBox1.PerformLayout();
        ((System.ComponentModel.ISupportInitialize)(this.APTimer)).EndInit();
        this.ResumeLayout(false);
        this.PerformLayout();

}

// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
void APTimer_Elapsed(object source,ElapsedEventArgs evt)
{
APTimer.Enabled=false;

// Update heading and altitude
CurHdg+=TState;
if (CurHdg>359) CurHdg-=360;
if (CurHdg<0)   CurHdg+=360;

CurAlt+=CState;

// Decide if we need to start or stop a turn
if (Math.Abs(CurHdg-DesHdg)<Epsilon)
   {
   TState=0;  //  Stay on this heading
   }
else
   {
   // We have to turn (or continue a turn)
   // Look at DesiredHeading and CurrentHeading to decide which direction to turn
   // then set TState accordingly (4 degrees per second is reasonable).
   // (Note that by always setting TState, we don't care what it was)


   // Decide which way to turn and how much of the turn is left
   double TurnDir=1; // 1=> right   -1=>left
   double TurnAmt=0;
   if (CurHdg>DesHdg)
      {
      if ((CurHdg-DesHdg)>180)
         {
         TurnDir=1;
         TurnAmt=DesHdg+360-CurHdg;
         }
      else
         {
         TurnDir=-1;
         TurnAmt=CurHdg-DesHdg;
         }
      }
   else // DesHdg>CurHdg
      {
      if ((DesHdg-CurHdg)>180)
         {
         TurnDir=-1;
         TurnAmt=CurHdg+360-DesHdg;
         }
      else
         {
         TurnDir=1;
         TurnAmt=DesHdg-CurHdg;
         }
      }
   TState=1.5*TurnDir; // ca. 4 deg/sec
   
   // Now decide if we should slow the turn
   if (TurnAmt<15) TState/=2;  // Cut turn rate in half when within 15 degrees
   if (TurnAmt<5)  TState/=2;  // Cut in half again when within 5 degrees
   }

// Do same sort of thing for altitude.
if (Math.Abs(CurAlt-DesAlt)<1)
   {
   CState=0;
   }
else
   {
   double ClimbDir=1;
   if (CurAlt>DesAlt) ClimbDir=-1;
   CState=20*ClimbDir;
   if (Math.Abs(CurAlt-DesAlt)<1000) CState=10*ClimbDir;
   if (Math.Abs(CurAlt-DesAlt)<100)  CState=5*ClimbDir;
   if (Math.Abs(CurAlt-DesAlt)<10)  CState=1*ClimbDir;
   }  

// Display updated states
this.lbCurHdg.Text="Cur Hdg:  "+String.Format("{0:000}",CurHdg);
this.lbCurAlt.Text="Cur Alt:  "+String.Format("{0:##,###}",CurAlt);
this.lbCurAlt.Refresh();
this.lbCurHdg.Refresh();

APTimer.Enabled=true;
}


private void Set_Click(object sender,System.EventArgs e)
{
DesHdg=Convert.ToInt32(this.tbCmdHdg.Text);
DesAlt=Convert.ToInt32(this.tbCmdAlt.Text);
}

#endregion
private System.Windows.Forms.TextBox tbCmdHdg;
private System.Windows.Forms.TextBox tbCmdAlt;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Button cbSet;
private System.Windows.Forms.Label lbCurHdg;
private System.Windows.Forms.Label lbCurAlt;
}
}

0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 5
  • 4
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now