Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Convention for QQuaternion conversion to/from Euler Angles



  • Hello all,

    This should be a fairly straightforward question. I was wondering what the convention is that Qt uses for converting between quaternions and Euler angles?

    The documentation says that the conversion corresponds "to a rotation of roll degrees around the z axis, pitch degrees around the x axis, and yaw degrees around the y axis (in that order)". Ignoring the confusing use of pitch, roll, yaw nomenclature (which is different than what I've seen in the aerospace industry), this implies that the quaternion is assuming a 2-1-3 euler rotation sequence (https://www.astro.rug.nl/software/kapteyn-beta/_downloads/attitude.pdf). However, the source code for the QQuaternion doesn't seem to agree with this.

    Additionally, does Qt assume body rotations (intrinsic) or rotations with respect to fixed axes (extrinsic)? I'm assuming that Qt assumes body rotations, but I'm not sure on that.

    Any ideas what convention is actually being used? It's important as I'm trying to interface with other code that uses Euler Angles with arbitrary configurations. Thanks all.



  • @feistykittykat As an addendum to this, it appears that the documentation on QQuaternion is incorrect, and reverses the convention. Their math is consistent with a conversion from a 3-1-2 body Euler rotation.



  • @feistykittykat said in Convention for QQuaternion conversion to/from Euler Angles:

    Ignoring the confusing use of pitch, roll, yaw nomenclature (which is different than what I've seen in the aerospace industry)

    AFAIK, yaw, pitch and roll are commonly used for robotics, but it is also mentioned as Aircraft principal axes.

    But unfortunately I have absolut no knowledge about quaternions, I would compare the calculated angles from Qt with the specific formulas. Here is also an online conversion from quaternions to euler angles.



  • @beecksche Sure, I'm familiar with the terminology, it's just that in my experience, roll is about the x-axis of the craft, yaw is about the z-axis, and pitch is about the y-axis. It's just a convention. My real gripe is that the math doesn't seem to match. I appreciate the tool that you've linked, but it doesn't really get to the crux of my question, which is whether or not Qt is actually using a 2-1-3 (Y-X-Z) sequence.



  • @feistykittykat

    If you think the docs are wrong, you can submit a bug to https://bugreports.qt.io/

    The math of the QQuaternion is based on: http://www.j3d.org/matrix_faq/matrfaq_latest.html

    See the source code

    Maybe this helps



  • @feistykittykat I have encountered the same issue regarding the convention today, and I was able to verify that the actual order in which the rotation is applied is indeed in the 2-1-3 (Y-X-Z) sequence, irrespective of the confusing yaw/pitch/roll nomenclature. I think at least specifying that the yaw/pitch/roll are specified in the camera coordinates would make it clearer, as X,Y,Z axes in robotics convention becomes -Z,-Y,X in (OpenGL) camera coordinates which results in the mixed terminology/axes specifications.

    Snippet for testing:

    bool IsQuaternionClose(const QQuaternion& lhs, const QQuaternion& rhs,
                           const float tol = 1e-6) {
      QVector3D axis{};
      float angle{0};
      (lhs * rhs.inverted()).getAxisAndAngle(&axis, &angle);
      return angle < tol;
    }
    
    void TestQuaternion(const float ax, const float ay, const float az) {
      const QQuaternion rx = QQuaternion::fromEulerAngles(ax, 0, 0);
      const QQuaternion rx2 = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, ax);
      assert(IsQuaternionClose(rx, rx2));
      const QQuaternion ry = QQuaternion::fromEulerAngles(0, ay, 0);
      const QQuaternion ry2 = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, ay);
      assert(IsQuaternionClose(ry, ry2));
      const QQuaternion rz = QQuaternion::fromEulerAngles(0, 0, az);
      const QQuaternion rz2 = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, az);
      assert(IsQuaternionClose(rz, rz2));
      const QQuaternion ryxz = QQuaternion::fromEulerAngles(ax, ay, az);
      assert(IsQuaternionClose(ry * rx * rz, ryxz));
    }
    

    Found the bug report (that loops back to this thread):
    https://bugreports.qt.io/browse/QTBUG-78548?jql=text ~ "qquaternion euler"
    Hopefully this will be addressed soon, as it is quite a confusing behavior.


Log in to reply