4

CMRotationMatrixにフィルターを適用する方法は? カルマンフィルターかもしれません。結果マトリックスの線形値を取得するには、CMRotationMatrix (transformFromCMRotationMatrix) のノイズを修正する必要があります。

このマトリックス値は XYZ に変換されます。私の場合、2D 画面で 3D を次のようにシミュレートしています。

// 行列を x, y にキャスト

vec4f_t v;
multiplyMatrixAndVector(v, projectionCameraTransform, boxMatrix);

float x = (v[0] / v[3] + 1.0f) * 0.5f;
float y = (v[1] / v[3] + 1.0f) * 0.5f;

CGPointMake(x * self.bounds.size.width, self.bounds.size.height - (y * self.bounds.size.height));

コード:

// 変数を定義

mat4f_t cameraTransform;

// 表示リンク ループを開始します

- (void)startDisplayLink
{
    displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
    [displayLink setFrameInterval:1];
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}

// 表示リンクのループを止める

- (void)stopDisplayLink
{
    [displayLink invalidate];
    displayLink = nil;      
}

// リンク表示のイベント

- (void)onDisplayLink:(id)sender
{
    CMDeviceMotion *d = motionManager.deviceMotion;

    if (d != nil) {
        CMRotationMatrix r = d.attitude.rotationMatrix;

        transformFromCMRotationMatrix(cameraTransform, &r);
        [self setNeedsDisplay];
    }
}

// 関数トリガー前 [self setNeedDisplay];

void transformFromCMRotationMatrix(vec4f_t mout, const CMRotationMatrix *m)
{    
    mout[0] = (float)m->m11;
    mout[1] = (float)m->m21;
    mout[2] = (float)m->m31;
    mout[3] = 0.0f;

    mout[4] = (float)m->m12;
    mout[5] = (float)m->m22;
    mout[6] = (float)m->m32;
    mout[7] = 0.0f;

    mout[8] = (float)m->m13;
    mout[9] = (float)m->m23;
    mout[10] = (float)m->m33;
    mout[11] = 0.0f;

    mout[12] = 0.0f;
    mout[13] = 0.0f;
    mout[14] = 0.0f;
    mout[15] = 1.0f;
}

// 行列-ベクトルおよび行列-行列 x 乗算ルーチン

void multiplyMatrixAndVector(vec4f_t vout, const mat4f_t m, const vec4f_t v)
{
    vout[0] = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3];
    vout[1] = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3];
    vout[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3];
    vout[3] = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3];
}
4

1 に答える 1

1

一般に、信号ノイズ比の改善と信号の平滑化を区別します。

信号の改善

すでにセンサー フュージョン アルゴリズムが実装されている Apple の Core Motion よりも優れたものになりたい場合は、結果が不確実な長期プロジェクトに備える必要があります。この場合、生の加速度計とジャイロ信号を取得して独自のセンサー フュージョン アルゴリズムを構築する方がよいでしょうが、ドリフト、異なる iPhone バージョンのハードウェア依存性、同じセンサー内のセンサーのハードウェアの違いなど、多くの問題に注意する必要があります。世代、...だから私のアドバイス:それを避けるためにあらゆることを試してください。

スムージング

これは、2 つ以上の信号を補間し、一種の平均を構築することを意味します。回転行列に直接使用する適切なアプローチについては知りませんが (おそらく存在する可能性があります)、代わりにクォータニオンを使用できます(詳細なリソース: OpenGL チュートリアル Using Quaternions to present rotationまたはQuaternion FAQ )。

このような補間の結果の四元数は、行列の方法と同様に投影を取得するためにベクトルで乗算できます (詳細については、iOS デバイスへの法線ベクトルの検索を参照してください)。

回転を表す 2 つの単位四元数間の補間は、Slerpを使用して実行できます。実際には、ウィキペディアで Geometry Slerp として説明されているものを使用します。2 つの時点t1およびt2と、対応する四元数q1およびq2と、それらの間の角距離ωがある場合、式は次のようになります。

q'(q1, q2, t) = sin((1- t) * オメガ) / sin(オメガ) * q0 + sin(t * オメガ) / sin(オメガ) * q1

両方の回転の平均が必要なため、t は 0.5 にする必要があります。オメガは内積で計算できます。

cos(オメガ) = q1.q2 = w1*w2 + x1*x2 + y1*y2 + z1*z2

2 つの四元数を使用するこのアプローチがまだニーズに合わない場合は、slerp (slerp (q1, q2)、slerp (q3, q4)) を使用してこれを繰り返すことができます。いくつかのメモ:

  • パフォーマンスの観点から見ると、実行ループで 3 つの sin 呼び出しと 1 つの arccos 呼び出しを 1 秒あたり 1/frequency 回実行するのはそれほど安くはありません。したがって、あまりにも多くのポイントを使用しないようにする必要があります
  • あなたの場合、特に高いセン​​サー周波数を使用している場合、すべての信号は互いに接近しています。非常に小さい角度に注意し、1/sin(omega) を爆発させる必要があります。この場合、sin(x) ≈ x を設定します。
  • ローパスフィルターなどの他のフィルターと同様に、使用する時点が多いほど、時間遅延が大きくなります。したがって、周波数が f の場合、2 つのポイントを使用すると約 0.5/f 秒の遅延が発生し、ダブル スラープでは 1.5/f 秒の遅延が発生します。
  • 何かがおかしいと思われる場合は、結果の四元数が単位四元数、つまり ||q|| であることを確認してください。= 1
  • パフォーマンスの問題が発生している場合は、Hacking Quaternionsを参照してください。

githubの C++ プロジェクトpbrtには、インスピレーションを得るためのクォータニオン クラスが含まれています。

于 2012-07-17T18:13:42.663 に答える