AndroidとiPhoneの加速度計アプリの実装方法を比較してみる
大きな違いとしてはiPhoneの場合加速度計のみ管理しているのに対してAndroidは他のセンサー(温度計や磁気センサーなど)と一括りにして管理しています。またiPhoneでは加速度計オブジェクトを直接取得できるのに対しAndroidでは加速度計オブジェクトを取得することができません(Android1.5からSensorクラスが追加され加速度計オブジェクトを取得できるようになりました)。
iPhoneの場合
- UIAccelerometerDelegateプロトコルを実装したクラスに加速度イベント(accelerometer:didAccelerate:メソッド)を実装する
- UIAccelerometer#sharedAccelerometerメソッドで加速度計のオブジェクトを取得する
- UIAccelerometer#delegateを設定すると加速度イベントが発生する
ローパスフィルター(重力の影響だけが残る)のサンプル
@interface HogeViewController : UIViewController <UIAccelerometerDelegate> { UIAccelerationValue accelX, accelY, accelZ; } @end #define kFilteringFactor 0.1 @implementation HogeViewController -(id)init { self = [super init]; if (!self) { return nil; } [self configureAccelerometer]; return self; } -(void)configureAccelerometer { // 加速度オブジェクトの初期化 UIAccelerometer *theAccelerometer = [UIAccelerometer sharedAccelerometer]; theAccelerometer.updateInterval = 1 / 50; theAccelerometer.delegate = self; } // 加速度イベント - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { UIAccelerationValue x, y, z; x = acceleration.x; y = acceleration.y; z = acceleration.z; // ローパスフィルター(重力の影響だけが残る) accelX = (x * kFilteringFactor) + (accelX * (1.0 - kFilteringFactor)); accelY = (y * kFilteringFactor) + (accelY * (1.0 - kFilteringFactor)); accelZ = (z * kFilteringFactor) + (accelZ * (1.0 - kFilteringFactor)); } @end
ハイパスフィルター(重力の影響が取り除かれる=瞬間的な加速度がわかる)のサンプル
ローパスの値をフィールド変数に退避するのがポイントです。Accessing Accelerometer Eventsに書いてある方法だとz軸に0.5の加速度がかかってしまいます(robaさんご指摘有り難うございました)。iPhone Dev CenterのサンプルプログラムAccelerometerGraphに正しいハイパスフィルターの方法が書いてありました。
@implementation HogeViewController // ローパスと同じなので省略 // 加速度イベント - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { UIAccelerationValue x, y, z; x = acceleration.x; y = acceleration.y; z = acceleration.z; // はじめにローパスの値を求める accelX = x * kFilteringFactor + accelX * (1.0 - kFilteringFactor); accelY = y * kFilteringFactor + accelY * (1.0 - kFilteringFactor); accelZ = z * kFilteringFactor + accelZ * (1.0 - kFilteringFactor); // ハイパスフィルター(重力の影響が取り除かれる=瞬間的な加速度がわかる) UIAccelerationValue highX, highY, highZ; highX = x - accelX; highY = y - accelY; highZ = z - accelZ; } @end
Android1.0,1.1系の場合
- SensorListenerを実装したクラスを定義する
- Activity#getSystemServiceでSensorManagerオブジェクトを取得する
- リスナーをセットする
public class AccelerometerActivity extends Activity implements SensorListener { private SensorManager sensorManager; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); } protected void onResume() { super.onResume(); sensorManager.registerListener(this, SensorManager.SENSOR_ACCELEROMETER | SensorManager.SENSOR_ORIENTATION, SensorManager.SENSOR_DELAY_FASTEST); } public void onSensorChanged(int sensor, float[] values) { switch(sensor) { // 傾き(角度が得られる) case SensorManager.SENSOR_ORIENTATION: : break; // 加速度 case SensorManager.SENSOR_ACCELEROMETER: : break; } } }
Android1.5系の場合
- SensorEventListenerを実装したクラスを定義する
- Activity#getSystemServiceでSensorManagerオブジェクトを取得する
- リスナーをセットする
public class SensorActivity extends Activity implements SensorEventListener { // SensorManagerのインスタンス private SensorManager sensorManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // SensorManagerのインスタンスを取得 sensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE); setContentView(R.layout.main); } @Override protected void onResume() { super.onResume(); List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); // リスナーの登録 for (Sensor s : sensors) { sensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_NORMAL); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // } @Override public void onSensorChanged(SensorEvent e) { switch(e.sensor.getType()) { // 加速度 case Sensor.TYPE_ACCELEROMETER: : break; // 傾き case Sensor.TYPE_ORIENTATION: : break; } } @Override protected void onStop() { super.onStop(); sensorManager.unregisterListener(this); } }