Датчик поворота не реагирует, если телефон перемещается медленно

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

Я вижу, что он «застревает» на этой строке, и если телефон перемещается слишком медленно, эта строка вернет false:

SensorManager.getRotationMatrix( R, I, mGravity, mGeomagnetic );

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

Вот мой код:

mSensorManager = (SensorManager)activityContext.getSystemService(Context.SENSOR_SERVICE);;
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
mMagnetometer  = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
mSensorManager.registerListener(this, mMagnetometer,  SensorManager.SENSOR_DELAY_GAME); 

А вот событие onSensorChanged:

@Override
public void onSensorChanged(SensorEvent event) 
{
    if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION)
        mGravity     = event.values;
    if (event.sensor.getType() == Sensor.TYPE_ORIENTATION)
        mGeomagnetic = event.values;

    if (mGravity != null && mGeomagnetic != null) 
    {
        // The code is triggering to this point fine

        float R[] = new float[9];
        float I[] = new float[9];

        float forceX = Float.valueOf(((float)((float)mGravity[0] ) ) ) / (float)4.0;
        float forceY = Float.valueOf(((float)((float)mGravity[1] ) ) ) / (float)4.0;
        float forceZ = Float.valueOf(((float)((float)mGravity[2] ) ) ) / (float)4.0;

        boolean success = SensorManager.getRotationMatrix( R, I, mGravity, mGeomagnetic );

        if (success) // <-- this is only triggering if phone is moved fast enough 
        {
            float orientation[] = new float[3];
            SensorManager.getOrientation(R, orientation);

            rotation_degrees_x = ((float)Math.toDegrees( orientation[0] ) + (float)180.0 ) / (float)360.0;
            rotation_degrees_y = ((float)Math.toDegrees( orientation[1] ) + (float)180.0 ) / (float)360.0;
            rotation_degrees_z = ((float)Math.toDegrees( orientation[2] ) + (float)180.0 ) / (float)360.0;

        }

        mGeomagnetic = null;
        mGravity     = null;
    }
}

Я также вижу, что постоянно срабатывает следующее условие:

if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE)

Поэтому я добавил следующую строку в условие:

Log.d( "ROTATIONDEBUG", "TYPE_MAGNETIC_FIELD: " + mGeomagnetic[0] + "\t" + mGeomagnetic[1] + "\t" + mGeomagnetic[1] );

И получили некоторые значения, отражающие малейшие изменения поворота:

TYPE_MAGNETIC_FIELD: 23.5      20.7    20.7
TYPE_MAGNETIC_FIELD: 23.2      22.2    22.2
TYPE_MAGNETIC_FIELD: 23.0      24.9    24.9
TYPE_MAGNETIC_FIELD: 22.4      27.5    27.5
TYPE_MAGNETIC_FIELD: 21.5      29.2    29.2
TYPE_MAGNETIC_FIELD: 21.6      30.2    30.2
TYPE_MAGNETIC_FIELD: 21.1      32.1    32.10
TYPE_MAGNETIC_FIELD: 20.7      34.8    34.8
TYPE_MAGNETIC_FIELD: 20.7      34.7    34.7
TYPE_MAGNETIC_FIELD: 20.9      35.0    35.0
TYPE_MAGNETIC_FIELD: 20.6      36.0    36.0

Но не могу понять, почему они не работают вместе, а строка SensorManager.getRotationMatrix возвращает false..

Я скачал несколько приложений для тестирования компаса и датчиков, и все они могли уловить это движение света, так что я предполагаю, что это проблема с моим кодом.

EDIT: я попробовал другой пример кода, но он дает мне точно такие же результаты:

SensorEventListener sensorEventListener = new SensorEventListener() {
    float[] mGravity;
    float[] mGeomagnetic;

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
            mGravity = event.values;
        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
            mGeomagnetic = event.values;
        if (mGravity != null && mGeomagnetic != null) {
            float R[] = new float[9];
            float I[] = new float[9];
            boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
            if (success) {
                float orientationData[] = new float[3];
                SensorManager.getOrientation(R, orientationData);
                azimuth = orientationData[0];
                pitch = orientationData[1];
                roll = orientationData[2];
            }
        }
    }
};

Пока я вижу эту закономерность:

Если я прочитаю значения непосредственно из приведенного ниже условия, я смогу получить «подробные/чувствительные» изменения:

 if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) 
    mGeomagnetic = event.values; 

НО, если я попытаюсь использовать getRotationMatrix, он вернет true только в том случае, если с помощью акселерометра будет обнаружено достаточное усилие:

 boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);

Как заставить SensorManager.getRotationMatrix срабатывать независимо от значения акселерометра? или как я могу преобразовать эти значения RAW в градусы?

Любые подсказки, пожалуйста? Спасибо!


person 0x29a    schedule 14.05.2018    source источник
comment
См. датчики движения и Датчики положения"Because the game rotation vector sensor does not use the magnetic field, relative rotations are more accurate, and not impacted by magnetic field changes. Use this sensor in a game if you do not care about where north is, and the normal rotation vector does not fit your needs because of its reliance on the magnetic field."   -  person Jon Goodwin    schedule 14.05.2018
comment
@JonGoodwin спасибо за ответ! после изменения типа датчика на TYPE_GAME_ROTATION_VECTOR, теперь он срабатывает нормально, но я думаю, что меня волнует, где находится север, как будто я кладу телефон прямо и наклоняю его в одном направлении, а затем в другом и делаю то же самое при перемещении телефона на 90° по оси X, и результат будет другим. Есть ли способ объединить оба? магнитный север и «быстрый триггер»? Спасибо!   -  person 0x29a    schedule 14.05.2018
comment
хорошо, я, наконец, смог запустить его, изменив SENSOR_DELAY_GAME на SENSOR_DELAY_NORMAL и используя обычный TYPE_GYROSCOPE, работающий как шарм!   -  person 0x29a    schedule 14.05.2018
comment
Круто, теперь ты эксперт! Ответьте на свой вопрос, чтобы другие могли увидеть технику. Удачи.   -  person Jon Goodwin    schedule 14.05.2018


Ответы (1)


Оказывается, изменив SENSOR_DELAY_GAME на SENSOR_DELAY_NORMAL и используя обычный TYPE_GYROSCOPE, проблема решена, и он срабатывает даже при малейшем движении!

person 0x29a    schedule 14.05.2018