Verifying Euler to Quaternion calculations
I'm new to Quaternions so to learn more about them I decided to read up on various resources and try to construct my own elementary Quaternion library in python and then validate it with results from Unity.
So what I do is create a new project and place a cube in space. I then add to that cube a new script that simply copies its transform.rotation to a public variable that I call "myQuaternion". A snippet of the innards of the script are:
public Quaternion myQuaternion;
void Update() {
myQuaternion = transform.rotation;
}
So that seems to be working as expected, I can inspect the cube, change its rotation and get different values in myQuaternion. Great.
So now based on this wikipedia section: https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Conversion
I create this python function:
def euler_to_quaternion(phi,theta,psi):
return [math.cos(math.pi*phi/360)*math.cos(math.pi*theta/360)*math.cos(math.pi*psi/360)+
math.sin(math.pi*phi/360)*math.sin(math.pi*theta/360)*math.sin(math.pi*psi/360),
math.sin(math.pi*phi/360)*math.cos(math.pi*theta/360)*math.cos(math.pi*psi/360)-
math.cos(math.pi*phi/360)*math.sin(math.pi*theta/360)*math.sin(math.pi*psi/360),
math.cos(math.pi*phi/360)*math.sin(math.pi*theta/360)*math.cos(math.pi*psi/360)+
math.sin(math.pi*phi/360)*math.cos(math.pi*theta/360)*math.sin(math.pi*psi/360),
math.cos(math.pi*phi/360)*math.cos(math.pi*theta/360)*math.sin(math.pi*psi/360)-
math.sin(math.pi*phi/360)*math.sin(math.pi*theta/360)*math.cos(math.pi*psi/360)]
So now I run some tests:
For rotation (45,0,0):
Unity: w=0.9238795325112867 x=0.3826834323650898 y=0.0 z=0.0
Python: w=0.9238795325112867 x=0.3826834323650898 y=0.0 z=0.0
Looks good, they match
For rotation (0,45,0):
Unity: w=0.9238795325112867 x=0.0 y=0.3826834323650898 z=0.0
Python: w=0.9238795325112867 x=0.0 y=0.3826834323650898 z=0.0
Again, a match
For rotation (0,0,45):
Unity: w=0.9238795325112867 x=0.0 y=0.0 z=0.3826834323650898
Python: w=0.9238795325112867 x=0.0 y=0.0 z=0.3826834323650898
Again, a match
So here's the problem:
For rotation (0,45,45):
Unity: w=0.8535533905932737 x=0.14644660940672624 y=0.3535533905932738 z=0.3535533905932738
Python: w=0.8535533905932737 x=-0.14644660940672624 y=0.3535533905932738 z=0.3535533905932738
So the polarity for the value of x is different, not good...
For rotation (45,45,0):
Unity: w= 0.8446231986207332 x=0.46193976625564337 y=0.1913417161825449 z=0.1913417161825449
Python: w= 0.8446231986207332 x= 0.1913417161825449 y=0.46193976625564337 z= 0.1913417161825449
Now things look even more confusing, any idea what is going on?
Update: Doing more research and working through this issue, I've discovered that order of operations is very important when doing rotations. I found that I needed to break the rotations into 3 separate quaternions and then multiply them in a specific order, here's some additional python code:
def quaternion_mult(q,r):
return [r[0]*q[0]-r[1]*q[1]-r[2]*q[2]-r[3]*q[3],
r[0]*q[1]+r[1]*q[0]-r[2]*q[3]+r[3]*q[2],
r[0]*q[2]+r[1]*q[3]+r[2]*q[0]-r[3]*q[1],
r[0]*q[3]-r[1]*q[2]+r[2]*q[1]+r[3]*q[0]]
def unity_euler_to_quaternion(x,y,z):
return quaternion_mult(quaternion_mult(euler_to_quaternion(0,y,0),
euler_to_quaternion(x,0,0)),
euler_to_quaternion(0,0,z))
Now when I do a unity_euler_to_quaternion(45,45,45), the answers match.
Whew!
Ahh, yep. Looks like you did find what you were looking for. As an additional resource for others who may come across this:
The x, y, and z angles represent a rotation z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).
http://docs.unity3d.com/ScriptReference/Transform-eulerAngles.html
Answer by Bunny83 · Mar 31, 2016 at 11:47 PM
Well, as you figured out yourself the order in which you apply the rotations matters. There is not just one valid euler angles representation. Unity uses the order Z-X-Y around worldspace axes. To express this combination by rotations around localspace axes you just need to reverse the order. So it's the same as Y-X-Z around the localspace axes.
I haven't looked at the conversion used on the wikipedia page, but i guess it's X-Y-Z or Z-Y-X so you need a different order of your rotations. That means the combination of all those sin / cos will look different.
You shouldn't use your "euler_to_quaternion" method inside your "unity_euler_to_quaternion" method. You do way to many calculations that way. You should first implement the AngleAxis method. I don't really know python so here's a C# example:
public static Quaternion AngleAxis(float aAngle, float aX, float aY, float aZ)
{
float s = Mathf.Sin(aAngle/2f);
return new Quaternion(Mathf.Cos(aAngle/2f), aX*s, aY*s, aZ*s);
}
This method should create a quaternion rotation of "aAngle" radians around the normalized axis defined by (aX,aY,aZ)
To create your 3 rotations you would simply use:
AngleAxis(x, 1f,0, 0 );
AngleAxis(y, 0, 1f,0 );
AngleAxis(z, 0, 0, 1f);
Finally multiply them in the right order. Keep in mind that you can calculate the combined result in one go just like the example on the wikipedia page. However i would recommend to cache each sin / cos value in a local variable. At the moment inside your "euler_to_quaternion" method you calculate each sin and cos value 4 times.
Thanks! For the first cut of code, I wanted to write something as loyal to the reference as possible, not thinking about efficiency. Once it has proven itself, I'll go in and optimize and validate against the first version of inefficient code.
It would be excellent to distill the Sin & Cos combinations for the Unity style rotations so that I did not need to multiply Quaternions at all. That might take a few sheets of paper :)
Your answer
Follow this Question
Related Questions
Quaternion Rotation - AngleAxis/EulerAngle 2 Answers
How to preserve Y rotation while setting transform.up? 1 Answer
Rotating Vector3, linecast 1 Answer
How to know whether the two object parallel intersected or perpendicularly intersected ?? 0 Answers
Can't really grasp how to keep my player level on X and Z while using LookRotation 0 Answers