2013-04-07 6 views
2

В моем проекте я пытаюсь управлять автомобилем, используя данные, полученные с помощью акселерометра устройства Android. (Левый, правый, прямой, обратный). Несмотря на то, что мне удалось считывать значения с акселерометра, показания подвержены частым изменениям, даже если устройство находится в стабильном положении. Может ли кто-нибудь предоставить мне лучший способ сделать это?Как читать значения акселерометра Android стабильным и точным способом?

Ниже приведен код, который я использовал

import android.content.Context; 
import android.hardware.Sensor; 
import android.hardware.SensorEvent; 
import android.hardware.SensorEventListener; 
import android.hardware.SensorManager; 

public class AccelerometerService { 
    private static SensorManager sensorManager; 
    private static SensorEventListener sensorEventListener; 
    private static boolean started = false; 

    private static float[] accelerometer = new float[3]; 
    private static float[] magneticField = new float[3]; 

    private static float[] rotationMatrix = new float[9]; 
    private static float[] inclinationMatrix = new float[9]; 
    private static float[] attitude = new float[3]; 

    private final static double RAD2DEG = 180/Math.PI; 

    private static int initialAzimuth = 0; 
    private static int initialPitch = 0; 
    private static int initialRoll = 0; 

    private static int[] attitudeInDegrees = new int[3]; 

    public static void start(final Context applicationContext) { 
     if(started) { 
      return; 
     } 

     sensorManager = (SensorManager) applicationContext 
       .getSystemService(Context.SENSOR_SERVICE); 

     sensorEventListener = new SensorEventListener() { 

      @Override 
      public void onSensorChanged(SensorEvent event) { 

       int type = event.sensor.getType(); 
       if(type == Sensor.TYPE_MAGNETIC_FIELD) { 
        magneticField = event.values.clone(); 
       } 
       if(type == Sensor.TYPE_ACCELEROMETER) { 
        accelerometer = event.values.clone(); 
       } 

       SensorManager.getRotationMatrix(rotationMatrix, inclinationMatrix, accelerometer, magneticField); 
       SensorManager.getOrientation(rotationMatrix, attitude); 

       attitudeInDegrees[0] = (int) Math.round(attitude[0] * RAD2DEG); //azimuth 
       attitudeInDegrees[1] = (int) Math.round(attitude[1] * RAD2DEG);  //pitch 
       attitudeInDegrees[2] = (int) Math.round(attitude[2] * RAD2DEG);  //roll 
      } 

      @Override 
      public void onAccuracyChanged(Sensor sensor, int accuracy) { 

      } 
     }; 
     sensorManager.registerListener(sensorEventListener, 
       sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), 
       SensorManager.SENSOR_DELAY_UI); 
     sensorManager.registerListener(sensorEventListener, 
       sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), 
       SensorManager.SENSOR_DELAY_UI); 

     started = true; 
    } 

    public static boolean getStarted() { 
     return started; 
    } 

    public static void stop() { 
     if(started) { 
     sensorManager.unregisterListener(sensorEventListener); 
     started = false; 
     } 
    } 
} 
+2

Это звучит, как вы хотите, чтобы сгладить данные: http://stackoverflow.com/questions/4611599/help-smoothing-data-from-a-sensor –

+1

Сколько флуктуации вы получаете? –

+1

Вы можете использовать «фильтр Калмана» http://stackoverflow.com/questions/1638864/filtering-accelerometer-data-noise – serhiisavruk

ответ

9

Старый вопрос, но я отвечать здесь ради других, которые находят этот вопрос.

Прежде всего, следующая ссылка @ andrew-morton - это именно то, что мне нужно, но было в псевдокоде, поэтому здесь есть Java для Android!

Во-вторых: Если вы можете (вы минимальный уровень API 9 или выше), просто используйте Sensor.TYPE_GRAVITY. Поскольку он уже сглажен. Если вам нужно поддерживать устаревшие API-интерфейсы (например, мне пришлось), этот код должен делать трюк!

И последнее: этот фрагмент кода несколько изменен из того, для чего я его использую, если вы хотите использовать его для себя, вам нужно создать getter для гравитации Vector3.

Примечания: Я обнаружил, что скользящее среднее значение 5 (MAX_SAMPLE_SIZE) довольно плавное. 10 еще более гладкая, но вы начинаете замечать отставание.

import android.hardware.Sensor; 
import android.hardware.SensorEvent; 
import android.hardware.SensorEventListener; 
import android.hardware.SensorManager; 
import java.util.ArrayList; 
import java.util.List; 

/** 
* Created by andy on 6/14/14. 
*/ 
public class AndroidGravityUpdate implements SensorEventListener { 
    private SensorManager sensorManager; 
    Vector3 gravity; 
    List<Float>[] rollingAverage = new List[3]; 

    private static final int MAX_SAMPLE_SIZE = 5; 

    AndroidGravityUpdate(SensorManager sensorManager) { 
     this.gravity = new Vector3(); 
     this.sensorManager = sensorManager; 

     if(sensorManager.getSensorList(Sensor.TYPE_GRAVITY).size() > 0){ 
      sensorManager.registerListener(
        this, 
        sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY), 
        SensorManager.SENSOR_DELAY_GAME 
      ); 
     } else if(sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() > 0) { 
      rollingAverage[0] = new ArrayList<Float>(); 
      rollingAverage[1] = new ArrayList<Float>(); 
      rollingAverage[2] = new ArrayList<Float>(); 

      sensorManager.registerListener(
        this, 
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), 
        SensorManager.SENSOR_DELAY_GAME 
       ); 
     } 

    } 

    @Override 
    public void onSensorChanged(SensorEvent event) { 
     if (event.sensor.getType()==Sensor.TYPE_GRAVITY){ 
      gravity.z = event.values[0]; 
      gravity.x = event.values[1]; 
      gravity.y = - event.values[2]; 
     } 
     else if (event.sensor.getType()==Sensor.TYPE_ACCELEROMETER) { 
      //For whatever reason, my Samsung only has "Accelerometer" 
      // But it is incredibly rough, so attempting to smooth 
      // it out with rolling averages. 
      rollingAverage[0] = roll(rollingAverage[0], event.values[0]); 
      rollingAverage[1] = roll(rollingAverage[1], event.values[1]); 
      rollingAverage[2] = roll(rollingAverage[2], -event.values[2]); 

      gravity.z = averageList(rollingAverage[0]); 
      gravity.x = averageList(rollingAverage[1]); 
      gravity.y = averageList(rollingAverage[2]); 
     } 
    } 

    public List<Float> roll(List<Float> list, float newMember){ 
     if(list.size() == MAX_SAMPLE_SIZE){ 
      list.remove(0); 
     } 
     list.add(newMember); 
     return list; 
    } 

    public float averageList(List<Float> tallyUp){ 

     float total=0; 
     for(float item : tallyUp){ 
      total+=item; 
     } 
     total = total/tallyUp.size(); 

     return total; 
    } 

    @Override 
    public void onAccuracyChanged(Sensor sensor, int accuracy) { 

    } 
} 

class Vector3 { 
    float x; 
    float y; 
    float z; 
} 
+0

Спасибо за это, он вроде как спас мне жизнь. –

+0

Можете ли вы использовать это для магнитного датчика? – 2017-01-12 03:38:41

+0

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