Unit quaternion for 3D rotations, held as four :f64 floats.
Components are named for WXYZ order (scalar first), and every operation returns a normalised unit quaternion suitable for representing a rotation.
Not tensor-backed
Like BB.Math.Vec3 and BB.Math.Transform2D, a single quaternion is far
cheaper as four BEAM floats than as an Nx tensor. A Hamilton product is
sixteen multiplies and twelve adds; dispatching those through eager Nx ops -
or through a defn, which retraces its expression graph per call - costs
orders of magnitude more than the arithmetic, and allocates every
intermediate. Orientation is built and read on every estimator sample, so that
lands squarely in the hot path. bb_estimator_ahrs used to carry its own
scalar quaternion for exactly this reason.
tensor/1 and from_tensor/1 convert at the boundary of code that genuinely
wants tensors, as do to_rotation_matrix/1 and from_rotation_matrix/1.
Examples
iex> BB.Math.Quaternion.new(1, 0, 0, 0)
BB.Math.Quaternion.new(1.0, 0.0, 0.0, 0.0)
iex> q = BB.Math.Quaternion.identity()
iex> BB.Math.Quaternion.w(q)
1.0
iex> q1 = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> q2 = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> q3 = BB.Math.Quaternion.multiply(q1, q2)
iex> BB.Math.Quaternion.angular_distance(q3, BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi()))
0.0
Summary
Functions
Computes the angular distance between two quaternions in radians.
Returns the conjugate of a quaternion.
Creates a quaternion from an axis-angle representation.
Creates a quaternion from Euler angles (roll, pitch, yaw).
Creates from a list in WXYZ order.
Creates a quaternion from a 3x3 rotation matrix.
Creates a quaternion from an existing {4} tensor.
Creates a quaternion representing the shortest rotation from one vector to another.
Creates from a list in XYZW order (for ROS/external system compatibility).
Returns the identity quaternion (no rotation).
Returns an identity quaternion as a raw tensor (for batch operations).
Returns the inverse of a quaternion.
Multiplies two quaternions (Hamilton product).
Creates a new quaternion from w, x, y, z components.
Normalises a quaternion to unit length.
Rotates a 3D vector by a quaternion.
Spherical linear interpolation between two quaternions.
Returns the quaternion as a {4} :f64 WXYZ tensor.
Converts a quaternion to axis-angle representation.
Converts a quaternion to Euler angles (roll, pitch, yaw).
Converts to a list in WXYZ order.
Converts a quaternion to a 3x3 rotation matrix as a list of rows.
Converts a quaternion to a 3x3 rotation matrix as a {3, 3} :f64 tensor.
Converts to a list in XYZW order (for ROS/external system compatibility).
Returns the W (scalar) component.
Returns the X component.
Returns the Y component.
Returns the Z component.
Types
Functions
Computes the angular distance between two quaternions in radians.
Returns a value between 0 and pi.
Examples
iex> q1 = BB.Math.Quaternion.identity()
iex> q2 = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> Float.round(BB.Math.Quaternion.angular_distance(q1, q2), 6)
1.570796
Returns the conjugate of a quaternion.
For unit quaternions, the conjugate equals the inverse.
Examples
iex> q = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> qc = BB.Math.Quaternion.conjugate(q)
iex> Float.round(BB.Math.Quaternion.z(qc), 6)
-0.707107
@spec from_axis_angle(BB.Math.Vec3.t(), number()) :: t()
Creates a quaternion from an axis-angle representation.
The angle is in radians.
Examples
iex> q = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> Float.round(BB.Math.Quaternion.w(q), 6)
0.707107
Creates a quaternion from Euler angles (roll, pitch, yaw).
Angles are in radians. Default order is :xyz (roll around X, pitch around Y, yaw around Z).
Supported orders: :xyz, :zyx
Examples
iex> q = BB.Math.Quaternion.from_euler(0, 0, :math.pi() / 2, :xyz)
iex> Float.round(BB.Math.Quaternion.z(q), 6)
0.707107
Creates from a list in WXYZ order.
Examples
iex> q = BB.Math.Quaternion.from_list([1.0, 0.0, 0.0, 0.0])
iex> BB.Math.Quaternion.w(q)
1.0
@spec from_rotation_matrix(Nx.Tensor.t() | [[number()]]) :: t()
Creates a quaternion from a 3x3 rotation matrix.
Accepts a {3, 3} tensor or a list of three three-element rows. Uses the
Shepperd method for numerical stability: the branch is chosen so the value
under the square root is largest, which keeps the division well conditioned.
Examples
iex> m = Nx.tensor([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
iex> q = BB.Math.Quaternion.from_rotation_matrix(m)
iex> BB.Math.Quaternion.w(q)
1.0
@spec from_tensor(Nx.Tensor.t()) :: t()
Creates a quaternion from an existing {4} tensor.
The tensor should be in WXYZ order. It will be normalised.
@spec from_two_vectors(BB.Math.Vec3.t(), BB.Math.Vec3.t()) :: t()
Creates a quaternion representing the shortest rotation from one vector to another.
Both vectors should be unit vectors (they will be normalised if not).
Returns the quaternion that rotates from to align with to.
Handles edge cases:
- Parallel vectors (from ≈ to): returns identity quaternion
- Anti-parallel vectors (from ≈ -to): returns 180° rotation around a perpendicular axis
Examples
iex> q = BB.Math.Quaternion.from_two_vectors(BB.Math.Vec3.unit_x(), BB.Math.Vec3.unit_y())
iex> rotated = BB.Math.Quaternion.rotate_vector(q, BB.Math.Vec3.unit_x())
iex> {Float.round(BB.Math.Vec3.x(rotated), 6), Float.round(BB.Math.Vec3.y(rotated), 6)}
{0.0, 1.0}
iex> q = BB.Math.Quaternion.from_two_vectors(BB.Math.Vec3.unit_z(), BB.Math.Vec3.unit_z())
iex> BB.Math.Quaternion.w(q)
1.0
Creates from a list in XYZW order (for ROS/external system compatibility).
Examples
iex> q = BB.Math.Quaternion.from_xyzw_list([0.0, 0.0, 0.0, 1.0])
iex> BB.Math.Quaternion.w(q)
1.0
@spec identity() :: t()
Returns the identity quaternion (no rotation).
Examples
iex> q = BB.Math.Quaternion.identity()
iex> {BB.Math.Quaternion.w(q), BB.Math.Quaternion.x(q), BB.Math.Quaternion.y(q), BB.Math.Quaternion.z(q)}
{1.0, 0.0, 0.0, 0.0}
@spec identity_tensor() :: Nx.Tensor.t()
Returns an identity quaternion as a raw tensor (for batch operations).
Returns the inverse of a quaternion.
For unit quaternions, this equals the conjugate.
Examples
iex> q = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> qi = BB.Math.Quaternion.inverse(q)
iex> qr = BB.Math.Quaternion.multiply(q, qi)
iex> Float.round(BB.Math.Quaternion.w(qr), 6)
1.0
Multiplies two quaternions (Hamilton product).
This composes the rotations: multiply(q1, q2) applies q2 first, then q1.
Examples
iex> q1 = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> q2 = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> q3 = BB.Math.Quaternion.multiply(q1, q2)
iex> {_axis, angle} = BB.Math.Quaternion.to_axis_angle(q3)
iex> Float.round(angle, 6)
3.141593
Creates a new quaternion from w, x, y, z components.
The quaternion is automatically normalised.
Examples
iex> q = BB.Math.Quaternion.new(1, 0, 0, 0)
iex> BB.Math.Quaternion.w(q)
1.0
Normalises a quaternion to unit length.
Falls back to the identity quaternion when the input is near-zero, so the result is always a valid unit rotation.
Examples
iex> q = BB.Math.Quaternion.normalise(%BB.Math.Quaternion{w: 2.0, x: 0.0, y: 0.0, z: 0.0})
iex> BB.Math.Quaternion.w(q)
1.0
@spec rotate_vector(t(), BB.Math.Vec3.t()) :: BB.Math.Vec3.t()
Rotates a 3D vector by a quaternion.
Examples
iex> q = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> v = BB.Math.Vec3.unit_x()
iex> rotated = BB.Math.Quaternion.rotate_vector(q, v)
iex> {Float.round(BB.Math.Vec3.x(rotated), 6), Float.round(BB.Math.Vec3.y(rotated), 6)}
{0.0, 1.0}
Spherical linear interpolation between two quaternions.
t should be between 0.0 and 1.0.
Examples
iex> q1 = BB.Math.Quaternion.identity()
iex> q2 = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi())
iex> q_mid = BB.Math.Quaternion.slerp(q1, q2, 0.5)
iex> {_axis, angle} = BB.Math.Quaternion.to_axis_angle(q_mid)
iex> Float.round(angle, 6)
1.570796
@spec tensor(t()) :: Nx.Tensor.t()
Returns the quaternion as a {4} :f64 WXYZ tensor.
@spec to_axis_angle(t()) :: {BB.Math.Vec3.t(), float()}
Converts a quaternion to axis-angle representation.
Returns {axis, angle} where axis is a BB.Math.Vec3 unit vector
and angle is in radians (0 to pi).
Examples
iex> q = BB.Math.Quaternion.from_axis_angle(BB.Math.Vec3.unit_z(), :math.pi() / 2)
iex> {axis, angle} = BB.Math.Quaternion.to_axis_angle(q)
iex> Float.round(angle, 6)
1.570796
iex> Float.round(BB.Math.Vec3.z(axis), 1)
1.0
Converts a quaternion to Euler angles (roll, pitch, yaw).
Returns {roll, pitch, yaw} in radians. Default order is :xyz.
Note: Euler angles can have gimbal lock issues near pitch = ±90°.
Examples
iex> q = BB.Math.Quaternion.from_euler(0.1, 0.2, 0.3, :xyz)
iex> {roll, pitch, yaw} = BB.Math.Quaternion.to_euler(q, :xyz)
iex> Float.round(roll, 6)
0.1
Converts to a list in WXYZ order.
Examples
iex> q = BB.Math.Quaternion.identity()
iex> BB.Math.Quaternion.to_list(q)
[1.0, 0.0, 0.0, 0.0]
Converts a quaternion to a 3x3 rotation matrix as a list of rows.
Examples
iex> BB.Math.Quaternion.to_rotation_list(BB.Math.Quaternion.identity())
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
@spec to_rotation_matrix(t()) :: Nx.Tensor.t()
Converts a quaternion to a 3x3 rotation matrix as a {3, 3} :f64 tensor.
Use to_rotation_list/1 to avoid building a tensor.
Examples
iex> q = BB.Math.Quaternion.identity()
iex> m = BB.Math.Quaternion.to_rotation_matrix(q)
iex> Nx.to_number(m[0][0])
1.0
Converts to a list in XYZW order (for ROS/external system compatibility).
Examples
iex> q = BB.Math.Quaternion.identity()
iex> BB.Math.Quaternion.to_xyzw_list(q)
[0.0, 0.0, 0.0, 1.0]
Returns the W (scalar) component.
Returns the X component.
Returns the Y component.
Returns the Z component.