【问题标题】:Finding normal vector to iOS device找到 iOS 设备的法线向量
【发布时间】:2012-06-05 08:15:53
【问题描述】:

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

请注意,这与方向不同,因为我不关心设备如何围绕 z 轴旋转。因此,如果我将 iPad 面朝下举过头顶,它会显示 (0,-1,0),即使我在头顶上方旋转它(就像直升机一样),它也会继续显示 (0,- 1,0):

我觉得这可能很容易,但由于我是四元数的新手并且不完全了解设备运动的参考框架选项,所以我整天都在逃避它。

【问题讨论】:

  • 你需要重力矢量吗?
  • 重力矢量不起作用。在上面的第一张图片中,左侧 (1,0,0) 和右侧 (-1,0,0) 示例的重力矢量相同。
  • 顺便问一下,这和四元数有什么关系?四元数有 四个 元素(因此名称中有“quat”(四元组))。这不只是俯仰滚转和偏航吗?
  • 这与表示方向的所有形式(无论是四元数、欧拉角还是旋转矩阵)有关,因为它们中的每一个都应该能够转换为我想要的这个值。俯仰/偏航/滚动只是表示与四元数相同的信息的不同方式(一种更糟糕的方式,因为它受到万向节锁定的影响)。由于这个原因以及许多其他原因,四元数是当今测量方向的首选方法。
  • 好吧,那么您必须将磁力计数据添加到您的重力读数中。

标签: ios gyroscope core-motion


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

将这些放在一起,当设备正面朝上放在桌子上时,您只需将四元数以正确的方式与法线向量相乘。现在我们需要这种表示旋转的四元数乘法的正确方法:根据Rotating vectors,这是由以下人员完成的:

n = q * e * q' 其中 q 是 CMAttitude [w, (x, y, z)], q' 传递的四元数 是它的共轭 [w, (-x, -y, -z)] 并且 e 是面朝上法线的四元数表示 [0, (0, 0, 1)] .不幸的是,Apple 的 CMQuaternion 是结构体,因此您需要一个小的帮助类。

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

四元数.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;

四元数.m:

- (Quaternion*) multiplyWithRight:(Quaternion*)q {
    double newW = w*q.w - x*q.x - y*q.y - z*q.z;
    double newX = w*q.x + x*q.w + y*q.z - z*q.y;
    double newY = w*q.y + y*q.w + z*q.x - x*q.z;
    double newZ = w*q.z + z*q.w + x*q.y - y*q.x;
    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 是角度的(余弦)。

如果您需要对它们进行更多操作,请查看cocoamath 开源项目。 Quaternion 类及其扩展 QuaternionOperations 是一个很好的起点。

为了完整起见,是的,您也可以使用矩阵乘法:

n = M * e

但我更喜欢四元数方式,它可以为您省去所有三角函数的麻烦并且性能更好。

【讨论】:

  • 我可以看到我有一个类似的问题。你能帮我解决这个iOS四元数数学吗? stackoverflow.com/questions/25363781/…
  • 优秀的一般答案,但请注意,在这种特定情况下,四元数的 x、y、z 部分已经给出了 OP 要求的方向。
【解决方案2】:

感谢 Kay 提出解决方案的起点。这是我对任何需要它的人的实现。我花了几个星期来听凯对我的情况提出的建议。作为提醒,我正在使用仅横向演示。我有更新变量 _isLandscapeLeft 的代码,以对矢量的方向进行必要的调整。

四元数.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

四元数.m

#import "Quaternion.h"

@implementation Quaternion


- (Quaternion*) multiplyWithRight:(Quaternion*)q {
    double newW = _w*q.w - _x*q.x - _y*q.y - _z*q.z;
    double newX = _w*q.x + _x*q.w + _y*q.z - _z*q.y;
    double newY = _w*q.y + _y*q.w + _z*q.x - _x*q.z;
    double newZ = _w*q.z + _z*q.w + _x*q.y - _y*q.x;
    _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.w x:cm.x y:cm.y z:cm.z];
    Quaternion *quatConjugate = [[Quaternion alloc] initWithValues:cm.w x:-cm.x y:-cm.y z:-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]];
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-10
    • 1970-01-01
    • 2023-03-18
    • 2019-04-19
    • 1970-01-01
    • 1970-01-01
    • 2013-11-28
    相关资源
    最近更新 更多