[Python] ECI/body frame quaternion confusion

Discussão em 'Python' iniciado por Stack, Janeiro 31, 2025.

  1. Stack

    Stack Membro Participativo

    I have just started trying to wrap my head around the basics of using quaternions in my rocket simulation to handle guidance maneuvers. In my sim, I keep an ECI-to-body quaternion transform updated at each time step. The body frame that I am using is a vehicle-carried NED frame. The positive x-axis points out the nose, the positive z-axis points down, and the positive y-axis completes the right hand rule. To make things simple, I launch the rocket vertically at a latitude of 0 deg and longitude of 0 deg. At launch, the ECI and body frames are exactly aligned. So at t=0, the rocket is pointed down the x-axis of the ECI frame, with the body frame's positive z-axis (which points out from the 'belly' of the rocket) pointing in the positive z-axis of the ECI frame. I use a quaternion of the form [w,x,y,z] that, when multiplied with a vector, rotates that vector and returns that rotated vector in the same frame. I believe this is called an active quaternion.

    Lets say that a positive rotation about the body frame's y-axis of 30 degrees is performed. Updating the ECI-to-body quaternion to represent this rotation gives [0.965, 0, 0.258, 0]. After this rotation is applied, i then want to express the rockets's body axes in the ECI frame. How do i do this? I originally thought that i should compute the inverse of the ECI-to-body transform, which would give me a body-to-ECI transform. I thought that i could just multiply this body-to-ECI transform with the 3 body axis vectors ([1,0,0], [0,1,0], and [0,0,1]) to give me those three vectors expressed in the ECI frame. For example, if I compute the inverse of this ECI-to-body transform, i get [0.965, 0, -0.258, 0]. If i then multiply this body-to-ECI transform with the body vector [1,0,0], I get [0.866, 0, 0.5]. However, the z-component of this vector is positive, even though a positive, right-handed rotation about the body y-axis was performed. This means that the rocket should be pointed slightly in the negative z-direction in the ECI frame after the rotation. What am I doing wrong?

    I notice that if I don't use the inverse (body-to-ECI), and instead use the ECI-to-body transform to multiply with the body frame basis vectors, I get the correct result. Is this the correct way of performing this computation? If I have thrust and aero body force vectors in the body frame, do I just multiply them with the ECI-to-body transform to express them in the ECI frame? That is very non-intuitive for me. Below is some Python code that details my problem:

    import numpy as np

    def normalize(q):
    """Normalize a quaternion to unit length."""
    return q / np.linalg.norm(q)

    def quaternion_conjugate(q):
    """Compute the conjugate of a quaternion."""
    w, x, y, z = q
    return np.array([w, -x, -y, -z])

    def quaternion_multiply(q1, q2):
    """Multiply two quaternions."""
    w1, x1, y1, z1 = q1
    w2, x2, y2, z2 = q2
    return np.array([
    w1*w2 - x1*x2 - y1*y2 - z1*z2,
    w1*x2 + x1*w2 + y1*z2 - z1*y2,
    w1*y2 - x1*z2 + y1*w2 + z1*x2,
    w1*z2 + x1*y2 - y1*x2 + z1*w2
    ])

    def rotate_vector(q, v):
    """Rotate a 3D vector v using quaternion q."""
    q = normalize(q)
    q_conj = quaternion_conjugate(q)
    q_v = np.hstack(([0], v))
    q_rotated = quaternion_multiply(quaternion_multiply(q, q_v), q_conj)
    return q_rotated[1:]

    def rotate_about_y(q, theta):
    """Rotate quaternion q by theta (radians) about the y-axis."""
    half_theta = theta / 2
    q_rot = np.array([np.cos(half_theta), 0, np.sin(half_theta), 0])
    return normalize(quaternion_multiply(q_rot, q))

    # ECI and body frame initially align
    eci_to_body = np.array([1,0,0,0])

    # Right-handed rotation of 30 degrees about body frame +y-axis
    theta = np.radians(30)

    eci_to_body = rotate_about_y(eci_to_body, theta)
    body_to_eci = quaternion_conjugate(eci_to_body)
    print(f"eci_to_body = {eci_to_body}") # [0.965, 0, 0.258, 0]
    print(f"body_to_eci = {body_to_eci}") # [0.965, 0, -0.258, 0]

    # Body frame x-axis (out the nose)
    pointing_vector_body = np.array([1,0,0])

    # Express the body frame x-axis in the ECI frame using body_to_eci. This
    # results in the pointing vector having the wrong sign for the z-component
    pointing_vector_eci_wrong = rotate_vector(body_to_eci, pointing_vector_body)
    print(f"pointing_vector_eci_wrong = {pointing_vector_eci_wrong}") # [0.866, 0, 0.5]

    # Express the body frame x-axis in the ECI frame using eci_to_body. This
    # gives the correct answer. Is this by accident, or is eci_to_body*body_vector
    # the correct way of transforming body frame vectors to the ECI frame?
    pointing_vector_eci_correct = rotate_vector(eci_to_body, pointing_vector_body)
    print(f"pointing_vector_eci_correct = {pointing_vector_eci_correct}") # [0.866, 0, -0.5]

    Continue reading...

Compartilhe esta Página