## Equations

heading = atan2(y * sin(angle)- x * z * (1 - cos(angle)) , 1 - (y^{2}
+ z^{2} ) * (1 - cos(angle)))

attitude = asin(x * y * (1 - cos(angle)) + z * sin(angle))

bank = atan2(x * sin(angle)-y * z * (1 - cos(angle)) , 1 - (x^{2} +
z^{2}) * (1 - cos(angle)))

except at the singularities, straight up:

heading = 2 * atan2(x * sin(angle/2),cos(angle/2))

bank = 0

straight down:

heading = -2 * atan2(x * sin(angle/2),cos(angle/2))

bank = 0

## Issues

- It is better to use atan2 function than atan function as described here. Note the warning on this page about the order of operands.
- This assumes the axis is already normalised so: ax*ax + ay*ay + az*az = 1
- For issues concerning asin and atan see here.
- Most trig functions (except openGL) use radians not degrees.

If you have a different result from that shown on this page it may be that you are using different standards, I have tried to keep the standards consistant accross this site and I have tried to define the standards that I am using here. One of these standards is that the order that the euler rotations are applied is 'NASA standard aeroplane' it uses a slightly different coordinate definition from VRML (z and y axis swapped). Also when working with aeroplanes we often work in terms of the position of external objects relative to the aircraft (i.e. the inverse of its position transform as explained here). therefore to get the expression normally used with NASA aeroplane we invert all inputs (change sign of every term with odd number of sine terms) invert output (conjugate quaternion) swap z and y for inputs (swap z and y columns) swap z and y for outputs (swap z and y rows). In the case of aircraft it can make sense to work in terms of the local frame of reference of the aircraft looking out, I have a version of this page that does that here, but be careful as this will no longer be compatible with the rest of this site.

However if you have checked these things and you still have a discrepency then I have probably made an error so please let me know here.

## Code

public void toEuler(double x,double y,double z,double angle) { double s=Math.sin(angle); double c=Math.cos(angle); double t=1-c; // if axis is not already normalised then uncomment this // double magnitude = Math.sqrt(x*x + y*y + z*z); // if (magnitude==0) throw error; // x /= magnitude; // y /= magnitude; // z /= magnitude; if ((x*y*t + z*s) > 0.998) { // north pole singularity detected heading = 2*atan2(x*Math.sin(angle/2),Math.cos(angle/2)); attitude = Math.PI/2; bank = 0; return; } if ((x*y*t + z*s) < -0.998) { // south pole singularity detected heading = -2*atan2(x*Math.sin(angle/2),Math.cos(angle/2)); attitude = -Math.PI/2; bank = 0; return; } heading = Math.atan2(y * s- x * z * t , 1 - (y*y+ z*z ) * t); attitude = Math.asin(x * y * t + z * s) ; bank = Math.atan2(x * s - y * z * t , 1 - (x*x + z*z) * t); }

## Derivation of Equations

Euler angles represent 3 rotations about the x,y and z axis in some given order. We can replace any sequence of rotations by one single rotation about some axis.

We need to be very careful about using Euler Angles and it is best to work in terms of quaternions or matricies whenever we can. As explained in euler section there are different types of Euler angles and the result will depend on the Euler definition used. Also the following calculations use a lot of trig functions and therefor will use a lot of CPU time. The accuracy may be low, specially near the singularity of the Euler system being used.

With these warnings in mind here is the calculation.

Euler can be defined in terms of a quaternion as shown here.

heading = atan2(2*qy*qw-2*qx*qz , 1 - 2*qy^{2} - 2*qz^{2})

attitude = asin(2*qx*qy + 2*qz*qw)

bank = atan2(2*qx*qw-2*qy*qz , 1 - 2*qx^{2} - 2*qz^{2})

This quaternion can be defined in terms of axis-angle as shown here.

qx = x * sin(angle/2)

qy = y * sin(angle/2)

qz = z * sin(angle/2)

qw = cos(angle/2)

So working out some products gives:

qx*qw = x * sin(angle/2) * cos(angle/2) = 0.5 * x * sin(angle) // using trig identity: sin(angle) = 2 sin(angle/2) cos(angle/2) from here

qy*qw = y * sin(angle/2) * cos(angle/2) = 0.5 * y * sin(angle)

qz*qw = z * sin(angle/2) * cos(angle/2) = 0.5 * z * sin(angle)

qx*qy = x * y * sin^{2}(angle/2) = x * y * (0.5 - 0.5*cos(angle)) //
using trig identity cos(angle) = 1 - 2 sin2(angle/2) from
here

qy*qz = y * z * sin^{2}(angle/2) = y * z * (0.5 - 0.5*cos(angle))

qx*qz = x * z * sin^{2}(angle/2) = x * z * (0.5 - 0.5*cos(angle))

qx^{2} = x^{2} * sin^{2}(angle/2) = x^{2} *
(0.5 - 0.5*cos(angle))

qy^{2} = y^{2} * sin^{2}(angle/2) = y^{2} *
(0.5 - 0.5*cos(angle))

qz^{2} = z^{2} * sin^{2}(angle/2) = z^{2} *
(0.5 - 0.5*cos(angle))

qw^{2} =cos^{2}(angle/2) = 0.5 * (cos(angle)+1) // using trig
identity cos(2A) = 2 cos2(A) - 1 from
here

So substituting in euler equations gives:

heading = atan2(2*qy*qw-2*qx*qz , 1 - 2*qy^{2} - 2*qz^{2})

attitude = asin(2*qx*qy + 2*qz*qw)

bank = atan2(2*qx*qw-2*qy*qz , 1 - 2*qx^{2} - 2*qz^{2})

heading = atan2(2*0.5 * y * sin(angle)-2*x * z * (0.5 - 0.5*cos(angle)) , 1
- 2*y^{2} * (0.5 - 0.5*cos(angle)) - 2*z^{2} * (0.5 - 0.5*cos(angle)))

attitude = asin(2*x * y * (0.5 - 0.5*cos(angle)) + 2*0.5 * z * sin(angle))

bank = atan2(2*0.5 * x * sin(angle)-2*y * z * (0.5 - 0.5*cos(angle)) , 1 - 2*x^{2}
* (0.5 - 0.5*cos(angle)) - 2*z^{2} * (0.5 - 0.5*cos(angle)))

heading = atan2(y * sin(angle)- x * z * (1 - cos(angle)) , 1 - y^{2}
* (1 - cos(angle)) - z^{2} * (1 - cos(angle)))

attitude = asin(x * y * (1 - cos(angle)) + z * sin(angle))

bank = atan2(x * sin(angle)-y * z * (1 - cos(angle)) , 1 - x^{2} * (1
- cos(angle)) - z^{2} * (1 - cos(angle)))

heading = atan2(y * sin(angle)- x * z * (1 - cos(angle)) , 1 - (y^{2}
+ z^{2} ) * (1 - cos(angle)))

attitude = asin(x * y * (1 - cos(angle)) + z * sin(angle))

bank = atan2(x * sin(angle)-y * z * (1 - cos(angle)) , 1 - (x^{2} +
z^{2}) * (1 - cos(angle)))

### singularities

On the quaternion to euler page that except when we have a singularity when we are going straight up or down

this happens at qx*qy + qz*qw = 0.5 and qx*qy + qz*qw = -0.5

north pole | south pole |
---|---|

qx*qy + qz*qw = 0.5 x * y * (0.5 - 0.5*cos(θ)) + 0.5 * z * sin(θ) = 0.5 x * y * (1 - cos(θ)) + z * sin(θ) = 1 |
qx*qy + qz*qw = -0.5 x * y * (1 - cos(θ)) + z * sin(θ) = -1 |

at this point: heading = 2 * atan2(qx,qw) bank = 0 |
at this point: heading = -2 * atan2(qx,qw) bank = 0 |

where:

- θ = angle

In order to try to get an intuative understanding of the singularities involved in converting other representations of 3D rotations to Euler angles it may help to look at the way we project the surface of a sphere onto a 2 dimensional map. This is a different case and it only involves two angles, latitude and longitude, but this simpler example may show the principles. This analogy is explained in more detail on this page.

At the north and south poles longitude does not matter so if we are converting some other description of north pole to latitude, longitude it might give any value for longitude, for example, infinity. So we have to be very careful at these points.

Similarly we can map Euler angles to quaternions (4 dimensional hypersphere). This maps a one dimensional space (rotations around 0,1,0 axis) to a two dimensional plane in Euler terms. This is where attitude = 90° and heading, bank vary:

On this plane lines of common orientation are diagonal lines, that is rotation around 0,1,0 axis are given by angle = heading+bank.

Similarly for the south pole.

## Example

we take the 90 degree rotation from this: | to this: |

As shown here the axis angle for this rotation is:

angle = 90 degrees

axis = 1,0,0

So using the above result:

heading = atan2(y * sin(angle)- x * z * (1 - cos(angle)) , 1 - (y^{2}
+ z^{2} ) * (1 - cos(angle)))

attitude = asin(x * y * (1 - cos(angle)) + z * sin(angle))

bank = atan2(x * sin(angle)-y * z * (1 - cos(angle)) , 1 - (x^{2} +
z^{2}) * (1 - cos(angle)))

we substitute our axis angle: angle=90 degrees, sin(angle) =1, 1 - cos(angle) =1, x=1, y=0, z=0

heading = atan2(0 / 1 )

attitude = asin(0 )

bank = atan2(1 / 0)

which gives:

heading = 0 degrees

bank = 90 degrees (since the tangent of 90 degrees is infinity)

attitude = 0 degrees

So this gives the correct result, it is banking by 90 degrees, but we have to be very careful about the following issues:

- One of the intermediate steps involves infinity (division by 0) therefore most maths libraries have an atan2 function to avoid this step.
- Most maths libraries use radians instead of degrees (apart from OpenGL).

## Angle Calculator and Further examples

I have put a java applet here which allows the values to be entered and the converted values shown along with a graphical representation of the orientation.

Also further examples in 90 degree steps here