38

iPhone/iPad を縦向きに、画面を手前にして正面に垂直に持っているとします。画面を自分に向けたまま、デバイスを片側に傾けます。CMMotionManager を使用してその静的傾斜角をどのように測定しますか? 簡単な答えがあるはずの簡単な質問のようですが、四元数と回転行列に消えない方法は見つかりません。

誰かが私に実際の例を教えてもらえますか?

4

4 に答える 4

57

見てくださいgravity

self.deviceQueue = [[NSOperationQueue alloc] init];
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 5.0 / 60.0;

// UIDevice *device = [UIDevice currentDevice];

[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical
                                                        toQueue:self.deviceQueue
                                                    withHandler:^(CMDeviceMotion *motion, NSError *error)
{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        CGFloat x = motion.gravity.x;
        CGFloat y = motion.gravity.y;
        CGFloat z = motion.gravity.z;
    }];
}];

この参照フレーム ( CMAttitudeReferenceFrameXArbitraryZVertical) では、zがゼロに近い場合、地面に垂直な平面上に保持し (たとえば、壁に対して保持しているように)、その平面上で回転するとxy値が変化します。垂直はxゼロにy近く、-1 に近い場所です。


この投稿を見て、このベクトルを角度に変換したい場合は、次のアルゴリズムを使用できることに気付きました。

デバイスが垂直から何度回転したかを計算する場合 (正は時計回り、負は反時計回り)、次のように計算できます。

// how much is it rotated around the z axis

CGFloat angle = atan2(y, x) + M_PI_2;           // in radians
CGFloat angleDegrees = angle * 180.0f / M_PI;   // in degrees

transformこれを使用して、Quartz 2Dプロパティを介してビューをどれだけ回転させるかを把握できます。

self.view.layer.transform = CATransform3DRotate(CATransform3DIdentity, -rotateRadians, 0, 0, 1);

(個人的には、startDeviceMotionUpdatesメソッドで回転角度を更新transformCADisplayLink、これを で更新します。これにより、画面の更新が角度の更新から分離されます。)

次の方法で、前後にどれだけ傾けたかを確認できます。

// how far it it tilted forward and backward

CGFloat r = sqrtf(x*x + y*y + z*z);
CGFloat tiltForwardBackward = acosf(z/r) * 180.0f / M_PI - 90.0f;
于 2013-03-26T20:51:05.830 に答える
10

遅い回答のようなものですが、githubとそれに付随するブログ記事で実際の例を見つけることができます。

上記の記事を要約すると、クォータニオンを使用すると、iPhone を縦に持ったときにおそらく直面するジンバル ロックの問題を回避できます。

チルト (またはヨー) を計算するコーディング部分は次のとおりです。

CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion;
double yaw = asin(2*(quat.x*quat.z - quat.w*quat.y));

// use the yaw value
// ...

単純なカルマン フィルターを追加してヨーを緩和することもできます。

CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion;
double yaw = asin(2*(quat.x*quat.z - quat.w*quat.y));

if (self.motionLastYaw == 0) {
    self.motionLastYaw = yaw;
}

// kalman filtering
static float q = 0.1;   // process noise
static float r = 0.1;   // sensor noise
static float p = 0.1;   // estimated error
static float k = 0.5;   // kalman filter gain

float x = self.motionLastYaw;
p = p + q;
k = p / (p + r);
x = x + k*(yaw - x);
p = (1 - k)*p;
self.motionLastYaw = x;

// use the x value as the "updated and smooth" yaw
// ...
于 2013-11-17T10:14:21.430 に答える
7

self.horizonこれは、UIViewを回転させて、デバイスを傾けたときに水平線と同じ高さを維持する例です。

- (void)startDeviceMotionUpdates 
{
    CMMotionManager* coreMotionManager = [[CMMotionManager alloc] init];
    NSOperationQueue* motionQueue = [[NSOperationQueue alloc] init]
    CGFloat updateInterval = 1/60.0;
    CMAttitudeReferenceFrame frame = CMAttitudeReferenceFrameXArbitraryCorrectedZVertical;
    [coreMotionManager setDeviceMotionUpdateInterval:updateInterval];
    [coreMotionManager startDeviceMotionUpdatesUsingReferenceFrame:frame
                            toQueue:motionQueue
                            withHandler:
     ^(CMDeviceMotion* motion, NSError* error){
         CGFloat angle =  atan2( motion.gravity.x, motion.gravity.y );
         CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
         self.horizon.transform = transform;
     }];
}

これは少し単純化されすぎています。アプリには CMMotionManager のインスタンスが 1 つしかないことを確認して、これを事前に初期化し、プロパティ経由でアクセスする必要があります。

于 2013-03-27T03:15:20.280 に答える