Как измерить угол наклона телефона в плоскости XY с помощью акселерометра в Android

Я пытался использовать данные оси Z из SensorEvent.values, но он не обнаруживает поворот моего телефона в плоскости XY, т.е. вокруг оси Z.

Я использую это в качестве справки для координатных осей. Это правильно?

оси

Как мне измерить это движение, используя значения акселерометра?

Эти игры делают что-то похожее: Extreme Skater, Doodle Jump.

PS: ориентация моего телефона будет альбомной.

2 ответа

Решение

По сути, здесь есть 2 случая: устройство лежит плоско, а не плоско. Плоский здесь означает, что угол между поверхностью экрана устройства и мировой плоскостью xy (я называю это наклоном) составляет менее 25 градусов или больше 155 градусов. Подумайте о том, чтобы телефон лежал ровно или немного наклонился от стола.

Для начала нужно нормализовать вектор акселерометра.
То есть, если g - вектор, возвращается по значениям события датчика акселерометра. В коде

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

Тогда наклон можно рассчитать как

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

таким образом

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

В случае плоской укладки вы должны использовать компас, чтобы увидеть, насколько сильно устройство вращается из исходного положения.

Для случая не плоского, вращение (наклон) рассчитывается следующим образом

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

Теперь поворот = 0 означает, что устройство находится в нормальном положении. Это портрет без наклона для большинства телефонов и, вероятно, пейзаж для планшетов. Таким образом, если вы держите телефон, как на картинке выше, и начинаете вращаться, вращение изменится, и когда телефон находится в альбомной ориентации, вращение будет 90 или -90 в зависимости от направления вращения.

Акселерометр достаточен для проверки, является ли телефон плоским, как очень хорошо продемонстрировал Хоан.

Для тех, кто прибывает сюда и хочет проверить, не является ли телефон плоским, а каково его вращение, это можно сделать с помощью датчика движения вектора вращения.

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

Затем, чтобы проверить, если телефон плоский, вы можете просто сравнить tilt а также pitch значения с допустимыми значениями. Например

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

Преимущество этого способа заключается в том, что вы можете проверить, удерживается ли телефон в каком-либо конкретном повороте.

Стоит отметить, что ориентация приложения не повлияет на значения высоты тона, наклона и азимута.

Отработка идеального ответа от @Dan

Он пропустил очень немного информации, на которую указал @davy307.

При инициализации mAccelerometer вы должны определить его как Sensor.TYPE_ROTATION_VECTOR, в противном случае он не будет иметь 3-й вектор поворота и вызовет исключение ArrayIndexOutOfBounds.

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

В противном случае, это идеальное решение... Ценится!

Другие вопросы по тегам