3D rotation data often moves between libraries as Euler angles, quaternions, matrices, or rotation vectors. SciPy stores the orientation in a Rotation object so Python code can translate one representation into another without changing the spatial rotation.

scipy.spatial.transform.Rotation creates rotations with from_euler(), from_quat(), from_matrix(), and from_rotvec(), then returns the format required by downstream code with matching as_… methods. The object is representation-independent, so one source orientation can be exported several ways.

A lowercase zyx Euler sequence in degrees keeps the axis order explicit before any quaternion or matrix export. Lowercase axes mean extrinsic rotations in SciPy, while uppercase axes mean intrinsic rotations; keep that convention consistent with the system that produced or consumes the data.

Steps to convert 3D rotations with SciPy:

  1. Create the rotation conversion demo script.
    rotation_convert_demo.py
    import numpy as np
    from scipy.spatial.transform import Rotation as R
     
    np.set_printoptions(precision=4, suppress=True)
     
    rotation = R.from_euler("zyx", [45, 20, 10], degrees=True)
     
    quat_xyzw = rotation.as_quat()
    matrix = rotation.as_matrix()
    rotvec = rotation.as_rotvec()
     
    print("Euler zyx degrees:")
    print(rotation.as_euler("zyx", degrees=True))
     
    print("\nQuaternion xyzw:")
    print(quat_xyzw)
     
    print("\nQuaternion wxyz:")
    print(rotation.as_quat(scalar_first=True))
     
    print("\nRotation matrix rows:")
    for row in matrix:
        print(row)
     
    print("\nRotation vector radians:")
    print(rotvec)
     
    print("\nRound-trip checks:")
    print("from_quat :", R.from_quat(quat_xyzw).approx_equal(rotation, atol=1e-12))
    print("from_matrix:", R.from_matrix(matrix).approx_equal(rotation, atol=1e-12))
    print("from_rotvec:", R.from_rotvec(rotvec).approx_equal(rotation, atol=1e-12))
  2. Run the script.
    $ python3 rotation_convert_demo.py
    Euler zyx degrees:
    [45. 20. 10.]
    
    Quaternion xyzw:
    [0.1455 0.127  0.3894 0.9006]
    
    Quaternion wxyz:
    [0.9006 0.1455 0.127  0.3894]
    
    Rotation matrix rows:
    [ 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:
    from_quat : True
    from_matrix: True
    from_rotvec: True
  3. Use from_euler() when the source data is an ordered angle sequence.

    Lowercase zyx means extrinsic rotations. Uppercase ZYX means intrinsic rotations. Euler outputs are convention-specific and can warn at gimbal lock while still representing the same orientation.

  4. Export the default quaternion when another library expects x, y, z, w order.

    as_quat() defaults to scalar-last order. Use scalar_first=True for w, x, y, z order.

  5. Export the rotation matrix for matrix-based code.

    Use assume_valid=True only for matrices already known to be orthogonal with determinant 1. Invalid matrices can otherwise produce incorrect rotations without a repair step.

  6. Export the rotation vector when the downstream format stores axis direction and angle together.

    The vector direction is the rotation axis, and its length is the angle in radians unless a method call explicitly requests degrees.

  7. Verify converted representations by rebuilding Rotation objects.

    approx_equal() compares the represented orientation, which is safer than comparing raw quaternion signs because q and -q describe the same spatial rotation.

  8. Remove the demo script when it was only created for the check.
    $ rm rotation_convert_demo.py