Search code examples
unity-game-enginequaternionsimu

How to get the relative rotation of 2 quaternions in Unity


I'm trying to obtain the angle between the upperarm en lowerarm using two IMU's respectively. I calibrate both imu's and use Quaternions to get global orientation of the upperarm and lowerarm object in Unity 3D.

Qimu1 = Quaternion from the imu located on the lowerarm

Qimu2 = Quaternion from the imu located on upperarm.

I made two objects in Unity representing the upperarm en lowerarm. Where the lowerarm is a child object of the upperarm and is connected to the upperarm using Character joint.

This is the script for the upperarm:

if (Input.GetKeyDown(KeyCode.Space))
        {
            Qstart2 = Qimu2;
        }

        transform.localRotation = Qstart2 * Quaternion.Inverse(Qimu2);

Lowerarm script:

if (Input.GetKeyDown(KeyCode.Space))
        {
            Qstart1 = Qimu1;
        }

        Qimu1new = Qstart1 * Quaternion.Inverse(Qimu1);
        Quaternion Qdiff = Quaternion.Inverse(Qimu1new) * Qimu2;
        transform.localRotation = Qdiff;

What i would like to acquire is a visual representation of arm movement in Unity3D using 2 IMU's. Now if i keep my elbow extended the script works. But as soon as i start to flex my elbow the lowerarm object starts to continuously rotate around the same axis.


Solution

  • We can use some algebra to find the calculation for the lower arm transform's relative rotation:

    The formula Qstartx * Quaternion.Inverse(Qimux) gives you a rotation for that part relative to the body:

    upper arm's rotation relative to body = Qstart2 * Quaternion.Inverse(Qimu2)
    lower arm's rotation relative to body = Qstart1 * Quaternion.Inverse(Qimu1)
    

    We know that since lower arm is a child of the upper arm:

    lower arm's rotation relative to body 
        = upper arm's rotation relative to body * lowerArmTransform.relativeRotation
    

    With substitution, we can determine:

    Qstart1 * Quaternion.Inverse(Qimu1) 
        = (Qstart2 * Quaternion.Inverse(Qimu2)) * lowerArmTransform.relativeRotation
    

    Multiply both sides on the left by Quaternion.Inverse(Qstart2 * Quaternion.Inverse(Qimu2)):

    Quaternion.Inverse(Qstart2 * Quaternion.Inverse(Qimu2)) * Qstart1 
    * Quaternion.Inverse(Qimu1) 
        = Quaternion.Inverse(Qstart2 * Quaternion.Inverse(Qimu2)) 
          * ( Qstart2 * Quaternion.Inverse(Qimu2) )
          * lowerArmTransform.relativeRotation
    

    Quaternion.Inverse(Qstart2 * Quaternion.Inverse(Qimu2)) * ( Qstart2 * Quaternion.Inverse(Qimu2) ) cancel out:

    Quaternion.Inverse(Qstart2 * Quaternion.Inverse(Qimu2)) * Qstart1 
    * Quaternion.Inverse(Qimu1) 
        = lowerArmTransform.relativeRotation
    

    Since Qimu1new = Qstart1 * Quaternion.Inverse(Qimu1);, we can conclude:

    lowerArmTransform.relativeRotation = Quaternion.Inverse(Qstart2 * Quaternion.Inverse(Qimu2)) * Qimu1new;