形象解说四元数Bydaode12122016-03-16前言:四元数(Quaternions)是由爱尔兰数学家哈密顿(WilliamRowanHamilton,1805-1865)在1843年发明的数学概念。复数、向量、矩阵都是数学中的基本要素,就如同编程中的数组、对象、集合那样。四元数是一种超复数,是复数与三维向量的复合体。四元数也有加法、减法、乘法、但是四元数的乘法不符合交换律(commutativelaw),即a*bb*a,而且,还有转置、规范化、共轭三种运算。由于它在描述三维旋转、姿态方面的一些特有优点,所以在飞行器(飞机,火箭,导弹等),机器人姿态的控制中常用到。数学手册中在代数结构的“群-环-域”中稍有点介绍,它属于不可交换的除环,称哈密顿四元数体。以下是一些四元数运算的效果图:四元数理论创立人:WilliamRowanHamilton,1805-1865一,四元数的几种表示形式:OpenTK中,为建立四元数提供了多种方式:publicQuaternion(floatx,floaty,floatz,floatw);publicQuaternion(OpenTK.Vector3v,floatw);例如用Quaternion(floatx,floaty,floatz,floatw):OpenTK.Quaternionq=newOpenTK.Quaternion(0.51f,-0.71f,0.31f,0.7071f);1,四元数建构方式一:i^2=j^2=k^2=-1ij=-ji=k,jk=-kj=i,ki=-ik=jq=w+ix+jy+kz,i,j,k分别对应轴向量X(1,0,0),Y(0,1,0),Z(0,0,1)2,四元数建构方式二:转动角之半+轴向量的方向余弦:3,四元数建构方式三:转动角之半+单位球面上的点:二,四元数的模如q是四元数,OpenTK中有:1,q.Length;返回值是:2,q.LengthSquared;返回值是:,与点积(内积)q·q是一致的。三,四元数的规范化[单位化]OpenTK.Quaternionq2=OpenTK.Quaternion.Normalize(q1);//单位化,使平方和等于1四元数建构方式二、方式三在这方面有明显优势。四,四元数的共轭OpenTK.Quaternionq2=OpenTK.Quaternion.Conjugate(q1);//共轭,仅向量取反方向若q=w+(ix+jy+kz);p=w-(ix+jy+kz);则q,p称为共轭,记q=p*或p=q*;共轭为取四元数中的标量与向量提供了方便:1、四元数的标量部:S=(q+q*)/2;2、四元数的向量部:V=(q-q*)/2;五,四元数的转置OpenTK.Quaternionq2=OpenTK.Quaternion.Invert(q1);//倒数,转置:方向相反,模互为倒数六,四元数的逻辑运算主要是相等,不相等的判断:q1==q2,q1!=q2boolb=q2.Equals(q1);七,四元数的算术运算运算符有:+、-、*,对应有Add(q,p)、Sub(q,p)、Multiply(q,*)加法的定义:publicstaticOpenTK.QuaternionAdd(OpenTK.Quaternionleft,OpenTK.Quaternionright)减法的定义:publicstaticOpenTK.QuaternionSub(OpenTK.Quaternionleft,OpenTK.Quaternionright)乘法有二种:publicstaticOpenTK.QuaternionMultiply(OpenTK.Quaternionquaternion,floatscale)publicstaticOpenTK.QuaternionMultiply(OpenTK.Quaternionleft,OpenTK.Quaternionright)注意,乘法没有交换律:q2*q1!=q1*q2乘法的定义和数学算法如下:例如:四元数与标量相乘,是对原四元数在正方向或反方向进行伸缩:OpenTK.Quaterniondq2=OpenTK.Quaterniond.Multiply(q1,0.5f);//q1*0.5四元数与四元数相乘,是两种旋转的叠加作用:OpenTK.Quaterniondq3=OpenTK.Quaterniond.Multiply(q2,q1);//q2*q1q3=q1·q2=(a1a2-b1b2-c1c2-d1d2)+i(a1b2+b1a2+c1d2-d1c2)+j(a1c2+a2c1+b2d1-d2b1)+k(a1d2+d1a2+b1c2-c1b2)四元数与四元数相乘是不容易心算的游戏,就方向的变化也很难摸准,见下例://向量连乘效果图:privatevoidQuatMults()//向量连乘效果图{qs[0]=newOpenTK.Quaternion(.5f,-.6f,-.7f,.4f);DwQuat(qs[0],Color.FromArgb(255,255,0),4f);qs[1]=newOpenTK.Quaternion(-.6f,-.5f,.4f,.6f);DwQuat(qs[1],Color.FromArgb(0,255,255),4f);for(inti=0;i255;i++){qs[i+2]=OpenTK.Quaternion.Multiply(qs[i],qs[i+1]);qs[i+2]=OpenTK.Quaternion.Normalize(qs[i+2]);DwQuat(qs[i+2],Color.FromArgb(i,i,i),2f);}}//表现四元数:privatevoidDwQuat(OpenTK.Quaternionq,Colorclr,floatd){//画线段:GL.LineWidth(d);GL.Begin(BeginMode.Lines);GL.Color3((byte)clr.R,(byte)clr.G,(byte)clr.B);GL.Vertex3(0,0,0);GL.Vertex3(q.X,q.Y,q.Z);GL.End();//画球:GL.PushMatrix();GL.Translate(q.X,q.Y,q.Z);GL.Color3((byte)clr.R,(byte)clr.G,(byte)clr.B);Glu.Sphere(Glu.NewQuadric(),.05,9,9);//画球GL.PopMatrix();}三维效果如下:正是这一特点,可以让程序建构出很多艺术品出来。四元数的除法,如果按乘法的逆运算是除法这一法则去找因数1、因数2,要解一个4X4的联列方程组,可参考下面的矩阵:先用克莱姆法则判断一下,它的解可能唯一,可能无解,也可能有无数多组解。设:q3=q1*q2(q1:左四元数因子,q2:右四元数因子),如果把左、右因子分别叫作左、右商,我们可以自己构造函数进行计算://除法1:求左商,右商:OpenTK.Quaternionq=QuaternionDiv(qs[1],qs[0],1);//LR=1:返回右商OpenTK.Quaternionq1=QuaternionDiv(qs[1],qs[0],-1);//LR=-1:返回左商//除法2:以qs[0]的转置乘qs[1]OpenTK.Quaternionq2=OpenTK.Quaternion.Multiply(qs[2],qs[1]);实际计算表明,两种除法不一致,所以不应该用转置来替代除法。四元数连除三维图为了测试255次连续相除,设计算法如下:进行12次迭代后制作了下图之前另一算法的效果图:八,“轴+角”方式转四元数publicstaticOpenTK.QuaternionFromAxisAngle(OpenTK.Vector3axis,floatangle)其中OpenTK.Vector3axis是轴向量,floatangle是以弧度表示的角如:OpenTK.Quaternionq3=OpenTK.Quaternion.FromAxisAngle(newVector3(0.4f,0.4f,0.8f),PI/2f);九,四元数转“轴+角”方式1,publicOpenTK.Vector4ToAxisAngle()2,publicvoidToAxisAngle(outOpenTK.Vector3axis,outfloatangle)其中OpenTK.Vector4形如(floatx,floaty,floatz,floatangle)floatangle是以弧度表示的角。如:OpenTK.Vector4v4=q3.ToAxisAngle();十,四元数到欧拉角的转换//四元数转欧拉角,欧拉角的球面RGB(用自定义类实现):privatevoidQuaternionToEulerAngle_BallRGB(){//声明类与结构:EularEL=newEular();Quaternionq=newQuaternion();EulerAngleea=newEulerAngle();//利用三角式生成四元数:doublePI=3.1416;//doublea=PI/8;doubleb=PI/2;doublec=0;//转轴:Y轴,转动角:PI/4//doublea=PI/2;doubleb=0;doublec=0;//转轴:X轴,转动角:PI//doublea=PI/2;doubleb=PI/2;doublec=0;//转轴:Y轴,转动角:PI//doublea=PI/2;doubleb=0;doublec=PI/2;//转轴:Z轴,转动角:PIdoublea=PI/2;//转动角:2a(=PI)doubleb=PI/2;doublec=0;for(b=-PI;bPI;b+=.1){for(c=-PI/2;cPI/2;c+=.1){q.w=Math.Cos(a);q.x=Math.Cos(b)*Math.Cos(c)*Math.Sin(a);q.y=Math.Sin(b)*Math.Cos(c)*Math.Sin(a);q.z=Math.Sin(c)*Math.Sin(a);//四元数转欧拉角:ea=EL.QuaternionToEulerAngle(q);byteR=(byte)(255*(PI+ea.pitch)/(2*PI));byteG=(byte)(255*(PI+ea.yaw)/(2*PI));byteB=(byte)(255*(PI+ea.roll)/(2*PI));GL.Color3(0f,0f,0f);GL.LineWidth(4f);GL.Begin(BeginMode.Lines);GL.Vertex3(0,0,0);GL.Color3(R,G,B);GL.Vertex3(q.x,q.y,q.z);GL.End();}}}四元数转欧拉角,欧拉角的球面RGB四元数转欧拉角的数学算法如下,不同的坐标系可能要作适当的调整:如果分别为绕Z轴、Y轴、X轴的旋转角度,则有:十一,欧拉角到四元数的转换privatevoidEulerAngleToQuaternion(){doubleA=Math.PI/6;doubleB=Math.PI/4;doubleC=Math.PI/2;doublec1=Math.Cos(A/2);doubles1=Math.Sin(A/2);doublec2=Math.Cos(B/2);doubles2=Math.Sin(B/2);doublec3=Math.Cos(C/2);doubles3=Math.Sin(C/2);doublew=c1*c2*c3-s1*s2*s3;doublex=s1*s2*c3+c1*c2*s3;doubley=s1*c2*c3+c1*s2*s3;doublez=c1*s2*c3-s1*c2*s3;//窗体