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.
Steps to convert 3D rotations with SciPy:
- Define the source Euler rotation in rotation_convert_demo.py.
- rotation_convert_demo.py
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.
- Append the representation exports below the rotation definition.
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.
- Append the round-trip checks below the export variables.
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.
- Append the reporting block below round_trip_checks.
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")
- Run rotation_convert_demo.py to export the rotation with round-trip validation.
$ 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
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.