Sensores de posición

La plataforma Android proporciona dos sensores que te permiten determinar la posición de un dispositivo: el sensor del campo geomagnético y el acelerómetro. Android también proporciona un sensor que permite determinar qué tan cerca está la cara de un dispositivo es con un objeto (conocido como sensor de proximidad). El el sensor de campos geomagnéticos y el sensor de proximidad se basan en hardware. Más probable fabricantes de teléfonos celulares y tablets incluyen un sensor de campos geomagnéticos. Del mismo modo, fabricantes de teléfonos celulares suelen incluir un sensor de proximidad para determinar cuándo dispositivo móvil se sostenga cerca del rostro de un usuario (por ejemplo, durante un llamada). Para determinar la orientación de un dispositivo, puedes usar las lecturas de el acelerómetro del dispositivo y el sensor de campos geomagnéticos.

Nota: El sensor de orientación dejó de estar disponible en Android 2.2 (nivel de API 8), y el tipo de sensor de orientación dejó de estar disponible en Android 4.4W (nivel de API 20).

Los sensores de posición sirven para determinar la posición física de un dispositivo en el marco de referencia del mundo. Por ejemplo, puedes usar el campo geomagnético junto con el acelerómetro para determinar la posición de un dispositivo en relación con el polo norte magnético. También puedes usar estos sensores determinar la orientación de un dispositivo en el marco de referencia de la aplicación. Los sensores de posición no suelen usarse para supervisar el movimiento o el movimiento del dispositivo como sacudir, inclinar o empujar (para obtener más información, consulta Sensores de movimiento).

El sensor de campo geomagnético y el acelerómetro devuelven arrays multidimensionales. de valores de sensores para cada SensorEvent. Por ejemplo: el sensor de campos geomagnéticos proporciona valores de fuerza de campos geomagnéticos para cada uno de los tres ejes de coordenadas durante un evento de sensor. Del mismo modo, el acelerómetro mide la aceleración aplicada al dispositivo durante una sensor de estado. Para obtener más información sobre los sistemas de coordenadas que se usan por sensores, consulta Sistemas de coordenadas de sensores. El sensor de proximidad proporciona un valor único para cada evento de sensor. En la tabla 1, se resumen los sensores de posición que se compatible con la plataforma de Android.

Tabla 1: Sensores de posición compatibles con la plataforma de Android.

Sensor Datos del evento del sensor Descripción Unidades de medición
TYPE_GAME_ROTATION_VECTOR SensorEvent.values[0] Indica el componente vectorial de rotación junto al eje x (x * sin(θ/2)). Sin unidades
SensorEvent.values[1] Indica el componente vectorial de rotación junto al eje y (y * sin(θ/2)).
SensorEvent.values[2] Indica el componente vectorial de rotación junto al eje z (z * sin(θ/2)).
TYPE_GEOMAGNETIC_ROTATION_VECTOR SensorEvent.values[0] Indica el componente vectorial de rotación junto al eje x (x * sin(θ/2)). Sin unidades
SensorEvent.values[1] Indica el componente vectorial de rotación junto al eje y (y * sin(θ/2)).
SensorEvent.values[2] Indica el componente vectorial de rotación junto al eje z (z * sin(θ/2)).
TYPE_MAGNETIC_FIELD SensorEvent.values[0] Fuerza del campo geomagnético junto al eje x. μT
SensorEvent.values[1] Fuerza del campo geomagnético junto al eje y.
SensorEvent.values[2] Fuerza del campo geomagnético junto al eje z.
TYPE_MAGNETIC_FIELD_UNCALIBRATED SensorEvent.values[0] Fuerza del campo geomagnético (sin calibración de hierro resistente) junto al eje x. μT
SensorEvent.values[1] Fuerza del campo geomagnético (sin calibración de hierro resistente) junto al eje y.
SensorEvent.values[2] Fuerza del campo geomagnético (sin calibración de hierro resistente) junto al eje z.
SensorEvent.values[3] Estimación del sesgo de hierro junto al eje x.
SensorEvent.values[4] Estimación del sesgo de hierro junto al eje y.
SensorEvent.values[5] Estimación del sesgo de hierro junto al eje z.
TYPE_ORIENTATION1 SensorEvent.values[0] Azimuth (ángulo en torno al eje z). Grados
SensorEvent.values[1] Pitch (ángulo en torno al eje x).
SensorEvent.values[2] Roll (ángulo en torno al eje y).
TYPE_PROXIMITY SensorEvent.values[0] Distancia desde el objeto.2 cm

1 Este sensor dejó de estar disponible en Android 2.2 (API nivel 8), y este tipo de sensor dejó de estar disponible en Android 4.4W (nivel de API 20). El marco de trabajo del sensor brinda métodos alternativos para adquirir dispositivos orientación, que se analizan en Procesamiento la orientación del dispositivo.

2 Algunos sensores de proximidad solo proporcionan valores binarios que representan la distancia y la distancia.

Cómo usar el sensor del vector de rotación del juego

El sensor del vector de rotación del juego es idéntico al Rotación sensor vectorial, excepto que no usa el campo geomagnético. Por lo tanto, el eje Y no apuntando hacia el norte, sino hacia otra referencia. Esa referencia se permite a la deriva mismo orden de magnitud con el giro del giroscopio alrededor del eje Z.

Como el sensor del vector de rotación del juego no usa el campo magnético, las rotaciones relativas sean más precisos y no se vean afectados por los cambios en el campo magnético. Usa este sensor en un juego si no importa dónde se encuentra el norte, y el vector de rotación normal no se ajusta a tus necesidades debido a su dependencia del campo magnético.

En el siguiente código, se muestra cómo obtener una instancia del vector de rotación predeterminado del juego sensor:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);

Cómo usar el sensor del vector de rotación geomagnético

El sensor del vector de rotación geomagnético es similar al sensor del vector de rotación, pero no usa el giroscopio. La precisión de este sensor es inferior a la del vector de rotación normal pero el consumo de energía se reduce. Usa este sensor solo si deseas recopilar datos de rotación información en segundo plano sin consumir demasiada batería. Este sensor es muy útil cuando se usa junto con el procesamiento por lotes.

En el siguiente código, se muestra cómo obtener una instancia de la rotación geomagnética predeterminada sensor vectorial:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR);

Calcula la orientación del dispositivo

Al procesar la orientación de un dispositivo, puedes controlar la posición de la en relación con el marco de referencia de la Tierra (específicamente, la Polo Norte). El siguiente código muestra cómo procesar la carga de un dispositivo orientación:

Kotlin

private lateinit var sensorManager: SensorManager
...
// Rotation matrix based on current readings from accelerometer and magnetometer.
val rotationMatrix = FloatArray(9)
SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading)

// Express the updated rotation matrix as three orientation angles.
val orientationAngles = FloatArray(3)
SensorManager.getOrientation(rotationMatrix, orientationAngles)

Java

private SensorManager sensorManager;
...
// Rotation matrix based on current readings from accelerometer and magnetometer.
final float[] rotationMatrix = new float[9];
SensorManager.getRotationMatrix(rotationMatrix, null,
    accelerometerReading, magnetometerReading);

// Express the updated rotation matrix as three orientation angles.
final float[] orientationAngles = new float[3];
SensorManager.getOrientation(rotationMatrix, orientationAngles);

El sistema calcula los ángulos de orientación usando las coordenadas geomagnéticas de un dispositivo de campo en combinación con el acelerómetro del dispositivo. Si usas estos dos, de hardware, el sistema proporciona datos para los tres ángulos de orientación:

  • Azimuth (grados de rotación sobre el eje z). Este es el ángulo entre la dirección actual de la brújula del dispositivo y el norte magnético. Si el borde superior del dispositivo apunta al norte magnético, el valor azimuth es 0. grados; si el borde superior apunta hacia el sur, el azimuth es de 180 grados. De forma similar, si el borde superior apunta hacia el este, el azimuth es de 90 grados, y si el mirando hacia el oeste, el azimut es de 270 grados.
  • Pitch (grados de rotación sobre el eje x). Este es el de ángulo entre un plano paralelo a la pantalla del dispositivo y un plano paralelo hasta el suelo. Si sostienes el dispositivo paralelo al suelo con la parte inferior más cerca de ti e inclinar el borde superior del dispositivo hacia el suelo, el ángulo de inclinación se vuelve positivo. Si se inclina en la dirección opuesta, alejar el borde superior del dispositivo del suelo puede provocar que el ángulo de inclinación se vuelva negativo. El rango de valores es de -90 grados a 90 grados.
  • Roll (grados de rotación sobre el eje y). Este es el de ángulo entre un plano perpendicular a la pantalla del dispositivo y un plano perpendicular al suelo. Si sostienes el dispositivo paralelo al suelo con el borde inferior más cerca de ti e inclina el borde izquierdo del dispositivo hacia el suelo, el ángulo de rodaje se vuelve positivo. Cómo inclinarse en sentido contrario en dirección IP, moviendo el borde derecho del dispositivo hacia el suelo, hace que el ángulo de balanceo se vuelva negativo. El rango de valores es de -180 grados a 180 grados.

Nota: La definición de rollo del sensor cambió para reflejar el la gran mayoría de las implementaciones en el ecosistema de geosensores.

Ten en cuenta que estos ángulos funcionan a partir de un sistema de coordenadas diferente al de la uno que se use en la aviación (para Yaw, Pitch y Roll). En el sistema de aviación, x se encuentra a lo largo del lado largo del plano, desde la cola hasta la nariz.

El sensor de orientación deriva sus datos mediante el procesamiento de los datos sin procesar del sensor según el acelerómetro y el sensor de campos geomagnéticos. Debido al pesado procesamiento involucrado, la exactitud y precisión de la orientación el sensor de datos ha disminuido. Específicamente, este sensor solo es confiable cuando el es 0. Como resultado, el sensor de orientación dejó de estar disponible en Android 2.2 (nivel de API 8), y el tipo de sensor de orientación dejó de estar disponible en Android 4.4 W (nivel de API 20). En lugar de usar datos sin procesar del sensor de orientación, te recomendamos usa el getRotationMatrix() junto con el Método getOrientation() para calcular los valores de orientación, como se indica en la siguiente muestra de código. Como parte de este proceso, puedes usar el remapCoordinateSystem() para traducir los valores de orientación al marco de tu aplicación referencia.

Kotlin

class SensorActivity : Activity(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private val accelerometerReading = FloatArray(3)
    private val magnetometerReading = FloatArray(3)

    private val rotationMatrix = FloatArray(9)
    private val orientationAngles = FloatArray(3)

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // Do something here if sensor accuracy changes.
        // You must implement this callback in your code.
    }

    override fun onResume() {
        super.onResume()

        // Get updates from the accelerometer and magnetometer at a constant rate.
        // To make batch operations more efficient and reduce power consumption,
        // provide support for delaying updates to the application.
        //
        // In this example, the sensor reporting delay is small enough such that
        // the application receives an update before the system checks the sensor
        // readings again.
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer ->
            sensorManager.registerListener(
                    this,
                    accelerometer,
                    SensorManager.SENSOR_DELAY_NORMAL,
                    SensorManager.SENSOR_DELAY_UI
            )
        }
        sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also { magneticField ->
            sensorManager.registerListener(
                    this,
                    magneticField,
                    SensorManager.SENSOR_DELAY_NORMAL,
                    SensorManager.SENSOR_DELAY_UI
            )
        }
    }

    override fun onPause() {
        super.onPause()

        // Don't receive any more updates from either sensor.
        sensorManager.unregisterListener(this)
    }

    // Get readings from accelerometer and magnetometer. To simplify calculations,
    // consider storing these readings as unit vectors.
    override fun onSensorChanged(event: SensorEvent) {
        if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
            System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)
        } else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {
            System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)
        }
    }

    // Compute the three orientation angles based on the most recent readings from
    // the device's accelerometer and magnetometer.
    fun updateOrientationAngles() {
        // Update rotation matrix, which is needed to update orientation angles.
        SensorManager.getRotationMatrix(
                rotationMatrix,
                null,
                accelerometerReading,
                magnetometerReading
        )

        // "rotationMatrix" now has up-to-date information.

        SensorManager.getOrientation(rotationMatrix, orientationAngles)

        // "orientationAngles" now has up-to-date information.
    }
}

Java

public class SensorActivity extends Activity implements SensorEventListener {

    private SensorManager sensorManager;
    private final float[] accelerometerReading = new float[3];
    private final float[] magnetometerReading = new float[3];

    private final float[] rotationMatrix = new float[9];
    private final float[] orientationAngles = new float[3];

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Do something here if sensor accuracy changes.
        // You must implement this callback in your code.
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Get updates from the accelerometer and magnetometer at a constant rate.
        // To make batch operations more efficient and reduce power consumption,
        // provide support for delaying updates to the application.
        //
        // In this example, the sensor reporting delay is small enough such that
        // the application receives an update before the system checks the sensor
        // readings again.
        Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        if (accelerometer != null) {
            sensorManager.registerListener(this, accelerometer,
                SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
        }
        Sensor magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        if (magneticField != null) {
            sensorManager.registerListener(this, magneticField,
                SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        // Don't receive any more updates from either sensor.
        sensorManager.unregisterListener(this);
    }

    // Get readings from accelerometer and magnetometer. To simplify calculations,
    // consider storing these readings as unit vectors.
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
          System.arraycopy(event.values, 0, accelerometerReading,
              0, accelerometerReading.length);
        } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            System.arraycopy(event.values, 0, magnetometerReading,
                0, magnetometerReading.length);
        }
    }

    // Compute the three orientation angles based on the most recent readings from
    // the device's accelerometer and magnetometer.
    public void updateOrientationAngles() {
        // Update rotation matrix, which is needed to update orientation angles.
        SensorManager.getRotationMatrix(rotationMatrix, null,
            accelerometerReading, magnetometerReading);

        // "rotationMatrix" now has up-to-date information.

        SensorManager.getOrientation(rotationMatrix, orientationAngles);

        // "orientationAngles" now has up-to-date information.
    }
}

Por lo general, no es necesario procesar datos ni filtrar los datos los ángulos de orientación sin procesar del dispositivo, excepto trasladar los valores de coordenadas de seguridad al marco de referencia de tu aplicación.

Cómo usar el sensor de campo geomagnético

El sensor de campo geomagnético te permite supervisar los cambios en el campo magnético de la Tierra. El El siguiente código te muestra cómo obtener una instancia del sensor de campo geomagnético predeterminado:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);

Nota: Si tu app se orienta a Android 12 (nivel de API 31) o más arriba, este sensor se con límite de frecuencia.

Este sensor proporciona datos sin procesar sobre la intensidad del campo (en μT) para cada uno de los tres ejes de coordenadas. En general, no es necesario usar este sensor directamente. En su lugar, puedes usar el vector de rotación para determinar el movimiento rotativo sin procesar, o puedes usar el acelerómetro y el campo geomagnético junto con el método getRotationMatrix() para obtener la matriz de rotación y la de inclinación. Luego, puedes usa estas matrices con getOrientation() y getInclination() para obtener el azimuth y datos de inclinación geomagnética.

Nota: Cuando pruebes tu app, puedes mejorar la precisión del sensor moviendo el dispositivo en forma de figura 8.

Cómo usar el magnetómetro sin calibrar

El magnetómetro sin calibrar es similar al campo geomagnético sensor, excepto que no se aplica calibración de hierro resistente al campo magnético. Calibración de fábrica se siguen aplicando al campo magnético. El magnetómetro sin calibrar es útil para manejar estimaciones de hierro resistente y de mala calidad. En general, geomagneticsensor_event.values[0] será de uncalibrated_magnetometer_event.values[0] - uncalibrated_magnetometer_event.values[3]. Es decir:

calibrated_x ~= uncalibrated_x - bias_estimate_x

Nota: Los sensores no calibrados proporcionan más resultados sin procesar y pueden incluyen cierto sesgo, pero sus mediciones contienen menos saltos de las correcciones aplicadas mediante calibración. Es posible que algunas aplicaciones prefieran estos resultados sin calibrar como más fluidos y más confiables. Por ejemplo, si una aplicación intenta realizar su propia fusión de sensores, pero, al introducir calibraciones, se pueden distorsionar los resultados.

Además del campo magnético, el magnetómetro sin calibrar también proporciona la estimado del sesgo de hierro resistente en cada eje. El siguiente código muestra cómo obtener una instancia de la magnetómetro no calibrado predeterminado:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED);

Cómo usar el sensor de proximidad

El sensor de proximidad permite determinar a qué distancia se encuentra un objeto respecto de un dispositivo. Lo siguiente te muestra cómo obtener una instancia del sensor de proximidad predeterminado:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);

El sensor de proximidad suele usarse para determinar a qué distancia se encuentra la cabeza de una persona respecto del rostro de un teléfono celular (por ejemplo, cuando un usuario realiza o recibe una llamada telefónica). Más probable los sensores de proximidad devuelven la distancia absoluta en cm, pero algunos los valores de distancia.

Nota: En algunos modelos de dispositivos, el sensor de proximidad se encuentra. debajo de la pantalla, lo que puede hacer que aparezca un punto intermitente en la pantalla si está habilitado mientras esta se encuentra .

El siguiente código muestra cómo usar el sensor de proximidad:

Kotlin

class SensorActivity : Activity(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private var proximity: Sensor? = null

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        // Get an instance of the sensor service, and use that to get an instance of
        // a particular sensor.
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // Do something here if sensor accuracy changes.
    }

    override fun onSensorChanged(event: SensorEvent) {
        val distance = event.values[0]
        // Do something with this sensor data.
    }

    override fun onResume() {
        // Register a listener for the sensor.
        super.onResume()

        proximity?.also { proximity ->
            sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

    override fun onPause() {
        // Be sure to unregister the sensor when the activity pauses.
        super.onPause()
        sensorManager.unregisterListener(this)
    }
}

Java

public class SensorActivity extends Activity implements SensorEventListener {
    private SensorManager sensorManager;
    private Sensor proximity;

    @Override
    public final void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Get an instance of the sensor service, and use that to get an instance of
        // a particular sensor.
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
    }

    @Override
    public final void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Do something here if sensor accuracy changes.
    }

    @Override
    public final void onSensorChanged(SensorEvent event) {
        float distance = event.values[0];
        // Do something with this sensor data.
    }

    @Override
    protected void onResume() {
        // Register a listener for the sensor.
        super.onResume();
        sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL);
      }

    @Override
    protected void onPause() {
        // Be sure to unregister the sensor when the activity pauses.
        super.onPause();
        sensorManager.unregisterListener(this);
    }
}

Nota: Algunos sensores de proximidad muestran valores binarios que representan "cerca de" o "lejos". En este caso, el sensor generalmente informa su valor de rango máximo en el estado lejano y uno menor en el estado "cercano". Por lo general, el valor lejano es un valor > 5 cm, pero puede variar de un sensor a otro. Puedes determinar el rango máximo de un sensor con el método getMaximumRange().

También debes leer: