我正在开发一款iOS应用程序,它将使用CoreMotion来计算运动范围。
由于万向节锁定,我很快就放弃了欧拉角。所以,现在我正在尝试使用四元数。
您可能知道,CMMotionManager
通过CMDeviceMotion
课程报告设备动态数据。这里的相关属性(出于我的目的)是attitude
属性,CMAttitude
的实例。当然,您可以访问pitch
,roll
和yaw
。在记录结果时我仍然这样做是为了更好地了解从设备中传出的数据(因为这些值非常直观地设想)。它还提供quaternion
属性,该属性是CMQuaternion
的实例。
基于这里和其他地方的许多小时的研究,我确信使用四元数是在任何方向上获得正确结果的正确方法。问题是我发现CMQuaternion
类/结构非常密集且难以理解。 Apple的文档稀疏,我无法从CMQuaternion
中删除我认为是有效的轴角度表示。 (如果某人有计算CMQuaternion
的轴角表示的数学计算,我全都耳朵!)
当我在他们的GLKit库中偶然发现Apple的GLKQuaternion
结构时,我以为我解决了这个问题。 GLKit具有从GLKQuaternion
的实例提供轴和角度的方法。甚至还有一个很好的构造方法:GLKQuaternionMake(x, y, z, w)
。
由于CMQuaternion
具有x,y,z和w属性,我推断我可以使用此方法基本上在CMQuaternion
和GLKQuaternion
的实例之间“强制转换”。我仍然不确定这是否正确。
无论如何,当我遇到一些特别奇怪的结果时,我正在从iPhone上记录结果。
我编写的代码具有预期目的,用于捕获初始姿态(当用户点击按钮时),然后对CoreMotion数据进行采样并确定起始位置与现在的位置。
以下是代码:
- (void)sampleDeviceMotion {
// I've tested this, and "self.startingAttitude" is set/reset correctly
if (self.startingAttitude == nil) {
self.startingAttitude = self.motionManager.deviceMotion.attitude;
}
CMQuaternion quaternion1 = self.startingAttitude.quaternion;
GLKQuaternion q1 = GLKQuaternionMake(quaternion1.x, quaternion1.y, quaternion1.z, quaternion1.w);
GLKVector3 v1 = GLKQuaternionAxis(q1);
float angle1 = GLKQuaternionAngle(q1);
CMQuaternion quaternion2 = self.motionManager.deviceMotion.attitude.quaternion;
GLKQuaternion q2 = GLKQuaternionMake(quaternion2.x, quaternion2.y, quaternion2.z, quaternion2.w);
GLKVector3 v2 = GLKQuaternionAxis(q2);
float angle2 = GLKQuaternionAngle(q2);
float dotProduct = GLKVector3DotProduct(v1, v2);
float length1 = GLKVector3Length(v1);
float length2 = GLKVector3Length(v2);
float cosineOfTheta = dotProduct / (length1 * length2);
float theta = acosf(cosineOfTheta);
theta = radiansToDegrees(theta);
float rotationDelta = angle2 - angle1;
rotationDelta = radiansToDegrees(rotationDelta);
printf("v1: (%.02f, %.02f, %.02f) v2: (%.02f, %.02f, %.02f) angle1: %.02f, angle2: %.02f - vectorDelta: %dº. rotationDelta: %dº (pitch: %.02f, roll: %.02f, yaw: %.02f)\n", v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, angle1, angle2, (int)theta, (int)rotationDelta, self.motionManager.deviceMotion.attitude.pitch, self.motionManager.deviceMotion.attitude.roll, self.motionManager.deviceMotion.attitude.yaw);
}
我看过那段代码超过20次,我看不到其中的任何缺陷,除非我对CMQuaternion
和GLKQuaternion
之间移动能力的假设存在缺陷。
我希望当设备平放在桌子上时,点击按钮并让设备保持静止会给我q1
和q2
基本相同的结果。我会理解数据中有少量“摆动”,但如果设备不移动,则不同四元数的轴和角度应该(几乎)相同。
有时我会得到它(当点击按钮并且设备保持静止时),但有时“初始”(self.startingAttitude
)和后续值会大量关闭(40º-70º)。
此外,在初始值和后续值一致的情况下,当我尝试测量旋转时,我得到了一个奇怪的结果。
我“旋转”手机(围绕“Z”轴旋转),并得到以下结果:
BEFORE:
v1:( - 0.03,-0.03,-1.00)v2:(0.01,-0.04,-1.00)angle1:0.02,angle2:0.01 - vectorDelta:2º。 rotationDelta:0º(音高:0.00,滚动:-0.00,偏航:-0.01)
AFTER:
v1:( - 0.03,-0.03,-1.00)v2:( - 0.00,-0.01,1.00)angle1:0.02,angle2:0.14 - vectorDelta:176º。 rotationDelta:7º(音高:-0.00,翻滚:-0.00,偏航:0.14)
我认为俯仰/滚转/偏航数据两次都是正确的。也就是说,我向手机传递了少量的偏航旋转。那么,为什么Z轴完全翻转而只有很小的偏航变化?