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_composition

Reconstructs the original rotation from swing and twist.

concatenate_quaternions

Reconstructs 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:

  1. 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.

  2. 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

Examples using pytransform3d.rotations.swing_twist_decomposition#

Swing-Twist Decomposition

Swing-Twist Decomposition

Tool-Axis Redundancy via Swing and Twist

Tool-Axis Redundancy via Swing and Twist