Показания компаса на SGS III

1

Мое приложение должно показать текущий подшипник устройства, используя его компас. Код, который я использую (ниже), отлично работает на моем Galaxy Nexus и Galaxy One, но компас круто крутится на Samsung Galaxy S III. Я попытался сделать цифру-8 для повторной калибровки устройства, но это ничего не меняет. Странно, что другие приложения компаса, загруженные из Google Play, отлично работают на SIII. Что может быть здесь?

float[] mGravity;
float[] mGeomagnetic;

public void onSensorChanged( SensorEvent event ) {
    float azimuth = 0f;
    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 orientation[] = new float[3];
            SensorManager.getOrientation(R, orientation);
            azimuth = orientation[0]; // orientation contains: azimut, pitch and roll
        }
     }

    //Discard 0.0-values
    if(azimuth == 0.0) { return; }

    //Convert the sensor value to degrees
    azimuth = (float) Math.toDegrees(azimuth); //same as azimuth = -azimuth*360/(2*3.14159f);


    //Smooth the sensor-output
    azimuth = smoothValues(azimuth);
}

//From http://stackoverflow.com/questions/4699417/android-compass-orientation-on-unreliable-low-pass-filter
//SmoothFactorCompass: The easing float that defines how smooth the movement will be (1 is no smoothing and 0 is never updating, my default is 0.5).
//SmoothThresholdCompass: The threshold in which the distance is big enough to turn immediately (0 is jump always, 360 is never jumping, my default is 30).
static final float SmoothFactorCompass = 0.5f;
static final float SmoothThresholdCompass = 30.0f;
float oldCompass = 0.0f;
private float smoothValues (float newCompass){
    if (Math.abs(newCompass - oldCompass) < 180) {
        if (Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
            oldCompass = newCompass;
        }
        else {
            oldCompass = oldCompass + SmoothFactorCompass * (newCompass - oldCompass);
        }
    }
    else {
        if (360.0 - Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
            oldCompass = newCompass;
        }
        else {
            if (oldCompass > newCompass) {
                oldCompass = (oldCompass + SmoothFactorCompass * ((360 + newCompass - oldCompass) % 360) + 360) % 360;
            } 
            else {
                oldCompass = (oldCompass - SmoothFactorCompass * ((360 - newCompass + oldCompass) % 360) + 360) % 360;
            }
        }
    }
    return oldCompass;
}
Теги:
android-sensors
samsung-mobile
galaxy

2 ответа

3
Лучший ответ

В настоящее время я изучаю механизм компаса на Android, и я бы рекомендовал начать с фильтра нижних частот в вашем случае. Что вам нужно сделать - это применять фильтр нижних частот для данных датчиков ACCELEROMETER и MAGNETIC_FIELD. Вот как я реализовал это:

private float[] accel;
private float[] geomagnetic;
float R[] = new float[9];
float I[] = new float[9];
float orientation[] = new float[3];

@Override
public void onSensorChanged(SensorEvent event)
{
    synchronized (this)
    {
        float azimuth = -1f;

        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
            accel = lowPass( event.values.clone(), accel );

        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
            geomagnetic = lowPass(event.values.clone(), geomagnetic);

        if (accel != null && geomagnetic != null)
        {

            boolean success = SensorManager.getRotationMatrix(R, I,
                    accel, geomagnetic);

            SensorManager.remapCoordinateSystem(R,
                    SensorManager.AXIS_X, SensorManager.AXIS_Z, R);

            if (success)
            {
                SensorManager.getOrientation(R, orientation);
                azimuth = orientation[0]; // orientation contains:
                                         // azimuth, pitch
                                         // and roll
                float newHeading = azimuth * 360 / (2 * 3.14159f);

                //do what you need to do with new heading
            } 
        }
    }
}


/*
 * time smoothing constant for low-pass filter 0 ≤ alpha ≤ 1 ; a smaller
 * value basically means more smoothing See:
 * http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization
 */
static final float ALPHA = 0.15f;

/**
 * @see http
 *      ://en.wikipedia.org/wiki/Low-pass_filter#Algorithmic_implementation
 * @see http
 *      ://developer.android.com/reference/android/hardware/SensorEvent.html
 *      #values
 */

protected float[] lowPass(float[] input, float[] output)
{
    if (output == null)
        return input;

    for (int i = 0; i < input.length; i++)
    {
        output[i] = output[i] + ALPHA * (input[i] - output[i]);
    }
    return output;
}
  • 0
    Круто, большое спасибо, это решило проблему :-)!
  • 0
    Как вы рассчитали ALPHA = 0,15? С научной точки зрения нельзя сделать это, закрыв глаза и выбрав случайное значение от 0 до 1. Может ли кто-нибудь уточнить расчет ALPHA в этом случае. Мне нужно обосновать, почему я использовал значение X АЛЬФА, в то время как моя частота дискретизации составляет 100 Гц. Любая помощь
Показать ещё 1 комментарий
0

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

На мой взгляд, чтение S3 вполне нормально под Android OS 4.3

event.values.clone;

Мой недавний опыт обратной связи данных заставил меня применить векторную логику вращения на телефоне SONY, загруженном датчиком Invesense, и, похоже, он работает, хотя мне все еще не нравится изменчивый ответ, но я живу с таким отзывом на ОС Android android 4.3, Мне также кажется, что любой загруженный в телефоне датчик инденсенса должен применять вектор вращения, чтобы он работал правильно

Ещё вопросы

Сообщество Overcoder
Наверх
Меню