This class is largely based on
scipy.spatial.transform.Rotation and wraps all
functionality that scipy’s Rotation class provides.
In addition the pyfar Rotation class adds the creation from perpendicular
view and up vectors through from_view_up, and the
representation as view / up in as_view_up.
A rotation can be represented with the triple of view, up and right
vectors and it is tied to the object’s local coordinate system.
Alternatively the object’s rotation can be illustrated with help of the
right hand: Thumb (view), index-finger (up) and middle finger (right).
Find a rotation between frames A and B which best aligns a set of
vectors a and b observed in these frames. The following loss
function is minimized to solve for the rotation matrix
\(C\):
where \(w_i\)’s are the weights corresponding to each vector.
The rotation is estimated with Kabsch algorithm [1], and solves what
is known as the “pointing problem”, or “Wahba’s problem” [2].
Note that the length of each vector in this formulation acts as an
implicit weight. So for use cases where all vectors need to be
weighted equally, you should normalize them to unit length prior to
calling this method.
There are two special cases. The first is if a single vector is given
for a and b, in which the shortest distance rotation that aligns
b to a is returned.
The second is when one of the weights is infinity. In this case, the
shortest distance rotation between the primary infinite weight vectors
is calculated as above. Then, the rotation about the aligned primary
vectors is calculated such that the secondary vectors are optimally
aligned per the above loss function. The result is the composition
of these two rotations. The result via this process is the same as the
Kabsch algorithm as the corresponding weight approaches infinity in
the limit. For a single secondary vector this is known as the
“align-constrain” algorithm [3].
For both special cases (single vectors or an infinite weight), the
sensitivity matrix does not have physical meaning and an error will be
raised if it is requested. For an infinite weight, the primary vectors
act as a constraint with perfect alignment, so their contribution to
rssd will be forced to 0 even if they are of different lengths.
Parameters:
a (array_like, shape (3,) or (N, 3)) – Vector components observed in initial frame A. Each row of a
denotes a vector.
b (array_like, shape (3,) or (N, 3)) – Vector components observed in another frame B. Each row of b
denotes a vector.
weights (array_like shape (N,), optional) – Weights describing the relative importance of the vector
observations. If None (default), then all values in weights are
assumed to be 1. One and only one weight may be infinity, and
weights must be positive.
return_sensitivity (bool, optional) – Whether to return the sensitivity matrix. See Notes for details.
Default is False.
Returns:
rotation (Rotation) – Best estimate of the rotation that transforms b to a.
rssd (float) – Stands for “root sum squared distance”. Square root of the weighted
sum of the squared distances between the given sets of vectors
after alignment. It is equal to sqrt(2*minimum_loss), where
minimum_loss is the loss function evaluated for the found
optimal rotation.
Note that the result will also be weighted by the vectors’
magnitudes, so perfectly aligned vector pairs will have nonzero
rssd if they are not of the same length. This can be avoided by
normalizing them to unit length prior to calling this method,
though note that doing this will change the resulting rotation.
sensitivity_matrix (ndarray, shape (3, 3)) – Sensitivity matrix of the estimated rotation estimate as explained
in Notes. Returned only when return_sensitivity is True. Not
valid if aligning a single pair of vectors or if there is an
infinite weight, in which cases an error will be raised.
Notes
The sensitivity matrix gives the sensitivity of the estimated rotation
to small perturbations of the vector measurements. Specifically we
consider the rotation estimate error as a small rotation vector of
frame A. The sensitivity matrix is proportional to the covariance of
this rotation vector assuming that the vectors in a was measured with
errors significantly less than their lengths. To get the true
covariance matrix, the returned sensitivity matrix must be multiplied
by harmonic mean [4] of variance in each observation. Note that
weights are supposed to be inversely proportional to the observation
variances to get consistent results. For example, if all vectors are
measured with the same accuracy of 0.01 (weights must be all equal),
then you should multiple the sensitivity matrix by 0.01**2 to get the
covariance.
Refer to [5] for more rigorous discussion of the covariance
estimation. See [6] for more discussion of the pointing problem and
minimal proper pointing.
This function does not support broadcasting or ND arrays with N > 2.
Any rotation can be expressed as a composition of 3 elementary
rotations.
For both Euler angles and Davenport angles, consecutive axes must
be are orthogonal (axis2 is orthogonal to both axis1 and
axis3). For Euler angles, there is an additional relationship
between axis1 or axis3, with two possibilities:
axis1 and axis3 are also orthogonal (asymmetric sequence)
axis1==axis3 (symmetric sequence)
For Davenport angles, this last relationship is relaxed [7], and only
the consecutive orthogonal axes requirement is maintained.
A slightly modified version of the algorithm from [8] has been used to
calculate Davenport angles for the rotation about a given sequence of
axes.
Davenport angles, just like Euler angles, suffer from the problem of
gimbal lock [9], where the representation loses a degree of freedom
and it is not possible to determine the first and third angles
uniquely. In this case, a warning is raised (unless the
suppress_warnings option is used), and the third angle is set
to zero. Note however that the returned angles still represent the
correct rotation.
Parameters:
axes (array_like, shape (..., [1 or 2 or 3], 3) or (..., 3)) – Axis of rotation, if one dimensional. If N dimensional, describes
the sequence of axes for rotations, where each axes[…, i, :] is
the ith axis. If more than one axis is given, then the second axis
must be orthogonal to both the first and third axes.
order (string) – If it belongs to the set {‘e’, ‘extrinsic’}, the sequence will be
extrinsic. If it belongs to the set {‘i’, ‘intrinsic’}, sequence
will be treated as intrinsic.
suppress_warnings (boolean, optional) – Disable warnings about gimbal lock. Default is False.
Returns:
angles – Shape depends on shape of inputs used to initialize object.
The returned angles are in the range:
First angle belongs to [\(-\pi\), \(\pi\)] radians (both
inclusive)
Third angle belongs to [\(-\pi\), \(\pi\)] radians (both
inclusive)
Second angle belongs to a set of size \(\pi\) radians,
given by: [-abs(lambda),np.pi-abs(lambda)], where
lambda is the angle between the first and third axes.
Any rotation can be expressed as a composition of 3 elementary
rotations. Once the axis sequence has been chosen, Euler angles define
the angle of rotation around each respective axis [10].
The algorithm from [11] has been used to calculate Euler angles for the
rotation about a given sequence of axes.
Euler angles suffer from the problem of gimbal lock [12], where the
representation loses a degree of freedom and it is not possible to
determine the first and third angles uniquely. In this case,
a warning is raised (unless the suppress_warnings option is used),
and the third angle is set to zero. Note however that the returned
angles still represent the correct rotation.
Parameters:
seq (string, length 3) – 3 characters belonging to the set {‘X’, ‘Y’, ‘Z’} for intrinsic
rotations, or {‘x’, ‘y’, ‘z’} for extrinsic rotations.
Adjacent axes cannot be the same.
Extrinsic and intrinsic rotations cannot be mixed in one function
call.
suppress_warnings (boolean, optional) – Disable warnings about gimbal lock. Default is False.
Returns:
angles – Shape depends on shape of inputs used to initialize object.
The returned angles are in the range:
First angle belongs to [\(-\pi\), \(\pi\)] radians (both
inclusive)
Third angle belongs to [\(-\pi\), \(\pi\)] radians (both
inclusive)
Second angle belongs to:
[\(-\pi/2\), \(\pi/2\)] radians if all axes are
different (like xyz)
[0, \(\pi\)] radians if first and third axes are the same
(like zxz)
MRPs are a 3 dimensional vector co-directional to the axis of rotation
and whose magnitude is equal to tan(theta/4), where theta is
the angle of rotation (in radians) [14].
MRPs have a singularity at \(2\pi\) radians which can be avoided by
ensuring the angle of rotation does not exceed \(\pi\) radians,
i.e. switching the direction of the rotation when it is past
\(\pi\) radians. This function will always return MRPs
corresponding to a rotation of less than or equal to \(\pi\)
radians.
Returns:
mrps – Shape depends on shape of inputs used for initialization.
Rotations in 3 dimensions can be represented using unit norm
quaternions [15].
The 4 components of a quaternion are divided into a scalar part w
and a vector part (x,y,z) and can be expressed from the angle
theta and the axis n of a rotation as follows:
There are 2 conventions to order the components in a quaternion:
scalar-first order – (w,x,y,z)
scalar-last order – (x,y,z,w)
The choice is controlled by scalar_first argument.
By default, it is False and the scalar-last order is used.
The mapping from quaternions to rotations is
two-to-one, i.e. quaternions q and -q, where -q simply
reverses the sign of each component, represent the same spatial
rotation.
Parameters:
canonical (bool, default False) – Whether to map the redundant double cover of rotation space to a
unique “canonical” single cover. If True, then the quaternion is
chosen from {q, -q} such that the w term is positive. If the w term
is 0, then the quaternion is chosen such that the first nonzero
term of the x, y, and z terms is positive.
scalar_first (bool, optional) – Whether the scalar component goes first or last.
Default is False, i.e. the scalar-last order is used.
Returns:
quat – Shape depends on shape of inputs used for initialization.
The view vector defines the forward-looking direction, while the up
vector defines what direction is “up”. These two vectors are
perpendicular to each other.
The rotation matrix uses a right-handed coordinate system with columns
[view,-right,up], where right=view×up. This method
extracts the view and up vectors from that representation.
Rotations in 3-D can be represented by a sequence of 3
rotations around a sequence of axes.
The three rotations can either be in a global frame of reference
(extrinsic) or in a body centred frame of reference (intrinsic), which
is attached to, and moves with, the object under rotation [17].
For both Euler angles and Davenport angles, consecutive axes must
be are orthogonal (axis2 is orthogonal to both axis1 and
axis3). For Euler angles, there is an additional relationship
between axis1 or axis3, with two possibilities:
axis1 and axis3 are also orthogonal (asymmetric sequence)
axis1==axis3 (symmetric sequence)
For Davenport angles, this last relationship is relaxed [18], and only
the consecutive orthogonal axes requirement is maintained.
Parameters:
axes (array_like, shape (3,) or (..., [1 or 2 or 3], 3)) – Axis of rotation, if one dimensional. If two or more dimensional,
describes the sequence of axes for rotations, where each
axes[…, i, :] is the ith axis. If more than one axis is given,
then the second axis must be orthogonal to both the first and third
axes.
order (string) – If it is equal to ‘e’ or ‘extrinsic’, the sequence will be
extrinsic. If it is equal to ‘i’ or ‘intrinsic’, sequence
will be treated as intrinsic.
angles (float or array_like, shape (..., [1 or 2 or 3])) – Angles specified in radians.
Each angle i in the last dimension of angles turns around the
corresponding axis axis[…, i, :]. The resulting rotation has the
shape np.broadcast_shapes(np.atleast_2d(axes).shape[:-2],
np.atleast_1d(angles).shape[:-1]) Dimensionless angles are thus
only valid for a single axis.
Rotations in 3-D can be represented by a sequence of 3
rotations around a sequence of axes. In theory, any three axes spanning
the 3-D Euclidean space are enough. In practice, the axes of rotation
are chosen to be the basis vectors.
The three rotations can either be in a global frame of reference
(extrinsic) or in a body centred frame of reference (intrinsic), which
is attached to, and moves with, the object under rotation [19].
Parameters:
seq (string) – Specifies sequence of axes for rotations. Up to 3 characters
belonging to the set {‘X’, ‘Y’, ‘Z’} for intrinsic rotations, or
{‘x’, ‘y’, ‘z’} for extrinsic rotations. Extrinsic and intrinsic
rotations cannot be mixed in one function call.
angles (float or array_like, shape (..., [1 or 2 or 3])) – Euler angles specified in radians.
Each character in seq defines one axis around which angles
turns. The resulting rotation has the shape
np.atleast_1d(angles).shape[:-1]. Dimensionless angles are thus
only valid for single character seq.
Rotations in 3 dimensions can be represented with 3 x 3 orthogonal
matrices [20]. If the input is not orthogonal, an approximation is
created by orthogonalizing the input matrix using the method described
in [21], and then converting the orthogonal rotation matrices to
quaternions using the algorithm described in [22]. Matrices must be
right-handed.
Parameters:
matrix (array_like, shape (..., 3, 3)) – A single matrix or an ND array of matrices, where the last two
dimensions contain the rotation matrices.
assume_valid (bool, optional) – Must be False unless users can guarantee the input is a valid
rotation matrix, i.e. it is orthogonal, rows and columns have unit
norm and the determinant is 1. Setting this to True without
ensuring these properties is unsafe and will silently lead to
incorrect results. If True, normalization steps are skipped, which
can improve runtime performance.
MRPs are a 3 dimensional vector co-directional to the axis of rotation
and whose magnitude is equal to tan(theta/4), where theta is
the angle of rotation (in radians) [23].
MRPs have a singularity at \(2\pi\) radians which can be avoided by
ensuring the angle of rotation does not exceed \(\pi\),
i.e. switching the direction of the rotation when it is past
\(\pi\) radians.
Parameters:
mrp (array_like, shape (..., 3)) – A single vector or an ND array of vectors, where the last dimension
contains the rotation parameters.
Rotations in 3 dimensions can be represented using unit norm
quaternions [24].
The 4 components of a quaternion are divided into a scalar part w
and a vector part (x,y,z) and can be expressed from the angle
theta and the axis n of a rotation as follows:
There are 2 conventions to order the components in a quaternion:
scalar-first order – (w,x,y,z)
scalar-last order – (x,y,z,w)
The choice is controlled by scalar_first argument.
By default, it is False and the scalar-last order is assumed.
Advanced users may be interested in the “double cover” of 3D space by
the quaternion representation [25]. As of version 1.11.0, the
following subset (and only this subset) of operations on a Rotation
r corresponding to a quaternion q are guaranteed to preserve
the double cover property: r=Rotation.from_quat(q),
r.as_quat(canonical=False), r.inv(), and composition using the
* operator such as r*r.
Parameters:
quat (array_like, shape (..., 4)) – Each row is a (possibly non-unit norm) quaternion representing an
active rotation. Each quaternion will be normalized to unit norm.
scalar_first (bool, optional) – Whether the scalar component goes first or last.
Default is False, i.e. the scalar-last order is assumed.
The view vector defines the forward-looking direction, while the up
vector defines what direction is “up”. These two vectors must be
perpendicular to each other.
The rotation is constructed using a right-handed coordinate system
where the right vector is computed as the cross product of view and up:
right=view×up. The resulting rotation matrix has columns
[view,-right,up], corresponding to the x, y, and z axes
respectively.
Parameters:
views (array_like, shape (..., 3) or (3,), Coordinates) – A single vector or a stack of vectors, giving the look-direction of
an object in three-dimensional space, e.g. from a listener, or the
acoustic axis of a loudspeaker, or the direction of a main lobe.
Views can also be passed as a Coordinates object.
ups (array_like, shape (..., 3) or (3,), Coordinates) – A single vector or a stack of vectors, giving the up-direction of
an object, which is usually the up-direction in world-space. Views
can also be passed as a Coordinates object.
The mean used is the chordal L2 mean (also called the projected or
induced arithmetic mean) [27]. If A is a set of rotation matrices,
then the mean M is the rotation matrix that minimizes the
following loss function:
where \(w_i\)’s are the weights corresponding to each matrix.
Parameters:
weights (array_like shape (..., N), optional) – Weights describing the relative importance of the rotations. If
None (default), then all values in weights are assumed to be
equal. If given, the shape of weights must be broadcastable to
the rotation shape. Weights must be non-negative.
axis (None, int, or tuple of ints, optional) – Axis or axes along which the means are computed. The default is to
compute the mean of all rotations.
Returns:
mean – Single rotation containing the mean of the rotations in the
current instance.
Formally, the rotations follow the Haar-uniform distribution over
the SO(3) group.
Parameters:
num (int or None, optional) – Number of random rotations to generate. If None (default), then
a single rotation is generated.
rng (numpy.random.Generator, optional) – Pseudorandom number generator state. When rng is None, a new
numpy.random.Generator is created using entropy from the
operating system. Types other than numpy.random.Generator are
passed to numpy.random.default_rng to instantiate a Generator.
shape (tuple of ints, optional) – Shape of random rotations to generate. If specified, num must
be None.
Returns:
random_rotation – Contains a single rotation if num is None. Otherwise contains
a stack of num rotation.
This function is optimized for efficiently sampling random rotation
matrices in three dimensions. For generating random rotation matrices
in higher dimensions, see scipy.stats.special_ortho_group.
Reduction of a rotation p is a transformation of the form
q=l*p*r, where l and r are chosen from left and
right respectively, such that rotation q has the smallest
magnitude.
If left and right are rotation groups representing symmetries of
two objects rotated by p, then q is the rotation of the
smallest magnitude to align these objects considering their symmetries.
Parameters:
left (Rotation, optional) – Object containing the left rotation(s). Default value (None)
corresponds to the identity rotation.
right (Rotation, optional) – Object containing the right rotation(s). Default value (None)
corresponds to the identity rotation.
return_indices (bool, optional) – Whether to return the indices of the rotations from left and
right used for reduction.