You are on page 1of 12

Contents

⎢⎥ ⎢
Comparison of the Linear and Nonlinear
Equations of Motion: Aircraft Simulation

Comparison of the Linear and Nonlinear Equations of Motion: Aircraft Simulation


Numerical Linearisation of the Equations of Motion

The following is a direct adaptation of an example given in my own undergraduate notes -


reproduced with permission. Thanks, Dougie.

The nonlinear equations of motion have been derived and will be repeated here:


˙
⎡U + QW − U R⎤

˙
V + RU − P W

⎣ ˙
W + P V − QU

Ṗ ⋅ I xx

Q̇ ⋅ I yy

⎣ Ṙ ⋅ I zz

⎡Φ̇ ⎤

˙
Θ

⎣˙⎦
Ψ
P

R

=
=


0
0
0
−mg sin Θ − D cos α + L sin α + T cos Θ T

Q ⋅ r (I zz − I yy )

P ⋅ R (I xx − I yy )

P ⋅ Q (I yy − I xx )

1 0

cos Φ

− sin Φ

sin Φ tan Θ

cos Φ

sin Φ

cos Θ

V = P = R = Φ = Ψ = Θ0 = 0
mg sin Φ cos Θ + F A

+
(P
2

sin Φ cos Θ

cos Φ cos Θ

and the equations describing longitudinal flight may now be we written:

m (U̇ + Q W ) = T − D cos α + L cos α − mg sin Θ

m (Ẇ − Q U ) = −L cos α − D sin α + mg cos Θ

˙
˙
I yy Q = M A − T Δ zT

Θ = Q
− sin Φ

cos Φ

cos Θ

⎣˙⎦

cos Φ tan Θ
Ψ

⎤ ⎡
P
2
− R )I xz

(Q ⋅ R − Ṗ )I xz ⎦

− sin Θ


Y

mg cos Φ cos Θ − D sin α − L cos α − T sin Θ T

˙
(R + P ⋅ Q)I xz ⎤

⎡ Φ̇⎤

˙
Θ

⎦ ⎣R ⎦

a simulation of this aircraft in the longitudinal plane will be presented in this section. By
constraining to longitudinal motion only, the solution of the nonlinear equations is heavily
simplified, but the analysis can be expanded for the unconstrained case and if we assume the
thrust inclination is zero. The simplifying assumption is:
+ FT

Q
=
Y


L

N


HS125 (Hawker 800) Business Jet)

The data for the HS125 Business jet are given in the table below. The aerodynamic model gives
the lift, drag, and pitching moment coefficients as:

C L = C L0 + C Lα α + C Lδ δe
e

2
CD = CD + CD α + CD 2
α
0 α α

CM = CM + CM α + CM δe + CM q
^
0 α δe q

Q c̄
q
^≜
Vf

Parameter Symbol Value

Mass m 7500kg

Wing Area S 32.8m 2

Pitching Moment of Inertia I yy 84,309kg m 2

MAC c̄ 2.29m

Thrustline vertical displacement Δ zT -0.378m

Lift Coefficients CL , CL , CL
0 α δe
0.895, 5.01, 0.722

Drag Coefficients CD , CD , CD
0 α α2
0.177, 0.232, 1.393

PM Coefficients CM , CM , CM
0 α α
2, C Mq -0.046, -1.087, -1.88, -7.055

The reduced nonlinear equations of motion can be constructed into a function eqnofmotion800:
def eqnofmotion800(y, t=[], Thrust=13878):
# The reduced state vector is a 7 x 1 vector
# y = [U, W, Q, Theta, X_E, Z_E, dE]

# Aircraft Parameters (all in SI)


m=7484.4 # mass = 7484.4kg etc.
s=32.8 #
Iyy=84309
cbar=2.29
dZt=-0.378
rho=1.225
CD0=0.177
CDa=0.232
CDa2= 1.393
CL0=0.895
CLa=5.01
CLde=0.722
CM0=-0.046
CMa=-1.087
CMde=-1.88
CMq=-7.055

# Get the current values of aircraft states


dE = y[6] # Elevator input

U=y[0] # Forward speed


W=y[1] # Heave velocity
Q=y[2] # Pitch rate
Theta=y[3] # Pitch attitude

# Determine some aerodynamic terms


Vf=np.sqrt(U**2 + W**2) # Flight speed
alpha = np.arctan(W/U) # Angle of attack
qh = Q*cbar/Vf # Nondimensional pitch rate

# Aerodynamic coefficients from Hawker model:


CL = CL0 + CLa*alpha + CLde*dE
CD = CD0 + CDa*alpha + CDa2*alpha*alpha
CM = CM0 + CMa*alpha + CMde * dE + CMq*qh

q_inf = .5*rho*Vf**2*s

# Dimensional aero terms


lift = q_inf*CL
drag = q_inf*CD
pm = q_inf*cbar*CM

# Simplified nonlinear equations of motion


ydot = np.zeros(7, dtype='float64')
ydot[0] = -Q*W + (lift*np.sin(alpha) - drag*np.cos(alpha) + Thrust)/m -
9.80665*np.sin(Theta) # Udot
ydot[1] = Q*U + (-lift*np.cos(alpha) - drag*np.sin(alpha))/m +
9.80665*np.cos(Theta) # Wdot
ydot[2] = (pm - Thrust*dZt)/Iyy # M
ydot[3] = Q # ThetaDot
ydot[4] = U*np.cos(Theta) - W*np.sin(Theta) # X_Edot (earth axes)
ydot[5] = U*np.sin(Theta) + W*np.cos(Theta) # Z_Edot (earth axes)
ydot[6] = 0 # No change to elevator

return ydot

To determine the transient aircraft response, first the trim state must be determined.

Trim State Determination


By definition, the trim case is found by setting the accelerations and angular velocities to zero.
Hence the equations of motion become:

0 = T − D cos α + L cos α − mg sin Θ

0 = −L cos α − D sin α + mg cos Θ

0 = M A − T Δ zT

The knowns in the above are flightspeed, V , density (from altitude), ρ, and climb angle/flight
f

path angle γ (setting U and W ). These may be solved via any means you like to get the
E E

reference trim state. An example of a Newton-Raphson solver[4] is included below as TrimState


which finds trim using TotalForces:
from ambiance import Atmosphere
import numpy as np

def TotalForces(T, de, Theta, Vf, h, gamma):


'''This gives the total forces for the HS 125 simplified longitudinal
aircraft
Inputs: Thrust/N, elevator deflection/deg, theta/rad, flightspeed/m/s,
flightpath/rad
altitude/m
'''

# Aircraft Parameters (all in SI)


m=7484.4 # mass = 7484.4kg etc.
s=32.8 #
Iyy=84309
cbar=2.29
dZt=-0.378
rho=1.225
CD0=0.177
CDa=0.232
CDa2= 1.393
CL0=0.895
CLa=5.01
CLde=0.722
CM0=-0.046
CMa=-1.087
CMde=-1.88
CMq=-7.055
Thrust=13878
g = 9.80665

qh = 0 # Trim definition

rho = Atmosphere(h).density

# Get the Ue and Ve


alpha = Theta - gamma
CL = CL0 + CLa*alpha + CLde*de
CD = CD0 + CDa*alpha + CDa2*alpha*alpha
CM = CM0 + CMa*alpha + CMde * de + CMq*qh

q_infs = .5*rho*Vf**2*s

# Dimensional lift
lift = q_infs*CL
drag = q_infs*CD
pm = q_infs*cbar*CM

# Determine lift and drag


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

F[0] = T - drag*np.cos(alpha) + lift*np.sin(alpha) - m*g*np.sin(Theta)


F[1] = -lift*np.cos(alpha) -drag*np.sin(alpha) + m*g*np.cos(Theta)
F[2] = pm - T*dZt
return F

def TrimState(Vf = 120 * 0.5144444, h = 0, gamma = 0):


'''This function solves for the longitudinal trim of the HS125 using a Newton-
Raphson method'''

# First guess and increments for the jacobian <---- may need to adjust these
T = 15000
dT = 1
de = 0*np.pi/180
dde = .01*np.pi/180
Theta = 2*np.pi/180
dTheta = dde;

# Gets the value of the functions at the initial guess


trim = TotalForces(T, de, Theta, Vf, h, gamma);

Trimstate = np.array([[T], [de], [Theta]])

itercount = 0
while max(abs(trim)) > 1e-5:
itercount = itercount + 1
# Get value of the function
trim = TotalForces(T, de, Theta, Vf, h, gamma);

# Get the Jacobian approximation (3 x 3)


JT = np.squeeze(TotalForces(T + dT, de, Theta, Vf, h, gamma)/dT)
Jde = np.squeeze(TotalForces(T, de + dde, Theta, Vf, h, gamma)/dde)
JTheta = np.squeeze(TotalForces(T, de, Theta + dTheta, Vf, h, gamma)/dTheta)
Jac = np.transpose(np.array([JT, Jde, JTheta]))

# Get the next iteration


Trimstate = Trimstate - np.dot(np.linalg.inv(Jac), trim)

T = Trimstate[0]
de = Trimstate[1]
Theta = Trimstate[2]

print(f"Converged after {itercount} iterations")


print(f'For inputs of Vf = {Vf:1.2f}m/s, h = {h/1e3:1.2f}km, gamma =
{gamma:1.2f}deg\n')
print(f'Thrust = {T[0]/1e3:1.2f}kN, de = {np.degrees(de[0]):1.2f}deg, Theta =
{np.degrees(Theta[0]):1.2f}deg\n')

return Trimstate

TrimState();

Converged after 1390 iterations


For inputs of Vf = 61.73m/s, h = 0.00km, gamma = 0.00deg

Thrust = 13.84kN, de = -0.98deg, Theta = 0.84deg

For a trim input of sea-level and 120kn, the trim is found to be - T = 13.84 kN, δ e = −0.98

,
Θ = 0.84

. A quick sense check shows that the aircraft is slightly nose up (less than a degree)
so the aerodynamic pitching moment from the wing/fuselage will be nose-up, and the elevator
is deflected trailing edge up to balance the pitching moment.

Transient Simulation
For level flight (trim), the initial conditions are given from from the previous step - if the aircraft
remains undisturbed, then there will be no variation in any of the parameters. The nonlinear
equations cannot be solved analytically, so if the aircraft response to control inputs is desired,
then a numerical scheme must be used.

The case of a positive elevator deflection corresponding to a stick-forward displacement will be


explored. First, the response to a δ will be explored - this is equivalent to the pilot
′ ∘
e
= 1

pushing the stick forward and holding it, keeping other controls constant.

The equations are solved numerically using scipy’s odeint (which is very similar to ODE45 in
MATLAB)
# Set the inputs
de = -1
de = np.radians(de)
h = 0
Vf = 120*.51444
gamma = 0

# Determine the trim state


trimstate = TrimState(Vf, h, gamma)
alpha = trimstate[2] - gamma
Ue = Vf*np.cos(alpha)
We = Vf*np.sin(alpha)
Qe = 0
ThetaE = trimstate[2]
Xe = 0
Ze = h;

# Initial conditions
from scipy.integrate import odeint
y0 = np.array([Ue[0], We[0], Qe, ThetaE[0], Xe, Ze, (de + trimstate[1])[0]])

t = np.linspace(0, 100, 1000)


output = odeint(eqnofmotion800, y0, t, args=(trimstate[0],))

U = output[:, 0]
W = output[:, 1]
Q = output[:, 2]
Theta = output[:, 3]
Alt = output[:, 5]
alpha = np.arctan2(W, U)
FS = np.sqrt(U**2 + W**2)

# Plot 'em
from plotly.subplots import make_subplots
import plotly.graph_objects as go

fig = make_subplots(rows=3, cols=2, subplot_titles=("Forward Speed", "Heave


Velocity", "Pitch Rate", "Pitch Attitude", "Altitude", "AoA"))

fig.add_trace(
go.Scatter(x = t, y = U, showlegend=False), row=1, col=1)

fig.add_trace(
go.Scatter(x = t, y = W, showlegend=False), row=1, col=2)

fig.add_trace(
go.Scatter(x = t, y = Q, showlegend=False), row=2, col=1)

fig.add_trace(
go.Scatter(x = t, y = Theta, showlegend=False), row=2, col=2)

fig.add_trace(
go.Scatter(x = output[:, 4], y = Alt, showlegend=False), row=3, col=1)

fig.add_trace(
go.Scatter(x = t, y = alpha, showlegend=False), row=3, col=2)

# Store these data for later


tNonlinear = t
Unonlinear = U
Wnonlinear = W
Qnonlinear = Q
Thetanonlinear = Theta

fig.update_xaxes(title_text="Time", row=1, col=1)


fig.update_yaxes(title_text=f"U / (m/s)", row=1, col=1)
fig.update_xaxes(title_text="Time", row=1, col=2)
fig.update_yaxes(title_text=f"W / (m/s)", row=1, col=2)
fig.update_xaxes(title_text="Time", row=2, col=1)
fig.update_yaxes(title_text="Q / (deg/s)", row=2, col=1)
fig.update_xaxes(title_text="Time", row=2, col=2)
fig.update_yaxes(title_text="θ / deg", row=2, col=2)
fig.update_xaxes(title_text="Horizontal Distance", row=3, col=1)
fig.update_yaxes(title_text="Alt / m", row=3, col=1)
fig.update_xaxes(title_text="Time", row=3, col=2)
fig.update_yaxes(title_text="AoA / deg", row=3, col=2)

Converged after 1390 iterations


For inputs of Vf = 61.73m/s, h = 0.00km, gamma = 0.00deg

Thrust = 13.84kN, de = -0.98deg, Theta = 0.84deg


U / (m/s)
Q / (deg/s)
Alt / m

Xu =
62
60
58
56
54

600
400
200
0
0

0.04

0.02

TotalForces gives values of the



∂X

∂u

⎢⎥
⎣ ˙⎦

0
θ

0

˙
Forward Speed

x =
2000

M u ≜ M u + M ẇ Z u



Time
Pitch Rate

Time
Altitude

≜ M δ 0 + M ẇ Z δ 0
50

50

Horizontal Distance


u

is, if the longitudinal force is a function of the aircraft states:

example

1
=
m
1
[
M
Zw
4000

It should be noted that the values above are total values and NOT perturbational values. We can
compare a linear model of the HS125 with the reduced nonlinear model, above - but rather than
sourcing HS 125 data of unknown legacy, it makes more sense (but more work) to use the
HS125 model to produce numerical derivatives based upon small perturbation theory using the
model itself.

This section shall stick with the HS 125 business jet. For the longitudinal equations of motion:



⎤ ⎡
Xu

Zu
Xw

0
w

Ax + Bu→
0

U0


q

M w ≜ M w + M ẇ Z w


100

θ / deg
100

AoA / deg

−g ⋅ cos θ 0

−g ⋅ sin θ 0

M θ ≜ −M ẇ g sin θ 0

For each of the derivatives contained above, a central-difference approach may be utilised. That

X = f (U , W , Q, Θ 0 , δ e 0 )

then each of the stability derivatives may be expressed as a numerical partial derivative. For
W / (m/s)

0
0.15

0.05

0.06
0.05
0.04
0.03
0.02

θ
3

1
0

0.1

0
0

⎤⎡

⎦⎣

f (U 0 + δU , W 0 , Q 0 , θ 0 , δ e ) − f (U 0 − δU , W 0 , Q 0 , θ 0 , δ e )

Note that the variable that the derivative is with respect to is altered, and the others are held
constant - this is a partial derivative. This can be repeated for the remaining derivatives. This
procedure has been used below to create stability derivatives.

,
X Z , and M force and moments, and then
NumerialDerivatives uses a central difference approach with TotalForces to create the
numerical derivatives about this trim state
0

2δU
u

θ

′⎦
+


Heave Velocity


0

0

Time

Time

δ0

M q ≜ M q + M ẇ U 0
50

Pitch Attitude

Time
AoA
50

50


[δ 0 ]

0
100

100

100

]
def perturbationalForces(U, W, Q, Theta, de, h, gamma, T):
'''This gives the total forces for the HS 125 simplified longitudinal
aircraft
Inputs: Thrust/N, elevator deflection/deg, theta/rad, flightspeed/m/s,
flightpath/rad
altitude/m
'''

# Aircraft Parameters (all in SI)


m=7484.4 # mass = 7484.4kg etc.
s=32.8 #
Iyy=84309
cbar=2.29
dZt=-0.378
rho=1.225
CD0=0.177
CDa=0.232
CDa2= 1.393
CL0=0.895
CLa=5.01
CLde=0.722
CM0=-0.046
CMa=-1.087
CMde=-1.88
CMq=-7.055
Thrust=13878
g = 9.80665

qh = 0 # Trim definition

rho = float(Atmosphere(h).density[0])

# Get the Ue and We and add the perturbational values


alpha = np.arctan2(W, U)
Vf = np.sqrt(U ** 2 + W **2)
Qhat = Q * cbar / Vf

# Recalculate alpha
CL = CL0 + CLa*alpha + CLde*de
CD = CD0 + CDa*alpha + CDa2*alpha*alpha
CM = CM0 + CMa*alpha + CMde * (de) + CMq*(Qhat)

q_infs = .5*rho*Vf**2*s

# Dimensional lift
lift = q_infs*CL
drag = q_infs*CD
pm = q_infs*cbar*CM

# Determine lift and drag


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

F[0] = float(T - drag*np.cos(alpha) + lift*np.sin(alpha) - m*g*np.sin(Theta))


F[1] = float(-lift*np.cos(alpha) -drag*np.sin(alpha) + m*g*np.cos(Theta))
F[2] = float(pm - T*dZt)
return F

def numericalDerivatives(Vf=120 * .514444, h = 0, de = np.radians(1), gamma=0):


"""
This approach uses a central difference small perturbation approach to estimate
the numerical
value of the stability derivatives
"""
trimstate = TrimState(Vf, h, gamma)
T = trimstate[0]
de = trimstate[1]
alpha = trimstate[2] - gamma
Ue = Vf * np.cos(alpha)
We = Vf * np.sin(alpha)
Qe = 0
ThetaE = trimstate[2]
Xe = 0
Ze = h

# Determine numerical derivatives


m=7484.4 #%mass = 7484.4kg etc.
Iyy=84309
g = 9.80665
# Get Xu
dUe = 2 #Vf * 0.01
dWe = 2 #Vf * 0.01
dQe = 0.1
dde = de * 0.01

# Derivatives wrt u
Xpde, Zpde, Mpde = perturbationalForces(Ue+dUe, We, Qe, ThetaE, de, h, gamma, T)
Xmde, Zmde, Mmde = perturbationalForces(Ue-dUe, We, Qe, ThetaE, de, h, gamma, T)

Xu = 1 / m *(Xpde - Xmde) /2 /dUe


Zu = 1 / m*(Zpde - Zmde) /2 /dUe
Mu = 1 / Iyy *(Mpde - Mmde) /2 /dUe

# Derivatives wrt w
[Xpdw, Zpdw, Mpdw] = perturbationalForces(Ue, We+dWe, Qe, ThetaE, de, h, gamma,
T)
[Xmdw, Zmdw, Mmdw] = perturbationalForces(Ue, We-dWe, Qe, ThetaE, de, h, gamma,
T)
Xw = 1 /m*(Xpdw - Xmdw) /2 /dWe
Zw = 1 /m*(Zpdw - Zmdw) /2 /dWe;
Mw = 1 /Iyy*(Mpdw - Mmdw) /2 /dWe;

# Derivatives wrt q
[Xpdq, Zpdq, Mpdq] = perturbationalForces(Ue, We, Qe+dQe, ThetaE, de, h, gamma,
T)
[Xmdq, Zmdq, Mmdq] = perturbationalForces(Ue, We, Qe-dQe, ThetaE, de, h, gamma,
T)
Xq = 1 / m *(Xpdq - Xmdq) /2 /dQe;
Zq = 1 / m *(Zpdq - Zmdq) /2 /dQe;
Mq = 1 / Iyy *(Mpdq - Mmdq) /2 /dQe;

# Derivatives wrt elevator


[_, Zpde, Mpde] = perturbationalForces(Ue, We, Qe, ThetaE, de+dde, h, gamma, T)
[_, Zmde, Mmde] = perturbationalForces(Ue, We, Qe, ThetaE, de-dde, h, gamma, T)
Zde = 1 / m *(Zpde - Zmde) /2 /dde;
Mde = 1 / Iyy *(Mpde - Mmde) /2 /dde;

# Put them into an A matrix


A = np.matrix(
[
[float(Xu), float(Xw), 0, -float(g*np.cos(ThetaE))],
[float(Zu), float(Zw), float(Ue), -float(g*np.sin(ThetaE))],
[float(Mu), float(Mw), float(Mq), 0],
[0, 0, 1, 0]
]
)

print(f"At this trim state, the approximation of the A matrix is:")


print(A)

print('Control matrix approximation:')


B = np.matrix(
[
[0], [float(Zde)], [float(Mde)], [0]
]
)
print(B)
return A, B

Alon, Blon = numericalDerivatives()

Converged after 1390 iterations


For inputs of Vf = 61.73m/s, h = 0.00km, gamma = 0.00deg

Thrust = 13.84kN, de = -0.98deg, Theta = 0.84deg

At this trim state, the approximation of the A matrix is:


[[-5.70764610e-02 1.25051144e-01 0.00000000e+00 -9.80559977e+00]
[-3.05079979e-01 -8.63347157e-01 6.17266688e+01 -1.43517817e-01]
[-1.47362059e-03 -3.66687419e-02 -5.44243612e-01 0.00000000e+00]
[ 0.00000000e+00 0.00000000e+00 1.00000000e+00 0.00000000e+00]]
Control matrix approximation:
[[ 0. ]
[-7.38503134]
[-3.90965145]
[ 0. ]]

With the system and control matrix above, the response of the aircraft to the same input
disturbance may be evaluated.
import control
import control.matlab

# We will make a B matrix to enable us to use the control system toolbox by _exciting
the aircraft_ through
# Elevator input. Turn from Zde in 1/radians to 1/degree to put a useful input in.
SIunits=True

# Turn the matrices into a state space object


LonSS = control.StateSpace(Alon, Blon, np.eye(Alon.shape[0]), np.zeros(Blon.shape))

# Look at the first 100 seconds response to a unit impulse in the only
timeVec = np.linspace(0, 100, 1000)
Time, [u, w, q, theta] = control.forced_response(LonSS, U=de *
np.ones(timeVec.shape), T=timeVec)

# u, w, q, theta = u[0], w[0], q[0], theta[0]

# Add back on the trim values

# Convert q and theta


q = np.degrees(q)
theta = np.degrees(theta)

from plotly.subplots import make_subplots


import plotly.graph_objects as go

fig = make_subplots(rows=2, cols=2, subplot_titles=("Forward Speed", "Heave


Velocity", "Pitch Rate", "Pitch Attitude"))

fig.add_trace(
go.Scatter(x = Time, y = u + Ue, showlegend=True, name="Linear",
line_color="blue"), row=1, col=1)

# tNonlinear = t
# Unonlinear = U
# Wnonlinear = W
# Qnonlinear = Q
# Thetanonlinear = Theta

fig.add_trace(
go.Scatter(x = tNonlinear, y = Unonlinear, line_color="blue", showlegend=True,
line={"dash":"dash"}, name="Nonlinear"), row=1, col=1)

fig.add_trace(
go.Scatter(x = Time, y = w+We, showlegend=False, line_color="blue"), row=1,
col=2)

fig.add_trace(
go.Scatter(x = tNonlinear, y = Wnonlinear, showlegend=False, line_color="blue",
line={"dash":"dash"}, name="Nonlinear"), row=1, col=2)

fig.add_trace(
go.Scatter(x = Time, y = np.radians(q), showlegend=False, line_color="blue"),
row=2, col=1)

fig.add_trace(
go.Scatter(x = Time, y = Qnonlinear, showlegend=False, line_color="blue", line=
{"dash":"dash"}), row=2, col=1)

fig.add_trace(
go.Scatter(x = Time, y = np.radians(theta), showlegend=False, line_color="blue"),
row=2, col=2)

fig.add_trace(
go.Scatter(x = Time, y = Thetanonlinear, showlegend=False, line_color="blue",
line={"dash":"dash"}), row=2, col=2)

# Make a label based upon the units


if SIunits:
speedlabel = "m/s"
else:
speedlabel = "ft/s"

fig.update_xaxes(title_text="Time", row=1, col=1)


fig.update_yaxes(title_text=f"u / ({speedlabel})", row=1, col=1)
fig.update_xaxes(title_text="Time", row=1, col=2)
fig.update_yaxes(title_text=f"w / ({speedlabel})", row=1, col=2)
fig.update_xaxes(title_text="Time", row=2, col=1)
fig.update_yaxes(title_text="q / (deg/s)", row=2, col=1)
fig.update_xaxes(title_text="Time", row=2, col=2)
fig.update_yaxes(title_text="θ / deg", row=2, col=2)
fig.show()
Forward Speed Heave Velocity
Linear
60 3
Nonlinear

w / (m/s)
u / (m/s)

2
55

1
0 50 100 0 50 100

Time Time

Pitch Rate Pitch Attitude


0.04
0.15
q / (deg/s)

0.02

θ / deg
0.1

0 0.05

0
0 50 100 0 50 100

Time Time

The linear model matches the response pretty well - with differences only really occuring after
significant time has passed.

It is clear that the aircraft response in pitch is a couple of two characteristic motions - one
highly-damped with a short period, and one with a much longer period. These are the short
period and Phugoid motions, respectively. They will be discussed in further detail in the following
chapter, but for the present discussion it can be seen that the linear model initially correlates
very well with the nonlinear model, but that it slowly begins to drift apart. This is to be expected
from any linear model - particularly as the model diverges away from the trim point at which the
stability derivatives were evaluated.

From an analysis standpoint, it is difficult to de-couple the motion in the first few seconds - the
motion from 0-5s comprises the superposition of short-period and Phugoid mode. Whilst the
Phugoid motion can be removed from the plot numerically, this requires determination of the
final value.

This highlights a chief benefit of the linear equation of motion - as will be explored in the next
Chapter, system stability information (ω , ζ ) can be determined directly from the system matrix
n

- with no need for transient analysis.

[4] You will not be tested on things like this in an examination in this course, but it’s really in your
interest to ensure that you can a) understand how this solver works and b) be confident you could
write something that does the same job yourself.

By Harry Smith
© Copyright 2022.

Aircraft Flight Mechanics by Harry Smith is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
Based on a work at www.aircraftflightmechanics.com.

You might also like