Android Sensors 復習 #1 加速度センサ

2013.05.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。ずいぶんと長い間、迷いの森(E○○el方眼)に二ヶ月ほど迷い込んでいました。こむろです。遅ればせながら、自分もIntelliJ+Android & Scalaな環境を整えました。楽しい!Cool!IntelliJ IDEA!

はじめに

センサーは、現実世界の情報を観測し、仮想世界へ入力するためのInterfaceの一つです。カメラなんかも現実世界の画像情報を仮想世界へ取り込むための観測センサーととらえればセンサーの一種でしょう。ここ最近、現実情報を入力するデバイスはとても身近になってきました。スマートフォンが広く普及したことで、人々は大量のセンサーデバイスが搭載された端末を肌身離さず持ち歩いています。これら大量のセンサーデバイスの情報を蓄積し、処理すればその端末の所有者が今どういう状況にあるのかという理解できるようになるかもしれません

センサーの数、処理できるデータの量、膨大なデータを蓄積するストレージなど、まだまだ問題は多々ありますが、その辺の問題も徐々にクリアされてきているので、現実世界 ⇒ 仮想世界への入り口は確実に広がっています

次は、仮想世界での処理出力結果を現実世界へReactさせるフェーズでしょうか。ロボットなんかはその一つですね。そう考えると人間の身体というのは、非常に精密で多量なセンサーを大量に積んでいる上に、内部処理した出力結果を現実に出力する手足というIFを持っているわけで・・・、つまり、人間ってよくできてんな。話が逸れすぎました

Androidに搭載されてるセンサーたち

  • 加速度センサ-
  • 地磁気センサー
  • 圧力センサー
  • 照度センサー
  • 重力センサー(APIレベル9~)
  • ジャイロスコープ
  • 近接センサー
  • 等々

SensorEvent#values

他にもNFCやらWifiやらGPSやらもあります *1

Androidにおいてセンサ情報を扱うAPIは、かなり初期のころから存在します。APIレベル3からその存在を確認できるものもあるので、現存するほぼ全ての端末に適用できる技術です(一部除く)。ただ、全てのセンサーが全ての端末に搭載されているわけではないので、プログラムからアクセスする前にセンサーの存在確認は必要です

試しに、自分の所有端末であるグローバル版のXPERIA Zではどんなセンサーが搭載されているかを見てみます

device-2013-05-14-120220

あれ・・・予想より多いですね。同じ用途のセンサーでも異なるものがいくつか搭載されてるようです(知らなかった・・・)

まずは、これらのセンサーの基本的な使い方の復習をしていきます

第一回「加速度センサー」

加速度センサーは端末にかかる加速度を検知することができます。加速度が登場するのは高校の古典物理あたりでしょうか。加速度は、物体の速度が変化する時に観測されるものなので、速度ではないのでご注意を。加速度の単位は「m/s^2」。そんなもの忘れたという方はこちらを参照されると良いかもしれません

使い方(大まかな手順)

  1. センサーマネージャを取得する
  2. センサーマネージャから加速度センサー情報を検知するSensorオブジェクトを取り出す
  3. Sensorオブジェクトにセンサ情報が変化した際に、どんなことをしたいかを定義したリスナを作成する
  4. センサーオブジェクトとリスナを結びつける
  5. 計測する

一つ一つ見ていきます

まずは、センサマネージャの取得

public class AccelerometerSampleActivity extends Activity {

    /** センサーマネージャオブジェクト */
    private SensorManager mSensorManager;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // センサーマネージャを獲得する
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    }
}

続いて加速度センサオブジェクトの取得

/** 加速度センサーオブジェクト */
private Sensor mAccelerometerSensor;

public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);

	// センサーマネージャを獲得する
	mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

	// マネージャから加速度センサーオブジェクトを取得
	mAccelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}

加速度センサをゲットできました∩( ・ω・)∩ <ばんじゃーい> だがしかし、このままでは使えません。センサ情報が更新された場合の動作を記述したリスナを登録します

センサ情報の更新を検知するInterfaceを実装する

まずは、センサ情報の更新を検知するために、SensorEventListenerを実装したクラスが必要になります。実装するInterfaceはSensorEventListenerになります。今回は実行するActivityに実装します

public class AccelerometerSampleActivity extends Activity implements SensorEventListener {

    /** センサーマネージャオブジェクト */
    private SensorManager mSensorManager;

    /** 加速度センサーオブジェクト */
    private Sensor mAccelerometerSensor;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // センサーマネージャを獲得する
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

        // マネージャから加速度センサーオブジェクトを取得
        mAccelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        //To change body of implemented methods use File | Settings | File Templates.
    }
}

実装が必要なメソッドは次の二つ

onSensorChanged はセンサーの値が更新された際にコールされます。onAccuracyChanged はセンサーの精度が変更された場合にコールされるようです

@Override
public void onSensorChanged(SensorEvent event) {
}

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

値を取得する

センサー情報を利用したいので、onSensorChangedの方に値を取得するコードを記述します。ここではX軸、Y軸、Z軸の加速度を取得してみます

@Override
public void onSensorChanged(SensorEvent event) {
     // 加速度センサの場合、以下の処理を実行
     if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
         StringBuilder builder = new StringBuilder();

         // 数値の単位はm/s^2
         // X軸
         float x = event.values[0];
         // Y軸
         float y = event.values[1];
         // Z軸
         float z = event.values[2];

         builder.append("X : " + (x) + "\n");
         builder.append("Y : " + (y) + "\n");
         builder.append("Z : " + (z) + "\n");

         // Logに出力
         Log.d(TAG, builder.toString());
     }
}

リスナを登録してSensorEventをキャッチ

しかし、このままでは使えません。センサ情報が更新された場合の動作を記述したリスナを登録してあげましょう

@Override
protected void onResume() {
     super.onResume();

     // 200msに一度SensorEventを観測するリスナを登録
     mSensorManager.registerListener(this, mAccelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
}

@Override
protected void onPause() {
     super.onPause();

     // 非アクティブ時にSensorEventをとらないようにリスナの登録解除
     mSensorManager.unregisterListener(this);
}

SensorManger#registerListenerの引数は、以下のようなパラメータを指定します

  1. 第一引数は、センサー情報が更新した際の動作を定義したリスナ(SensorEventListener)を指定します。
    今回は、Activity自身に実装してあるので、thisを指定しています
  2. 第二引数は、どのセンサー情報を観測するかの対象を指定します。
    今回は加速度センサーを監視したいので、加速度センサーオブジェクトを指定します
  3. 第三引数は、センサー情報の更新の精度を指定します。この精度が細かければ細かいほど、情報の更新頻度が高まるためセンサ情報がより細かく観測できます。それぞれ用途に応じて変更します。
    今回は、センサーの情報更新制度はSensorManager.SENSOR_DELAY_NORMALを指定します。これはおよそ200msの更新頻度でSensorEventをキャッチします

準備完了

値が取れました。valuesの配列にどのような値が格納されているかセンサーによって異なります。詳しくは公式のリファレンスを参照してください。それぞれセンサーで検出した生のデータがfloatの形で格納されているのが確認できます

動作確認

accelerate

テーブルなどの平らな部分の上においてみます。Z軸方向に常に加速度がかかってるようです。種明かしをするまでもないですが、これは地球の重力加速度になります。およそ9.8前後の加速度を保ってうろうろしているはずです。ちなみにこれが0を示す場合は、Free Fall状態。要するに自然落下している状態です

これを使えば、端末の落下時に「今わたしのスマートフォンが落下しています」という辞世の句をTwitterに投稿することができるかもしれませんね(結構な高さが必要そうですが)

ソースコード一式

少し(かなり?)手を加えてありますが、ソースコードはこちらにあります。よろしければ参考にしてください

com4dc - Github

つぎへ

今回のサンプルで作成した加速度センサーの値は、ノイズがかなり載っています。実際使うにあたってどの程度の精度を求めるかによりますが、より精度の高いデータが必要な場合はノイズ除去が必要です。次は、ノイズ除去の際に利用されるフィルタあたりをさっとまとめマス

それではみなさんごきげんよう

参考

脚注

  1. まあ、これはセンサーかと言われると?マークが付きますが