objective-c ios gps accelerometer gyroscope

objective c - Cómo aplicar un filtro en CMRotationMatrix usando CADisplayLink



objective-c ios (1)

¿Cómo aplicar un filtro en CMRotationMatrix ? tal vez el filtro kalman. Necesito arreglar el ruido de CMRotationMatrix (transformFromCMRotationMatrix), para obtener valores lineales de matriz de resultados

Los valores de esta matriz se convertirán en XYZ, en mi caso simulo 3D en una pantalla 2D así:

// Casting matriz para 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));

código :

// definir la variable

mat4f_t cameraTransform;

// inicia el ciclo de enlace de pantalla

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

// detener el bucle de enlace de pantalla

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

// evento del enlace de visualización

- (void)onDisplayLink:(id)sender { CMDeviceMotion *d = motionManager.deviceMotion; if (d != nil) { CMRotationMatrix r = d.attitude.rotationMatrix; transformFromCMRotationMatrix(cameraTransform, &r); [self setNeedsDisplay]; } }

// disparador de función antes de [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; }

// Rutinas de multiplicación Matrix-vector y 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]; }


En general, distinguiría entre mejorar la relación de ruido de señal y suavizar la señal.

Mejora de la señal

Si realmente quieres ser mejor que Core Motion de Apple que ya ha implementado un algoritmo de fusión de sensores, mantente preparado para un proyecto a largo plazo con un resultado incierto. En este caso, sería mejor tomar las señales del acelerómetro y del giroscopio sin procesar para construir su propio algoritmo de fusión del sensor, pero hay que preocuparse por muchos problemas como deriva, dependencia del hardware de diferentes versiones de iPhone, diferencias de hardware de los sensores dentro del mismo generación, ... Así que mi consejo: prueba todo para evitarlo.

Suavizado

Esto solo significa interpolar dos o más señales y construir un tipo de promedio. No conozco ningún método adecuado para usar directamente matrices de rotación (tal vez haya uno) pero puedes usar cuaterniones en su lugar (más recursos: Tutorial OpenGL usando Quaternions para representar la rotación o preguntas frecuentes de Quaternion ).

El cuaternión resultante de dicha interpolación se puede multiplicar con su vector para obtener la proyección de manera similar a la matriz (para obtener más información, puede buscar el vector normal en el dispositivo iOS ).

La interpolación entre dos unidades de cuaterniones que representan rotaciones se puede lograr con Slerp . En la práctica, usarás lo que se describe como Geometric Slerp en Wikipedia. Si tiene dos puntos en el tiempo t1 y t2 y los cuaterniones correspondientes q1 y q2 y la distancia angular omega entre ellos, la fórmula es:

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

t debería ser 0.5 porque quiere el promedio entre ambas rotaciones. Omega puede calcularse mediante el producto escalar:

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

Si este enfoque que utiliza dos cuaterniones aún no coincide con sus necesidades, puede repetir esto usando slerp (slerp (q1, q2), slerp (q3, q4)). Algunas notas:

  • Desde un punto de vista de rendimiento para ver, no es tan barato realizar tres llamadas de pecado y una de arco en el ciclo de ejecución 1 / frecuencia por segundo. Por lo tanto, debes evitar usar demasiados puntos
  • En su caso, todas las señales están cerca una de la otra, especialmente cuando se utilizan frecuencias de sensor alto. Debes cuidarte de los ángulos que son muy pequeños y dejar que 1 / sen (omega) explote. En este caso, establece sin (x) ≈ x
  • Al igual que en otros filtros, como el filtro de paso bajo, cuantos más puntos en el tiempo use, más demora tendrá. Por lo tanto, si tiene frecuencia f, obtendrá una demora de aproximadamente 0.5 / seg cuando utilice dos puntos y 1.5 / f para la doble slerp.
  • Si algo parece extraño, comprueba que tus cuaterniones resultantes sean cuaterniones unitarios, es decir || q || = 1
  • Si tiene problemas de rendimiento, puede echar un vistazo a Hacking Quaternions

El pbrt del proyecto de C ++ en github contiene una clase de cuaternión para inspirarse.