Convertir los valores del campo magnético X, Y, Z desde el dispositivo en el marco de referencia global

Cuando se utiliza el sensor TYPE_MAGNETOMETER, se obtienen los valores X, Y, Z de la intensidad del campo magnético en relación con la orientación del dispositivo. Lo que quiero obtener es convertir estos valores en marco de referencia global, aclarando: el usuario toma el dispositivo, mide estos valores, que gire el dispositivo para algunos grados alrededor de cualquier eje y obtiene ~ los mismos valores. Por favor, busque preguntas similares a continuación: Obtención de valores de campos magnéticos en coordenadas globales ¿Cómo puedo obtener el vector de campo magnético, independientemente de la rotación del dispositivo? En esta respuesta se describe la solución de la muestra (es para la aceleración lineal, pero creo que no importa): https://stackoverflow.com/a/11614404/2152255 Lo usé y tengo 3 valores, X siempre es muy Pequeño (no creo que sea correcto), Y y Z están bien, pero todavía cambian un poco cuando giro el dispositivo. ¿Cómo podría ajustarse? ¿Y podría ser resuelto todo? Utilizo el filtro simple de Kalman para aproximar los valores de la medida, porque w / o él consigo valores diferentes reservados incluso si el dispositivo no se está moviendo / girando en absoluto. Por favor, busque mi código abajo:

import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.opengl.Matrix; import android.os.Bundle; import android.view.View; import android.widget.CheckBox; import android.widget.TextView; import com.test.statistics.filter.kalman.KalmanState; import com.example.R; /** * Activity for gathering magnetic field statistics. */ public class MagneticFieldStatisticsGatheringActivity extends Activity implements SensorEventListener { public static final int KALMAN_STATE_MAX_SIZE = 80; public static final double MEASUREMENT_NOISE = 5; /** Sensor manager. */ private SensorManager mSensorManager; /** Magnetometer spec. */ private TextView vendor; private TextView resolution; private TextView maximumRange; /** Magnetic field coordinates measurements. */ private TextView magneticXTextView; private TextView magneticYTextView; private TextView magneticZTextView; /** Sensors. */ private Sensor mAccelerometer; private Sensor mGeomagnetic; private float[] accelerometerValues; private float[] geomagneticValues; /** Flags. */ private boolean specDefined = false; private boolean kalmanFiletring = false; /** Rates. */ private float nanoTtoGRate = 0.00001f; private final int gToCountRate = 1000000; /** Kalman vars. */ private KalmanState previousKalmanStateX; private KalmanState previousKalmanStateY; private KalmanState previousKalmanStateZ; private int previousKalmanStateCounter = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main2); mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mGeomagnetic = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); vendor = (TextView) findViewById(R.id.vendor); resolution = (TextView) findViewById(R.id.resolution); maximumRange = (TextView) findViewById(R.id.maximumRange); magneticXTextView = (TextView) findViewById(R.id.magneticX); magneticYTextView = (TextView) findViewById(R.id.magneticY); magneticZTextView = (TextView) findViewById(R.id.magneticZ); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST); mSensorManager.registerListener(this, mGeomagnetic, SensorManager.SENSOR_DELAY_FASTEST); } /** * Refresh statistics. * * @param view - refresh button view. */ public void onClickRefreshMagneticButton(View view) { resetKalmanFilter(); } /** * Switch Kalman filtering on/off * * @param view - Klaman filetring switcher (checkbox) */ public void onClickKalmanFilteringCheckBox(View view) { CheckBox kalmanFiltering = (CheckBox) view; this.kalmanFiletring = kalmanFiltering.isChecked(); } @Override public void onSensorChanged(SensorEvent sensorEvent) { if (sensorEvent.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) { return; } synchronized (this) { switch(sensorEvent.sensor.getType()){ case Sensor.TYPE_ACCELEROMETER: accelerometerValues = sensorEvent.values.clone(); break; case Sensor.TYPE_MAGNETIC_FIELD: if (!specDefined) { vendor.setText("Vendor: " + sensorEvent.sensor.getVendor() + " " + sensorEvent.sensor.getName()); float resolutionValue = sensorEvent.sensor.getResolution() * nanoTtoGRate; resolution.setText("Resolution: " + resolutionValue); float maximumRangeValue = sensorEvent.sensor.getMaximumRange() * nanoTtoGRate; maximumRange.setText("Maximum range: " + maximumRangeValue); } geomagneticValues = sensorEvent.values.clone(); break; } if (accelerometerValues != null && geomagneticValues != null) { float[] Rs = new float[16]; float[] I = new float[16]; if (SensorManager.getRotationMatrix(Rs, I, accelerometerValues, geomagneticValues)) { float[] RsInv = new float[16]; Matrix.invertM(RsInv, 0, Rs, 0); float resultVec[] = new float[4]; float[] geomagneticValuesAdjusted = new float[4]; geomagneticValuesAdjusted[0] = geomagneticValues[0]; geomagneticValuesAdjusted[1] = geomagneticValues[1]; geomagneticValuesAdjusted[2] = geomagneticValues[2]; geomagneticValuesAdjusted[3] = 0; Matrix.multiplyMV(resultVec, 0, RsInv, 0, geomagneticValuesAdjusted, 0); for (int i = 0; i < resultVec.length; i++) { resultVec[i] = resultVec[i] * nanoTtoGRate * gToCountRate; } if (kalmanFiletring) { KalmanState currentKalmanStateX = new KalmanState(MEASUREMENT_NOISE, accelerometerValues[0], (double)resultVec[0], previousKalmanStateX); previousKalmanStateX = currentKalmanStateX; KalmanState currentKalmanStateY = new KalmanState(MEASUREMENT_NOISE, accelerometerValues[1], (double)resultVec[1], previousKalmanStateY); previousKalmanStateY = currentKalmanStateY; KalmanState currentKalmanStateZ = new KalmanState(MEASUREMENT_NOISE, accelerometerValues[2], (double)resultVec[2], previousKalmanStateZ); previousKalmanStateZ = currentKalmanStateZ; if (previousKalmanStateCounter == KALMAN_STATE_MAX_SIZE) { magneticXTextView.setText("x: " + previousKalmanStateX.getX_estimate()); magneticYTextView.setText("y: " + previousKalmanStateY.getX_estimate()); magneticZTextView.setText("z: " + previousKalmanStateZ.getX_estimate()); resetKalmanFilter(); } else { previousKalmanStateCounter++; } } else { magneticXTextView.setText("x: " + resultVec[0]); magneticYTextView.setText("y: " + resultVec[1]); magneticZTextView.setText("z: " + resultVec[2]); } } } } } private void resetKalmanFilter() { previousKalmanStateX = null; previousKalmanStateY = null; previousKalmanStateZ = null; previousKalmanStateCounter = 0; } @Override public void onAccuracyChanged(Sensor sensor, int i) { } } 

Gracias a todos los que leen este post y que publiquen algunas ideas sobre el problema por adelantado.

En mi comentario sobre la respuesta verificada en el enlace que proporcionaste anteriormente, me referí a mi respuesta simple en calcular la aceleración en referencia al norte verdadero

Permítanme responder aquí de nuevo con más claridad. La respuesta es el producto de la matriz de rotación y los valores del campo magnético . Si lee más sobre el "X siempre es muy pequeño" es el valor correcto.

El acelerómetro y los sensores de campo magnético miden la aceleración del dispositivo y el campo magnético de la tierra en la ubicación del dispositivo, respectivamente. Son vectores en el espacio dimentional 3, dejan llamarlos a y m respectivamente.
Si permanece inmóvil y gira el dispositivo, teóricamente m no cambia asumiendo que no hay interferencia magnética de objetos circundantes (en realidad m debería cambiar poco, si se mueve, ya que el campo magnético de la tierra debería cambiar poco en una corta distancia). Pero a cambio, aunque no debe ser drástico en la mayoría de la situación.

Ahora bien, un vector v en el espacio tridimensional puede representarse por 3-tuplas (v_1, v_2, v_3) con respecto a alguna base ( e_1 , e_2 , e_3 ), es decir v = v_1 e_1 + v_2 e_2 + v_3 e_3 . (V_1, v_2, v_3) se llaman las coordenadas de v con respecto a la base ( e_1 , e_2 , e_3 ).

En los dispositivos Android, la base es ( x , y , z ) donde, para la mayoría del teléfono, x está a lo largo del lado más corto y apunta a la derecha, y está a lo largo del lado más largo y apuntando hacia arriba yz es perpendicular a la pantalla y señalando.
Ahora esta base cambia a medida que cambia la posición del dispositivo. Se puede pensar que estas bases son una función del tiempo ( x (t), y (t), z (t)), en términos matemáticos se trata de un sistema de coordenadas en movimiento.

Por lo tanto, aunque m no cambia, pero el evento.valores devuelve por los sensores son diferentes porque la base es diferente (voy a hablar de fluctuación más tarde). Como es, los valores de evento son inútiles porque nos da las coordenadas pero no sabemos cuál es la base, es decir, con respecto a alguna base que conocemos.

Ahora la pregunta es: ¿es posible encontrar las coordenadas de a y m con respecto a la base del mundo fijo ( w_1 , w_2 , w_3 ) donde w_1 apunta hacia el Este, w_2 apunta hacia el Norte magnético y w_3 apunta hacia el cielo?

La respuesta es sí proporcionada 2 asunciones importantes se satisfacen.
Con estos 2 supuestos es sencillo calcular (sólo unos cuantos productos cruzados) el cambio de matriz de base R desde la base ( x , y , z ) hasta la base ( w_1 , w_2 , w_3 ), que en Android se denomina Rotación Matriz Entonces las coordenadas de un vector v con respecto a la base ( w_1 , w_2 , w_3 ) se obtienen multiplicando R con las coordenadas de v con respecto a ( x , y , z ). Así, las coordenadas de m con respecto al sistema de coordenadas del mundo son sólo el producto de la matriz de rotación y los valores event.values devueltos por el sensor TYPE_MAGNETIC_FIELD y similarmente para a .

En android, la matriz de rotación se obtiene llamando a getRotationMatrix (float [] R, float [] I, float [] gravity, float [] geomagnético) y normalmente pasamos los valores de acelerómetro devueltos para el parámetro de gravedad y los valores del campo magnético para El geomagnético.

Los 2 supuestos importantes son:
1- El parámetro de gravedad representa un vector situado en w_3 , más particularmente es el menos del vector influenciado por la gravedad solamente.
Por lo tanto, si pasa los valores del acelerómetro sin filtrar, la matriz de rotación estará ligeramente desactivada. Ése es porqué usted necesita filtrar el acelerómetro de modo que los valores del filtro sean aproximadamente apenas el vector negativo de la gravedad. Dado que la aceleración gravitatoria es el factor dominante en el vector acelerómetro, normalmente el filtro de paso bajo es suficiente.
2- El parámetro geomagnético representa un vector situado en el plano atravesado por los vectores w_2 y w_3 . Eso es lo que miente en el plano del Cielo Norte. Por lo tanto, en términos de la base ( w_1 , w_2 , w_3 ), la primera coordenada debe ser 0. Por lo tanto, el "X es siempre muy pequeño" como lo ha indicado anteriormente es el valor correcto, idealmente debería ser 0. Ahora el magnético Los valores de campo fluctuarán bastante. Esto es algo que se espera, al igual que una aguja de brújula regular no se detendrá si lo mantiene en su mano y su mano se sacude un poco. Además, puede obtener interferencia de los objetos que le rodean y en este caso los valores del campo magnético son impredecibles. Una vez probé mi aplicación de brújula sentada cerca de una mesa de "piedra" y mi brújula estaba en más de 90 grados, sólo usando una brújula real que me di cuenta de que no hay nada malo en mi aplicación y la "piedra" Verdadero campo magnético fuerte.
Con la gravedad como un factor dominante que puede filtrar los valores del acelerómetro, pero sin ningún otro conocimiento, ¿cómo se ajusta los valores magnéticos? ¿Cómo sabes si hay o no hay interferencia de los objetos circundantes?

Usted puede hacer mucho más como el conocimiento completo de su posición espacial del dispositivo etc con la comprensión de la matriz de la rotación .

Según la explicación anterior, haga esto

 private static final int TEST_GRAV = Sensor.TYPE_ACCELEROMETER; private static final int TEST_MAG = Sensor.TYPE_MAGNETIC_FIELD; private final float alpha = (float) 0.8; private float gravity[] = new float[3]; private float magnetic[] = new float[3]; public void onSensorChanged(SensorEvent event) { Sensor sensor = event.sensor; if (sensor.getType() == TEST_GRAV) { // Isolate the force of gravity with the low-pass filter. gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]; gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]; gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]; } else if (sensor.getType() == TEST_MAG) { magnetic[0] = event.values[0]; magnetic[1] = event.values[1]; magnetic[2] = event.values[2]; float[] R = new float[9]; float[] I = new float[9]; SensorManager.getRotationMatrix(R, I, gravity, magnetic); float [] A_D = event.values.clone(); float [] A_W = new float[3]; A_W[0] = R[0] * A_D[0] + R[1] * A_D[1] + R[2] * A_D[2]; A_W[1] = R[3] * A_D[0] + R[4] * A_D[1] + R[5] * A_D[2]; A_W[2] = R[6] * A_D[0] + R[7] * A_D[1] + R[8] * A_D[2]; Log.d("Field","\nX :"+A_W[0]+"\nY :"+A_W[1]+"\nZ :"+A_W[2]); } } 
  • ¿Cómo puede configurar TI SensorTag para recopilar datos para su posterior recuperación?
  • ¿Cómo implementar el sensor de giroscopio en android?
  • Calcular el consumo de energía del sensor Android
  • SensorManager.getRotationMatrix parámetro de gravedad o parámetros de acelerómetro en android?
  • ¿Cómo obtener el azimut de un teléfono con lecturas de brújula y lecturas de giroscopio?
  • ¿Cómo detectar el movimiento de un dispositivo Android?
  • Mejor manera de implementar alertas de proximidad android
  • Determinación del norte con el teléfono Android
  • ¿Cómo habilitar el simulador de sensores en android?
  • Android: cómo afectan las lecturas de los sensores a la duración de la batería
  • Las velocidades de eventos de sensores "personalizadas" no parecen funcionar con SensorManager.registerListener (receptor SensorEventListener, Sensor sensor, tasa int)
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.