如何使用CADisplayLink在CMRotationMatrix上应用过滤器

时间:2012-07-12 22:27:45

标签: objective-c ios gps accelerometer gyroscope

如何在 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;
}

// Matrix-vector和matrix-matricx乘法例程

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];
}

1 个答案:

答案 0 :(得分:1)

一般来说,我会区分提高信噪比和消除信号。

信号改善

如果您真的想要比Apple已经实施传感器融合算法的Core Motion更好,请为具有不确定结果的长期项目做好准备。在这种情况下,您最好采用原始加速度计和陀螺仪信号来构建您自己的传感器融合算法,但您必须关注许多问题,如漂移,不同iPhone版本的硬件依赖性,传感器内的硬件差异一代,...所以我的建议:尽量避免它。

平滑

这仅仅意味着插入两个或更多信号并构建平均值。我不知道任何合适的方法直接用于旋转矩阵(可能有一个),但你可以使用quaternions代替(更多资源:OpenGL Tutorial Using Quaternions to represent rotationQuaternion FAQ)。

这种插​​值的结果四元数可以与矢量相乘,以类似于矩阵方式获得投影(您可以查看Finding normal vector to iOS device以获取更多信息)。

表示旋转的两个单位四元数之间的插值可以使用Slerp来完成。在实践中,您将使用维基百科中描述为Geometric Slerp的内容。如果您有两个时间点 t1 t2 以及相应的四元数 q1 q2 且角距<他们之间的em> omega ,公式为:

q'(q1,q2,t)= sin((1-t)* omega)/ sin(ω)* q0 + sin(t * omega)/ sin(ω)* q1

t应为0.5,因为您需要两次旋转之间的平均值。 Omega可以通过点积计算:

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

如果使用两个四元数的方法仍然不符合您的需要,可以使用slerp(slerp(q1,q2),slerp(q3,q4))重复此操作。一些说明:

  • 从性能的角度来看,在你的运行循环1 /频率时间每秒执行三次sin和一次arccos调用并不便宜。因此,你应该避免使用太多的分数
  • 在您的情况下,所有信号彼此接近,尤其是在使用高传感器频率时。你必须注意非常小的角度让1 / sin(ω)爆炸。在这种情况下,设置sin(x)≈x
  • low-pass filter等其他过滤器一样,您使用的时间点越多,延迟时间就越长。因此,如果您的频率为f,那么当使用两个点时,你将获得大约0.5 / f秒的延迟,而对于双重slerp,你将获得1.5 / f的延迟。
  • 如果某些内容看起来很奇怪,请检查结果四元数是否为单位四元数,即|| q || = 1
  • 如果您遇到性能问题,可以查看Hacking Quaternions

github上的C ++项目pbrt包含一个四元数类,可以从中获得灵感。