26

CMAttitudeを使用して、iPad / iPhoneの画面のガラスに垂直なベクトル(地面に対して)を知りたいと思います。そのため、次のようなベクトルが得られます。

ここに画像の説明を入力してください

これは、デバイスがz軸を中心にどのように回転するかを気にしないという点で、向きとは異なることに注意してください。したがって、iPadを頭の上に下向きに持っていると、(0、-1,0)と表示され、(ヘリコプターのように)頭の上で回転させても(0、-と表示され続けます)。 1,0):

ここに画像の説明を入力してください

これはかなり簡単なことのように感じますが、私はクォータニオンに不慣れで、デバイスモーションの参照フレームオプションを完全に理解していないため、一日中回避されていました。

4

2 に答える 2

22
  1. あなたの場合、デバイスの回転はデバイスの法線の回転に等しいと言えます(法線自体の周りの回転は、指定したように無視されます)
  2. CMMotionManager.deviceMotionを介して取得できる CMAttitudeは、参照フレームを基準にした回転を提供します 。そのプロパティのクォータニオン、回転行列、およびオイラー角は、まったく異なる表現です。
  3. 参照フレームは、CMMotionManagerのstartDeviceMotionUpdatesUsingReferenceFrameメソッドを使用してデバイスモーションの更新を開始するときに指定できます。iOS 4までは、multiplyByInverseOfAttitudeを使用する必要がありました

これをまとめると、デバイスがテーブルに上向きに置かれているときに、クォータニオンに法線ベクトルを正しい方法で乗算する必要があります。ここで、回転を表すこの正しい四元数乗算の方法が必要です。回転ベクトルによると、これは次のように実行されます。

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

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*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

しかし、三角法の煩わしさをすべて軽減し、パフォーマンスを向上させるクォータニオン方式をお勧めします。

于 2012-05-31T15:45:36.527 に答える
3

ソリューションの開始点を提供してくれたKayに感謝します。これがそれを必要とする人のための私の実装です。私は自分の状況についてケイのアドバイスをするために数週間を過ごしました。ヘッドアップとして、私は風景のみのプレゼンテーションを使用しています。ベクトルの方向に必要な調整を行うために変数_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*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]];
}
于 2015-02-13T11:25:30.777 に答える