1

我的應用程序需要使用指南針顯示設備的當前方位。我使用的代碼(下圖)在我的Galaxy Nexus和Galaxy One上工作得很好,但指南針在三星Galaxy S III上瘋狂地旋轉。我試圖用數字8來重新校準設備,但這並沒有改變任何東西。奇怪的是,從Google Play下載的其他指南針應用程序在SIII上工作得很好。這裏可能是什麼問題?SGS III上的指南針讀數

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

回答

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

太棒了,非常感謝,解決了這個問題:-)! – Nick

+0

你是怎麼計算ALPHA = 0.15科學的,人們不能通過閉眼和從0到1中選擇一個隨機值來做到這一點。任何人都可以在這種情況下詳細說明ALPHA的計算。我需要證明我爲什麼使用ALPHA的X值,而我的採樣率是100Hz。任何幫助 – user3123466

+0

爲什麼不呢?這就是我所做的:)這是「平滑度」係數,取決於您的偏好(平滑輸出值=較小的alpha)。所以首先,你需要想出如何衡量你的偏好的想法(因爲這只是在這個等式中變量),只有在這之後你才能創建一個公式來計算阿爾法:) –

0

可能是你應該從傳感器讀取第一個克隆的值,然後低通濾波器,如果這也不行,我知道索尼XPERIA手機是非常敏感不像三星是相當穩定。

在我看來,S3讀數下的Android OS 4.3

event.values.clone; 

我最近的經驗對夫婦的數據反饋力我申請裝有Invesense傳感器SONY手機上的旋轉向量邏輯相當確定的,它似乎是工作,儘管我仍然不喜歡這種波濤洶涌的反應,但是我已經在SONY android 4.3 OS上接受過這種反應。在我看來,任何手機裝載的創新傳感器都需要應用旋轉矢量才能正常工作