Skip to content

North American X‑15 — Longitudinal Dynamics

The X‑15 is an experimental hypersonic research rocket plane. This page mirrors the ELV layout: quick start, math model, derivatives, and API.

X‑15 Model

  • Quick start

    Launch the environment or the model within minutes.

    See example

  • Model API

    Python class documentation for the X‑15.

    Go to API

  • Gymnasium environment

    Ready environment for RL agents.

    Explore

  • Theory

    State equations and numerical parameters.

    Learn more

Control object structure

The model is defined in the state space:

\[\dot{x} = A x + B u, \quad y = C x + D u\]

where:

\[ x = \begin{bmatrix} u & \alpha & q & \theta \end{bmatrix}^{\top}, \quad u_{in} = \eta \]

Units

Angles and angular rates use radians. API methods can output values in degrees.

Mathematical model

\[ \dot{x} = A x + B u, \qquad y = C x + D u \]

Numerical matrices (example linearization):

\[ \begin{bmatrix} \dot{u} \\ \dot{\alpha} \\ \dot{q} \\ \dot{\theta} \end{bmatrix} = \begin{bmatrix} -0.0087 & -0.0190 & 0 & -32.174 \\ 0.0117 & -0.3110 & 1936 & 0 \\ 0.000471 & -0.0067 & -0.1820 & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} u \\ \alpha \\ q \\ \theta \end{bmatrix} + \begin{bmatrix} 0.0129 \\ -0.1844 \\ -0.0001225 \\ 0 \end{bmatrix} \eta \]

Derivatives (numerical values)

  • Matrix A (derivatives):
Coefficient Value
x_u -0.0087
x_α -0.0190
x_q 0
x_θ -32.174
z_u 0.0117
z_α -0.3110
z_q 1936
z_θ 0
m_u 0.000471
m_α -0.0067
m_q -0.1820
m_θ 0
  • Input η (column B):
Coefficient Value
x_η 0.0129
z_η -0.1844
m_η -0.0001225

Sources

  1. Heffley R. K., Jewell W. F. Aircraft handling qualities data. – NASA, 1972. № AD‑A277031.
  2. Etkin B., Reid L. D. Dynamics of flight. – New York : Wiley, 1959. – Vol. 2

Reward

The default reward function returns the negative absolute tracking error for the pitch angle:

\[r_t = -|\theta(t) - \theta_{\text{ref}}(t)|\]

Higher reward (closer to 0) indicates better tracking performance. A custom reward function can be passed via the reward_func parameter.

Quick start

import gymnasium as gym 
import numpy as np

from tensoraerospace.envs import LinearLongitudinalX15
from tensoraerospace.utils import generate_time_period
from tensoraerospace.signals.standard import unit_step

dt = 0.01
tp = generate_time_period(tn=20, dt=dt)
number_time_steps = len(tp)
reference_signals = unit_step(degree=5, tp=tp, time_step=10, output_rad=True).reshape(1, -1)

env = gym.make(
    'LinearLongitudinalX15-v0',
    number_time_steps=number_time_steps, 
    initial_state=[[0],[0],[0],[0]],
    reference_signal=reference_signals,
)
state, info = env.reset()
for _ in range(200):
    action = np.array([[0.1]])
    state, reward, terminated, truncated, info = env.step(action)
    if terminated or truncated:
        break
import numpy as np
from tensoraerospace.aerospacemodel import LongitudinalX15

dt = 0.01
number_time_steps = 200

x0 = np.array([0.0, 0.0, 0.0, 0.0])

model = LongitudinalX15(
    x0=x0,
    number_time_steps=number_time_steps,
    selected_state_output=["u", "alpha", "q", "theta"],
    dt=dt,
)

for t in range(number_time_steps - 1):
    u = np.array([[0.05]])
    x_next = model.run_step(u)

Python API

LongitudinalX15(x0, number_time_steps, selected_state_output=None, t0=0, dt=0.01)

Bases: ModelBase

North American X-15 in longitudinal control channel.

.. warning:: This linearized model uses the FPS (foot-pound-second) unit system, NOT SI. The A/B matrices are taken directly from the reference Simulink source tensoraerospace/aerospacemodel/simulinkModel/x15/x15_data.m where g = 32.174 ft/s^2 and the trim velocity U0 = 1936 ft/s (~Mach 1.8 at altitude). Linear velocities u and w are perturbations in ft/s, and the gravity term A[0,3] = -32.174 is in ft/s^2.

If SI-valued states are fed in (e.g. ``u`` in m/s), the dynamics
will be silently inconsistent with the matrices. Callers that
operate in SI must convert (1 m/s = 3.28084 ft/s) at the
environment boundary.

Parameters:

Name Type Description Default
x0 ndarray | list[float]

Initial state of the control object.

required
number_time_steps int

Number of time steps.

required
selected_state_output optional

Selected states of the control object. Defaults to None.

None
t0 int

Initial time. Defaults to 0.

0
dt float

Discretization frequency. Defaults to 0.01.

0.01
Action space

ele: elevator [rad] (input saturation at |ele| <= 25 deg, rate limit 60 deg/s, both converted to radians internally).

State space (FPS units): u: Longitudinal aircraft velocity perturbation [ft/s] w: Normal aircraft velocity perturbation [ft/s] q: Pitch angular velocity [rad/s] theta: Pitch angle [rad]

Output space (FPS units): u: Longitudinal aircraft velocity perturbation [ft/s] w: Normal aircraft velocity perturbation [ft/s] q: Pitch angular velocity [rad/s] theta: Pitch angle [rad]

Initialize LongitudinalX15 instance.

Parameters:

Name Type Description Default
x0 ndarray | list[float]

Initial state of the control object.

required
number_time_steps int

Number of time steps.

required
selected_state_output list[str] | None

Selected states of the control object. Defaults to None.

None
t0 float

Initial time. Defaults to 0.

0
dt float

Discretization frequency. Defaults to 0.01.

0.01

import_linear_system()

Saved linearized matrices.

Units: matrices are in FPS (foot-pound-second) system, matching the reference Simulink file x15_data.m. In the A matrix the last column of the first row is -g = -32.174 ft/s^2 and A[1,2] = U0 = 1936 ft/s is the trim airspeed, not an SI (m/s) quantity. See the class docstring for a full unit list.

initialise_system(x0, number_time_steps)

System initialization.

Parameters:

Name Type Description Default
x0 ndarray | list[float]

Initial state of the control object.

required
number_time_steps int

Number of time steps in iteration.

required

run_step(ut_0)

Execute one time step iteration.

Parameters:

Name Type Description Default
ut_0 ndarray

Control vector.

required

Returns:

Type Description
ndarray

np.ndarray: Control object state at step t+1.

update_system_attributes()

Update attributes that change with each time step.

get_state(state_name, to_deg=False, to_rad=False)

Get state array history.

Parameters:

Name Type Description Default
state_name str

State name.

required
to_deg bool

Convert to degrees. Defaults to False.

False
to_rad bool

Convert to radians. Defaults to False.

False

Returns:

Type Description
ndarray

np.ndarray: Array of selected state history.

Example

state_hist = model.get_state('alpha', to_deg=True)

get_control(control_name, to_deg=False, to_rad=False)

Get control signal array history.

Parameters:

Name Type Description Default
control_name str

Control signal name.

required
to_deg bool

Convert to degrees. Defaults to False.

False
to_rad bool

Convert to radians. Defaults to False.

False

Returns:

Type Description
ndarray

np.ndarray: Array of selected control signal history.

Example

control_hist = model.get_control('stab', to_deg=True)

get_output(state_name, to_deg=False, to_rad=False)

Get output signal array history.

Parameters:

Name Type Description Default
state_name str

Output signal name.

required
to_deg bool

Convert to degrees. Defaults to False.

False
to_rad bool

Convert to radians. Defaults to False.

False

Returns:

Type Description
ndarray

np.ndarray: Array of selected output signal history.

Example

output_hist = model.get_output('alpha', to_deg=True)

plot_output(output_name, time, lang='rus', to_deg=False, to_rad=False, figsize=(10, 10))

Plot output signal.

Parameters:

Name Type Description Default
output_name str

Output signal name.

required
time ndarray

Time array.

required
lang str

Label language ('rus' or 'eng'). Defaults to "rus".

'rus'
to_deg bool

Convert to degrees. Defaults to False.

False
to_rad bool

Convert to radians. Defaults to False.

False
figsize tuple

Figure size. Defaults to (10, 10).

(10, 10)

Returns:

Type Description
Figure

plt.Figure: Matplotlib figure object.

Raises:

Type Description
Exception

If both to_rad and to_deg are specified, or if output_name is invalid.

Example

fig = model.plot_output('alpha', time_array, lang='rus', to_deg=True)

LinearLongitudinalX15(initial_state, reference_signal, number_time_steps, tracking_states=None, state_space=None, control_space=None, output_space=None, reward_func=None)

Bases: Env

Simulation of LongitudinalX15 in OpenAI Gym for training agents.

.. warning:: The underlying :class:LongitudinalX15 uses FPS units (ft, lb, s). Linear velocities in the state vector are in ft/s, not m/s.

State vector: [u, w, q, theta] (FPS units) where: u - longitudinal velocity perturbation (ft/s) w - normal velocity perturbation (ft/s) q - pitch rate (rad/s) theta - pitch angle (rad)

Parameters:

Name Type Description Default
initial_state ndarray | list[float]

Initial state.

required
reference_signal ndarray | Callable

Reference signal.

required
number_time_steps int

Number of simulation steps.

required
tracking_states list[str] | None

Tracked states.

None
state_space list[str] | None

State space.

None
control_space list[str] | None

Control space.

None
output_space list[str] | None

Full output space (including noise).

None
reward_func Callable | None

Reward function (WIP status).

None

reward(state, ref_signal, ts) staticmethod

Evaluate control performance.

Parameters:

Name Type Description Default
state _type_

Current state.

required
ref_signal _type_

Reference state.

required
ts _type_

Time step.

required

Returns:

Name Type Description
reward float

Control performance evaluation.

step(action)

Execute a simulation step.

Parameters:

Name Type Description Default
action ndarray

Array of control signals for selected control surfaces.

required

Returns:

Name Type Description
next_state ndarray

Next state of the control object.

reward ndarray

Evaluation of control algorithm actions.

done bool

Simulation status, whether completed or not.

logging any

Additional information (not used).

reset(seed=None, options=None)

Reset simulation environment to initial conditions.

Parameters:

Name Type Description Default
seed int

Seed for random number generator.

None
options dict

Additional options for initialization.

None

render()

Visual display of actions in the environment. Status: WIP.

Raises:

Type Description
NotImplementedError

Rendering is not available.