Orientation data often crosses application boundaries in a format the receiving system cannot use directly. SciPy can hold one 3D rotation independently of its Euler-angle, quaternion, matrix, or rotation-vector representation, which keeps the spatial orientation unchanged during conversion.
The scipy.spatial.transform.Rotation class separates the source representation from the exported one. A rotation created with from_euler() can therefore supply quaternion components, a 3 x 3 rotation matrix, and an axis-angle rotation vector without applying the rotation again.
Lowercase zyx selects extrinsic rotations around fixed axes, while uppercase ZYX selects intrinsic rotations around moving axes. Match the axis sequence, angle units, and quaternion component order expected by both systems before exchanging orientation data.
import numpy as np from scipy.spatial.transform import Rotation as R np.set_printoptions(precision=4, suppress=True) euler_zyx_degrees = np.array([45.0, 20.0, 10.0]) rotation = R.from_euler("zyx", euler_zyx_degrees, degrees=True)
Lowercase axes denote extrinsic rotations; uppercase axes denote intrinsic rotations. Mixing the two conventions changes the represented orientation.
quaternion_xyzw = rotation.as_quat() rotation_matrix = rotation.as_matrix() rotation_vector = rotation.as_rotvec()
as_quat() returns x, y, z, w order by default; pass scalar_first=True when the receiving system requires w, x, y, z. The rotation-vector magnitude is in radians unless degrees=True is requested.
round_trip_checks = { "quaternion": R.from_quat( quaternion_xyzw ).approx_equal(rotation, atol=1e-12), "matrix": R.from_matrix( rotation_matrix ).approx_equal(rotation, atol=1e-12), "rotation vector": R.from_rotvec( rotation_vector ).approx_equal(rotation, atol=1e-12), }
approx_equal() compares orientations instead of raw quaternion components, so opposite-sign quaternions that represent the same rotation do not cause a false mismatch.
print("Euler zyx degrees:", euler_zyx_degrees) print("Quaternion xyzw:", quaternion_xyzw) print("Rotation matrix:") print(rotation_matrix) print("Rotation vector radians:", rotation_vector) print("Round-trip checks:") for representation, passed in round_trip_checks.items(): print(f"{representation}: {passed}") if not all(round_trip_checks.values()): raise SystemExit("round-trip check failed")
$ python3 rotation_convert_demo.py Euler zyx degrees: [45. 20. 10.] Quaternion xyzw: [0.1455 0.127 0.3894 0.9006] Rotation matrix: [[ 0.6645 -0.6645 0.342 ] [ 0.7384 0.6544 -0.1632] [-0.1154 0.361 0.9254]] Rotation vector radians: [0.301 0.2627 0.8057] Round-trip checks: quaternion: True matrix: True rotation vector: True