Euler Angles

The Euler Angles are defined by the following immutable structure:

struct EulerAngles{T}
    a1::T
    a2::T
    a3::T
    rot_seq::Symbol
end

in which a1, a2, and a3 define the angles and the rot_seq is a symbol that defines the axes. The valid values for rot_seq are:

  • :XYX, :XYZ, :XZX, :XZY, :YXY, :YXZ, :YZX, :YZY, :ZXY, :ZXZ, :ZYX, and ZYZ.

The constructor for this structure is:

function EulerAngles(a1::T1, a2::T2, a3::T3, rot_seq::Symbol = :ZYX) where {T1,T2,T3}

in which a EulerAngles with angles a1, a2, and a3 [rad] and rotation sequence rot_seq will be created. Notice that the type of the returned structure will be selected according to the input types T1, T2, and T3. If rot_seq is omitted, then it defaults to :ZYX.

julia> EulerAngles(1, 1, 1)EulerAngles{Int64}:
  R(Z) :  1 rad  ( 57.2958°)
  R(Y) :  1 rad  ( 57.2958°)
  R(X) :  1 rad  ( 57.2958°)
julia> EulerAngles(1, 1, 1.0f0, :XYZ)EulerAngles{Float32}: R(X) : 1.0 rad ( 57.2958°) R(Y) : 1.0 rad ( 57.2958°) R(Z) : 1.0 rad ( 57.2958°)
julia> EulerAngles(1., 1, 1, :XYX)EulerAngles{Float64}: R(X) : 1.0 rad ( 57.2958°) R(Y) : 1.0 rad ( 57.2958°) R(X) : 1.0 rad ( 57.2958°)

Operations

Multiplication

The multiplication of two Euler angles is defined here as the composition of the rotations. Let $\Theta_1$ and $\Theta_2$ be two sequences of Euler angles (instances of the structure EulerAngles). Thus, the operation:

\[\Theta_{2,1} = \Theta_2 \cdot \Theta_1\]

will return a new set of Euler angles $\Theta_{2,1}$ that represents the composed rotation of $\Theta_1$ followed by $\Theta_2$. Notice that $\Theta_{2,1}$ will be represented using the same rotation sequence as $\Theta_2$.

julia> a1 = EulerAngles(1, 0, 0, :ZYX)EulerAngles{Int64}:
  R(Z) :  1 rad  ( 57.2958°)
  R(Y) :  0 rad  ( 0.0°)
  R(X) :  0 rad  ( 0.0°)
julia> a2 = EulerAngles(0, -1, 0, :YZY)EulerAngles{Int64}: R(Y) : 0 rad ( 0.0°) R(Z) : -1 rad (-57.2958°) R(Y) : 0 rad ( 0.0°)
julia> a2 * a1EulerAngles{Float64}: R(Y) : 0.0 rad ( 0.0°) R(Z) : 0.0 rad ( 0.0°) R(Y) : 0.0 rad ( 0.0°)
julia> a1 = EulerAngles(1, 1, 1, :YZY)EulerAngles{Int64}: R(Y) : 1 rad ( 57.2958°) R(Z) : 1 rad ( 57.2958°) R(Y) : 1 rad ( 57.2958°)
julia> a2 = EulerAngles(0, 0, -1, :YZY)EulerAngles{Int64}: R(Y) : 0 rad ( 0.0°) R(Z) : 0 rad ( 0.0°) R(Y) : -1 rad (-57.2958°)
julia> a2 * a1EulerAngles{Float64}: R(Y) : 1.0 rad ( 57.2958°) R(Z) : 1.0 rad ( 57.2958°) R(Y) : 1.31938e-16 rad ( 7.55951e-15°)
julia> a1 = EulerAngles(1.3, 2.2, 1.4, :XYZ)EulerAngles{Float64}: R(X) : 1.3 rad ( 74.4845°) R(Y) : 2.2 rad ( 126.051°) R(Z) : 1.4 rad ( 80.2141°)
julia> a2 = EulerAngles(-1.4, -2.2, -1.3, :ZYX)EulerAngles{Float64}: R(Z) : -1.4 rad (-80.2141°) R(Y) : -2.2 rad (-126.051°) R(X) : -1.3 rad (-74.4845°)
julia> a2 * a1EulerAngles{Float64}: R(Z) : -8.32667e-17 rad (-4.77083e-15°) R(Y) : 3.33067e-16 rad ( 1.90833e-14°) R(X) : -1.11022e-16 rad (-6.36111e-15°)

Inversion

The inv function applied to Euler angles will return the inverse rotation. If the Euler angles $\Theta$ represent a rotation through the axes $a_1$, $a_2$, and $a_3$ by angles $\alpha_1$, $\alpha_2$, and $\alpha_3$, then $\Theta^{-1}$ is a rotation through the axes $a_3$, $a_2$, and $a_1$ by angles $-\alpha_3$, $-\alpha_2$, and $-\alpha_1$.

julia> a = EulerAngles(1, 2, 3, :ZYX)EulerAngles{Int64}:
  R(Z) :  1 rad  ( 57.2958°)
  R(Y) :  2 rad  ( 114.592°)
  R(X) :  3 rad  ( 171.887°)
julia> inv(a)EulerAngles{Int64}: R(X) : -3 rad (-171.887°) R(Y) : -2 rad (-114.592°) R(Z) : -1 rad (-57.2958°)
julia> a = EulerAngles(1.2, 3.3, 4.6, :XYX)EulerAngles{Float64}: R(X) : 1.2 rad ( 68.7549°) R(Y) : 3.3 rad ( 189.076°) R(X) : 4.6 rad ( 263.561°)
julia> a * inv(a)EulerAngles{Float64}: R(X) : -1.92593e-34 rad (-1.10348e-32°) R(Y) : 0.0 rad ( 0.0°) R(X) : 0.0 rad ( 0.0°)
Warning

All the operations related to Euler angles first convert them to DCM or Quaternions, and then the result is converted back to Euler angles. Hence, the performance will not be good.