Android:加速度計で速度と距離を測定する

グーグルの携帯電話を手に入れてから、私の頭の中を定期的にさまよっています。「この携帯電話で何をするのがとても楽しいだろうか?」 もちろん、加速度を測定してください! そして、結果として、速度と移動距離を計算します。 もちろん、加速度計のみを使用すると、測定にいくつかの制限が課せられます。まず、動きが直線的である必要があり、次に、空間内の装置の向きが変わらないようにし、第三に、測定を開始する前にセンサーを較正することをお勧めします。 すぐに言わなければならない-これらの要件を緩和する方法がありますが、それについては後で詳しく説明します。



主な質問は、いつものように、「なぜ?」です。 GPSがあるのはなぜですか? まあ、本当のポイント。 ただし、GPSはどこでも機能しませんが、加速度計です-電話であなたと一緒です。 たとえば、地下鉄で衛星をキャッチしようとしましたか?..



「理由」がわかったら、「方法」に進みます...



加速の変化に対応するには、SensorEventListenerインターフェースをどこかに実装する必要があります。 どうすればいいかまだわかっていないので、抽象クラスを作成します

public abstract class Accelerometer implements SensorEventListener { protected float lastX; protected float lastY; protected float lastZ; public abstract Point getPoint(); public void onAccuracyChanged(Sensor arg0, int arg1) { } }
      
      





同時に、センサーカウンターの読み取り値を保存するクラス:

 public class Point { private float x = 0; private float y = 0; private float z = 0; private int cnt = 1; public float getX() { return x/(float)cnt; } public float getY() { return y/(float)cnt; } public float getZ() { return z/(float)cnt; } public Point(float x, float y, float z, int cnt) { this.x = x; this.y = y; this.z = z; this.cnt = cnt; } }
      
      







そして次に何をすべきかを考えてください。 SENSOR_DELAY_GAMEモードでセンサーからの情報を更新する期間は約20ミリ秒です。 多くの場合、これで十分です。タスクではこれは必要ありません。 一方、測定値の取得頻度を減らすと、「放出」に陥り、精度が低下するリスクがあります。 たとえば、最後の1秒間に何らかの形で平均加速度値を定期的に受信することは論理的です。 配列を保存して平均値を計算するのは費用がかかります。取得したすべての値を加算して数で割るのははるかに簡単です。 また、まだ実装されていないキャリブレーションであるdX、dY、dZも提供しています。

結果は次のとおりです。

 public class XYZAccelerometer extends Accelerometer { private static final int BUFFER_SIZE = 500; // calibration private float dX = 0; private float dY = 0; private float dZ = 0; // buffer variables private float X; private float Y; private float Z; private int cnt = 0; // returns last SenorEvent parameters public Point getLastPoint(){ return new Point(lastX, lastY, lastZ, 1); } // returrns parameters, using buffer: average acceleration // since last call of getPoint(). public Point getPoint(){ if (cnt == 0){ return new Point(lastX, lastY, lastZ, 1); } Point p = new Point(X, Y, Z, cnt); reset(); return p; } // resets buffer public void reset(){ cnt = 0; X = 0; Y = 0; Z = 0; } public void onSensorChanged(SensorEvent se) { float x = se.values[SensorManager.DATA_X] + dX; float y = se.values[SensorManager.DATA_Y] + dY; float z = se.values[SensorManager.DATA_Z] + dZ; lastX = x; lastY = y; lastZ = z; X+= x; Y+= y; Z+= z; if (cnt < BUFFER_SIZE-1) { cnt++; } else { reset(); } } public void setdX(float dX) { this.dX = dX; } public void setdY(float dY) { this.dY = dY; } public void setdZ(float dZ) { this.dZ = dZ; } }
      
      







許可を得て、センサーのキャリブレーション方法の説明をスキップします。 しばらくの間悔い改めを取り除いてから、XYZ加速度計で適切なdX、dY、dZを設定する必要があると言うだけで十分です。 この手順を無視することはできません。 私たちが寝ている間 、重力の加速度は絶えず作用しており、センサーがそれを測定します。



最も重要なのは、インターバルのモーションパラメータを保存および計算するためのクラスを取得することです。



 public class MeasurePoint { private float x; private float y; private float z; private float speedBefore; private float speedAfter; private float distance; private float acceleration; private long interval; public MeasurePoint(float x, float y, float z, float speedBefore, long interval) { this.x = x; this.y = y; this.z = z; this.speedBefore = speedBefore; this.interval = interval; speedAfter = 0; calc(); } private void calc(){ //Acceleration as projection of current vector on average acceleration = Math.sqrt(this.x*this.x+this.y*this.y*+this.z*this.z); float t = ((float)interval / 1000f); speedAfter = speedBefore + acceleration * t; distance = speedBefore*t + acceleration*t*t/2; } // add getters }
      
      







そして、実験全体に関する情報を保存するクラス:



 public class MeasureData { // points from accelerometr private LinkedList accData; private LinkedList data; // timer interval of generating points private long interval; public MeasureData(long interval) { this.interval = interval; accData = new LinkedList (); data = new LinkedList (); } public void addPoint(Point p){ accData.add(p); } public void process(){ for(int i = 0; i < accData.size(); ++i){ Point p = accData.get(i); float speed = 0; if(i > 0){ speed = data.get(i-1).getSpeedAfter(); } data.add(new MeasurePoint(p.getX(), p.getY(), p.getZ(), speed, interval)); } } public float getLastSpeed(){ return data.getLast().getSpeedAfter(); } public float getLastSpeedKm(){ float ms = getLastSpeed(); return ms*3.6f; } }
      
      







すべてがシンプルで明確だと思います。 アクティビティでこれを使用するだけです...ところで、まだありません。 レイアウトから始めましょう:



 <serviceLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <serviceButton android:id="@+id/btn" android:text="TEST" android:layout_width="300px" android:layout_height="200px" android:onClick="onButtonTest" /> <serviceTextView android:id = "@+id/txt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text=":" /> <service/LinearLayout>
      
      







そしてコード:



 public class TestActivity extends Activity { static final int TIMER_DONE = 2; static final int START = 3; private StartCatcher mStartListener; private XYZAccelerometer xyzAcc; private SensorManager mSensorManager; private static final long UPDATE_INTERVAL = 500; private static final long MEASURE_TIMES = 20; private Timer timer; private TextView tv; private Button testBtn; int counter; private MeasureData mdXYZ; /** handler for async events*/ Handler hRefresh = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case TIMER_DONE: onMeasureDone(); String es1 = Float.toString(Math.round(mdXYZ.getLastSpeedKm()*100)/100f); tv.append(" END SPEED " + es1 + " \n"); enableButtons(); break; case START: tv.append(" START"); timer = new Timer(); timer.scheduleAtFixedRate( new TimerTask() { public void run() { dumpSensor(); } }, 0, UPDATE_INTERVAL); break; } } }; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tv = (TextView) findViewById(R.id.txt); testBtn = (Button) findViewById(R.id.btn); } @Override protected void onResume() { super.onResume(); tv.append("\n .."); mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); setAccelerometer(); setStartCatcher(); mSensorManager.registerListener(xyzAcc, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME); } @Override protected void onPause() { mSensorManager.unregisterListener(xyzAcc); super.onPause(); } public void onButtonTest(View v) { disableButtons(); mdXYZ = new MeasureData(UPDATE_INTERVAL); counter = 0; tv.setText(""); hRefresh.sendEmptyMessage(START); } void dumpSensor() { ++counter; mdXYZ.addPoint(xyzAcc.getPoint()); if (counter > MEASURE_TIMES) { timer.cancel(); hRefresh.sendEmptyMessage(TIMER_DONE); } } private void enableButtons() { testBtn.setEnabled(true); } private void setAccelerometer() { xyzAcc = new XYZAccelerometer(); mSensorManager.registerListener(xyzAcc, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI); } private void disableButtons() { testBtn.setEnabled(false); } private void onMeasureDone() { mdXYZ.process(); } }
      
      







以上です。 驚くべきことに、平坦な経路では、この方法は非常に優れた測定精度を提供します。



1つの実験のグラフを囲みます。青い線は加速度計によって計算された速度、赤い線はGPSから最大周波数で取得された速度です。 ブラックブロット-実験終了時の速度計の速度。



画像







All Articles