Quaternion
Quaternions are hypercomplex number with 4 dimensions that can be used to represent 3D rotations. In this package, a quaternion $\mathbf{q}$ is represented by
using the following immutable structure:
struct Quaternion{T<:Real}
q0::T
q1::T
q2::T
q3::T
endInitialization
There are several ways to create a quaternion.
Provide all the elements:
julia> q = Quaternion(1.0, 0.0, 0.0, 0.0)
Quaternion{Float64}:
+ 1.0 + 0.0.i + 0.0.j + 0.0.kProvide the real and imaginary parts as separated numbers:
julia> r = sqrt(2)/2;
julia> v = [sqrt(2)/2; 0; 0];
julia> q = Quaternion(r,v)
Quaternion{Float64}:
+ 0.7071067811865476 + 0.7071067811865476.i + 0.0.j + 0.0.kProvide the real and imaginary parts as one single vector:
julia> v = [1.;2.;3.;4.];
julia> q = Quaternion(v)
Quaternion{Float64}:
+ 1.0 + 2.0.i + 3.0.j + 4.0.kProvide just the imaginary part, in this case the real part will be 0:
julia> v = [1.;0.;0.];
julia> q = Quaternion(v)
Quaternion{Float64}:
+ 0.0 + 1.0.i + 0.0.j + 0.0.kCreate an identity quaternion using the
eyefunction:
julia> q = eye(Quaternion) # Creates an identity quaternion of type `Float64`.
Quaternion{Float64}:
+ 1.0 + 0.0.i + 0.0.j + 0.0.k
julia> q = eye(Quaternion{Float32}) # Creates an identity quaternion of type `Float32`.
Quaternion{Float32}:
+ 1.0 + 0.0.i + 0.0.j + 0.0.k
julia> a = eye(q) # Creates an identity quaternion with the same type of `q`.
Quaternion{Float32}:
+ 1.0 + 0.0.i + 0.0.j + 0.0.kCreate a zero quaternion using the
zerosfunction:
julia> q = zeros(Quaternion) # Creates a zero quaternion of type `Float64`.
Quaternion{Float64}:
+ 0.0 + 0.0.i + 0.0.j + 0.0.k
julia> q = zeros(Quaternion{Float32}) # Creates a zero quaternion of type `Float32`.
Quaternion{Float32}:
+ 0.0 + 0.0.i + 0.0.j + 0.0.k
julia> a = zeros(q) # Creates a zero quaternion with the same type of `q`.
Quaternion{Float32}:
+ 0.0 + 0.0.i + 0.0.j + 0.0.kIndividual elements of the quaternion can be accessed by:
q.q0
q.q1
q.q2
q.q3Since the type Quaternion is immutable, its components cannot be changed individually after the creation. Hence, the following operation will lead to an error:
q.q0 = 1.0 # This is not defined and will not work.If you want to modify a single value for the quaternion, then you need to create another one:
q = Quaternion(1.0, q.q1, q.q2, q.q3)This can be annoying sometimes, but using an immutable type provided a huge performance boost for the algorithm.
Operations
The sum between quaternions and the multiplication between a quaternion and a scalar are defined as usual:
julia> q1 = Quaternion(1.0,1.0,0.0,0.0);
julia> q2 = Quaternion(1.0,2.0,3.0,4.0);
julia> q1+q2
Quaternion{Float64}:
+ 2.0 + 3.0.i + 3.0.j + 4.0.kjulia> q1 = Quaternion(1.0,2.0,3.0,4.0);
julia> q1*3
Quaternion{Float64}:
+ 3.0 + 6.0.i + 9.0.j + 12.0.k
julia> 4*q1
Quaternion{Float64}:
+ 4.0 + 8.0.i + 12.0.j + 16.0.kThere are also the following functions available:
julia> q = Quaternion(1.0,2.0,3.0,4.0);
julia> conj(q) # Returns the complex conjugate of the quaternion.
Quaternion{Float64}:
+ 1.0 - 2.0.i - 3.0.j - 4.0.k
julia> copy(q) # Creates a copy of the quaternion.
Quaternion{Float64}:
+ 1.0 + 2.0.i + 3.0.j + 4.0.k
julia> inv(q) # Computes the multiplicative inverse of the quaternion.
Quaternion{Float64}:
+ 0.03333333333333333 - 0.06666666666666667.i - 0.1.j - 0.13333333333333333.k
julia> inv(q)*q
Quaternion{Float64}:
+ 1.0 + 0.0.i + 5.551115123125783e-17.j + 0.0.k
julia> imag(q) # Returns the vectorial / imaginary part of the quaternion.
3-element StaticArrays.SArray{Tuple{3},Float64,1,3}:
2.0
3.0
4.0
julia> norm(q) # Computes the norm of the quaternion.
5.477225575051661
julia> real(q) # Returns the real part of the quaternion.
1.0
julia> vect(q) # Returns the vectorial / imaginary part of the quaternion.
3-element StaticArrays.SArray{Tuple{3},Float64,1,3}:
2.0
3.0
4.0The operation a/q is equal to a*inv(q) if a is a scalar.
The multiplication between quaternions is also defined using the Hamilton product:
Hence:
julia> q1 = Quaternion(cosd(15), sind(15), 0.0, 0.0);
julia> q2 = Quaternion(cosd(30), sind(30), 0.0, 0.0);
julia> q1*q2
Quaternion{Float64}:
+ 0.7071067811865475 + 0.7071067811865475.i + 0.0.j + 0.0.kIf a quaternion $\mathbf{q}$ is multiplied by a vector $\mathbf{v}$, then the vector is converted to a quaternion with real part 0, $\mathbf{q}_v = 0 + \mathbf{v}$, and the quaternion multiplication is performed as usual:
Hence:
julia> q1 = Quaternion(cosd(22.5), sind(22.5), 0.0, 0.0);
julia> v = [0;1;0];
julia> v*q1
Quaternion{Float64}:
+ 0.0 + 0.0.i + 0.9238795325112867.j - 0.3826834323650898.k
julia> q1*v
Quaternion{Float64}:
+ 0.0 + 0.0.i + 0.9238795325112867.j + 0.3826834323650898.kConverting reference frames using quaternions
Given the reference frames A and B, let $\mathbf{w}$ be a unitary vector in which a rotation about it of an angle $\theta$ aligns the reference frame A with the reference frame B (in this case, $\mathbf{w}$ is aligned with the Euler Axis and $\theta$ is the Euler angle). Construct the following quaternion:
Then, a vector $\mathbf{v}$ represented in reference frame A ($\mathbf{v}_a$) can be represented in reference frame B using:
Hence:
julia> qBA = Quaternion(cosd(22.5), sind(22.5), 0.0, 0.0);
julia> vA = [0;1;0];
julia> vB = vect(inv(qBA)*vA*qBA);
julia> vB
3-element StaticArrays.SArray{Tuple{3},Float64,1,3}:
0.0
0.707107
-0.707107A SArray is returned instead of the usual Array. This is a static vector created by the package StaticArrays. Generally, you can treat this vector as any other one. The only downside is that you cannot modify individual components because it is immutable.