I’m trying to get better results for quaternion to euler conversions than I seem to get from the native Blender conversions. It’s turning out to be tricky. Apparently eulers are wily beasts.
I have had the most success with the following code, but it fails significantly with the gimbal lock zones.
def quat_to_euler2(q1): #adapted from Maths - Conversion Quaternion to Euler - Martin Baker #http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm sqw = q1.w * q1.w sqx = q1.x * q1.x sqy = q1.y * q1.y sqz = q1.z * q1.z unit = sqx + sqy + sqz + sqw # if normalised is one, otherwise is correction factor test = q1.x * q1.y + q1.z * q1.w if (test > 0.499999*unit) and (test < 0.500001*unit): # singularity at north pole heading = 2 * math.atan2(q1.x,q1.w) attitude = math.pi/2 bank = 0 elif (test < -0.499999*unit) and (test > -0.500001*unit): # singularity at south pole heading = -2 * math.atan2(q1.x,q1.w) attitude = -math.pi/2 bank = 0 else: heading = math.atan2(2 * q1.y * q1.w-2 * q1.x * q1.z , sqx - sqy - sqz + sqw) attitude = math.asin(2 * test/unit) bank = math.atan2(2 * q1.x * q1.w-2 * q1.y * q1.z , -sqx + sqy - sqz + sqw) return r2d(bank),r2d(heading),r2d(attitude)
At the suggestion of someone on another message board, I adapted the following from another source. It returns somewhat worse results with the gimbal zones.
#This produces too many gimbal errors def matToEuler_v2(mat): #mat can be 3X3 or 4X4 #adapted from The Matrix and Quaternions FAQ #www.j3d.org/matrix_faq/matrfaq_latest.html angle_y = math.asin(mat) #Calculate Y-axis angle C = math.cos(angle_y) angle_y = r2d(angle_y) if math.fabs(C) > 0.005: #Gimball lock? x1 = mat/C #No, so get X-axis angle y1 = -mat/C angle_x = r2d(math.atan2(y1,x1)) x1 = mat/C #Get Z-axis angle y1 = -mat/C angle_z = r2d(math.atan2(y1,x1)) else: #Gimball lock has occurred angle_x = 0 #Set X-axis angle to zero x1 = mat #And calculate Z-axis angle y1 = mat angle_z = r2d(math.atan2(y1,x1)) return angle_x, angle_y, angle_z #Radians conversion borrowed from xsi export, by "Elira" def r2d(r): #radians to degrees return round(r*180.0/math.pi,4) #Approx. 57.295828 before rounding #This works def quatToMat3(quat): w,x,y,z = quat m00 = 1 - 2*y**2 - 2*z**2 m01 = 2*(x*y) - 2*(w*z) m02 = 2*(x*z) + 2*(w*y) m10 = 2*(x*y) + 2*(w*z) m11 = 1 - 2*x**2 - 2*z**2 m12 = 2*(y*z) - 2*(w*x) m20 = 2*(x*z) - 2*(w*y) m21 = 2*(y*z) + 2*(w*x) m22 = 1 - 2*x**2 - 2*y**2 mat = [[m00,m01,m02],[m10,m11,m12],[m20,m21,m22]] return mat
When I asked at this other board about tightening up the conditions for the gimbal/singularity handling, I received the following response:
The solution is probably dependant on the behaviour of asin and cos in the problem region.
To get some idea of the evaluation shortcuts and argument reduction that takes place inside these functions you can refer to the routines from fdlibm hosted at netlib.
I’d suggest confirming that your are getting values that produce what your expecting when passed to other the functions.
It may be that you’ll need to add more special case processing to deal with some NaNs.
I’ve looked into Nans, and Python’s built-in method for handling them seems to be the new Decimal module in Python 2.4. But this isn’t available with Blender’s normal installation. So I thought I should ask if there is any known alternate way to guarantee good results with very small numbers and such. Or if the Decimal module could be considered for inclusion as part of the Blender distribution, perhaps. It seems like it might be useful.
Does anyone have any thoughts or suggestions on any of this?