Link to home
Start Free TrialLog in
Avatar of TheTechGuysNYC
TheTechGuysNYC

asked on

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!
ASKER CERTIFIED SOLUTION
Avatar of jrscherer
jrscherer
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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


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.
Avatar of TheTechGuysNYC
TheTechGuysNYC

ASKER

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


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?
Simply:

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

// Start timer
timer.start();
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
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?
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;
}
}