Applying quaternion rotation to a vector time series

做~自己de王妃 提交于 2021-01-04 05:57:40

问题


I have a time series of 3D vectors in a Python numpy array similar to the following:

array([[-0.062, -0.024,  1.   ],
       [-0.071, -0.03 ,  0.98 ],
       [-0.08 , -0.035,  0.991],
       [-0.083, -0.035,  0.98 ],
       [-0.083, -0.035,  0.977],
       [-0.082, -0.035,  0.993],
       [-0.08 , -0.034,  1.006],
       [-0.081, -0.032,  1.008],
       .......

I want to rotate each vector around a specified axis through a specified angle theta. I have been using quaternions to achieve this for one vector as found here in henneray's answer.

v1 = np.array ([1, -2, 0])
axis = np.array([-4, -2,  3])
theta = 1.5

rot_axis = np.insert(axis, 0, 0, axis=0)
axis_angle = (theta*0.5) * rot_axis/np.linalg.norm(rot_axis)
vec = quat.quaternion(*v1)
qlog = quat.quaternion(*axis_angle)
q = np.exp(qlog)
v_prime = q * vec * np.conjugate(q)
v_prime_vec = v_prime.imag

My question is, what is the fastest way to apply the same rotation to each vector in v1?

You cannot create a quaternion from v1 if v1 contains a 2D array of vectors, so I could use a loop to rotate each array element in turn; however, in henneray's answer in the link above, it is mentioned that the quaternions could be applied to 'appropriately vectorised numpy arrays'. Does anyone has any suggestions on how this could be implemented?

(A side question: if my theta and axis variables were arrays of equal length to v1, could the same method also be used to rotate each vector in v1 through a corresponding rotation?)


回答1:


It is necessary to first convert the [x,y,z] Cartesian vectors into 4-vectors with the first component equal to zero [0,x,y,z]. Then you can cast this to a quaternion array to do vectorised calculations.

This function below takes an array of Cartesian vectors and rotates them about a single rotation axis. You will need to make sure the norm of this axis is equal to your rotation angle theta.

def rotate_vectors(vecs, axis):
    """
    Rotate a list of 3D [x,y,z] vectors about corresponding 3D axis
    [x,y,z] with norm equal to the rotation angle in radians

    Parameters
    ----------
    vectors : numpy.ndarray with shape [n,3]
        list of [x,y,z] cartesian vector coordinates
    axis : numpy.ndarray with shape [3]
        [x,y,z] axis to rotate corresponding vectors about
    """
    # Make an 4 x n array of zeros
    vecs4 = np.zeros([vecs.shape[0],vecs.shape[1]+1])
    # Fill the imaginary i, j, k components with x, y, z values, leaving the real part w=0
    vecs4[:,1:] = vecs
    # Convert to quaternion array
    vecsq = quat.as_quat_array(vecs4)

    # Make a rotation quaternion
    qrot = quat.from_rotation_vector(axis)
    # Rotate vectors
    vecsq_rotated = qrot * vecsq * qrot.conjugate()
    # Cast quaternion array to float and return only imaginary components (ignore real part)
    return quat.as_float_array(vecsq_rotated)[:,1:]

As a bonus, this function takes an array of rotation axes to rotate each vector by the corresponding axes.

def rotate_vectors_each(vecs, axes):
    """
    Rotate a list of 3D [x,y,z] vectors about corresponding 3D axes
    [x,y,z] with norm equal to the rotation angle in radians

    Parameters
    ----------
    vectors : numpy.ndarray with shape [n,3]
        list of [x,y,z] cartesian vector coordinates
    axes : numpy.ndarray with shape [n,3]
        axes to rotate corresponding vectors about
        n = pulse shape time domain
        3 = [x,y,z]
    """
    # Make an 4 x n array of zeros
    vecs4 = np.zeros([vecs.shape[0],vecs.shape[1]+1])
    # Fill the imaginary i, j, k components with x, y, z values, leaving the real part w=0
    vecs4[:,1:] = vecs
    # Convert to quaternion array
    vecsq = quat.as_quat_array(vecs4)

    # Make an 4 x n array of zeros
    rots4 = np.zeros([rots.shape[0],rots.shape[1]+1])
    # Fill the imaginary i, j, k components with x, y, z values, leaving the real part w=0
    rots4[:,1:] = rots
    # Convert to quaternion array and take exponential
    qrots = np.exp(quat.as_quat_array(0.5 * rots4))

    # Rotate vectors
    vecsq_rotated = qrots * vecsq * qrots.conjugate()

    return quat.as_float_array(vecsq_rotated)[:,1:]

Note that with so many conversions between axis angle and quaternion representation, this will give you little performance improvement over rotation matrix algebra. Quaternions really only benefit when you are rotating a vector through many sequential rotations, whereby you can stack the quaternion multiplication.




回答2:


One "fast" way to do the rotation calculation itself would be to turn your quaternion into a 3x3 direction cosine matrix, have your vectors in a single 3xN contiguous matrix, and then call a BLAS library routine (e.g., dgemm) to do a standard matrix multiply. A good BLAS library with large N would do this calculation multi-threaded.



来源:https://stackoverflow.com/questions/64988678/applying-quaternion-rotation-to-a-vector-time-series

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!