0

I'm working with Euler angles and SciPy's implementation of them. I'm having a hard time understanding how SciPy initializes rotations matrices from Eulers, or how it represents matrices as Eulers, or both. From reading the documentation, I would assume that a single call

from_euler("ZYX", (yaw, pitch, roll))

is equivalent to a multiplication of three matrices computed from one axis each, but a simple test invalidates this assumption:

from scipy.spatial.transform import Rotation as R

def r1(roll, pitch, yaw):
    r = R.from_euler("ZYX", (yaw, pitch, roll), degrees=True)
    yaw, pitch, roll = r.as_euler("ZYX", degrees=True)
    print(f"r:{roll:5.1f} p:{pitch:5.1f} y:{yaw:5.1f}")

def r2(roll, pitch, yaw):
    r = R.from_euler("ZYX", (0, 0, roll), degrees=True) * \
        R.from_euler("ZYX", (0, pitch, 0), degrees=True) * \
        R.from_euler("ZYX", (yaw, 0, 0), degrees=True)
    yaw, pitch, roll = r.as_euler("ZYX", degrees=True)
    print(f"r:{roll:5.1f} p:{pitch:5.1f} y:{yaw:5.1f}")

r1(35, 25, 115)  # as expected: "r:  35.0 p: 25.0 y: 115.0"
r2(35, 25, 115)  # surprising:  "r:   5.5 p:-41.8 y: 120.9"

I would be grateful for any pointers to documentation that can elaborate further on who SciPy does, or, alternatively, the obvious-to-everyone-else thing I'm missing about rotation matrices.

iter
  • 4,171
  • 8
  • 35
  • 59

1 Answers1

2

Maybe try:

from scipy.spatial.transform import Rotation as R

def r1(roll, pitch, yaw):
    r = R.from_euler("ZYX", (yaw, pitch, roll), degrees=True)
    yaw, pitch, roll = r.as_euler("ZYX", degrees=True)
    print(f"r:{roll:5.1f} p:{pitch:5.1f} y:{yaw:5.1f}")

def r2(roll, pitch, yaw):
    r = R.from_euler("ZYX", (0, 0, roll), degrees=True) * \
        R.from_euler("ZYX", (0, pitch, 0), degrees=True) * \
        R.from_euler("ZYX", (yaw, 0, 0), degrees=True)
    yaw, pitch, roll = r.as_euler("ZYX", degrees=True)
    print(f"r:{roll:5.1f} p:{pitch:5.1f} y:{yaw:5.1f}")

# solution
def r3(roll, pitch, yaw):
    r = R.from_euler("ZYX", (yaw, 0, 0), degrees=True) * \
        R.from_euler("ZYX", (0, pitch, 0), degrees=True) * \
        R.from_euler("ZYX", (0, 0, roll), degrees=True)
    yaw, pitch, roll = r.as_euler("ZYX", degrees=True)
    print(f"r:{roll:5.1f} p:{pitch:5.1f} y:{yaw:5.1f}")

r1(35, 25, 115)  # as expected: "r:  35.0 p: 25.0 y: 115.0"
r2(35, 25, 115)  # surprising:  "r:   5.5 p:-41.8 y: 120.9"
r3(35, 25, 115)  # as expected: "r:  35.0 p: 25.0 y: 115.0"
PumpkinQ
  • 81
  • 3
  • Thank you @PumpkinQ. It appears that my disconnect (at least one of them) is in how the order of operations works for matrix composition. Thank you for pointing out the correct order. – iter Nov 14 '22 at 19:45