1

I have a quaternion representing the orientantion of an object (yellow box and sphere). I would like to know if it is possible to split that quaternion into other quaternions that give us the rotation of each local axis (X, Y and Z).

Due to I do not have enough reputation to post images directly, I have to put just the link of the image

What I have been doing until now is getting the Euler representation and work with it, but it is not the correct solution for my particular case:

Given two points (blue boxes), I want to limit the orientation of my object so that it can't point out of the grey plane, even if my quaternion looks out of that plane.
I want to split (decompose) the quaternion, because when my object reachs the limit of the plane (for instance, the right), I want to make it stay there (in that component), and then rotate my object in the vertical component, using one of the new splitted quaternion.

I am working with Unity.

I hope it is understandable my problem :)

4
  • would Quaternion rotation = Quaternion.Euler(new Vector3(transform.eulerAngles.x, 0, 0)); be applicable? Commented Apr 25, 2017 at 9:25
  • Thanks @Peh for the edition. Thanks Ryan, but I want to avoid Euler angles because the original quaternion comes from an external IMU, and limiting the orientantion with Euler angles I have had some troubles Commented Apr 25, 2017 at 9:58
  • Quaternion.eulerAngles is a Thing, but I suspect that's what you're already using. I know the wxyz values won't help at all as individually they're incomplete. Best I can think of is to calculate a Euler AngleTo() (you'll have to write this method) on the two blue boxes and check that both the X and Y components thereof are greater than 0 (or lessthan, as appropriate). Commented Apr 25, 2017 at 13:39
  • 1
    stackoverflow.com/questions/3684269/… 1.Decompose 2 Limit 3. Compose Commented Apr 26, 2017 at 9:08

2 Answers 2

4

EDIT: The below quickly breaks down when more than one euler axis is non-zero. Refer to Component of a quaternion rotation around an axis instead.

(thank you @LyrePyre for noticing and @minorlogic for the link).

Original:

Here's a way to get the local rotation of just the y-axis. This function can be modified to get the x or z-axis.

/// <summary> isolate the y-Component of a rotation </summary>
private Quaternion yRotation(Quaternion q)
{
    float theta = Mathf.Atan2(q.y, q.w);

    // quaternion representing rotation about the y axis
    return new Quaternion(0, Mathf.Sin(theta), 0, Mathf.Cos(theta));
}

You can verify the result in the Unity inspector by converting to Euler:

public float yLocal;
void Update()
{
    yLocal = yRotation(this.transform.rotation).eulerAngles.y;
}
Sign up to request clarification or add additional context in comments.

3 Comments

This does not yield correct values for a lot of different inputs, but especially inputs where the euler axis of the quaternion is non-zero for all x, y, and z. For example, we expect that an input of Quaternion.Euler(12, 34, 56) would give us 34 from your yRotation() implementation, but instead it gives 27.6. This makes it unsuitable if the reason for this implementation is to isolate monoaxial rotations from input noise.
I think in order to properly decompose per-axis component(s) out of any 3D rotation, you need to account for the rotation order with which your engine constructs rotation/transform matrices. (Word on the street is, Unity composes rotations in ZYX order, although Unity's docs say it uses ZXY. I guess I can't be certain which it is unless I write some code to test it myself...) I briefly tried to derive a formula for this, but I tapped out and returned to using Unity's built-in Euler decomposition. Non-trivial!
@LyrePyre Thanks! Tested it myself and found the same not-working result. :-/ Marked the question as incorrect. The question minorlogic linked to in the comments below the question (and now linked in this answer as well) might be the answer you want.
0

I have worked with IMUs before and to my knowledge with Unity if the object is reading in the IMU data as quaternions, then you will not encounter the gimbo lock problem. So you can indeed create a reference to the object's rotation and convert those to euler angels, and then convert back to Quaternions before applying it to the object.

However, if you are simply wanting to limit rotation I would do something like this:

//the last rotation was pointed at the grey zone.
private Quaternion prevAllowedRotation;

void FixedUpdate(){
    if(!isValidRotation()){
        this.transform.rotation = prevAllowedRotation;
    }else{
        this.transform.lookAt(lockToArea());
    }
}


private bool isValidRotation(){
    //I chose forward based on the image, find the direction that works for you
    Ray ray = new Ray(this.transform.position, this.transform.forward);
    RaycastHit hit;
    if(Physics.Raycast(ray, out hit, 10f){
        if(hit.transform.tag == "wall"){
            return true;
        }
    }
    return false;
}


private Vector3 lockToArea(Gameobject grey){
    Vector3 center = grey.transform.position;
    //figure out how to get the position of the facing direction on the same plane as the grey area.
    Vector3 pointerPosition = getPointerPosition();
    RaycastHit hit;
    if(Physics.Linecast(pointerPosition, center, hit)){
        return hit.point;
    }
    return Vector3.zero;

}

private Vector3 getPointerOnPlane(){
    Ray ray = new Ray(this.transform.position, this.transform.forward);
    // you need to figure out how to dyanmically get the distance so that the ray lands on the same plane as the vector
    float distance = 0; 
    return ray.GetPoint(distance);       
}

This locks its rotation to only be pointing at the grey cube. This should help you a lot, you just need to get the point that the IMU data is reading in at and is on the same plane as the grey cube's center.

2 Comments

thanks @dan for your answer. It is really close to what I expect, but I want to go one step beyond. My rotation is limited to the area of the grey plane, OK, but the problem now is when my cube is pointing out of the plane (the raw rotation I mean), it is stuck at one side of the plane. For instance, if I get my cube out of the right side of the plane and move it vertically, it remains stuck in the last allowed position, instead of move up and down along the right edge. Is it easy to reach? Thanks again.
@javinair look at the latest edits I made, I didn't have enough time to do the last step which was finish the getPointerOnPlane but if you figure that out, then you have exactly what you want. I made these edits after I re-read your question. I am also glad it worked, as I wasn't able to test it XD.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.