My Photo

« BS11でエスカフローネ再放送 | Main | 靴が小さかった »

October 09, 2011

Androidアプリ開発メモ025:モーションセンサー その2

前のモーションセンサーに関する記事でハイパスフィルタ、ローパスフィルタについては少し触れたが、正確に理解していたわけではないので、Android SDK グループのディスカッションに投稿して聞いた。
下記ブログがわかりやすいかも。まあ、離散数学(に限らず数学全般)がわかっていない自分としては完全に理解したとはとても言いがたいが^^;

加速度センサ と ローパスフィルタ (波形) | アンドロイドな日々

自分は前述のようにGoogleグループのAndroid SDK グループに入ったが、Googleグループにはほかに日本Androidの会ってのもある。というか、後者のほうがメンバーが全然多い。
日本Androidの会の方にも入っておこう^^;

センサの取得
まずSensorManagerをContext#getSystemSevice()で取得する。

android.content.Contextクラス
public abstract Object getSystemService(String name)
nameによってシステムレベルサービスのハンドルを返す。
name:センサーマネージャを取得する場合はContext.SENSOR_SERVICE。
ほかにWINDOW_SERVICE,LAYOUT_INFLATER_SERVICE,ACTIVITY_SERVICE,POWER_SERVICE,NOTIFICATION_SERVICEなどなど。
戻り値:サービス。nameが存在しない場合はnull
SensorManagerを取得したら、それからgetDefaultSensor()かgetSensorList()でandroid.hardware.Sensorを取得する。
android.hardware.SensorManagerクラス
public Sensor getDefaultSensor(int type)
与えられたタイプのためのデフォルトセンサーを得るためにこのメソッドを使う。返されたセンサーは複合センサーで、そのデータは平均化またはフィルタリングされている可能性があることに注意。もし生の線さにアクセスする必要があれば、getSensorListを使え。
type:要求されるセンサのタイプ。
Sensor.TYPE_ACCELEROMETER:加速度センサ
Sensor.TYPE_ORIENTATION:向き、傾きセンサ。廃止予定
戻り値:要求されたタイプとマッチするデフォルトセンサ。
android.hardware.SensorManagerクラス
public List<sensor>getSensorList(int type)
利用できるセンサのリストを得るために使う。
WEBに載ってるサンプルコードではgetSensorList()でセンサーの配列を取得して先頭のセンサーを使うものが多いが、getDefaultSensor()のソースを見ると中でgetSensorList()を呼んでリストの先頭のセンサーを返しているだけ(リストが空ならnullを返す)なので、現状では簡単にgetDefaultSensor()を使えばいいと思う。

SensorEventListener
センサーのイベントリスナandroid.hardware.SensorEventListenerインタフェースは次の2つのメソッドを持つ。

public abstract void onSensorChanged(SensorEvent event)
センサーの値が変化した際に呼ばれる。

public abstract void onAccuracyChanged(Sensor sensor, int accuracy)
センサーの精度が変化した際に呼ばれる。
SensorManagerに対するリスナの登録・解除は以下のメソッドを使用する。
android.hardware.SensorManagerクラス
public boolean registerListener (SensorEventListener listener, Sensor sensor, int rate) 
センサーにイベントを登録する。
rate:センサイベントが通知されるレート。これはただのシステムへのヒントである。イベントは指定されたレートより速くまたは遅く受信される可能性がある。普通はより速く受信される。値はSENSOR_DELAY_NORMAL, SENSOR_DELAY_UI, SENSOR_DELAY_GAME, or SENSOR_DELAY_FASTESTのうちの1つか、イベント間の求められる遅延(単位はマイクロ秒)でなければならない。
rateの値は以下の規定値がmsecでの指定となる。
SENSOR_DELAY_NORMAL:画面の向きの変化に適したレート(デフォルト)
SENSOR_DELAY_UI:ユーザーインタフェースに適したレート
SENSOR_DELAY_GAME:ゲーム適したレート
SENSOR_DELAY_FASTEST:可能限り速く
android.hardware.SensorManagerクラス
public void unregisterListener (SensorEventListener listener, Sensor sensor) 
センサーからイベントを登録解除する。
実機(Xperia ray)でSENSOR_DELAY_NORMALの場合、200ミリ秒毎にセンサイベントが発生する。

実装例:

public void onCreate(Bundle savedInstanceState) {
  // ビューの処理とか
  
  // センサーマネージャの取得
  sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
  
  // センサーの取得
  accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
  orientation = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);	// TYPE_ORIENTATIONは廃止予定
}

@Override
protected void onResume() {
  super.onResume();
  
  // リスナの登録
  if (accelerometer != null) {
    sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
  }
  if (orientation != null) {
    sensorManager.registerListener(this, orientation, SensorManager.SENSOR_DELAY_NORMAL);
  }
}

@Override
protected void onStop() {
  super.onStop();
  
  // リスナの登録解除
  if (accelerometer != null) {
    sensorManager.unregisterListener(this, accelerometer);
  }
  if (orientation != null) {
    sensorManager.unregisterListener(this, orientation);
  }
}

/**
 * センサの値の変更時の処理
 */
@Override
public void onSensorChanged(SensorEvent event) {
  // 加速度の処理
  if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
    // X軸加速度
    gravity[0] = ALPHA * gravity[0] + (1 - ALPHA) * event.values[0];
    acceleration[0] = event.values[0] - gravity[0];
    // Y軸加速度
    gravity[1] = ALPHA * gravity[1] + (1 - ALPHA) * event.values[1];
    acceleration[1] = event.values[1] - gravity[1];
    // Z軸加速度
    gravity[2] = ALPHA * gravity[2] + (1 - ALPHA) * event.values[2];
    acceleration[2] = event.values[2] - gravity[2];
  }
  
  // 方位角、ピッチ、ロールの取得
  if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
    azimuth  = event.values[0];	// アジマス(方位角)
    pitch = event.values[1];	// ピッチ
    roll = event.values[2];	// ロール
  }
  
  // TODO:表示するとか何か
}

/**
 * センサの精度変更時の処理
 */
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
  // TODO:何か処理
}
しつこいようだけど自分の数学の知識ゼロなので、以下の記述の信頼性もゼロです^^;

加速度はセンサの生値をローパスフィルタ(指数加重平均)を通して重力加速度を取得して、生の値からそれを引いて重力加速度の影響を除外した加速度を算出しているつもり。
ローパスフィルタの処理は重力加速度を算出されると思うのだが、掲示板でそういうことでしょ?と聞いたら、
「違うと思う。単に高周波をカットしているだけだ。」
と言われた。高周波をカットした値を加速度として使えと。
しかし、APIリファレンスのSensorEventに載っているサンプルコードではローパスフィルタを通して重力を算出しているように思えたけどなあ。
ローパスフィルタを通した値は、重力の影響だけ強く残った、実際の加速度とは大きく乖離した値になると思うんだけど。
方位の方は生の値をそのまま使っている。これも加重平均を取った方がいいと掲示板で言われたが、それだと値が収束するまですごい時間がかかる。粘性の高い油の中に磁石があるみたい。方位知るのに5秒も10秒も待たされるのは、どうもなあ。

« BS11でエスカフローネ再放送 | Main | 靴が小さかった »

Androidアプリ開発」カテゴリの記事

Comments

Post a comment

(Not displayed with comment.)

TrackBack

TrackBack URL for this entry:
http://app.cocolog-nifty.com/t/trackback/26461/52949473

Listed below are links to weblogs that reference Androidアプリ開発メモ025:モーションセンサー その2:

« BS11でエスカフローネ再放送 | Main | 靴が小さかった »

April 2017
Sun Mon Tue Wed Thu Fri Sat
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof