Guidance, Navigation and Control · Theory · M10
M10-T3 — Air Data, Angle of Attack and Wind
An aircraft flies in air that is moving. Air data measures the vehicle against the air, the inertial system against the ground, and the wind is what they disagree by.
Navigation and control use two velocities: relative to the ground (what the position needs) and relative to the air (what the aerodynamics needs). Their difference is the wind. This lesson defines angle of attack and sideslip from the air-relative velocity, computes the wind from the velocity triangle with the error each input contributes, and shows that a single full turn lets the airspeed sensor be calibrated against the satellite velocity: the ground velocity traces a circle whose centre is the wind and whose radius is the true airspeed.
Learning objectives
After this lesson, you should be able to:
- Compute angle of attack and sideslip from the components of the air-relative velocity.
- Estimate the wind vector from ground velocity, airspeed and heading, and find the error each source contributes.
- Estimate angle of attack from inertial data and the effect of an unmodelled vertical wind.
- Calibrate an airspeed sensor and find the wind from the ground-velocity circle of a turn.
The intuition
UAV M5-T1: UAV equations of motion gives the wind triangle for a multirotor, and M7-T5: The Aiding Sensors of Aircraft and Spacecraft derived the pressure-based altitude and airspeed from their measurement equations. This lesson uses both to answer a different question: given what the sensors say, what are the angle of attack, the sideslip and the wind? Hopper's range has a wind that varies with height, and every term below applies to its descent as well.
Angles from the air-relative velocity
The air-relative velocity of the aircraft has body-axis components . The angle of attack is , the angle between the velocity and the body x axis in the plane of symmetry, and the sideslip is . These are what the aerodynamic forces depend on: lift on , side force on . They are measured by vanes, flush pressure ports or pitot probes with several holes, or estimated from inertial data and a wind estimate: the angle of attack is the pitch angle minus the air-relative flight path angle.
The cost of an unknown vertical wind
If the inertial flight path angle is used in place of the air-relative one, the angle of attack inherits the vertical wind: . A vertical wind of 1 m/s at 25 m/s is 2.3°, of 2 m/s 4.6°: large against a stall angle of about 15°. A rising thermal or a downdraft is invisible to the inertial system, which is why angle of attack from inertial data alone is a poor stall-protection signal.
Wind from the triangle
The ground velocity is the air velocity plus the wind: . With from the satellite receiver and from the airspeed, heading and angles, the wind is the difference. Each input contributes an error that goes straight into the wind: a heading error of 2° at 20 m/s gives 0.70 m/s; an airspeed scale error of 5% gives 1.00 m/s; both together give 1.23 m/s. Wind estimates are only as good as the heading and the airspeed calibration.
The circle
Fly a full turn at constant airspeed and heading rate. The air velocity vector rotates through 360° with fixed length, so the ground velocity, which adds the constant wind, traces a circle: centre the wind, radius the true airspeed. A circle fit to the satellite velocity returns both, without needing the heading or the airspeed sensor at all. Fitting 72 samples with 0.15 m/s of noise gives a centre of (6.02, −3.51) m/s (true (6.0, −3.5)) and a radius of 20.04 m/s (true 20.0): the sensor that reads 21.0 m/s is high by a factor of 1.048 (true 1.05). Over a 90° arc only, the wind error is 0.33 m/s and the airspeed error 0.25 m/s.
Angle of attack and sideslip need the air-relative velocity; the ground velocity differs from it by the wind. Wind from the triangle inherits every heading and airspeed error, and a full turn calibrates both against the satellite velocity.
Definitions
- Air-relative velocity
- The velocity of the aircraft with respect to the air mass, with body components and magnitude .
- Angle of attack and sideslip
- , .
- Wind
- The velocity of the air mass over the ground: .
- Flight path angle
- The angle of the velocity above the horizontal; in the plane of symmetry for the air-relative angle .
- Airspeed scale factor
- The ratio of the indicated to the true airspeed.
Derivation: the wind errors, and the circle fit
Wind errors from the triangle, and why a turn gives a circle.
North and east components of the air velocity from the airspeed and the heading ψ.
Differentiate. The airspeed error acts along the heading and the heading error across it, with magnitudes and .
The two are orthogonal, so they add in root sum square: m/s for 5% airspeed (1.0 m/s) and 2° (0.70 m/s), matching the computed 1.23.
The circle
During the turn the wind is constant and the air-velocity direction χ(t) sweeps round while its magnitude V stays constant.
Every ground-velocity sample is at the same distance V from the point w: a circle of radius V centred at the wind.
Write the circle in the form linear in its parameters: a least-squares fit of to the samples is a 3 × 3 linear system.
The scale factor of the airspeed sensor is the indicated airspeed over the fitted radius.
The wind is the difference between the ground velocity and the air velocity, so its error is the sum, in quadrature, of the airspeed error along the heading and the heading error across it. A turn makes the ground velocity trace a circle that exposes both the wind and the true airspeed.
A worked example
Numbers from the program for a vehicle at 25 m/s and for a turn at 20 m/s.
- Angles. For α = 4° and β = 0, the air-relative components are u = 24.94, v = 0, w = 1.74 m/s, and the inverse relations return 4.00° and 0. For α = 10°, β = 3°: u = 24.59, v = 1.31, w = 4.34 m/s, returning 10.00° and 3.00°.
- Inertial alpha. With pitch 8° and flight path angle 5°, α = 3°. An unmodelled vertical wind of 0.5, 1.0 and 2.0 m/s shifts the estimate by 1.15°, 2.29° and 4.57°.
- Wind from the triangle. Ground velocity (18.4, 7.2) m/s north and east, heading 38°, airspeed 20 m/s: wind (2.64, −5.11) m/s, 5.75 m/s in speed. A heading error of 2° gives a wind error of 0.70 m/s; an airspeed 5% high or low, 1.00 m/s; heading +2° and airspeed +5% together, 1.23 m/s.
- The circle. A full turn at a true airspeed of 20 m/s with a wind of (6.0, −3.5) m/s and 0.15 m/s of velocity noise (72 samples): the fitted centre is (6.02, −3.51) m/s and the radius 20.04 m/s. The sensor reads 21.0 m/s, so the scale factor is 1.048 against the true 1.05.
- A partial arc. From a 90° arc only, the centre is (6.26, −3.30) m/s and the radius 19.75 m/s: errors of 0.33 m/s in the wind and 0.25 m/s in the airspeed, larger because the fit is poorly conditioned along the arc.
import math
import gnckit as g
D2R = math.pi / 180
def lcg(seed):
s = seed
while True:
s = (1103515245 * s + 12345) % 2147483648
yield (s / 2147483648.0 - 0.5) * 3.4641016
print('Angle of attack and sideslip from the air-relative velocity in body axes (u forward, v right, w down):')
V = 25.0
for alpha_deg, beta_deg in ((4.0, 0.0), (10.0, 3.0)):
a, b = alpha_deg * D2R, beta_deg * D2R
u = V * math.cos(a) * math.cos(b); v = V * math.sin(b); w = V * math.sin(a) * math.cos(b)
print(f' alpha {alpha_deg:4.1f} deg, beta {beta_deg:3.1f} deg: u = {u:.2f}, v = {v:.2f}, w = {w:.2f} m/s; back: alpha = atan2(w, u) = {math.degrees(math.atan2(w, u)):.2f}, beta = asin(v / V) = {math.degrees(math.asin(v / V)):.2f}')
print('Angle of attack from inertial data: alpha = theta - gamma, with gamma from the ground velocity corrected for the vertical wind')
theta, gamma = 8.0, 5.0
print(f' pitch {theta} deg, flight path angle {gamma} deg: alpha = {theta - gamma:.1f} deg')
for wz in (0.0, 0.5, 1.0, 2.0):
err = math.degrees(math.atan2(wz, V))
print(f' an unmodelled vertical wind of {wz:3.1f} m/s (up) shifts the air-relative flight path angle by {err:5.2f} deg, so the alpha estimate is wrong by the same')
print('Wind from the wind triangle: wind = ground velocity - air velocity, with the air velocity from airspeed, heading and angles')
vg = (18.4, 7.2) # ground velocity, north and east (m/s) from GNSS
Va, psi = 20.0, 38.0 * D2R
wind_true = (vg[0] - Va * math.cos(psi), vg[1] - Va * math.sin(psi))
print(f' heading {math.degrees(psi):.0f} deg, airspeed {Va:.0f} m/s: wind = ({wind_true[0]:+.2f}, {wind_true[1]:+.2f}) m/s, speed {math.hypot(*wind_true):.2f} m/s')
def wind_est(Va_m, psi_m):
return (vg[0] - Va_m * math.cos(psi_m), vg[1] - Va_m * math.sin(psi_m))
for label, Va_m, psi_m in (('heading off by 2 deg', Va, psi + 2 * D2R), ('airspeed 5% high', Va * 1.05, psi), ('airspeed 5% low', Va * 0.95, psi), ('both: heading +2 deg, airspeed +5%', Va * 1.05, psi + 2 * D2R)):
w = wind_est(Va_m, psi_m)
e = math.hypot(w[0] - wind_true[0], w[1] - wind_true[1])
print(f' {label:36s}: wind error {e:.2f} m/s')
print('A circle in the ground-velocity plane: flying a full turn, the ground velocity traces a circle of radius = true airspeed centred on the wind')
rnd = lcg(5)
Vtrue, scale = 20.0, 1.05 # the airspeed sensor reads 5% high
wind = (6.0, -3.5)
pts = []
for k in range(72):
ang = 2 * math.pi * k / 72
pts.append((wind[0] + Vtrue * math.cos(ang) + 0.15 * next(rnd), wind[1] + Vtrue * math.sin(ang) + 0.15 * next(rnd)))
n = len(pts)
Sx = sum(p[0] for p in pts); Sy = sum(p[1] for p in pts)
Sxx = sum(p[0] ** 2 for p in pts); Syy = sum(p[1] ** 2 for p in pts); Sxy = sum(p[0] * p[1] for p in pts)
rhs = [-sum((p[0] ** 2 + p[1] ** 2) * p[0] for p in pts), -sum((p[0] ** 2 + p[1] ** 2) * p[1] for p in pts), -sum(p[0] ** 2 + p[1] ** 2 for p in pts)]
a_, b_, c_ = g.mat_solve([[Sxx, Sxy, Sx], [Sxy, Syy, Sy], [Sx, Sy, float(n)]], rhs)
cx, cy = -a_ / 2, -b_ / 2
R = math.sqrt(cx * cx + cy * cy - c_)
print(f' circle fit of 72 ground-velocity samples (0.15 m/s noise): centre ({cx:+.2f}, {cy:+.2f}) m/s (true wind {wind}), radius {R:.2f} m/s (true airspeed {Vtrue})')
print(f' indicated airspeed {scale * Vtrue:.1f} m/s against the fitted {R:.2f}: scale factor estimate {scale * Vtrue / R:.3f} (true {scale})')
print(' a partial arc of 90 degrees only:')
sub = pts[:18]
n2 = len(sub)
Sx = sum(p[0] for p in sub); Sy = sum(p[1] for p in sub)
Sxx = sum(p[0] ** 2 for p in sub); Syy = sum(p[1] ** 2 for p in sub); Sxy = sum(p[0] * p[1] for p in sub)
rhs = [-sum((p[0] ** 2 + p[1] ** 2) * p[0] for p in sub), -sum((p[0] ** 2 + p[1] ** 2) * p[1] for p in sub), -sum(p[0] ** 2 + p[1] ** 2 for p in sub)]
a2, b2, c2 = g.mat_solve([[Sxx, Sxy, Sx], [Sxy, Syy, Sy], [Sx, Sy, float(n2)]], rhs)
cx2, cy2 = -a2 / 2, -b2 / 2
R2 = math.sqrt(cx2 * cx2 + cy2 * cy2 - c2)
print(f' centre ({cx2:+.2f}, {cy2:+.2f}), radius {R2:.2f}: the wind error is {math.hypot(cx2 - wind[0], cy2 - wind[1]):.2f} m/s and the airspeed error {abs(R2 - Vtrue):.2f} m/s: the geometry of the arc matters')Angle of attack and sideslip from the air-relative velocity in body axes (u forward, v right, w down): alpha 4.0 deg, beta 0.0 deg: u = 24.94, v = 0.00, w = 1.74 m/s; back: alpha = atan2(w, u) = 4.00, beta = asin(v / V) = 0.00 alpha 10.0 deg, beta 3.0 deg: u = 24.59, v = 1.31, w = 4.34 m/s; back: alpha = atan2(w, u) = 10.00, beta = asin(v / V) = 3.00 Angle of attack from inertial data: alpha = theta - gamma, with gamma from the ground velocity corrected for the vertical wind pitch 8.0 deg, flight path angle 5.0 deg: alpha = 3.0 deg an unmodelled vertical wind of 0.0 m/s (up) shifts the air-relative flight path angle by 0.00 deg, so the alpha estimate is wrong by the same an unmodelled vertical wind of 0.5 m/s (up) shifts the air-relative flight path angle by 1.15 deg, so the alpha estimate is wrong by the same an unmodelled vertical wind of 1.0 m/s (up) shifts the air-relative flight path angle by 2.29 deg, so the alpha estimate is wrong by the same an unmodelled vertical wind of 2.0 m/s (up) shifts the air-relative flight path angle by 4.57 deg, so the alpha estimate is wrong by the same Wind from the wind triangle: wind = ground velocity - air velocity, with the air velocity from airspeed, heading and angles heading 38 deg, airspeed 20 m/s: wind = (+2.64, -5.11) m/s, speed 5.75 m/s heading off by 2 deg : wind error 0.70 m/s airspeed 5% high : wind error 1.00 m/s airspeed 5% low : wind error 1.00 m/s both: heading +2 deg, airspeed +5% : wind error 1.23 m/s A circle in the ground-velocity plane: flying a full turn, the ground velocity traces a circle of radius = true airspeed centred on the wind circle fit of 72 ground-velocity samples (0.15 m/s noise): centre (+6.02, -3.51) m/s (true wind (6.0, -3.5)), radius 20.04 m/s (true airspeed 20.0) indicated airspeed 21.0 m/s against the fitted 20.04: scale factor estimate 1.048 (true 1.05) a partial arc of 90 degrees only: centre (+6.26, -3.30), radius 19.75: the wind error is 0.33 m/s and the airspeed error 0.25 m/s: the geometry of the arc matters
In robotics
Air data and wind estimates are inputs to nearly every fixed-wing function.
- Stall protection and envelope limits. The limits of the next lesson are written in angle of attack and airspeed, so they need the air-relative angles, from vanes or a wind-corrected estimate, not inertial angles.
- Navigation. The wind estimate lets a navigation filter propagate position with the airspeed and heading during a satellite outage (dead reckoning with the wind as a slowly varying state), and the wind compensates the guidance of the path-following law of module 8.
- Calibration flights. Orbit manoeuvres (full turns) and box patterns in calm conditions calibrate the airspeed scale factor and the pitot position error; the circle fit is the simplest form and needs only the satellite velocity.
- Heading dependence. The wind error from the heading is proportional to airspeed, so for fast aircraft a heading error of 1° at 100 m/s is 1.7 m/s: wind estimation needs a good heading, which is why it comes from the filter and not from a magnetic compass alone.
- Turbulence. The wind is not constant: gusts over a few seconds change it by several m/s. A wind estimator is a filter with a model of the wind as a slowly varying state and a process noise set from the turbulence level.
The loops that use these angles are in M10-T2: The Autopilot by Successive Loop Closure, and the touchdown requirement that depends on them is the flare of M10-T4: Approach and Automatic Landing. The pressure-to-airspeed conversion was derived in M7-T5: The Aiding Sensors of Aircraft and Spacecraft.
Common misconceptions
“Angle of attack is the pitch angle.”
It is the angle between the velocity relative to the air and the body axis. In a climb at 8° pitch and a 5° path, α is 3°; with an unknown vertical wind of 1 m/s it is wrong by 2.3° if the inertial path is used.
“Groundspeed and airspeed differ only in a headwind.”
They differ by the whole wind vector, including its crosswind component, which changes the heading needed to track a line (the crab angle) as well as the ground speed.
“A better airspeed sensor gives a better wind.”
The wind error also contains the heading error times the airspeed (0.70 m/s for 2° at 20 m/s), which no airspeed sensor improvement removes. Both inputs set the error.
“The circle fit needs the heading.”
It needs only the satellite velocity: the circle's centre and radius give the wind and the true airspeed directly. That independence is what makes it a calibration of the airspeed sensor.
“A short arc is enough for the circle fit.”
A 90° arc gave a wind error of 0.33 m/s and an airspeed error of 0.25 m/s against 0.02 m/s and 0.04 m/s for the full circle: the fit is poorly conditioned when the points cover little of the circle.
Key takeaways
- α = atan2(w, u) and β = arcsin(v/Va) are defined from the air-relative velocity.
- An unmodelled vertical wind wz shifts an inertial angle-of-attack estimate by about wz/V: 2.3° for 1 m/s at 25 m/s.
- The wind is vg − va; its error is : 1.23 m/s for 5% airspeed and 2° heading at 20 m/s.
- In a full turn the ground velocity traces a circle with centre the wind and radius the true airspeed.
- A circle fit of 72 samples recovered the wind to 0.02 m/s and the airspeed scale factor to 1.048 (true 1.05).
- A 90° arc gives errors of 0.33 and 0.25 m/s: the fit needs the arc to cover the circle.
References and further reading
Each source is marked free or paid. Free to read does not mean free to reuse: the course resources page lists every source with its licence.
- R. W. Beard and T. W. McLain, Small Unmanned Aircraft: Theory and Practice, Princeton University Press, 2012, the chapters on the wind triangle and on state estimation. Paid.
- T. A. Johansen, A. Cristofaro, K. Sørensen, J. M. Hansen and T. I. Fossen, "On estimation of wind velocity, angle-of-attack and sideslip angle of small UAVs using standard sensors", International Conference on Unmanned Aircraft Systems, 2015. Paid (IEEE); a pre-print is on the NTNU repository.
- W. Gracey, Measurement of Aircraft Speed and Altitude, NASA Reference Publication 1046, 1980, the classic reference. Free to read from the NASA Technical Reports Server; US government work.
- PX4 documentation, EKF2 tuning (wind estimation and airspeed scale) , for how a flight stack estimates wind and airspeed scale. Free to read; licence CC BY 4.0.
Knowledge check
Three questions. Write an answer, then reveal the explanation. Your answers stay in your browser.
Question 1
A vehicle flies at 20 m/s with a heading error of 3°. What wind error does it cause, and how does it compare with a 5% airspeed scale error?
Explanation
The heading error gives Va δψ = 20 × 0.0524 = 1.05 m/s across the heading. The 5% airspeed error gives 1.0 m/s along it. They are about equal; combined in quadrature they give √(1.05² + 1.0²) = 1.45 m/s. A crude sensor and a crude heading both matter at this speed, and for faster aircraft the heading term dominates because it scales with the airspeed.
Question 2
Ground-velocity samples over a full turn lie on a circle with centre (6, −3.5) m/s and radius 20 m/s. The airspeed indicator reads 21 m/s. What are the wind and the sensor scale factor?
Explanation
The centre of the circle is the wind: 6 m/s east and 3.5 m/s south, a speed of 6.9 m/s. The radius is the true airspeed, 20 m/s. The sensor reads 21 m/s, so its scale factor is 21/20 = 1.05, 5% high. The indicated airspeed should be divided by 1.05 (or the sensor recalibrated) before it is used in the wind triangle or in a stall-speed margin.
Question 3
Why is an inertial-only estimate of angle of attack unreliable in a thermal?
Explanation
A thermal is a vertical air movement, which the inertial system cannot see: it measures the vehicle's velocity over the ground. The angle of attack depends on the velocity relative to the air, so the estimate α = θ − γ is wrong by about wz/V: 2.3° for a 1 m/s updraft at 25 m/s. Since stall is at about 15°, that is a loss of 15% of the margin, and an up-draft makes the real α larger than the estimate, in the unsafe direction.
What’s next
Related content
Nothing below is required to finish this lesson.
- BeforeM10-T2: The Autopilot by Successive Loop Closure
Close rate, attitude and path loops in order with the bandwidth separation of module 1, and add a yaw damper and turn coordination.
Other courses on this site treat neighbouring topics from their own angle:
- UAV M1-T2: Multirotor, fixed-wing and VTOL architectures
- UAV M5-T1: UAV equations of motion (wind triangle)
- UAV M6-T6: Flight modes
- UAV M6-T3: PX4 architecture
- Navigation M9-T5: Other Platforms: Legged, Aerial, Marine and Off-Road
This course leaves the following to the pages that already teach them:
Get notified when the next lesson and new labs are released.
One email per new lesson or lab.