pytransform3d.rotations.swing_twist_decomposition#
- pytransform3d.rotations.swing_twist_decomposition(q, axis, eps=2.220446049250313e-16)[source]#
Decompose quaternion into swing and twist about an axis.
Any rotation \(\boldsymbol{q}\) can be split into a twist about a given axis \(\boldsymbol{e}\) and a swing that rotates the axis itself, such that
\[\boldsymbol{q} = \boldsymbol{q}_\text{swing} \boldsymbol{q}_\text{twist},\]where the twist is applied first. The twist \(\boldsymbol{q}_\text{twist}\) is a rotation about \(\boldsymbol{e}\) and the swing \(\boldsymbol{q}_\text{swing}\) is a rotation about an axis that is orthogonal to \(\boldsymbol{e}\) [1].
The twist is obtained by projecting the vector part of the quaternion onto the twist axis and renormalizing; the swing is then recovered as \(\boldsymbol{q}_\text{swing} = \boldsymbol{q} \boldsymbol{q}_\text{twist}^{-1}\).
This decomposition is useful, for example, to enforce joint limits, to separate the roll about a link axis from the remaining orientation, or to isolate the rotation about a symmetry axis.
- Parameters:
- qarray-like, shape (4,)
Unit quaternion to represent rotation: (w, x, y, z).
- axisarray-like, shape (3,)
Twist axis. Does not have to be normalized.
- epsfloat, optional (default: np.finfo(float).eps)
Threshold for degeneracy detection
- Returns:
- swingarray, shape (4,)
Unit quaternion that represents the swing, a rotation about an axis orthogonal to the twist axis.
- twistarray, shape (4,)
Unit quaternion that represents the twist, a rotation about the given axis.
- Raises:
- ValueError
If the twist axis is the zero vector.
See also
swing_twist_compositionReconstructs the original rotation from swing and twist.
concatenate_quaternionsReconstructs the original rotation from swing and twist via
concatenate_quaternions(swing, twist).
Notes
Beyond the algorithm of [1], this implementation fixes two conventions for cases the paper leaves open:
Degenerate twist. When
twist_norm < eps, both the scalar part and the projection of the vector part onto the twist axis vanish. This happens for a rotation by \(\pi\) about an axis orthogonal to the twist axis, where the twist is undefined. We resolve it by returning the identity as the twist and letting the swing carry the full rotation.Canonical hemisphere. The twist is normalized to a non-negative scalar part (\(w \geq 0\)). Since \(\boldsymbol{q}\) and \(-\boldsymbol{q}\) represent the same rotation, this makes the returned twist unique and keeps the twist angle in \([-\pi, \pi]\).
References
[1] (1,2)Dobrowolski, P. (2015). Swing-twist decomposition in Clifford algebra. https://arxiv.org/abs/1506.05481