Note

This page was generated from a Jupyter notebook.

Rudder Kick

Simulate a pilot performing a rudder kick test by inputing a rudder input based on a ramp input. Aileron input is also included to maintain a steady heading sideslip (SHSS). The time histories of the control inputs and beta (sideslip angle) are plotted.

[1]:
import sys

if 'google.colab' in sys.modules:
    print('Running on Google Colab – installing jsbsim …')
    !pip install jsbsim

PATH_TO_JSBSIM_FILES = None

[2]:
import jsbsim
import matplotlib.pyplot as plt
import math

# --- Configuration Section ---
# Global variables that must be modified to match your particular need
# The aircraft name
# Note - It should match the exact spelling of the model file
AIRCRAFT_NAME="737"

# --- JSBSim Initialization ---
# These lines initialize the flight dynamics model.

# Avoid flooding the console with log messages
jsbsim.FGJSBBase().debug_lvl = 0

# Create a flight dynamics model (FDM) instance.
fdm = jsbsim.FGFDMExec(PATH_TO_JSBSIM_FILES)

# Load the aircraft model
fdm.load_model(AIRCRAFT_NAME)

# Set engines running
fdm['propulsion/set-running'] = -1


# --- Simulation Parameters ---
# These lines set the simulation parameters.

# Set alpha range for trim solutions
fdm['aero/alpha-max-rad'] = math.radians(12)   # Maximum angle of attack in radians.
fdm['aero/alpha-min-rad'] = math.radians(-4.0) # Minimum angle of attack in radians.

# Get the simulation time step (delta time).
dt = fdm.get_delta_t()

# Max control deflection
aileronMax = 1   # Maximum normarized aileron command (-1~1)
rudderMax = 0.92 # Maximum normarized rudder command (-1~1)

# Number of seconds for control surface to reach max deflection
risetime = 3

# Calculate the increment (change) in control surface deflection command per time step.
diAileron = aileronMax / (risetime/dt)
diRudder = rudderMax / (risetime/dt)


# --- Data Storage ---
# This section initializes lists to record simulation data.

times = []      # List to record the simulation time at each step.
betas = []      # List to record the beta angle at each step.
bankAngle = []  # List to record the bank angle at each step.
ailerons = []   # List to record the aileron control surface deflection.
rudder = []     # List to record the rudder control surface deflection.


# --- Simulation Initialization ---
# This line initializes the flight dynamics model.

# Initial conditions
fdm['ic/h-sl-ft'] = 1000  #altitude above sea level (ft)
fdm['ic/vc-kts'] = 200    #calibrated airspeed (kts)
fdm['ic/gamma-deg'] = 0   #flight path angle (deg)
fdm['ic/beta-deg'] = 0    #sideslip angle (deg)

# Initialize the aircraft with initial conditions
fdm.run_ic()

# Attempt to trim the aircraft.
try:
    # 1 means straight flight by using all changeable control variables.
    fdm['simulation/do_simple_trim'] = 1

except jsbsim.TrimFailureError:
    print("Trim failed, continuing rudder kick in an untrimmed state.")
    pass  # Ignore trim failure


# --- Simulation Loop ---
# This is the main simulation loop that runs the simulation for a specified duration.

# Time to run for in seconds.
run_period = 20

for i in range(int(run_period/dt)):
    # Advance the simulation by one time step.
    fdm.run()

    # Record the simulation data.
    times.append(fdm.get_sim_time())
    betas.append(fdm['aero/beta-deg'])
    bankAngle.append(fdm['attitude/phi-deg'])
    ailerons.append(fdm['fcs/aileron-cmd-norm'])
    rudder.append(fdm['fcs/rudder-cmd-norm'])

    # Control Surface Update
    aileronCmd = fdm['fcs/aileron-cmd-norm']
    rudderCmd = fdm['fcs/rudder-cmd-norm']

    # Increment the aileron command if it's less than the maximum.
    if aileronCmd < aileronMax:
        aileronCmd += diAileron
        fdm['fcs/aileron-cmd-norm'] = aileronCmd

    # Increment the rudder command if it's less than the maximum.
    if rudderCmd < rudderMax:
        rudderCmd += diRudder
        fdm['fcs/rudder-cmd-norm'] = rudderCmd


# --- Plot Results ---
# This section plots the simulation results.

plt.rcParams["figure.figsize"] = (12, 8) # Set the figure size.
fig, ax1 = plt.subplots()

# Plot the beta data on the primary y-axis.
ax1.set_xlabel('Time (s)')
ax1.set_ylabel('Beta (deg)')
line1 = ax1.plot(times, betas, label='Beta', color='red')

# Create a secondary y-axis for the control surface positions.
ax2 = ax1.twinx()

# Plot the aileron and rudder commands on the secondary y-axis.
ax2.set_ylabel('Inceptor Position')
line2 = ax2.plot(times, ailerons, label='Aileron')
line3 = ax2.plot(times, rudder, label='Rudder')

# Add a legend to the plot.
ax1.legend(handles=line1+line2+line3, loc=4)

# Set the title of the plot.
plt.title('Rudder Kick')

# Display the plot.
plt.show()
../_images/notebooks_07_rudder_kick_2_0.png