web-dev-qa-db-fra.com

Comment mesurer l'inclinaison du téléphone dans le plan XY en utilisant l'accéléromètre en Android

J'ai essayé d'utiliser les données de l'axe Z de SensorEvent.values, mais cela ne détecte pas la rotation de mon téléphone dans le plan XY, c'est-à-dire. autour de l'axe Z.

Je l'utilise comme référence pour les axes de coordonnées. Est-ce correct?

axes

Comment mesurer ce mouvement à l'aide des valeurs de l'accéléromètre?

Ces jeux font quelque chose de similaire: Extreme Skater , Doodle Jump.

PS: l'orientation de mon téléphone sera paysage.

29
udiboy1209

Essentiellement, il y a 2 cas ici: l'appareil repose à plat et non à plat. Plat signifie ici que l'angle entre la surface de l'écran de l'appareil et le plan mondial xy (je l'appelle l'inclinaison) est inférieur à 25 degrés ou supérieur à 155 degrés. Pensez au téléphone à plat ou inclinez-vous un peu sur une table.

Vous devez d'abord normaliser le vecteur accéléromètre.
C'est-à-dire si g est le vecteur renvoyé par les valeurs d'événement du capteur de l'accéléromètre. Dans du code

float[] g = new float[3]; 
g = event.values.clone();

double norm_Of_g = Math.sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2]);

// Normalize the accelerometer vector
g[0] = g[0] / norm_Of_g
g[1] = g[1] / norm_Of_g
g[2] = g[2] / norm_Of_g

Ensuite, l'inclinaison peut être calculée comme

int inclination = (int) Math.round(Math.toDegrees(Math.acos(g[2])));

Donc

if (inclination < 25 || inclination > 155)
{
    // device is flat
}
else
{
    // device is not flat
}

Dans le cas d'une pose à plat, vous devez utiliser une boussole pour voir la rotation de l'appareil par rapport à la position de départ.

Dans le cas de pas à plat, la rotation (inclinaison) est calculée comme suit

int rotation = (int) Math.round(Math.toDegrees(Math.atan2(g[0], g[1])));

Maintenant, la rotation = 0 signifie que l'appareil est en position normale. C'est portrait sans inclinaison pour la plupart des téléphones et probablement paysage pour tablette. Donc, si vous tenez un téléphone comme sur votre photo ci-dessus et commencez à tourner, la rotation changera et lorsque le téléphone sera en paysage, la rotation sera de 90 ou -90 dépend du sens de rotation.

65
Hoan Nguyen

L'accéléromètre est suffisant pour vérifier si le téléphone est plat comme Hoan l'a très bien démontré.

Pour tous ceux qui arrivent ici et qui cherchent non seulement à vérifier si le téléphone est plat, mais aussi à déterminer la rotation du téléphone, cela peut être obtenu grâce au Rotation Vector Motion Sensor .

private double pitch, tilt, azimuth;

@Override
public void onSensorChanged(SensorEvent event) {
    //Get Rotation Vector Sensor Values
    double[] g = convertFloatsToDoubles(event.values.clone());

    //Normalise
    double norm = Math.sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2] + g[3] * g[3]);
    g[0] /= norm;
    g[1] /= norm;
    g[2] /= norm;
    g[3] /= norm;

    //Set values to commonly known quaternion letter representatives
    double x = g[0];
    double y = g[1];
    double z = g[2];
    double w = g[3];

    //Calculate Pitch in degrees (-180 to 180)
    double sinP = 2.0 * (w * x + y * z);
    double cosP = 1.0 - 2.0 * (x * x + y * y);
    pitch = Math.atan2(sinP, cosP) * (180 / Math.PI);

    //Calculate Tilt in degrees (-90 to 90)
    double sinT = 2.0 * (w * y - z * x);
    if (Math.abs(sinT) >= 1)
        tilt = Math.copySign(Math.PI / 2, sinT) * (180 / Math.PI);
    else
        tilt = Math.asin(sinT) * (180 / Math.PI);

    //Calculate Azimuth in degrees (0 to 360; 0 = North, 90 = East, 180 = South, 270 = West)
    double sinA = 2.0 * (w * z + x * y);
    double cosA = 1.0 - 2.0 * (y * y + z * z);
    azimuth = Math.atan2(sinA, cosA) * (180 / Math.PI);
}

private double[] convertFloatsToDoubles(float[] input)
{
    if (input == null)
        return null;

    double[] output = new double[input.length];

    for (int i = 0; i < input.length; i++)
        output[i] = input[i];

    return output;
}

Ensuite, pour vérifier si le téléphone est plat, vous pouvez simplement comparer les valeurs tilt et pitch avec des valeurs de tolérance. Par exemple

public boolean flatEnough(double degreeTolerance) {
    return tilt <= degreeTolerance && tilt >= -degreeTolerance && pitch <= degreeTolerance && pitch >= -degreeTolerance;
}

L'avantage de le faire de cette façon est que vous pouvez vérifier si le téléphone est maintenu dans une rotation spécifique.

Il convient de noter que l'orientation de l'application n'affectera pas les valeurs de tangage, d'inclinaison et d'azimut.

3
Dan

Travailler sur la réponse parfaite de @Dan

Il a manqué un tout petit peu d'informations que @ davy307 a souligné.

Lors de l'initialisation du mAccéléromètre, vous devez le définir comme Sensor.TYPE_ROTATION_VECTOR sinon, il n'aura pas le 3ème vecteur de rotation et lèvera une exception ArrayIndexOutOfBounds.

mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

Sinon, c'est une solution parfaite ... Appréciée!

3
Dayan