field guide
Thrust vector from attitude angles
Mapping attitude angles to a body thrust vector in the NED frame - how a desired orientation becomes the direction the rotors push.
Given total thrust magnitude and attitude (roll , pitch , yaw ), how do you find the inertial-frame components of the force vector perpendicular to the quadcopter’s plane?
The setup
The quadcopter’s “plane” is the body-fixed xy-plane — the arms form an X or + in this plane. The vector perpendicular to it is the body-fixed z-axis, pointing up through the centre of mass (by convention).
All four rotors spin to produce thrust along this axis. In body coordinates:
Total thrust is purely along body-z. There is no body-x or body-y thrust component — those come from tilting this vector via attitude.
To find the components in the world/inertial frame, we need to rotate by the quadcopter’s current orientation. That orientation is described by the Euler angles and the rotation matrix that maps body→world.
The rotation matri·x (derived)
The world frame is NED (north-east-down) or ENU (east-north-up) depending on your convention — the math is the same either way, the axes just swap labels.
We build the rotation matrix from body to world as a sequence of three intrinsic rotations, applied in ZYX order (yaw → pitch → roll). This is the standard aeronautical convention.
Step 1 — Yaw about body z-axis:
This rotates the body’s x-y plane around the vertical axis — pointing the nose north/south/east/west.
Step 2 — Pitch about the (now-rotated) body y-axis:
Tilts the nose up or down.
Step 3 — Roll about the (now-rotated) body x-axis:
Banks left or right.
The full rotation is the product of these three matrices, applied right to left (the first rotation is rightmost):
Which expands to:
Applying to the thrust vector
Since only the third column of multiplies against the non-zero entry of , we get:
These are equations (15)–(17) from the quadcopter PID tuning paper in the vault.
Why derive the full matrix?
The third column is all you need right now — you only care about one body-axis vector (body-z → world). But the full matrix will reappear when you need to:
The rotation matrix is how you implement the coordinate transform that rotating-coordinate-frames describes for any vector measured from a rotating platform. The operation is the discrete version of the carrying term applied to the body-z thrust vector — same geometry, matrix form.
- Go the other way (world → body): invert the rotation (which for a rotation matrix is just the transpose ). You need this when taking a world-frame target direction (e.g. “fly north-east”) and decomposing it into body-relative pitch/roll commands.
- Convert angular velocities () to Euler angle rates () for your attitude estimator — that uses a different matrix but comes from the same rotation sequence. The body-rate cross-coupling — when the gyroscopic term matters and when it doesn’t — is covered in euler-body-rate-cross-coupling.
- Rotate arbitrary vectors, like wind, magnetic field, or GPS velocity into body frame for sensor fusion.
So yes, deriving it once and keeping it in a reference note is worthwhile. You’ll use different columns/rows of it as the quadcopter code grows.
In code
void thrust_body_to_world(float T, float phi, float theta, float psi,
float *fx, float *fy, float *fz) {
float c_phi = cosf(phi), s_phi = sinf(phi);
float c_th = cosf(theta), s_th = sinf(theta);
float c_psi = cosf(psi), s_psi = sinf(psi);
*fx = T * (c_phi * s_th * c_psi + s_phi * s_psi);
*fy = T * (c_phi * s_th * s_psi - s_phi * c_psi);
*fz = T * (c_phi * c_th);
}Feed from your complementary filter (MPU-6050 → quaternion → Euler angles, or direct Madgwick/Mahony output) and from your control law. The resulting goes into your position controller or state estimator as the applied force in world coordinates.
Common pitfalls
- Euler angle convention matters. ZYX (yaw → pitch → roll) is standard for aerospace. If your code uses XYZ or some other order, the matrix changes. Verify against a known test case (e.g. → thrust should tilt forward, giving positive and reduced ).
- Gimbal lock at . Euler angles become degenerate. If your quadcopter ever pitches straight vertical (unlikely in normal flight but possible in a crash), switch to quaternions in the estimator and extract the third column directly from the rotation matrix without going through Euler angles.
- Sign conventions. Make sure your attitude estimator and your controller use the same sign convention for roll/pitch. Inconsistent sign flips will cause the quad to fly in the wrong direction and likely crash.