Skip to content

Lesson 4 — Fundamentals of Flight Dynamics

1. Introduction to Flight Dynamics

Flight dynamics is a branch of aeromechanics that studies the motion of an aircraft in space under the action of aerodynamic forces, thrust, gravity, and control inputs. It explains why an airplane flies the way it does and how it can be controlled.

To analyze aircraft motion, two main coordinate systems are introduced:

  1. Earth-fixed (inertial) frame: Tied to the Earth, used to determine the aircraft’s position and trajectory.
  2. Body-fixed frame: Rigidly attached to the aircraft. Its axes are aligned with the aircraft’s structural axes:
    • Longitudinal axis (Ox): From tail to nose. Rotation about it is called roll.
    • Lateral axis (Oy): Along the wingspan. Rotation about it is called pitch.
    • Vertical axis (Oz): Perpendicular to the first two. Rotation about it is called yaw.

2. Forces Acting on an Aircraft

Aircraft motion is determined by the balance of four primary forces:

  • Weight: Always directed vertically downward toward the Earth’s center.
  • Lift: Generated by the wing and directed perpendicular to the freestream velocity. It balances the weight.
  • Drag: Directed opposite to the motion. It resists forward motion.
  • Thrust: Produced by the propulsion system (engines) and directed forward to overcome drag.

In steady, straight-and-level flight at constant speed, lift equals weight and thrust equals drag.

3. Equations of Motion

The motion of an aircraft as a rigid body is described by six nonlinear differential equations: three for translational motion of the center of mass, and three for rotational motion about the center of mass.

These equations are complicated, so in practice they are often linearized for small deviations about a steady operating point (e.g., straight-and-level flight). This allows the complex motion to be separated into two simpler components: longitudinal and lateral-directional motion.

4. Dynamic Stability and Its Modes

As discussed earlier, dynamic stability characterizes how the aircraft returns to equilibrium. Analysis of the linearized equations reveals characteristic oscillatory behaviors called stability modes.

4.1. Longitudinal Modes

  1. Short-period mode: Fast, typically well-damped oscillations of angle of attack and pitch angle with nearly constant airspeed. Pilots perceive these as nose “bobs.” Good handling qualities require quick decay.
  2. Phugoid mode: Very slow, lightly damped (sometimes lightly unstable) oscillations in altitude and airspeed at nearly constant angle of attack. Pilots can easily manage these and they are not considered critical for safety.

4.2. Lateral–Directional Modes

  1. Dutch roll: Coupled yawing and rolling oscillations. The airplane weaves its nose left-right while rocking wing to wing. This can be uncomfortable, so modern aircraft often include yaw dampers.
  2. Spiral mode: A slow, aperiodic motion. With spiral instability, a small bank slowly increases, entering a spiral. A pilot can easily correct it, but unattended it can develop into a steep spiral.
  3. Roll subsidence: Not an oscillation but a measure of how quickly roll rate decays after an aileron input. Faster decay implies crisper roll response.

5. References

  1. Flight dynamics — Wikipedia. – URL: https://en.wikipedia.org/wiki/Flight_dynamics_(fixed-wing_aircraft)
  2. Fundamentals of aircraft flight dynamics — MIPT (RU). – URL: https://oat.mai.ru/book/glava06/6_1/6_1.htm
  3. Balakin V.L. Flight Dynamics of an Airplane: Trajectory and Performance Calculations. — Samara: SSAU, 2002. – URL: https://repo.ssau.ru/bitstream/Uchebnye-izdaniya/Dinamika-poleta-samoleta-Raschet-traektorii-i-letnyh-harakteristik-konspekt-lekcii-Tekst-elektronnyi-85702/1/%D0%91%D0%B0%D0%BB%D0%B0%D0%BA%D0%B8%D0%BD%20%D0%92.%D0%9B.%20%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D0%BA%D0%B0%20%D0%BF%D0%BE%D0%BB%D0%B5%D1%82%D0%B0%202002.pdf

6. Practical Python Example

We will simulate a simplified longitudinal aircraft model and analyze its stability modes.

import numpy as np
import matplotlib.pyplot as plt
import control as ctrl
from scipy.linalg import eigvals
import cmath

def aircraft_longitudinal_model():
    """
    Simplified longitudinal model of a light aircraft
    State variables: [u, w, q, theta]
    u - longitudinal velocity
    w - vertical velocity
    q - pitch rate
    theta - pitch angle
    """

    # Aerodynamic derivatives (typical for a light aircraft)
    # Units scaled to dimensionless form

    A = np.array([
        [-0.045,  0.036,  0.0,   -9.81],  # equation for u
        [-0.369, -2.02,   1.76,   0.0],   # equation for w
        [ 0.191, -3.96,  -2.98,   0.0],   # equation for q
        [ 0.0,    0.0,    1.0,    0.0]    # equation for theta
    ])

    # Control — elevator deflection
    B = np.array([
        [ 0.0],      # influence on u
        [-0.282],    # influence on w
        [-9.72],     # influence on q
        [ 0.0]       # influence on theta
    ])

    # Outputs — measured quantities
    C = np.array([
        [1, 0, 0, 0],    # longitudinal speed
        [0, 0, 1, 0],    # pitch rate
        [0, 0, 0, 1]     # pitch angle
    ])

    D = np.zeros((3, 1))

    return A, B, C, D

def analyze_flight_modes(A):
    """Analyze longitudinal modes"""

    print("ANALYSIS OF LONGITUDINAL FLIGHT MODES")
    print("="*60)

    # Compute eigenvalues
    eigenvals = eigvals(A)

    print("System eigenvalues:")
    modes = []

    for i, lam in enumerate(eigenvals):
        real_part = np.real(lam)
        imag_part = np.imag(lam)

        if abs(imag_part) < 1e-6:  # real root
            print(f"λ_{i+1} = {real_part:.4f} (real root)")
            modes.append(('real', real_part, 0))
        else:
            # Complex-conjugate pair
            freq = abs(imag_part)
            damping = -real_part
            damping_ratio = damping / np.sqrt(real_part**2 + imag_part**2)
            period = 2*np.pi / freq if freq > 0 else np.inf

            print(f"λ_{i+1} = {real_part:.4f} ± {abs(imag_part):.4f}j")
            print(f"   Frequency: {freq:.4f} rad/s")
            print(f"   Period: {period:.2f} s")
            print(f"   Damping ratio: {damping_ratio:.4f}")

            modes.append(('complex', real_part, imag_part, freq, period, damping_ratio))

    # Mode classification
    print("\nMODE CLASSIFICATION:")

    for i, mode in enumerate(modes):
        if mode[0] == 'real':
            real_part = mode[1]
            if abs(real_part) < 0.1:
                print(f"Mode {i+1}: Slow aperiodic (possibly phugoid-like)")
            else:
                print(f"Mode {i+1}: Fast aperiodic")
        else:
            freq = mode[3]
            period = mode[4]
            damping_ratio = mode[5]

            if period > 10:
                print(f"Mode {i+1}: Long-period (phugoid)")
                print(f"   - Slow oscillations of altitude and speed")
                print(f"   - Period: {period:.1f} s")
            elif period < 5:
                print(f"Mode {i+1}: Short-period")
                print(f"   - Fast oscillations of angle of attack and pitch")
                print(f"   - Period: {period:.1f} s")

            if damping_ratio > 0.7:
                print("   - Well damped")
            elif damping_ratio > 0.3:
                print("   - Moderately damped")
            else:
                print("   - Lightly damped")

    return eigenvals, modes

def plot_mode_analysis(eigenvals, A, B, C, D):
    """Plot mode analysis figures"""

    # Build system
    sys = ctrl.ss(A, B, C, D)

    fig, axes = plt.subplots(2, 2, figsize=(15, 10))

    # 1. Pole map
    axes[0, 0].set_title('Pole map')
    for lam in eigenvals:
        axes[0, 0].plot(np.real(lam), np.imag(lam), 'rx', markersize=10, markeredgewidth=2)

    axes[0, 0].axvline(x=0, color='k', linestyle='--', alpha=0.5, label='Stability boundary')
    axes[0, 0].axvspan(-10, 0, alpha=0.2, color='green', label='Stable region')
    axes[0, 0].grid(True, alpha=0.3)
    axes[0, 0].set_xlabel('Real part')
    axes[0, 0].set_ylabel('Imag part')
    axes[0, 0].legend()

    # 2. Channel step responses
    t = np.linspace(0, 30, 1000)

    try:
        # Response to an elevator step
        t_step, y_step = ctrl.step_response(sys, t)

        # Longitudinal speed
        axes[0, 1].plot(t_step, y_step[0], label='u (longitudinal speed)')
        axes[0, 1].set_title('Response to elevator step')
        axes[0, 1].set_xlabel('Time (s)')
        axes[0, 1].set_ylabel('u')
        axes[0, 1].grid(True)

        # Pitch rate
        axes[1, 0].plot(t_step, y_step[1], label='q (pitch rate)', color='orange')
        axes[1, 0].set_title('Pitch rate')
        axes[1, 0].set_xlabel('Time (s)')
        axes[1, 0].set_ylabel('q (rad/s)')
        axes[1, 0].grid(True)

        # Pitch angle
        axes[1, 1].plot(t_step, y_step[2], label='θ (pitch angle)', color='green')
        axes[1, 1].set_title('Pitch angle')
        axes[1, 1].set_xlabel('Time (s)')
        axes[1, 1].set_ylabel('θ (rad)')
        axes[1, 1].grid(True)

    except Exception as e:
        print(f"Error while plotting step responses: {e}")

    plt.tight_layout()
    plt.show()

def simulate_flight_disturbance():
    """Simulate response to a disturbance (wind gust)"""

    A, B, C, D = aircraft_longitudinal_model()

    # Build system
    sys = ctrl.ss(A, B, C, D)

    # Model a wind gust as a short pulse
    t = np.linspace(0, 20, 1000)

    # Impulse-like disturbance at t = 1 s
    u = np.zeros_like(t)
    disturbance_idx = np.where(t >= 1.0)[0][0]
    u[disturbance_idx:disturbance_idx+10] = 0.1  # brief elevator deflection

    # Simulate response
    t_sim, y_sim, x_sim = ctrl.lsim(sys, u, t, return_x=True)

    plt.figure(figsize=(15, 8))

    # State variables
    plt.subplot(2, 3, 1)
    plt.plot(t_sim, x_sim[:, 0])
    plt.title('Longitudinal speed u')
    plt.xlabel('Time (s)')
    plt.ylabel('u (m/s)')
    plt.grid(True)

    plt.subplot(2, 3, 2)
    plt.plot(t_sim, x_sim[:, 1])
    plt.title('Vertical speed w')
    plt.xlabel('Time (s)')
    plt.ylabel('w (m/s)')
    plt.grid(True)

    plt.subplot(2, 3, 3)
    plt.plot(t_sim, x_sim[:, 2])
    plt.title('Pitch rate q')
    plt.xlabel('Time (s)')
    plt.ylabel('q (rad/s)')
    plt.grid(True)

    plt.subplot(2, 3, 4)
    plt.plot(t_sim, x_sim[:, 3])
    plt.title('Pitch angle θ')
    plt.xlabel('Time (s)')
    plt.ylabel('θ (rad)')
    plt.grid(True)

    plt.subplot(2, 3, 5)
    plt.plot(t_sim, u)
    plt.title('Control input')
    plt.xlabel('Time (s)')
    plt.ylabel('δe (rad)')
    plt.grid(True)

    # Phase portrait (angle of attack vs pitch rate)
    plt.subplot(2, 3, 6)
    alpha = np.arctan(x_sim[:, 1] / (x_sim[:, 0] + 1e-6))  # approximate AoA
    plt.plot(alpha, x_sim[:, 2])
    plt.title('Phase portrait (α vs q)')
    plt.xlabel('Angle of attack α (rad)')
    plt.ylabel('Pitch rate q (rad/s)')
    plt.grid(True)

    plt.tight_layout()
    plt.show()

# Main program
if __name__ == "__main__":
    # Get aircraft model
    A, B, C, D = aircraft_longitudinal_model()

    print("AIRCRAFT LONGITUDINAL MODEL")
    print("="*50)
    print("State variables: [u, w, q, θ]")
    print("u - longitudinal speed")
    print("w - vertical speed")
    print("q - pitch rate")
    print("θ - pitch angle")
    print()
    print("Matrix A:")
    print(A)
    print("\nMatrix B:")
    print(B)
    print("\nMatrix C:")
    print(C)

    # Mode analysis
    eigenvals, modes = analyze_flight_modes(A)

    # Stability check
    print(f"\n{'='*60}")
    print("STABILITY ASSESSMENT")
    print(f"{'='*60}")

    stable = all(np.real(lam) < 0 for lam in eigenvals)
    if stable:
        print("✓ The aircraft is statically and dynamically stable")
    else:
        print("✗ The aircraft is unstable")

    # Plots
    plot_mode_analysis(eigenvals, A, B, C, D)

    # Disturbance simulation
    print("\nSimulating response to a disturbance...")
    simulate_flight_disturbance()

This example demonstrates:

  1. A state-space longitudinal aircraft model with realistic aerodynamic coefficients
  2. Stability mode analysis — automatic identification of short- and long-period modes
  3. Classification of dynamic characteristics by oscillation period and damping ratio
  4. Pole and transient visualizations across multiple control channels
  5. Response to disturbances (e.g., wind gusts) with phase portraits