私が以前書いた、これ 、や、これ 、などのセンサーの投稿は、結構内容が古くなってしまいました。
これを調べていた頃はまだG1が出る前(9月)だったので、その当時のOpenIntents のSensorSimulatorの振る舞いや、当時のAPIリファレンスを見ながら書いたものです。しかし、実際にDev Phoneを購入して試してみるとDev Phoneで実際に取得できるセンサーの値がちょっと違っていることがわかりました。また加えてAPIに拡張や変更があったことも原因で、前に書いた記事を鵜呑みにすると、うまくいかないことがあります。ググってもAndroid SDKの加速度センサーを使った日本語の情報があまり出てこないようで、私のこのBLOGに辿り着かれる方が結構いらっしゃるので、ずっと修正したいと思っていました。
全て網羅するわけではないのですが、いくつかの差異をここに示そうと思います。もしかすると、G1特有の挙動かもしれず、別機種ではまた別の値となるかもしれませんが、ご参考までに。
加速度センサーの値:
以前は、1Gのときに1.0fを示すと書きましたが、仕様が変更になったようで、m/s^2のままです。なので、1Gのときは9.82fになるのだと思います。1Gのときに1.0にしたい場合はSensorManager.GRAVITY_EARTH で割ると出てきます。地球以外で使う場合は別の値で割ればいいと思います。
傾度センサーの値:
以前は、0番目からyaw, pitch, rollと思っていましたが、違うようです。通常のロールピッチヨー角の考え方とは異なります。0番目はyawでなく、代わりにAzimuthという方位を示す値が入ります。この値は0以上360未満の範囲を取ります。0は北、90は東、180は南、270は西です。この値を使うことで、方位だけなら磁気センサーの値を計算せずとも簡単に取ることが出来ます。傾きはpitchとrollのみで判断します。多分以前よりも簡単です。ロールピッチヨーの3つの値の組み合わせだと、どのような経緯を経て、今の傾きになったのかを追跡することが出来ます。それは確かに必要な局面はあるかもしれませんが、通常はその時点の傾きのスナップショットのみが欲しいことが殆どでしょうから、pitchとrollだけでも判断できますし、そのように判断した方が簡単だと思います。でも、加速度センサーの値から傾きは判断できますので、(方位は楽かもですが)傾度センサーの値は別に要らないかなあ、とも思います。
サンプル:
先日、Android SDK WG で、加速度センサーを使った簡単なサンプルを皆で作って遊びました。
作ったアプリの仕様は
・端末を振っているときに、背景の色を変えてください
・振るのを止めたら、背景の色を元(黒)に戻してください
・端末の傾きに応じて、画面に(縦、横、水平などの)文字を表示してください
という感じです。実装したアプリは、例えばこんな動きをします。
多分実装の方法は無数に考えられるのですが、Activityのひとつの実装例を丸ごと載せておきます。
package net.grandnature.android.sdkwg.examples.sensor;
import java.text.DecimalFormat;
import android.app.Activity;
import android.graphics.Color;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class Main extends Activity implements SensorListener {
View layout;
TextView accelerometerValue;
TextView orientationValue;
TextView filteredAccelerationValue;
TextView filteredOrientationValue;
TextView orientation;
SensorManager sensorManager;
static DecimalFormat format;
static {
format = new DecimalFormat();
format.applyLocalizedPattern("#0.000");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// --- views
setContentView(R.layout.main);
layout = findViewById(R.id.layout);
accelerometerValue = (TextView)findViewById(R.id.accelerometer_value);
orientationValue = (TextView)findViewById(R.id.orientation_value);
filteredAccelerationValue = (TextView)findViewById(R.id.filtered_acceleration_value);
filteredOrientationValue = (TextView)findViewById(R.id.filtered_orientation_value);
orientation = (TextView)findViewById(R.id.orientation);
// --- sensors
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
}
@Override
protected void onStop() {
sensorManager.unregisterListener(this);
super.onStop();
}
@Override
protected void onResume() {
super.onResume();
sensorManager.registerListener(this,
SensorManager.SENSOR_ACCELEROMETER |
SensorManager.SENSOR_ORIENTATION,
SensorManager.SENSOR_DELAY_FASTEST);
}
private float[] currentOrientationValues = {0.0f, 0.0f, 0.0f};
private float[] currentAccelerationValues = {0.0f, 0.0f, 0.0f};
public void onSensorChanged(int sensor, float[] values) {
switch(sensor) {
case SensorManager.SENSOR_ACCELEROMETER:
accelerometerValue.setText(convertFloatsToString(values));
// 傾き(ハイカット)
currentOrientationValues[0] = values[0] * 0.1f + currentOrientationValues[0] * (1.0f - 0.1f);
currentOrientationValues[1] = values[1] * 0.1f + currentOrientationValues[1] * (1.0f - 0.1f);
currentOrientationValues[2] = values[2] * 0.1f + currentOrientationValues[2] * (1.0f - 0.1f);
// 加速度(ローカット)
currentAccelerationValues[0] = values[0] - currentOrientationValues[0];
currentAccelerationValues[1] = values[1] - currentOrientationValues[1];
currentAccelerationValues[2] = values[2] - currentOrientationValues[2];
filteredAccelerationValue.setText(convertFloatsToString(currentAccelerationValues));
filteredOrientationValue.setText(convertFloatsToString(currentOrientationValues));
// 振ってる? 絶対値(あるいは2乗の平方根)の合計がいくつ以上か?
// 実装例
float targetValue =
Math.abs(currentAccelerationValues[0]) +
Math.abs(currentAccelerationValues[1]) +
Math.abs(currentAccelerationValues[2]);
if(targetValue > 22.0f)
layout.setBackgroundColor(Color.YELLOW);
else if(targetValue < 10.0f)
layout.setBackgroundColor(Color.BLACK);
// かたむきは?3つの絶対値(あるいは2乗の平方根)のうちどれがいちばんでかいか?
// 実装例
if(Math.abs(currentOrientationValues[0]) > 7.0f) {
orientation.setText("横");
} else if(Math.abs(currentOrientationValues[1]) > 7.0f) {
orientation.setText("縦");
} else if(Math.abs(currentOrientationValues[2]) > 7.0f) {
orientation.setText("水平");
} else {
orientation.setText("");
}
break;
case SensorManager.SENSOR_ORIENTATION:
orientationValue.setText(convertFloatsToString(values));
break;
default:
}
}
private String convertFloatsToString(float[] values) {
return
String.valueOf(format.format(values[0])) + ", " +
String.valueOf(format.format(values[1])) + ", " +
String.valueOf(format.format(values[2]));
}
public void onAccuracyChanged(int sensor, int accuracy) {
}
}
サンプルのEclipseプロジェクト:
Eclipseのプロジェクトごと圧縮してここ に置いています。
・ SensorExample.zip