|
1 | 1 | # -*- coding: utf-8 -*-
|
2 | 2 | """
|
3 |
| -Dummy example |
4 |
| -
|
5 |
| -@author: asaez |
| 3 | +Python Flight Mechanics Engine (PyFME). |
| 4 | +Copyright (c) AeroPython Development Team. |
| 5 | +Distributed under the terms of the MIT License. |
| 6 | +
|
| 7 | +Example with trimmed aircraft. |
| 8 | +The main purpose of this example is to check if the aircraft trimmed in a given |
| 9 | +state maintains the trimmed flight condition. |
| 10 | +Trimmed in stationary, horizontal, symmetric, wings level flight. |
6 | 11 | """
|
7 | 12 |
|
8 | 13 | import numpy as np
|
9 | 14 | import matplotlib.pyplot as plt
|
10 | 15 |
|
| 16 | +from pyfme.aircrafts import cessna_310 |
11 | 17 | from pyfme.models.system import System
|
| 18 | +from pyfme.utils.trimmer import steady_state_flight_trim |
| 19 | +from pyfme.utils.anemometry import calculate_alpha_beta_TAS |
| 20 | +from pyfme.environment.isa import atm |
12 | 21 |
|
13 | 22 |
|
| 23 | +if __name__ == '__main__': |
14 | 24 |
|
15 |
| -def forces(): |
16 |
| - return np.array([0, 0, -9.8*500]) |
17 |
| - |
18 |
| - |
19 |
| -def moments(): |
20 |
| - return np.array([0, 0, 0]) |
21 |
| - |
| 25 | + # Aircraft parameters. |
| 26 | + mass, inertia = cessna_310.mass_and_inertial_data() |
22 | 27 |
|
23 |
| -if __name__ == '__main__': |
| 28 | + # Initial conditions. |
| 29 | + TAS_ = 312 * 0.3048 # m/s |
| 30 | + h = 8000 * 0.3048 # m |
| 31 | + psi_0 = 3 # rad |
| 32 | + x_0, y_0 = 0, 0 # m |
24 | 33 |
|
25 |
| - # Case params |
26 |
| - mass = 500 |
27 |
| - inertia = np.array([[1, 0, 0], |
28 |
| - [0, 1, 0], |
29 |
| - [0, 0, 1]]) |
| 34 | + # Trimming. |
| 35 | + trim_results = steady_state_flight_trim(cessna_310, h, TAS_, gamma=0, |
| 36 | + turn_rate=0) |
30 | 37 |
|
31 |
| - # initial conditions |
32 |
| - u, v, w = 0, 0, 0 |
33 |
| - p, q, r = 0, 0, 0 |
| 38 | + lin_vel, ang_vel, theta, phi, alpha_, beta_, control_vector = trim_results |
34 | 39 |
|
35 |
| - theta, phi, psi = 0, 0, 0 |
36 |
| - x, y, z = 0, 0, 5000 |
| 40 | + # Time. |
| 41 | + t0 = 0 # s |
| 42 | + tf = 30 # s |
| 43 | + dt = 1e-2 # s |
37 | 44 |
|
38 |
| - t0 = 0 |
39 |
| - tf = 10 |
40 |
| - dt = 1e-2 |
41 | 45 | time = np.arange(t0, tf, dt)
|
42 | 46 |
|
| 47 | + # Results initialization. |
43 | 48 | results = np.zeros([time.size, 12])
|
44 | 49 |
|
45 |
| - velocities = np.empty([time.size, 6]) |
46 |
| - results[0, 0:6] = u, v, w, p, q, r |
47 |
| - results[0, 6:9] = theta, phi, psi |
48 |
| - results[0, 9:12] = x, y, z |
| 50 | + results[0, 0:3] = lin_vel |
| 51 | + results[0, 3:6] = ang_vel |
| 52 | + results[0, 6:9] = theta, phi, psi_0 |
| 53 | + results[0, 9:12] = x_0, y_0, h |
| 54 | + alpha = np.empty_like(time) |
| 55 | + alpha[0] = alpha_ |
| 56 | + beta = np.empty_like(time) |
| 57 | + beta[0] = beta_ |
| 58 | + TAS = np.empty_like(time) |
| 59 | + TAS[0] = TAS_ |
| 60 | + |
| 61 | + # Linear Momentum and Angular Momentum eqs. |
| 62 | + equations = System(integrator='dopri5', |
| 63 | + model='euler_flat_earth', |
| 64 | + jac=False) |
| 65 | + u, v, w = lin_vel |
| 66 | + p, q, r = ang_vel |
| 67 | + |
| 68 | + equations.set_initial_values(u, v, w, |
| 69 | + p, q, r, |
| 70 | + theta, phi, psi_0, |
| 71 | + x_0, y_0, h) |
| 72 | + |
| 73 | + _, _, rho, _ = atm(h) |
| 74 | + |
| 75 | + # Define control vectors. |
| 76 | + d_e, d_a, d_r, d_t = control_vector |
| 77 | + |
| 78 | + attitude = theta, phi, psi_0 |
| 79 | + |
| 80 | + # Rename function to make it shorter |
| 81 | + forces_and_moments = cessna_310.get_forces_and_moments |
| 82 | + for ii, t in enumerate(time[1:]): |
49 | 83 |
|
50 |
| - # Linear Momentum and Angular Momentum eqs |
51 |
| - equations = System(integrator='dopri5', model='euler_flat_earth', jac=False) |
52 |
| - equations.set_initial_values(u, v, w, p, q, r, theta, phi, psi, x, y, z) |
| 84 | + forces, moments = forces_and_moments(TAS[ii], rho, alpha[ii], beta[ii], |
| 85 | + d_e, 0, d_a, d_r, d_t, attitude) |
53 | 86 |
|
| 87 | + results[ii+1, :] = equations.propagate(mass, inertia, forces, moments, |
| 88 | + dt) |
54 | 89 |
|
55 |
| - for ii, t in enumerate(time[1:]): |
56 |
| - results[ii+1, :] = equations.propagate(mass, inertia, forces(), moments(), |
57 |
| - dt=dt) |
| 90 | + lin_vel = results[ii+1, 0:3] |
| 91 | + ang_vel = results[ii+1, 3:6] |
| 92 | + attitude = results[ii+1, 6:9] |
| 93 | + position = results[ii+1, 9:12] |
| 94 | + |
| 95 | + alpha[ii+1], beta[ii+1], TAS[ii+1] = calculate_alpha_beta_TAS(*lin_vel) |
| 96 | + |
| 97 | + _, _, rho, _ = atm(position[2]) |
58 | 98 |
|
59 | 99 | velocities = results[:, 0:6]
|
60 | 100 | attitude_angles = results[:, 6:9]
|
61 | 101 | position = results[:, 9:12]
|
62 | 102 |
|
63 |
| - # TODO: improve graphics: legend title.... |
| 103 | + # PLOTS |
| 104 | + plt.close('all') |
| 105 | + plt.style.use('ggplot') |
| 106 | + |
64 | 107 | plt.figure('pos')
|
65 |
| - plt.plot(time, position[:, 0]) |
66 |
| - plt.plot(time, position[:, 1]) |
67 |
| - plt.plot(time, position[:, 2]) |
| 108 | + plt.plot(time, position[:, 0], label='x') |
| 109 | + plt.plot(time, position[:, 1], label='y') |
| 110 | + plt.plot(time, position[:, 2], label='z') |
| 111 | + plt.xlabel('time (s)') |
| 112 | + plt.ylabel('position (m)') |
| 113 | + plt.legend() |
68 | 114 |
|
69 | 115 | plt.figure('angles')
|
70 |
| - plt.plot(time, attitude_angles[:, 0]) |
71 |
| - plt.plot(time, attitude_angles[:, 1]) |
72 |
| - plt.plot(time, attitude_angles[:, 2]) |
| 116 | + plt.plot(time, attitude_angles[:, 0], label='theta') |
| 117 | + plt.plot(time, attitude_angles[:, 1], label='phi') |
| 118 | + plt.plot(time, attitude_angles[:, 2], label='psi') |
| 119 | + plt.xlabel('time (s)') |
| 120 | + plt.ylabel('attitude (rad)') |
| 121 | + plt.legend() |
73 | 122 |
|
74 | 123 | plt.figure('velocities')
|
75 |
| - plt.plot(time, velocities[:, 0]) |
76 |
| - plt.plot(time, velocities[:, 1]) |
77 |
| - plt.plot(time, velocities[:, 2]) |
| 124 | + plt.plot(time, velocities[:, 0], label='u') |
| 125 | + plt.plot(time, velocities[:, 1], label='v') |
| 126 | + plt.plot(time, velocities[:, 2], label='w') |
| 127 | + plt.plot(time, TAS, label='TAS') |
| 128 | + plt.xlabel('time (s)') |
| 129 | + plt.ylabel('velocity (m/s)') |
| 130 | + plt.legend() |
78 | 131 |
|
79 | 132 | plt.figure('ang velocities')
|
80 |
| - plt.plot(time, velocities[:, 3]) |
81 |
| - plt.plot(time, velocities[:, 4]) |
82 |
| - plt.plot(time, velocities[:, 5]) |
| 133 | + plt.plot(time, velocities[:, 3], label='p') |
| 134 | + plt.plot(time, velocities[:, 4], label='q') |
| 135 | + plt.plot(time, velocities[:, 5], label='r') |
| 136 | + plt.xlabel('time (s)') |
| 137 | + plt.ylabel('angular velocity (rad/s)') |
| 138 | + plt.legend() |
| 139 | + |
| 140 | + plt.figure('aero angles') |
| 141 | + plt.plot(time, alpha, label='alpha') |
| 142 | + plt.plot(time, beta, label='beta') |
| 143 | + plt.xlabel('time (s)') |
| 144 | + plt.ylabel('angle (rad)') |
| 145 | + plt.legend() |
| 146 | + |
| 147 | + plt.figure('2D Trajectory') |
| 148 | + plt.plot(position[:, 0], position[:, 1]) |
| 149 | + plt.xlabel('X (m)') |
| 150 | + plt.ylabel('Y (m)') |
| 151 | + plt.legend() |
0 commit comments