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).
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 :)
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;
}
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.
来源:https://stackoverflow.com/questions/43606135/split-quaternion-into-axis-rotations