计算2个3D笛卡尔坐标系之间的转换四元数

时间:2013-05-20 11:25:28

标签: transformation quaternions cartesian

我有两个具有已知单位向量的笛卡尔坐标系:

系统A(x_A,y_A,z_A)

系统B(x_B,y_B,z_B)

两个系统共享相同的原点(0,0,0)。我正在尝试计算四元数,以便系统B中的向量可以在系统A中表示。

我熟悉四元数的数学概念。我已经从这里实现了所需的数学:http://content.gpwiki.org/index.php/OpenGL%3aTutorials%3aUsing_Quaternions_to_represent_rotation

一种可能的解决方案是计算欧拉角并将其用于3个四元数。将它们相乘将导致最后一个,这样我就可以转换我的向量:

v(A)= q * v(B)* q_conj

但是这会再次包含Gimbal Lock,这是在开始时不使用欧拉角的原因。

任何想法如何解决这个问题?

6 个答案:

答案 0 :(得分:5)

您可以通过本文所述的方法计算表示从一个坐标系到另一个坐标系的最佳转换的四元数:

Paul J. Besl和Neil D. McKay “3-D形状的登记方法”,传感器融合IV:控制范例和数据结构,586(1992年4月30日); http://dx.doi.org/10.1117/12.57955

该论文不是开放式访问,但我可以向您展示Python实现:

def get_quaternion(lst1,lst2,matchlist=None):
    if not matchlist:
        matchlist=range(len(lst1))
    M=np.matrix([[0,0,0],[0,0,0],[0,0,0]])

    for i,coord1 in enumerate(lst1):
        x=np.matrix(np.outer(coord1,lst2[matchlist[i]]))
        M=M+x

    N11=float(M[0][:,0]+M[1][:,1]+M[2][:,2])
    N22=float(M[0][:,0]-M[1][:,1]-M[2][:,2])
    N33=float(-M[0][:,0]+M[1][:,1]-M[2][:,2])
    N44=float(-M[0][:,0]-M[1][:,1]+M[2][:,2])
    N12=float(M[1][:,2]-M[2][:,1])
    N13=float(M[2][:,0]-M[0][:,2])
    N14=float(M[0][:,1]-M[1][:,0])
    N21=float(N12)
    N23=float(M[0][:,1]+M[1][:,0])
    N24=float(M[2][:,0]+M[0][:,2])
    N31=float(N13)
    N32=float(N23)
    N34=float(M[1][:,2]+M[2][:,1])
    N41=float(N14)
    N42=float(N24)
    N43=float(N34)

    N=np.matrix([[N11,N12,N13,N14],\
              [N21,N22,N23,N24],\
              [N31,N32,N33,N34],\
              [N41,N42,N43,N44]])


    values,vectors=np.linalg.eig(N)
    w=list(values)
    mw=max(w)
    quat= vectors[:,w.index(mw)]
    quat=np.array(quat).reshape(-1,).tolist()
    return quat

此函数返回您要查找的四元数。参数lst1和lst2是numpy.arrays的列表,其中每个数组代表一个3D矢量。如果两个列表的长度均为3(并且包含正交单位向量),则四元数应该是精确的变换。如果提供更长的列表,则会获得最小化两个点集之间差异的四元数。 可选的matchlist参数用于告诉函数lst2的哪个点应转换为lst1中的哪个点。如果没有提供匹配列表,则该函数假定lst1中的第一个点应与lst2中的第一个点匹配,依此类推......

C ++中3点集的类似函数如下:

#include <Eigen/Dense>
#include <Eigen/Geometry>

using namespace Eigen;

/// Determine rotation quaternion from coordinate system 1 (vectors
/// x1, y1, z1) to coordinate system 2 (vectors x2, y2, z2)
Quaterniond QuaternionRot(Vector3d x1, Vector3d y1, Vector3d z1,
                          Vector3d x2, Vector3d y2, Vector3d z2) {

    Matrix3d M = x1*x2.transpose() + y1*y2.transpose() + z1*z2.transpose();

    Matrix4d N;
    N << M(0,0)+M(1,1)+M(2,2)   ,M(1,2)-M(2,1)          , M(2,0)-M(0,2)         , M(0,1)-M(1,0),
         M(1,2)-M(2,1)          ,M(0,0)-M(1,1)-M(2,2)   , M(0,1)+M(1,0)         , M(2,0)+M(0,2),
         M(2,0)-M(0,2)          ,M(0,1)+M(1,0)          ,-M(0,0)+M(1,1)-M(2,2)  , M(1,2)+M(2,1),
         M(0,1)-M(1,0)          ,M(2,0)+M(0,2)          , M(1,2)+M(2,1)         ,-M(0,0)-M(1,1)+M(2,2);

    EigenSolver<Matrix4d> N_es(N);
    Vector4d::Index maxIndex;
    N_es.eigenvalues().real().maxCoeff(&maxIndex);

    Vector4d ev_max = N_es.eigenvectors().col(maxIndex).real();

    Quaterniond quat(ev_max(0), ev_max(1), ev_max(2), ev_max(3));
    quat.normalize();

    return quat;
}

答案 1 :(得分:1)

您使用的是哪种语言?如果是c ++,请随意使用我的开源库:

http://sourceforge.net/p/transengine/code/HEAD/tree/transQuaternion/

缺点是,您需要将矢量转换为四元数,进行计算,然后将四元数转换为变换矩阵。

以下是代码段:

来自矢量的四元数:

cQuat nTrans::quatFromVec( Vec vec ) {
    float angle = vec.v[3];
    float s_angle = sin( angle / 2);
    float c_angle = cos( angle / 2);
    return (cQuat( c_angle, vec.v[0]*s_angle, vec.v[1]*s_angle, 
                   vec.v[2]*s_angle )).normalized();
 }

对于四元数的矩阵:

Matrix nTrans::matFromQuat( cQuat q ) {
    Matrix t;
    q = q.normalized();
    t.M[0][0] = ( 1 - (2*q.y*q.y + 2*q.z*q.z) );
    t.M[0][1] = ( 2*q.x*q.y + 2*q.w*q.z);         
    t.M[0][2] = ( 2*q.x*q.z - 2*q.w*q.y);   
    t.M[0][3] = 0;
    t.M[1][0] = ( 2*q.x*q.y - 2*q.w*q.z);        
    t.M[1][1] = ( 1 - (2*q.x*q.x + 2*q.z*q.z) ); 
    t.M[1][2] = ( 2*q.y*q.z + 2*q.w*q.x);         
    t.M[1][3] = 0;
    t.M[2][0] = ( 2*q.x*q.z + 2*q.w*q.y);       
    t.M[2][1] = ( 2*q.y*q.z - 2*q.w*q.x);        
    t.M[2][2] = ( 1 - (2*q.x*q.x + 2*q.y*q.y) );
    t.M[2][3] = 0;
    t.M[3][0] = 0;                  
    t.M[3][1] = 0;                  
    t.M[3][2] = 0;              
    t.M[3][3] = 1;
    return t;
 }

答案 2 :(得分:1)

我刚遇到同样的问题。我正在寻找一个解决方案,但我陷入困境。

因此,您需要两个坐标系中都知道的向量。在我的例子中,我在设备的坐标系(重力和磁场)中有2个正交向量,我想找到从设备坐标旋转到全局方向的四元数(其中North是正Y,而“up”是积极的Z)。所以,在我的例子中,我测量了设备坐标空间中的向量,并且我定义了向量本身,以形成全局系统的标准正交基础。

考虑到这一点,考虑四元数的轴角解释,有一些向量V,设备的坐标可以围绕该向量旋转某个角度以匹配全局坐标。我将调用我的(负)重力矢量G和磁场M(两者都归一化)。

V,G和M都描述了单位球面上的点。 Z_dev和Y_dev也是如此(我的设备坐标系的Z和Y基础)。 目标是找到一个旋转,将G映射到Z_dev,将M映射到Y_dev。 对于V将G旋转到Z_dev,由G和V定义的点之间的距离必须与由V和Z_dev定义的点之间的距离相同。在等式中:

| V - G | = | V - Z_dev |

该等式的解形成一个平面(所有点与G和Z_dev等距)。但是,V被约束为单位长度,这意味着解是一个以原点为中心的环 - 仍然是无限多的点。

但是,Y_dev,M和V也是如此:

| V - M | = | V - Y_dev |

对此的解决方案也是以原点为中心的环。这些环有两个交叉点,其中一个是另一个的负面。两者都是有效的旋转轴(在一种情况下旋转角度只是负值)。

使用上面的两个等式,以及每个向量都是单位长度的事实,你应该能够求解V.

然后你只需找到要旋转的角度,你应该能够使用从V到你对应的基础的矢量(G和Z_dev对我来说)。

最终,我在代数的最后解决了V ..但无论哪种方式,我认为你需要的一切都在这里 - 也许你会有比我更好的运气。

答案 3 :(得分:0)

你需要将A的方向表示为A作为四元数Q.然后,B中的任何向量都可以转换为A中的向量。通过使用从Q导出的旋转矩阵R,vectorInA = R * vectorInB。

在本网站提供的Matlab / Octave库中有一个演示脚本(包括一个很好的可视化):http://simonbox.info/index.php/blog/86-rocket-news/92-quaternions-to-model-rotations

答案 4 :(得分:0)

您可以仅使用四元数代数来计算所需的内容。

给出两个单位向量v 1 和v 2 ,您可以将它们直接嵌入四元数代数中,并获得相应的纯四元数q 1 , q 2 。将两个向量对齐的旋转四元数Q:

Q q 1 Q * = q 2

由:

给出

Q = q 1 (q 1 + q 2 )/(|| q 1 + q 2 ||)

以上产品为四元数产品。

答案 5 :(得分:0)

按您所给的定义3x3矩阵A和B,因此A的列为x_A,x_B和x_C,而B的列定义相似。然后,将坐标系A转换为B的变换T为解TA = B,因此T = BA ^ {-1}。从变换的旋转矩阵T中,您可以使用标准方法计算四元数。

相关问题