寻找iOS设备的正常vector

我想使用CMAttitude来了解iPad / iPhone屏幕(相对于地面)的玻璃垂直向量。 因此,我会得到如下的载体:

在这里输入图像说明

请注意,这与方向不同,因为我不在乎设备如何围绕z轴旋转。 所以,如果我把iPad的头向下放在头上,它会读取(0,-1,0),甚至当我在头上旋转(如直升机)时,它会继续读取(0, – 1,0):

在这里输入图像说明

我觉得这可能很容易,但是因为我是四元组的新手,并没有完全理解设备运动的参考框架选项,所以它一直在逃避我。

  1. 在你的情况下,我们可以说设备的旋转等于设备的正常旋转(围绕正常的旋转本身就像你指定的那样被忽略)
  2. 可以通过CMMotionManager.deviceMotion获取的CMAttitude提供相对于参考框架的旋转。 它的性质四元数,旋转matrix和欧拉angular仅仅是不同的表示。
  3. 使用CMMotionManager的startDeviceMotionUpdatesUsingReferenceFrame方法启动设备运动更新时,可以指定参考框架。 在iOS 4之前,您必须使用multiplyByInverseOfAttitude

把它们放在一起,只需要在设备面朝上放在桌子上时,将四元数与正常vector相乘即可。 现在我们需要用四元数乘法表示一个旋转的正确方法 :根据旋转vector,这是通过以下方式完成的:

其中q是由CMAttitude [w,(x,y,z)]传递的四元数, q'是它的共轭[w,(-x,-y,-z)], e是面向上的四元数表示法向[0,(0,0,1)]。 不幸的是苹果的CMQuaternion是结构,因此你需要一个小的帮助类。

Quaternion e = [[Quaternion alloc] initWithValues:0 y:0 z:1 w:0]; CMQuaternion cm = deviceMotion.attitude.quaternion; Quaternion quat = [[Quaternion alloc] initWithValues:cm.xy:cm.yz:cm.zw: cm.w]; Quaternion quatConjugate = [[Quaternion alloc] initWithValues:-cm.xy:-cm.yz:-cm.zw: cm.w]; [quat multiplyWithRight:e]; [quat multiplyWithRight:quatConjugate]; // quat.x, .y, .z contain your normal 

Quaternion.h:

 @interface Quaternion : NSObject { double w; double x; double y; double z; } @property(readwrite, assign)double w; @property(readwrite, assign)double x; @property(readwrite, assign)double y; @property(readwrite, assign)double z; 

Quaternion.m:

 - (Quaternion*) multiplyWithRight:(Quaternion*)q { double newW = w*qw - x*qx - y*qy - z*qz; double newX = w*qx + x*qw + y*qz - z*qy; double newY = w*qy + y*qw + z*qx - x*qz; double newZ = w*qz + z*qw + x*qy - y*qx; w = newW; x = newX; y = newY; z = newZ; // one multiplication won't denormalise but when multipling again and again // we should assure that the result is normalised return self; } - (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2 { if ((self = [super init])) { x = x2; y = y2; z = z2; w = w2; } return self; } 

我知道四元数在开始时有点奇怪,但一旦你有了一个想法,他们真的很棒。 它帮助我将四元数想象成一个围绕向量(x,y,z)的旋转,w是angular度的(余弦)。

如果你需要做更多与他们一起看看cocoamath开源项目。 Quaternion类和它的扩展QuaternionOperations类是一个很好的起点。

为了完整性,是的,你可以用matrix乘法来做到这一点:

n = M * e

但我更喜欢四元数的方式,它可以节省您所有的三angular函数,并且性能更好。

感谢凯为解决scheme的出发点。 这是我需要它的人的实现。 我根据凯的build议,对我的情况做了几个小小的推特。 作为一个头,我正在使用一个风景只有演示文稿。 我有代码更新variables_isLandscapeLeft进行必要的调整向量的方向。

Quaternion.h

  @interface Quaternion : NSObject{ //double w; //double x; //double y; //double z; } @property(readwrite, assign)double w; @property(readwrite, assign)double x; @property(readwrite, assign)double y; @property(readwrite, assign)double z; - (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2; - (Quaternion*) multiplyWithRight:(Quaternion*)q; @end 

Quaternion.m

 #import "Quaternion.h" @implementation Quaternion - (Quaternion*) multiplyWithRight:(Quaternion*)q { double newW = _w*qw - _x*qx - _y*qy - _z*qz; double newX = _w*qx + _x*qw + _y*qz - _z*qy; double newY = _w*qy + _y*qw + _z*qx - _x*qz; double newZ = _w*qz + _z*qw + _x*qy - _y*qx; _w = newW; _x = newX; _y = newY; _z = newZ; // one multiplication won't denormalise but when multipling again and again // we should assure that the result is normalised return self; } - (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2 { if ((self = [super init])) { _x = x2; _y = y2; _z = z2; _w = w2; } return self; } @end 

而我的游戏课使用四元数进行拍摄:

 -(void)fireWeapon{ ProjectileBaseClass *bullet = [[ProjectileBaseClass alloc] init]; bullet.position = SCNVector3Make(0, 1, 0); [self.rootNode addChildNode:bullet]; Quaternion *e = [[Quaternion alloc] initWithValues:0 x:0 y:0 z:1]; CMQuaternion cm = _currentAttitude.quaternion; Quaternion *quat = [[Quaternion alloc] initWithValues:cm.wx:cm.xy:cm.yz:cm.z]; Quaternion *quatConjugate = [[Quaternion alloc] initWithValues:cm.wx:-cm.xy:-cm.yz:-cm.z]; quat = [quat multiplyWithRight:e]; quat = [quat multiplyWithRight:quatConjugate]; SCNVector3 directionToShoot; if (_isLandscapeLeft) { directionToShoot = SCNVector3Make(quat.y, -quat.x, -quat.z); }else{ directionToShoot = SCNVector3Make(-quat.y, quat.x, -quat.z); } SCNAction *shootBullet = [SCNAction moveBy:directionToShoot duration:.1]; [bullet runAction:[SCNAction repeatActionForever:shootBullet]]; }