A rotation, as the quaternion wxyz Monty passes around.
Mirrors tbp/monty/geometry.py::Rotation, which wraps scipy's
Rotation — so what is mirrored here is the behaviour Monty relies on, not
scipy's surface: rotation vectors, rotation matrices, Euler angles and
quaternions each convert to the others, a rotation can be inverted, and it can
be applied to a vector or a matrix of them.
Two conventions are kept exactly as scipy has them, because getting either wrong rotates a reference frame without any number looking wrong:
- a quaternion is
{w, x, y, z}here (Monty'sQuaternionWXYZ), while the[x, y, z, w]order is what scipy returns fromas_quat()— the conversion functions say which is which; from_euler("xyz", angles, degrees: true)is scipy's extrinsic order: rotate about x, then about the (fixed) y, then about the (fixed) z.
Summary
Functions
The rotation that best aligns two sets of vectors: scipy's
Rotation.align_vectors.
Applies this rotation to a 3-vector, or to each row of a list of them.
The rotation of angle radians about axis.
The rotation of three Euler angles in degrees, applied about the fixed x, then
y, then z axes (scipy's from_euler("xyz", angles, degrees=True)).
The rotation a 3×3 rotation matrix describes.
The rotation a quaternion {w, x, y, z} describes, normalised.
The rotation of no angle, w = 1.
The rotation that undoes this one.
The angle this rotation turns through, in radians, in [0, π].
The weighted mean of several rotations: the rotation closest to all of them.
This rotation after other: applying the result is applying other then this.
The three Euler angles in degrees about the fixed x, y and z axes.
The three Euler angles about the fixed x, y and z axes, in radians.
The 3×3 rotation matrix this rotation applies.
The quaternion {w, x, y, z}.
The quaternion [x, y, z, w], which is the order scipy returns.
Types
Functions
The rotation that best aligns two sets of vectors: scipy's
Rotation.align_vectors.
Answers {rotation, rssd}, where rotation turns each b towards the a it
was paired with, and rssd is what is left over — the square root of the
weighted sum of squared distances, sqrt(Σ wᵢ |aᵢ − R bᵢ|²), which is zero
when the two sets are the same up to a rotation.
scipy minimises that sum by singular value decomposition (the Kabsch
algorithm). The same minimiser is the leading eigenvector of a 4×4 symmetric
matrix built from the two sets' correlation — Horn's closed form — and that is
what this uses, because mean/2 already finds leading eigenvectors the same
way. Both answer a proper rotation, never a reflection.
Two sets that lie on one line do not determine a rotation about that line. scipy warns and answers with one of the rotations that fit; so does this, and any of them is as close as the others.
Applies this rotation to a 3-vector, or to each row of a list of them.
The rotation of angle radians about axis.
A zero axis has no rotation to describe, and {:error, :zero_axis} is the
honest answer — scipy normalises the axis and produces NaN, which is worse.
The rotation of three Euler angles in degrees, applied about the fixed x, then
y, then z axes (scipy's from_euler("xyz", angles, degrees=True)).
Extrinsic order means the composed rotation is Rz · Ry · Rx: x turns
first, in the frame the axes were given in.
The rotation a 3×3 rotation matrix describes.
The rotation a quaternion {w, x, y, z} describes, normalised.
@spec identity() :: t()
The rotation of no angle, w = 1.
The rotation that undoes this one.
The angle this rotation turns through, in radians, in [0, π].
This is what Monty compares against pose_similarity_threshold: two rotations
are the same pose when rotating from one to the other turns by less than the
threshold.
The weighted mean of several rotations: the rotation closest to all of them.
Averaging rotation matrices and calling the result a rotation gives
something that is not orthonormal, so the average is the rotation nearest to
the weighted sum — scipy's Rotation.mean, which is the chordal L2 mean.
Quaternions make that a dominant-eigenvector problem: the sum of weighted
outer products of the quaternions has the answer as its leading eigenvector.
The dominant eigenvector is found by power iteration. Where the two leading eigenvalues tie, more than one rotation is equally close to the inputs and any of them is a correct answer.
This rotation after other: applying the result is applying other then this.
@spec to_euler_degrees(t(), non_neg_integer() | nil) :: vector()
The three Euler angles in degrees about the fixed x, y and z axes.
The angles are rounded to decimal_places, because Monty compares them as
labels (np.round(pose.as_euler(...), 3) % 360) and an unrounded 359.9999
would read as a different pose from 0.0. nil leaves them as computed, which
is what scipy answered and what a log keeps.
The three Euler angles about the fixed x, y and z axes, in radians.
scipy's as_euler("xyz"), unrounded — which is what Monty compares when it
asks whether a pose has changed: any difference, however small, is a
difference. Use to_euler_degrees/2 to show angles to a reader instead.
The 3×3 rotation matrix this rotation applies.
The quaternion {w, x, y, z}.
The quaternion [x, y, z, w], which is the order scipy returns.