代码之家  ›  专栏  ›  技术社区  ›  karamazovbros

处理:使用四元数旋转三维对象适用于X轴,但不适用于Y轴或Z轴?

  •  0
  • karamazovbros  · 技术社区  · 7 年前

    我正在从串行设备的输入创建四元数。在处理过程中,我围绕下面代码中的X轴旋转。我的四元数对象接受输入并使用set函数设置值、Euler角度和规格化。数学有问题吗?

    我对z和y的旋转进行了评论,但基本上物体并没有很好地旋转,或者与x轴相比,它是不稳定的,这是完美的。我在下面的代码中犯了什么错误?

    Quaternion q = new Quaternion(s);
    q.set(x, y, z, w);
    q = q.Euler(q.eulerAngles);
    translate(x, y);
    rotateX(q.eulerAngles.x);
    //rotateY(q.eulerAngles.y);
    //rotateZ(q.eulerAngles.z);
    shape(model);
    rotateX(-q.eulerAngles.x);
    translate(-x, -y);
    

    public class Quaternion {
    
       PApplet s;
       public float w,x,y,z;
       public PVector eulerAngles;
    
       public Quaternion(PApplet s, float x, float y, float z, float w){
         this.s = s;
         this.x = x;
         this.y = y;
         this.z = z;
         this.w = w;
         normalize();
       }
    
       public Quaternion(Quaternion q){
         this.s = q.s;
         this.w = q.w;
         this.x = q.x;
         this.y = q.y;
         this.z = q.z;
       }
    
       public Quaternion normalize() {
         float magnitude = w*w + x*x + y*y + z*z;
         if(magnitude != 0.0 && magnitude != 1.0){
           magnitude = 1.0f / s.sqrt(magnitude);
           w *= magnitude;
           x *= magnitude;
           y *= magnitude;
           z *= magnitude;
         }
         eulerAngles = setEulerAngles();
         return this;
       }
    
       public Quaternion set(float x, float y, float z, float w) {
         this.x = x;
         this.y = y;
         this.z = z;
         this.w = w;
         return normalize();
       }
    
       // Returns a rotation that rotates z degrees around 
       // the z axis, x degrees around the x axis, and y 
       // degrees around the y axis.
       public Quaternion Euler(){
          float roll = eulerAngles.x;
          float pitch = eulerAngles.y;
          float yaw = eulerAngles.z;
    
          float cr = (float)Math.cos(roll * 0.5);
          float sr = (float)Math.sin(roll * 0.5);
          float cp = (float)Math.cos(pitch * 0.5);
          float sp = (float)Math.sin(pitch * 0.5);
          float cy = (float)Math.cos(yaw * 0.5);
          float sy = (float)Math.sin(yaw * 0.5);
    
          w = cy*cr*cp + sy*sr*sp;
          x = cy*sr*cp - sy*cr*sp;
          y = cy*cr*sp + sy*sr*cp;
          z = sy*cr*cp - cy*sr*sp;
          return normalize();
       }
    
    
       // set euler angle representation of 
       // the rotation in 3-dim PVector
       private PVector setEulerAngles(){
    
         // roll: x-axis rotation
         float sinr = 2.0f * (w*x + y*z);
         float cosr = 1.0f - 2.0f * (x*x + y*y);
         float roll = (float)Math.atan2(sinr, cosr);
    
         // pitch: y-axis rotation
         float sinp = 2.0f * (w*y - z*x);
         float pitch = 0.0f;
         if(Math.abs(sinp) >= 1){
           pitch = (float)Math.copySign(Math.PI/2, sinp);
         } else {
           pitch = (float)Math.asin(sinp);
         }
    
         // yaw: z-axis rotation
         float siny = 2.0f * (w*z + x*y);
         float cosy = 1.0f - 2.0f * (y*y + z*z);
         float yaw = (float)Math.atan2(siny, cosy);
    
         return new PVector(roll, pitch, yaw);
       }
    }
    
    1 回复  |  直到 7 年前
        1
  •  0
  •   Nico Schertler    7 年前

    据我所知,从方法中得到的欧拉角应该按zyx顺序应用,而不是按xyz顺序应用。但无论如何,不要乱用欧拉角,除非你真的需要。在这种情况下,你不会。

    相反, convert the quaternion to a rotation matrix 并使用 applyMatrix() . 这里不会有歧义。

    要还原转换,请不要应用反转转换(就像使用 rotateX(-q.eulerAngles.x) translate(-x, -y) )在开发过程中,很容易混淆顺序或忘记转换。相反,使用 pushMatrix() / popMatrix() resetMatrix

    顺便说一句,我觉得你的四元数类的定义很混乱。有些方法返回我不希望返回的值(例如 normalize() )此外,我不认为用四元数存储欧拉角表示是一个好主意。即使你认为是这样,我也不明白这个方法的目的 Euler() 因为它既没有参数,也不能从外部设置Euler角度。