スマホのスピンやターンを検知するセンサーとしてGyroscopeがあります。
gyro driftを補正した TYPE_GYROSCOPE と
gyro drift補正の無いTYPE_GYROSCOPE_UNCALIBRATED があります。
2021.2.1
Gyroscope
Gyroscopeの座標系は加速度センサーと同じです。端末x, y, z軸の反時計方向での回転速度、角速度を計測します。単位は[rad/s]
Gyroscopeには2タイプあります
TYPE_GYROSCOPE_UNCALIBRATED はジャイロドリフトの補正がないセンサー出力です。ジャイロドリフトの補正がないと放置しておくとバイアスがかかったように徐々にずれていきます。
ただキャリブレーションによる値のジャンプがより滑らかで、この他のセンサー値との合算を考えるケースではこのセンサー値を使う方が一般的には役に立ちます。但し、工場出荷補正と温度補正は適用されます。
加速度センサーはAndroid Studioのemulatorを使ってemulateできましたが、Gyroscopeは使えないようです。
TYPE_GYROSCOPE
実装を簡単に説明すると
- SensorEventListener の設定
- SensorManager のインスタンス生成
- registerListener の登録
- onSensorChanged でセンサー値の受け取り
となります。実際ににやって見ましょう。
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 141 142 143 |
//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.util.Log; import android.widget.TextView; import java.util.Locale; 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 gyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); if(gyro != null){ sensorManager.registerListener(this, gyro, SensorManager.SENSOR_DELAY_UI); } else{ String ns = "No Support"; textView.setText(ns); } } // 解除するコードも入れる! @Override protected void onPause() { super.onPause(); // Listenerを解除 sensorManager.unregisterListener(this); } @Override public void onSensorChanged(SensorEvent event) { Log.d("debug","onSensorChanged"); if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) { float sensorX = event.values[0]; float sensorY = event.values[1]; float sensorZ = event.values[2]; String strTmp = String.format(Locale.US, "Gyroscope\n " + " X: %f\n Y: %f\n Z: %f",sensorX, sensorY, 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="#dfe" 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> |
また、スクリーンが回転すると面倒なので固定しておきます。
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" ... <application ... <activity android:name=".MainActivity" android:screenOrientation="portrait" ... > ... </activity> </application> </manifest> |
実機で動かしてみましょう
TYPE_GYROSCOPE_UNCALIBRATED
実装はTYPE_GYROSCOPEと同様です。
センサー出力にestimated driftがそれぞれx, y, z軸についてリポートされますが、その関係性はこのようになります。
calibrated_x ~= uncalibrated_x – bias_estimate_x
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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
//package your.package.name; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.hardware.SensorEvent; import android.hardware.Sensor; 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 gyro = sensorManager.getDefaultSensor( Sensor.TYPE_GYROSCOPE_UNCALIBRATED); if(gyro != null){ sensorManager.registerListener(this, gyro, SensorManager.SENSOR_DELAY_UI); } else{ String ns = "No Support"; textView.setText(ns); } } // 解除するコードも入れる! @Override protected void onPause() { super.onPause(); // Listenerを解除 sensorManager.unregisterListener(this); } @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE_UNCALIBRATED) { float sensorX = event.values[0]; float sensorY = event.values[1]; float sensorZ = event.values[2]; float sensorEDX = event.values[3]; float sensorEDY = event.values[4]; float sensorEDZ = event.values[5]; String strTmp = "Gyroscope\n" + " X: " + sensorX + "\n" + " Y: " + sensorY + "\n" + " Z: " + sensorZ + "\n\n" + " ドリフト予測\n" + " driftX: " + sensorEDX + "\n" + " driftY: " + sensorEDY + "\n" + " driftZ: " + sensorEDZ + "\n" ; 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) { } } |
レイアウトはTYPE_GYROSCOPEと同じです。
関連ページ:
Sensor 一覧を取得する
加速度センサー:Accelerometer
加速度センサーで球ころがし
Gyroscope ジャイロセンサー
Reference:
Sensor
SensorEvent
SensorManager
Motion Sensors