هل هناك خوارزمية لتحويل دورات الكواترنيون إلى دورات زاوية أويلر؟

StackOverflow https://stackoverflow.com/questions/1031005

سؤال

هل هناك خوارزمية موجودة لتحويل تمثيل الكواترنيون للدوران إلى تمثيل زاوية أويلر؟ترتيب التناوب لتمثيل أويلر معروف ويمكن أن يكون أيًا من التباديل الستة (أي.xyz، xzy، yxz، yzx، zxy، zyx).لقد رأيت خوارزميات لأمر دوران ثابت (عادةً ما يكون عنوان ناسا أو البنك أو اتفاقية اللفة) ولكن ليس لأمر التناوب التعسفي.

علاوة على ذلك، نظرًا لوجود تمثيلات متعددة لزاوية أويلر ذات اتجاه واحد، ستكون هذه النتيجة غامضة.وهذا مقبول (لأن التوجه لا يزال صالح, ، قد لا يكون هو الذي يتوقع المستخدم رؤيته)، ومع ذلك سيكون من الأفضل لو كانت هناك خوارزمية لها حدود للتدوير (أي.عدد درجات الحرية والحدود على كل درجة من الحرية) في الاعتبار وأسفرت عن تمثيل أويلر "الأكثر منطقية" في ضوء تلك القيود.

لدي شعور بأن هذه المشكلة (أو شيء مشابه) قد تكون موجودة في IK أو مجالات ديناميكيات الجسم الصلبة.


تم حلها: لقد أدركت للتو أنه قد لا يكون من الواضح أنني قمت بحل هذه المشكلة باتباع خوارزميات Ken Shoemake من Graphics Gems.لقد أجبت على سؤالي في ذلك الوقت، ولكن خطر لي أنه قد لا يكون من الواضح أنني فعلت ذلك.انظر الإجابة أدناه لمزيد من التفاصيل.


فقط للتوضيح - أعرف كيفية التحويل من الكواترنيون إلى ما يسمى "تيت بريانالتمثيل - ما كنت أسميه مؤتمر "ناسا".هذا هو ترتيب التدوير (بافتراض أن المحور "Z" لأعلى) لـ zxy.أحتاج إلى خوارزمية ل الجميع أوامر التناوب.

ربما يكون الحل إذن هو أخذ تحويل أمر zxy واشتقاق منه خمسة تحويلات أخرى لأوامر التناوب الأخرى.أعتقد أنني كنت آمل أن يكون هناك حل أكثر شمولاً.على أية حال، أنا مندهش لأنني لم أتمكن من إيجاد الحلول الموجودة هناك.

بالإضافة إلى ذلك، وربما ينبغي أن يكون هذا سؤالًا منفصلاً تمامًا، فإن أي تحويل (بافتراض ترتيب دوران معروف بالطبع) سيحدد واحد تمثيل أويلر، ولكن هناك في الواقع الكثير.على سبيل المثال، بالنظر إلى ترتيب دوران yxz، فإن التمثيلين (0,0,180) و (180,180,0) متكافئان (وسوف ينتج عنهما نفس الرباعي).هل هناك طريقة لتقييد الحل باستخدام حدود درجات الحرية؟كما تفعل في IK وديناميكيات الجسم الصلبة؟أي.في المثال أعلاه، إذا كانت هناك درجة واحدة فقط من الحرية حول المحور Z، فيمكن تجاهل التمثيل الثاني.


لقد قمت بتتبع ورقة واحدة يمكن أن تكون خوارزمية فيها هذا قوات الدفاع الشعبي لكن يجب أن أعترف أنني أجد صعوبة في اتباع المنطق والرياضيات.بالتأكيد هناك حلول أخرى هناك؟هل أمر التناوب التعسفي نادر جدًا حقًا؟من المؤكد أن كل حزمة ثلاثية الأبعاد رئيسية تسمح بالرسوم المتحركة الهيكلية مع الاستيفاء الرباعي (أي.Maya، Max، Blender، إلخ) هل يجب أن تحل هذه المشكلة بالضبط؟

هل كانت مفيدة؟

المحلول

يبدو هذا وكأنه حالة كلاسيكية من التكنولوجيا القديمة التي تم التغاضي عنها - فقد تمكنت من استخراج نسخة من Graphics Gems IV من المرآب ويبدو أن Ken Shoemake ليس لديه فقط خوارزمية للتحويل من زوايا أويلر اِعتِباطِيّ ترتيب التناوب، ولكنه يجيب أيضًا على معظم أسئلتي الأخرى حول هذا الموضوع.تحية للكتب.لو كان بإمكاني التصويت لصالح السيد.قم بإجابة Shoemake وكافئه بنقاط السمعة.

أعتقد أن أي شخص يعمل مع زوايا أويلر يجب أن يحصل على نسخة من Graphics Gems IV من مكتبته المحلية ويقرأ القسم الذي يبدأ بالصفحة 222.يجب أن يكون التفسير الأوضح والأكثر إيجازًا للمشكلة التي قرأتها حتى الآن.


إليك رابطًا مفيدًا وجدته منذ ذلك الحين - http://www.cgafaq.info/wiki/Euler_angles_from_matrix - يتبع هذا نفس نظام Shoemake؛يتم ترميز التباديل الـ 24 المختلفة لترتيب التدوير كأربعة معلمات منفصلة - المحور الداخلي، والتكافؤ، والتكرار، والإطار - مما يسمح لك بعد ذلك بتقليل الخوارزمية من 24 حالة إلى 2.يمكن أن تكون ويكي مفيدة بشكل عام - لم أصادفها من قبل.

يبدو أن الرابط القديم المقدم معطل هنا is another copy of "Computing Euler angles from a rotation matrix ".

نصائح أخرى

في نظام الإحداثيات الديكارتية الأيمن مع توجيه المحور Z للأعلى، قم بما يلي:

struct Quaternion
{
    double w, x, y, z;
};

void GetEulerAngles(Quaternion q, double& yaw, double& pitch, double& roll)
{
    const double w2 = q.w*q.w;
    const double x2 = q.x*q.x;
    const double y2 = q.y*q.y;
    const double z2 = q.z*q.z;
    const double unitLength = w2 + x2 + y2 + z2;    // Normalised == 1, otherwise correction divisor.
    const double abcd = q.w*q.x + q.y*q.z;
    const double eps = 1e-7;    // TODO: pick from your math lib instead of hardcoding.
    const double pi = 3.14159265358979323846;   // TODO: pick from your math lib instead of hardcoding.
    if (abcd > (0.5-eps)*unitLength)
    {
        yaw = 2 * atan2(q.y, q.w);
        pitch = pi;
        roll = 0;
    }
    else if (abcd < (-0.5+eps)*unitLength)
    {
        yaw = -2 * ::atan2(q.y, q.w);
        pitch = -pi;
        roll = 0;
    }
    else
    {
        const double adbc = q.w*q.z - q.x*q.y;
        const double acbd = q.w*q.y - q.x*q.z;
        yaw = ::atan2(2*adbc, 1 - 2*(z2+x2));
        pitch = ::asin(2*abcd/unitLength);
        roll = ::atan2(2*acbd, 1 - 2*(y2+x2));
    }
}

لقد كنت أبحث لعدة أيام عن حل مماثل، وأخيراً صادفت هذا الموقع الذي يحتوي على خوارزمية لتحويل الكواترنيونات إلى دورات أويلر وتايت بريان التعسفية!

إليك الرابط: http://bediyap.com/programming/convert-quaternion-to-euler-rotations/

وهذا هو الكود:

///////////////////////////////
// Quaternion to Euler
///////////////////////////////
enum RotSeq{zyx, zyz, zxy, zxz, yxz, yxy, yzx, yzy, xyz, xyx, xzy,xzx};

void twoaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){
  res[0] = atan2( r11, r12 );
  res[1] = acos ( r21 );
  res[2] = atan2( r31, r32 );
}

void threeaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){
  res[0] = atan2( r31, r32 );
  res[1] = asin ( r21 );
  res[2] = atan2( r11, r12 );
}

void quaternion2Euler(const Quaternion& q, double res[], RotSeq rotSeq)
{
    switch(rotSeq){
    case zyx:
      threeaxisrot( 2*(q.x*q.y + q.w*q.z),
                     q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                    -2*(q.x*q.z - q.w*q.y),
                     2*(q.y*q.z + q.w*q.x),
                     q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                     res);
      break;

    case zyz:
      twoaxisrot( 2*(q.y*q.z - q.w*q.x),
                   2*(q.x*q.z + q.w*q.y),
                   q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                   2*(q.y*q.z + q.w*q.x),
                  -2*(q.x*q.z - q.w*q.y),
                  res);
      break;

    case zxy:
      threeaxisrot( -2*(q.x*q.y - q.w*q.z),
                      q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                      2*(q.y*q.z + q.w*q.x),
                     -2*(q.x*q.z - q.w*q.y),
                      q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                      res);
      break;

    case zxz:
      twoaxisrot( 2*(q.x*q.z + q.w*q.y),
                  -2*(q.y*q.z - q.w*q.x),
                   q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                   2*(q.x*q.z - q.w*q.y),
                   2*(q.y*q.z + q.w*q.x),
                   res);
      break;

    case yxz:
      threeaxisrot( 2*(q.x*q.z + q.w*q.y),
                     q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                    -2*(q.y*q.z - q.w*q.x),
                     2*(q.x*q.y + q.w*q.z),
                     q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                     res);
      break;

    case yxy:
      twoaxisrot( 2*(q.x*q.y - q.w*q.z),
                   2*(q.y*q.z + q.w*q.x),
                   q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                   2*(q.x*q.y + q.w*q.z),
                  -2*(q.y*q.z - q.w*q.x),
                  res);
      break;

    case yzx:
      threeaxisrot( -2*(q.x*q.z - q.w*q.y),
                      q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                      2*(q.x*q.y + q.w*q.z),
                     -2*(q.y*q.z - q.w*q.x),
                      q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                      res);
      break;

    case yzy:
      twoaxisrot( 2*(q.y*q.z + q.w*q.x),
                  -2*(q.x*q.y - q.w*q.z),
                   q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                   2*(q.y*q.z - q.w*q.x),
                   2*(q.x*q.y + q.w*q.z),
                   res);
      break;

    case xyz:
      threeaxisrot( -2*(q.y*q.z - q.w*q.x),
                    q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                    2*(q.x*q.z + q.w*q.y),
                   -2*(q.x*q.y - q.w*q.z),
                    q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                    res);
      break;

    case xyx:
      twoaxisrot( 2*(q.x*q.y + q.w*q.z),
                  -2*(q.x*q.z - q.w*q.y),
                   q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                   2*(q.x*q.y - q.w*q.z),
                   2*(q.x*q.z + q.w*q.y),
                   res);
      break;

    case xzy:
      threeaxisrot( 2*(q.y*q.z + q.w*q.x),
                     q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                    -2*(q.x*q.y - q.w*q.z),
                     2*(q.x*q.z + q.w*q.y),
                     q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                     res);
      break;

    case xzx:
      twoaxisrot( 2*(q.x*q.z - q.w*q.y),
                   2*(q.x*q.y + q.w*q.z),
                   q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                   2*(q.x*q.z + q.w*q.y),
                  -2*(q.x*q.y - q.w*q.z),
                  res);
      break;
    default:
      std::cout << "Unknown rotation sequence" << std::endl;
      break;
   }
}

لقد قمت بنشر بحثي بعنوان "تحويل زاوية Quaternion إلى زاوية أويلر لتسلسل الدوران التعسفي باستخدام الطرق الهندسية" على موقع الويب الخاص بي على noelhughes.net.لدي أيضًا خوارزميات لتحويل أي مجموعة من زوايا أويلر إلى كواترنيون وكواتيرنيون إلى/من مصفوفة جيب التمام التي سأنشرها في نهاية هذا الأسبوع.هذه موجودة أيضًا على موقع Martin Bakers الإلكتروني، على الرغم من صعوبة العثور عليها.ابحث في جوجل عن اسمي، نويل هيوز، والكواتيرنيون، وستجده.

أنا حلها بهذه الطريقة:

الخطوة 1:تأكد من الاتفاقية التي تريدها لتدوير أويلر، على سبيل المثال، zyx.

الخطوة 2:حساب مصفوفة الدوران التحليلية للدوران.على سبيل المثال، إذا كنت تريد R(zyx),

**R***zyx* = **R***x*( فاي ) * **ص***ي*( ثيتا ) * **ر***ض*( رطل لكل بوصة مربعة )، حيث تصبح العناصر

R11 =  cos(theta)*cos(psi)
R12 = -cos(theta)*sin(psi)
R13 =  sin(theta)
R21 =  sin(psi)*cos(phi) + sin(theta)*cos(psi)*sin(phi)
R22 =  cos(psi)*cos(phi) - sin(theta)*sin(psi)*sin(phi)
R23 = -cos(theta)*sin(phi)
R31 =  sin(psi)*sin(phi) - sin(theta)*cos(psi)*cos(phi)
R32 =  cos(psi)sin(phi) + sin(theta)*sin(psi)*cos(phi)
R33 =  cos(theta)*cos(phi) 

الخطوه 3:من خلال الفحص، يمكنك العثور على جيب أو ظا للزوايا الثلاث باستخدام العناصر المذكورة أعلاه.في هذا المثال،

tan(phi) = -R23/R33

sin(theta) = -R13

tan(psi) = -R12/R11

الخطوة 4:احسب مصفوفة الدوران من الكواترنيون الخاص بك (انظر ويكيبيديا)، بالنسبة للعناصر التي تحتاجها لحساب الزوايا كما في 3) أعلاه.

يمكن حساب الاتفاقيات الأخرى باستخدام نفس الإجراء.

إليك ورقة كتبتها عن تحويل الكواترنيون إلى زوايا أويلر.

الرابط 1

لقد قمت أيضًا بوضع عدد من المستندات في هذا الموقع تناقش جوانب مختلفة من الكواترنيونات وزوايا أويلر ومصفوفات الدوران (DCM).

الرابط 2

بالنسبة لأولئك الذين يتعثرون في هذه الصفحة أثناء البحث في Google، فقد عثرت مؤخرًا على اشتقاقات لهذه التحويلات لجميع العناصر الـ 12 الجوهرية لـ Tait-Bryan (1-2-3، 3-2-1، وما إلى ذلك) وأويلر السليم (1-2-1، 3-1-3، الخ) تسلسلات الدوران في المرجعين التاليين:

شكرا ل frodo2975 للرابط الثاني.

ويكيبيديا يوضح كيف يمكنك استخدام أجزاء الرباعي وحساب زوايا أويلر.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top