20

デバイス モーション マネージャーのリファレンス フレーム (ジャイロ用) を変更して、重力ベクトルを Y 軸に配置できるようにしたいと考えています。

通常、Device Motion Manager Updates を開始すると、電話機の z 軸のみが重力に合わせられます。

これを変更して、磁力計を使用して x 軸を磁極または真の北極に揃えることができます。これで、X 軸が北を指し、Z 軸が下を指します。

私がやりたいことは、Y 軸 (負) を下に向け (重力に合わせて)、X 軸を真の磁極に向けることです。

私が望む結果は、携帯電話を垂直 (ポートレート) の向きで静止させたときに、携帯電話の右側が北極に揃えられ、すべての測定値 (ロール、ピッチ、ヨー) が 0 として読み取られるようにすることです。これは、携帯電話を X 軸で回転させるとピッチが変化し、Y 軸を中心に回転させるとヨーが変化します。

これまでのところ、以前に保存された姿勢の逆数を乗算すると、独自の基準フレームを設定できることがわかっています (手動でこの向きに携帯電話を設定し、その姿勢を保存して、新しい姿勢に逆数を掛け続けることができるように)この保存されたものと私のすべての測定値は、私が望むものとまったく同じになります)。

しかし、手動で設定することはオプションではありません。プログラムでこれを行うにはどうすればよいですか?

独自の姿勢参照フレームを作成する機能はないと思います。または、少なくとも姿勢に回転行列を掛ける機能があれば、おそらくこれを解決できます。(すべての姿勢にピッチの 90 度の変化を掛けるだけだからです)。

私が自分自身を明確に説明したことを願っています、

提案をいただければ幸いです。ありがとう

PD: iPhone の向きの座標は次のとおりです。

ここに画像の説明を入力

4

2 に答える 2

4

擬似コード:

  1. デバイスモーションの更新を開始します
  2. バックグラウンドでカメラプレビューを開始します;)
  3. デバイスからの現在の重力の読み取り値をCMAccelerationとしてキャプチャします...重力をローカル変数に保存したら。
  4. 次に、2つのベクトルを取得し、それらの間の角度を取得する必要があります。この場合、デバイスの重力(0,0、-1)と実際の重力ベクトル...
  5. 次に、シータをシータプライムに変換します...CoreMotionの参照方向に一致する変換
  6. アニメーション化するタイマーを設定します。
  7. アニメーション中に、motionManagerのdeviceMotionプロパティのrotationMatrixの逆を取得します。
  8. デバイスの現在の姿勢を反映するように、正しい順序で変換を適用します(ヨー、ピッチ、オイラーモードでのロール、またはデバイスのクォータニオン回転...基本的に同じことを言う3つの異なる方法)

コードは次のとおりです。

- (void) initMotionCapture
{
    firstGravityReading = NO;
    referenceAttitude = nil;

    if (motionManager == nil)
    {
        self.motionManager = [CMMotionManager new];
    }
    motionManager.deviceMotionUpdateInterval = 0.01;
    self.gravityTimer = [NSTimer scheduledTimerWithTimeInterval:1/60.0 
                                 target:self 
                                 selector:@selector(getFirstGravityReading) 
                                 userInfo:nil repeats:YES];
}


- (void) getFirstGravityReading
{
    CMAcceleration currentGravity; 

    CMDeviceMotion *dm = motionManager.deviceMotion;
    referenceAttitude = dm.attitude;
    currentGravity = dm.gravity;

    [motionManager startDeviceMotionUpdates];

    if (currentGravity.x !=0 && 
        currentGravity.y !=0 && currentGravity.z !=0)
    {
        NSLog(@"Gravity = (%f,%f,%f)", 
              currentGravity.x, currentGravity.y, currentGravity.z);

        firstGravityReading = YES;
        [gravityTimer invalidate];
        self.gravityTimer = nil;
        [self setupCompass];
    }
}

- (void) setupCompass
{
    //Draw your cube... I am using a quartz 3D perspective hack!
    CATransform3D initialTransform = perspectiveTransformedLayer.sublayerTransform;
    initialTransform.m34 = 1.0/-10000;


    //HERE IS WHAT YOU GUYS NEED... the vector equations!
    NSLog(@"Gravity = (%f,%f,%f)", 
          currentGravity.x, currentGravity.y, currentGravity.z);

    //we have current gravity vector and our device gravity vector of (0, 0, -1)
    // get the dot product
    float dotProduct = currentGravity.x*0 + 
                       currentGravity.y*0 + 
                       currentGravity.z*-1;
    float innerMagnitudeProduct = currentGravity.x*currentGravity.x + 
                                  currentGravity.y + currentGravity.y + 
                                  currentGravity.z*currentGravity.z;
    float magnitudeCurrentGravity = sqrt(innerMagnitudeProduct);
    float magnitudeDeviceVector = 1; //since (0,0,-1) computes to: 0*0 + 0*0 + -1*-1 = 1

    thetaOffset = acos(dotProduct/(magnitudeCurrentGravity*magnitudeDeviceVector));
    NSLog(@"theta(degrees) = %f", thetaOffset*180.0/M_PI);

    //Now we have the device angle to the gravity vector (0,0,-1)
    //We must transform these coordinates to match our 
    //device's attitude by transforming to theta prime
    float theta_deg = thetaOffset*180.0/M_PI;
    float thetaPrime_deg = -theta_deg + 90; // ThetaPrime = -Theta + 90 <==> y=mx+b

    NSLog(@"thetaPrime(degrees) = %f", thetaOffset*180.0/M_PI);

    deviceOffsetRotation = 
        CATransform3DMakeRotation((thetaPrime_deg) * M_PI / 180.0, 1, 0, 0);
    initialTransform = CATransform3DConcat(deviceOffsetRotation, initialTransform);

    perspectiveTransformedLayer.sublayerTransform = initialTransform;

    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:1/60.0 
                                   target:self 
                                   selector:@selector(tick) 
                                   userInfo:nil 
                                   repeats:YES];

}

- (void) tick
{
    CMRotationMatrix rotation;

    CMDeviceMotion *deviceMotion = motionManager.deviceMotion;
    CMAttitude *attitude = deviceMotion.attitude;

    if (referenceAttitude != nil)
    {
        [attitude multiplyByInverseOfAttitude:referenceAttitude];
    }
    rotation = attitude.rotationMatrix;

    CATransform3D rotationalTransform = perspectiveTransformedLayer.sublayerTransform;

    //inverse (or called the transpose) of the attitude.rotationalMatrix
    rotationalTransform.m11 = rotation.m11;
    rotationalTransform.m12 = rotation.m21;
    rotationalTransform.m13 = rotation.m31;

    rotationalTransform.m21 = rotation.m12;
    rotationalTransform.m22 = rotation.m22;
    rotationalTransform.m23 = rotation.m32;

    rotationalTransform.m31 = rotation.m13;
    rotationalTransform.m32 = rotation.m23;
    rotationalTransform.m33 = rotation.m33;

    rotationalTransform = 
        CATransform3DConcat(deviceOffsetRotation, rotationalTransform);
    rotationalTransform = 
        CATransform3DConcat(rotationalTransform, 
                            CATransform3DMakeScale(1.0, -1.0, 1.0));


    perspectiveTransformedLayer.sublayerTransform = rotationalTransform;
}
于 2012-05-27T13:44:41.677 に答える
-1

これがお役に立てば幸いです

CMAttitude インスタンスで使用される参照フレームを変更できます。そのためには、その参照フレームを含む姿勢オブジェクトをキャッシュし、それを引数としてmultiplyByInverseOfAttitude:に渡します。メッセージを受け取る姿勢引数は、その参照フレームからの姿勢の変化を表すように変更されます。

これがどのように役立つかを確認するために、ユーザーがデバイスを回転させてスイングする野球の試合を考えてみましょう。通常、投球の開始時には、バットは静止位置にあります。その後、バットは、デバイスの姿勢がピッチの開始時の位置からどのように変化したかによって決定される方向でレンダリングされます。

-(void) startPitch {

// referenceAttitude is an instance variable

referenceAttitude = [motionManager.deviceMotion.attitude retain];

}

- (void)drawView {

CMAttitude *currentAttitude = motionManager.deviceMotion.attitude;

[currentAttitude multiplyByInverseOfAttitude: referenceAttitude];

// render bat using currentAttitude .....

[self updateModelsWithAttitude:currentAttitude];

[renderer render];

}

詳細については、以下のリンクを参照してください。

http://db-in.com/blog/2011/04/cameras-on-opengl-es-2-x/

于 2012-02-04T03:29:51.580 に答える