スマホに搭載されている加速度センサーを使うと、スマホを傾けると球が転がるようなアプリを作ることができます。これはMEMSと呼ばれる半導体を使っているのですね
2021.2.1
Accelerometer
加速度センサーは端末のx, y, z軸方向の加速度を計測します。単位[m/s²]
実際(?)にその動きをAndroid Studioのemulatorから加速度センサーの動きを見てみたいと思います。
Virtuial Sensors
emulatorを立ち上げて右バーのmore「…」をクリックするとExtended controlsダイアログが表れます。
「Virtual sensors」を選択、右サイドに端末が現れ、その上にある「Device Pose」をセットして、画面のスマホをマウスでドラッグすると「Rotation」によって「Resulting values」にx,y,z方向のセンサー出力が表示されます。
加速度なので動かす速度の変化で値が変わるのが分かります。
こちらはGoogleのSensorで説明されているTest with the Android Emulatorの動画です。
Coding
codingに移ります。実装方法を簡単に説明すると
- SensorEventListener の設定
- SensorManager のインスタンス生成
- registerListener の登録
- onSensorChanged でセンサー値の受け取り
となります。以下のコードはAndroid Studioから取り出せるSampleの中の「Accelemrometer Play」から切り出して簡単にしたものです。
センサー情報を取得する部分はセンサーのデバイス名やメーカー名などでアプリを作る時にはあまり関係ないので省いてもいいでしょう。
MainActivity.java
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
//package your.package.name import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.widget.TextView; public class MainActivity extends AppCompatActivity implements SensorEventListener { private SensorManager sensorManager; private TextView textView, textInfo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Get an instance of the SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); textInfo = findViewById(R.id.text_info); // Get an instance of the TextView textView = findViewById(R.id.text_view); } @Override protected void onResume() { super.onResume(); // Listenerの登録 Sensor accel = sensorManager.getDefaultSensor( Sensor.TYPE_ACCELEROMETER); sensorManager.registerListener(this, accel, SensorManager.SENSOR_DELAY_NORMAL); //sensorManager.registerListener(this, accel, SensorManager.SENSOR_DELAY_FASTEST); //sensorManager.registerListener(this, accel, SensorManager.SENSOR_DELAY_GAME); //sensorManager.registerListener(this, accel, SensorManager.SENSOR_DELAY_UI); } // 解除するコードも入れる! @Override protected void onPause() { super.onPause(); // Listenerを解除 sensorManager.unregisterListener(this); } @Override public void onSensorChanged(SensorEvent event) { float sensorX, sensorY, sensorZ; if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { sensorX = event.values[0]; sensorY = event.values[1]; sensorZ = event.values[2]; String strTmp = "加速度センサー\n" + " X: " + sensorX + "\n" + " Y: " + sensorY + "\n" + " Z: " + sensorZ; textView.setText(strTmp); showInfo(event); } } // (お好みで)加速度センサーの各種情報を表示 private void showInfo(SensorEvent event){ // センサー名 StringBuffer info = new StringBuffer("Name: "); info.append(event.sensor.getName()); info.append("\n"); // ベンダー名 info.append("Vendor: "); info.append(event.sensor.getVendor()); info.append("\n"); // 型番 info.append("Type: "); info.append(event.sensor.getType()); info.append("\n"); // 最小遅れ int data = event.sensor.getMinDelay(); info.append("Mindelay: "); info.append(data); info.append(" usec\n"); // 最大遅れ data = event.sensor.getMaxDelay(); info.append("Maxdelay: "); info.append(data); info.append(" usec\n"); // レポートモード data = event.sensor.getReportingMode(); String stinfo = "unknown"; if(data == 0){ stinfo = "REPORTING_MODE_CONTINUOUS"; }else if(data == 1){ stinfo = "REPORTING_MODE_ON_CHANGE"; }else if(data == 2){ stinfo = "REPORTING_MODE_ONE_SHOT"; } info.append("ReportingMode: "); info.append(stinfo); info.append("\n"); // 最大レンジ info.append("MaxRange: "); float fData = event.sensor.getMaximumRange(); info.append(fData); info.append("\n"); // 分解能 info.append("Resolution: "); fData = event.sensor.getResolution(); info.append(fData); info.append(" m/s^2\n"); // 消費電流 info.append("Power: "); fData = event.sensor.getPower(); info.append(fData); info.append(" mA\n"); textInfo.setText(info); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } } |
activity_main.xml
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 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#afe" tools:context=".MainActivity"> <TextView android:id="@+id/text_info" android:layout_marginTop="40dp" android:layout_marginStart="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/text_view" android:layout_marginTop="20dp" android:layout_marginStart="20dp" android:textSize="20sp" android:textColor="#00f" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> |
ちなみに、加速度センサーを試すとどうしても傾けたりしますが、そうすると画面が回転してしまうことがあります。
android:screenOrientation=”portrait”
を使うと画面の回転を「portrait」に固定できます。
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testaccelerometer"> <application ... > <activity android:name=".MainActivity" android:screenOrientation="portrait" > <intent-filter> ... </intent-filter> </activity> </application> </manifest> |
Nexus 5X での結果です。
スマホをディスクの上に置くと、Z値が10近い値となりますがこれは重力のGのせいです。
この加速度データはMEMSからの生データなのでノイズに反応しすぎて使いづらいかもしれません。使う用途によってフィルターを入れるのがいいでしょう。
また、getReportingMode()はそれによって返ってくるタイミングが変わるので実装上は気を付ける必要があると思います。
samplingPeriodUs
センサーのサンプリング期間はスペックでは
1 2 3 |
registerListener (SensorEventListener listener, Sensor sensor, int samplingPeriodUs) |
とあるのでこの samplingPeriodUs にusecオーダーで値を設定すればいいのかと思ったのですがそうではないようでした。Nuxus 5Xでのテストでは
input | 10 | 100 | 1,000 | 5,000 | 8,000 | 10,000 | 15,000 | 20,000 | 50,000 | 100,000 |
period[ms] | 2.5 | 2.5 | 2.5 | 5.0 | 5.0 | 10.0 | 10.0 | 20.0 | 20.0 | 20.0 |
となりましたこれは階段状に変化するもので設定値に対して比例していません。
結局これらを使えということでしょうか
SENSOR_DELAY_FASTEST | 最速通知頻度(デバイスのスペック次第) |
SENSOR_DELAY_NORMAL | 通常のサンプリング |
SENSOR_DELAY_GAME | ゲームに適した間隔 |
SENSOR_DELAY_UI | ゲームではないアプリ向け |
Nexus 5Xで使っているAccelerometerはBosche BMI10でしたので
あくまでこの端末でこのデバイスでのケースです。
因みにこのdatasheetによれば最小遅れは2.5msecのようなので実験と合っています。
次はこれを使って球ころがしをしてみます。
関連ページ:
Sensor 一覧を取得する
加速度センサー:Accelerometer
加速度センサーで球ころがし
Gyroscope ジャイロセンサー
Reference:
Sensor
SensorEvent
SensorManager