2013-07-31 5 views
8

У меня очень неприятная проблема с видом AR, действующим как компас. Поэтому, когда я держу телефон в портрете (так, чтобы экран указывал на мое лицо), я вызываю remapCoordinateSystem, что высота тона равна 0 при удержании этого портрета. Тогда азимут (функциональность компаса) совершенен, но как только я наклоняю телефон, азимут разрушается, если я наклоняюсь вперед, азимут увеличивается, и если я наклоняюсь назад, он уменьшается.Android getOrientation Azimuth загрязняется, когда телефон наклонен

Я использую 2 датчика для получения показаний, Sensor.TYPE_MAGNETIC_FIELD и Sensor.TYPE_GRAVITY.

Я использую lowpassfilter, который довольно простой, он реализован с константой alpha и используется непосредственно для считывания значений с датчиков.

Вот мой код:

float[] rotationMatrix = new float[9]; 
SensorManager.getRotationMatrix(rotationMatrix, null, gravitymeterValues, 
    magnetometerValues); 

float[] remappedRotationMatrix = new float[9]; 

SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X, 
    SensorManager.AXIS_Z, remappedRotationMatrix); 

float results[] = new float[3]; 
SensorManager.getOrientation(remappedRotationMatrix, results); 

float azimuth = (float) (results[0] * 180/Math.PI); 
if (azimuth < 0) { 
    azimuth += 360; 
} 

float pitch = (float) (results[1] * 180/Math.PI); 
float roll = (float) (results[2] * 180/Math.PI); 

Как вы видите, нет никакой магии здесь. Я называю этот фрагмент кода, когда гравитационные параметры и свойства magnetometerValues ​​готовы к использованию.

Мой вопрос в том, как остановить азимут от спячки, когда я наклоняю телефон?

Я проверил бесплатное приложение в Google Play Store, Compass, и он не решил эту проблему, но я надеюсь, что есть решение.

У меня есть 2 решения в виде:

  1. сделать вид AR работать только в очень связях углы наклона, прямо сейчас у меня есть что-то вроде pitch >= -5 && pitch <= 30. Если это не заполнено, пользователю будет показан экран, который попросит его/ее повернуть телефон на портрет.

  2. Как-то использовать подачу для подавления азимута, это похоже на довольно специфичное для устройства решение, но, конечно, я открыт для предложений.

Я также могу добавить, что я искал пару часов для достойного решения, и я не нашел ни одного, который дал мне все лучшие решения, чем 2) здесь.

Заранее благодарен!

+0

Что делать вам фильтр низких частот? Вам не нужно фильтровать при использовании TYPE_GRAVITY. –

+0

@HoanNguyen Нужно ли мне пропускать 'Sensor.TYPE_MAGNETIC_FIELD'? Теперь я пропускаю как 'Sensor.TYPE_MAGNETIC_FIELD', так и' Sensor.TYPE_GRAVITY'. –

+1

Нет, использование фильтра нижних частот заключается в удалении ускорения в направлении x y. Матрица getRotationMatrix потребовала, чтобы третий параметр был приблизительно равен ускорению только в направлении z.Если вы используете TYPE_GRAVItY, нет необходимости использовать фильтр нижних частот, но у многих устройств нет TYPE_GRAVITY, поэтому вам необходимо использовать TYPE_ACCELEROMETER и фильтр нижних частот для устройств, у которых нет TYPE_GRAVITY. –

ответ

14

Для полного кода см https://github.com/hoananguyen/dsensor
Хранить историю и в среднем, я не знаю, правильную интерпретацию тангажа и крена поэтому следующий код только по азимуту.

Члены класса

private List<float[]> mRotHist = new ArrayList<float[]>(); 
private int mRotHistIndex; 
// Change the value so that the azimuth is stable and fit your requirement 
private int mHistoryMaxLength = 40; 
float[] mGravity; 
float[] mMagnetic; 
float[] mRotationMatrix = new float[9]; 
// the direction of the back camera, only valid if the device is tilted up by 
// at least 25 degrees. 
private float mFacing = Float.NAN; 

public static final float TWENTY_FIVE_DEGREE_IN_RADIAN = 0.436332313f; 
public static final float ONE_FIFTY_FIVE_DEGREE_IN_RADIAN = 2.7052603f; 

onSensorChanged

@Override 
public void onSensorChanged(SensorEvent event) 
{ 
    if (event.sensor.getType() == Sensor.TYPE_GRAVITY) 
    { 
     mGravity = event.values.clone(); 
    } 
    else 
    { 
     mMagnetic = event.values.clone(); 
    } 

    if (mGravity != null && mMagnetic != null) 
    { 
      if (SensorManager.getRotationMatrix(mRotationMatrix, null, mGravity, mMagnetic)) 
      { 
       // inclination is the degree of tilt by the device independent of orientation (portrait or landscape) 
       // if less than 25 or more than 155 degrees the device is considered lying flat 
       float inclination = (float) Math.acos(mRotationMatrix[8]); 
       if (inclination < TWENTY_FIVE_DEGREE_IN_RADIAN 
         || inclination > ONE_FIFTY_FIVE_DEGREE_IN_RADIAN) 
       { 
        // mFacing is undefined, so we need to clear the history 
        clearRotHist(); 
        mFacing = Float.NaN; 
       } 
       else 
       { 
        setRotHist(); 
        // mFacing = azimuth is in radian 
        mFacing = findFacing(); 
       } 
      } 
    } 
} 

private void clearRotHist() 
{ 
    if (DEBUG) {Log.d(TAG, "clearRotHist()");} 
    mRotHist.clear(); 
    mRotHistIndex = 0; 
} 

private void setRotHist() 
{ 
    if (DEBUG) {Log.d(TAG, "setRotHist()");} 
    float[] hist = mRotationMatrix.clone(); 
    if (mRotHist.size() == mHistoryMaxLength) 
    { 
     mRotHist.remove(mRotHistIndex); 
    } 
    mRotHist.add(mRotHistIndex++, hist); 
    mRotHistIndex %= mHistoryMaxLength; 
} 

private float findFacing() 
{ 
    if (DEBUG) {Log.d(TAG, "findFacing()");} 
    float[] averageRotHist = average(mRotHist); 
    return (float) Math.atan2(-averageRotHist[2], -averageRotHist[5]); 
} 

public float[] average(List<float[]> values) 
{ 
    float[] result = new float[9]; 
    for (float[] value : values) 
    { 
     for (int i = 0; i < 9; i++) 
     { 
      result[i] += value[i]; 
     } 
    } 

    for (int i = 0; i < 9; i++) 
    { 
     result[i] = result[i]/values.size(); 
    } 

    return result; 
} 
+0

Отличный материал! Теперь мой азимут намного лучше, только проблема заключается в быстрых движениях, которые он прыгает много, но я думаю, вы должны жить с этим. Большое спасибо за ваш код. Еще один вопрос: какая скорость обновления - ваши датчики? UI/Game/какой? Спасибо –

+2

Я использую Normal, и это достаточно хорошо для моей цели. –

+0

Вы используете этот код в приложении для компаса? Я использую пользовательский интерфейс, и это очень часто, у меня есть отличные результаты с вашим кодом, кстати! –