我正在开发一款 iOS 应用,它将使用 CoreMotion 来计算运动范围。
由于万向节锁定,我很快就放弃了欧拉角。所以,现在我正在尝试使用四元数。
您可能知道,CMMotionManager
通过CMDeviceMotion
类报告设备运动数据。这里的相关属性(为了我的目的)是attitude
属性,CMAttitude
的一个实例。当然,您可以从中访问pitch
,roll
和yaw
。在记录结果时我仍然这样做是为了更好地了解从设备中传出的数据(因为这些值非常直观地设想)。它还提供quaternion
属性,它是CMQuaternion
的一个实例。
基于这里和其他地方的许多小时的研究,我确信使用四元数是在任何方向上获得正确结果的正确方法。问题是我发现CMQuaternion
class/structure 非常密集且难以理解。 Apple 的文档很少,我无法退出我认为是CMQuaternion
的有效 axis-angle 表示。 (如果某人有从CMQuaternion
计算 axis-angle 表示的数学计算,我全都耳朵!)
当我在他们的 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”轴旋转),并得到以下结果:
之前:
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,roll:-0.00,yaw:-0.01)
后:
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,roll:-0.00,yaw:0.14)
我相信 pitch/roll/yaw 数据两次都是正确的。也就是说,我向手机传递了少量的偏航旋转。那么,为什么 Z 轴完全翻转而只有很小的偏航变化?