Android プラットフォームには、デバイスの動きをモニタリングできるセンサーがいくつか用意されています。
可能なセンサーのアーキテクチャは、センサーのタイプによって異なります。
- 重力、直線加速度、回転ベクトル、大きな動き、歩数カウンタ、歩数検出センサーは、ハードウェア ベースまたはソフトウェア ベースです。
- 加速度計とジャイロスコープのセンサーは常にハードウェアベースです。
ほとんどの Android 搭載デバイスには加速度計が搭載されており、その多くはジャイロスコープを搭載しています。ソフトウェア ベースのセンサーは、データの取得に 1 つ以上のハードウェア センサーを使用することが多いため、使用可能かどうかはより変動します。デバイスに応じて、これらのソフトウェア ベースのセンサーは、加速度計と磁力計、またはジャイロスコープからデータを取得できます。
モーション センサーは、傾斜、シェイク、回転、スイングなどのデバイスの動きをモニタリングするのに役立ちます。動きは通常、直接的なユーザー入力(ゲーム内で車を操るユーザー、ゲーム内でボールをコントロールするユーザーなど)の反射ですが、デバイスが座っている物理的な環境(車を運転しながら一緒に移動するなど)を反映する場合もあります。最初のケースでは、デバイスの参照フレームまたはアプリの参照フレームを基準として動きをモニタリングします。2 番目のケースでは、世界の参照フレームを基準として動きをモニタリングします。モーション センサー自体は通常、デバイスの位置のモニタリングには使用されませんが、地磁気センサーなどの他のセンサーと併用して、世界の基準フレームに対するデバイスの位置を決定できます(詳しくは、位置センサーをご覧ください)。
すべてのモーション センサーは、SensorEvent
ごとにセンサー値の多次元配列を返します。たとえば、単一のセンサー イベントの間、加速度計は 3 つの座標軸の加速度データを返し、ジャイロスコープは 3 つの座標軸の回転速度データを返します。これらのデータ値は、他の SensorEvent
パラメータとともに float
配列(values
)で返されます。表 1 に、Android プラットフォームで使用可能なモーション センサーをまとめています。
表 1. Android プラットフォームでサポートされているモーション センサー。
センサー | センサー イベント データ | 説明 | 測定単位 |
---|---|---|---|
TYPE_ACCELEROMETER |
SensorEvent.values[0] |
x 軸に沿った加速度(重力を含む)。 | m/秒2 |
SensorEvent.values[1] |
y 軸に沿った加速度(重力を含む)。 | ||
SensorEvent.values[2] |
z 軸に沿った加速度(重力を含む)。 | ||
TYPE_ACCELEROMETER_UNCALIBRATED |
SensorEvent.values[0] |
バイアス補正なしの X 軸に沿って測定された加速度。 | m/秒2 |
SensorEvent.values[1] |
バイアス補正なしの Y 軸に沿って測定された加速度。 | ||
SensorEvent.values[2] |
バイアス補正なしの Z 軸に沿って測定された加速度。 | ||
SensorEvent.values[3] |
推定バイアス補正を適用した場合の X 軸に沿って測定された加速度。 | ||
SensorEvent.values[4] |
推定バイアス補正後の Y 軸に沿って測定された加速度。 | ||
SensorEvent.values[5] |
推定バイアス補正後の Z 軸に沿って測定された加速度。 | ||
TYPE_GRAVITY |
SensorEvent.values[0] |
x 軸に沿った重力。 | m/秒2 |
SensorEvent.values[1] |
y 軸に沿った重力。 | ||
SensorEvent.values[2] |
z 軸に沿った重力。 | ||
TYPE_GYROSCOPE |
SensorEvent.values[0] |
X 軸を中心とした回転速度。 | rad/秒 |
SensorEvent.values[1] |
y 軸を中心とした回転速度。 | ||
SensorEvent.values[2] |
Z 軸を中心とした回転速度。 | ||
TYPE_GYROSCOPE_UNCALIBRATED |
SensorEvent.values[0] |
X 軸を中心とした回転速度(ドリフト補正なし)。 | rad/秒 |
SensorEvent.values[1] |
y 軸を中心とした回転速度(ドリフト補正なし)。 | ||
SensorEvent.values[2] |
Z 軸を中心とした回転速度(ドリフト補正なし)。 | ||
SensorEvent.values[3] |
X 軸を中心とする推定ドリフト。 | ||
SensorEvent.values[4] |
y 軸を中心とする推定ドリフト。 | ||
SensorEvent.values[5] |
Z 軸を中心とする推定ドリフト。 | ||
TYPE_LINEAR_ACCELERATION |
SensorEvent.values[0] |
x 軸に沿った加速度(重力を除く)。 | m/秒2 |
SensorEvent.values[1] |
y 軸に沿った加速度(重力を除く)。 | ||
SensorEvent.values[2] |
z 軸に沿った加速度(重力を除く)。 | ||
TYPE_ROTATION_VECTOR |
SensorEvent.values[0] |
x 軸に沿った回転ベクトル成分(x × sin(サー/2))。 | 単位なし |
SensorEvent.values[1] |
y 軸に沿った回転ベクトルの成分(y * sin(z/2))。 | ||
SensorEvent.values[2] |
z 軸に沿った回転ベクトル成分(z × sin(z/2))。 | ||
SensorEvent.values[3] |
回転ベクトルのスカラー成分((cos(q/2)))。1 | ||
TYPE_SIGNIFICANT_MOTION |
なし | なし | なし |
TYPE_STEP_COUNTER |
SensorEvent.values[0] |
センサーがアクティブになっているときに、最後の再起動以降にユーザーが実行したステップ数。 | 歩 |
TYPE_STEP_DETECTOR |
なし | なし | なし |
1 スカラー コンポーネントは省略可能な値です。
回転ベクトル センサーと重力センサーは、モーションの検出とモニタリングに最もよく使用されているセンサーです。回転ベクトル センサーは特に汎用性が高く、ジェスチャーの検出、角度変化のモニタリング、相対的な向きの変化のモニタリングなど、動きに関連する幅広いタスクに使用できます。たとえば、ゲーム、拡張現実アプリ、2 次元または 3 次元のコンパス、カメラ手ぶれ補正アプリを開発する場合は、回転ベクトル センサーが理想的です。ほとんどの場合、加速度計や地磁気センサー、方向センサーを使用するよりも、これらのセンサーを使用するほうが適切です。
Android オープンソース プロジェクトのセンサー
Android オープンソース プロジェクト(AOSP)は、重力センサー、直線加速度センサー、回転ベクトル センサーの 3 つのソフトウェア ベースのモーション センサーを提供しています。これらのセンサーは Android 4.0 で更新され、安定性とパフォーマンスを向上させるため、他のセンサーに加えてデバイスのジャイロスコープを使用するようになりました。これらのセンサーを試す場合は、getVendor()
メソッドと getVersion()
メソッド(ベンダーは Google LLC、バージョン番号 3)を使用して識別できます。Android システムはこれら 3 つのセンサーをセカンダリ センサーと見なすため、ベンダーとバージョン番号でこれらのセンサーを識別する必要があります。たとえば、デバイス メーカーが独自の重力センサーを提供している場合、AOSP の重力センサーはセカンダリ重力センサーとして表示されます。これら 3 つのセンサーはすべてジャイロスコープに依存しています。デバイスにジャイロスコープがない場合、これらのセンサーは表示されず、使用できません。
重力センサーを使用する
重力センサーは、重力の方向と大きさを示す 3 次元ベクトルを提供します。通常、このセンサーは空間内のデバイスの相対的な向きを判断するために使用されます。次のコードは、デフォルトの重力センサーのインスタンスを取得する方法を示しています。
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
単位は加速度センサーで使用される単位(m/s2)と同じで、座標系は加速度センサーで使用されるものと同じです。
注: デバイスの静止時には、重力センサーの出力は加速度計の出力と同じになります。
直線加速度計を使用する
直線加速度センサーは、重力を除く各デバイスの軸に沿った加速度を表す 3 次元ベクトルを提供します。この値を使用してジェスチャー検出を実行できます。この値は、推測航法を使用する慣性ナビゲーション システムへの入力としても機能します。次のコードは、デフォルトの直線加速度センサーのインスタンスを取得する方法を示しています。
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
概念的には、このセンサーは以下の関係に従って加速度データを提供します。
linear acceleration = acceleration - acceleration due to gravity
このセンサーは通常、重力の影響を受けずに加速度データを取得する場合に使用します。たとえば、このセンサーを使用して車の速度を測定できます。直線加速度センサーには必ずオフセットがあるため、削除する必要があります。これを行う最も簡単な方法は、アプリケーションに調整ステップを構築することです。キャリブレーションの際に、デバイスをテーブルにセットするようユーザーに依頼し、3 つの軸すべてのオフセットを読み取ることができます。そして、加速度センサーの直接的な測定値からそのオフセットを減算して、実際の直線加速度を算出できます。
センサーの座標系は、加速度センサーで使用される座標系と測定単位(m/s2)と同じです。
回転ベクトル センサーを使用する
回転ベクトルは、角度と軸の組み合わせとしてデバイスの向きを表します。この軸では、デバイスが軸(x、y、または z)を中心としてある角度 Z だけ回転しています。次のコードは、デフォルトの回転ベクトル センサーのインスタンスを取得する方法を示しています。
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
回転ベクトルの 3 つの要素は、次のように表現されます。
![x*sin(q/2)、y*sin(q/2)、z*sin(q/2)](https://developer.android.com/static/images/guide/topics/sensors/rotational_vec.png?authuser=19&hl=ja)
ここで、回転ベクトルの大きさは sin(O/2) に等しく、回転ベクトルの方向は回転軸の方向と等しくなります。
![](https://developer.android.com/static/images/axis_globe.png?authuser=19&hl=ja)
図 1. 回転ベクトル センサーで使用される座標系。
回転ベクトルの 3 つの要素は、単位四元数の最後の 3 つの要素(cos(z/2)、x*sin(q/2)、y*sin(q/2)、z*sin(q/2))に等しくなります。回転ベクトルの要素は単位がありません。x 軸、y 軸、z 軸は、加速度センサーと同じ方法で定義されます。基準座標系は、直接直交基底として定義されます(図 1 を参照)。この座標系には次の特徴があります。
- X はベクトル積 Y x Z として定義されます。デバイスの現在地において地面に対して接線の方向であり、ほぼ東を指しています。
- Y は、デバイスの現在地において地面に対して接線の方向であり、北極を指しています。
- Z は空を指しており、地表に対して垂直です。
回転ベクトル センサーの使用方法を示すサンプル アプリケーションについては、 RotationVectorDemo.java をご覧ください。
大型モーション センサーを使用する
大きなモーション センサーは、大きな動きが検出されるたびにイベントをトリガーし、イベントを無効にします。大きな動作とは、歩行、サイクリング、車での座席など、ユーザーの現在地の変化につながる可能性がある動作です。次のコードは、デフォルトの重要なモーション センサーのインスタンスを取得する方法と、イベント リスナーを登録する方法を示しています。
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val mSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION) val triggerEventListener = object : TriggerEventListener() { override fun onTrigger(event: TriggerEvent?) { // Do work } } mSensor?.also { sensor -> sensorManager.requestTriggerSensor(triggerEventListener, sensor) }
Java
private SensorManager sensorManager; private Sensor sensor; private TriggerEventListener triggerEventListener; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION); triggerEventListener = new TriggerEventListener() { @Override public void onTrigger(TriggerEvent event) { // Do work } }; sensorManager.requestTriggerSensor(triggerEventListener, mSensor);
詳しくは TriggerEventListener
をご覧ください。
歩数計センサーを使用する
歩数計センサーは、センサーがアクティブになっている間の最後の再起動以降にユーザーが歩いた歩数を提供します。歩数計は、歩行検出センサーよりもレイテンシは長く(最大 10 秒)、精度は高くなります。
注: Android 10(API レベル 29)以降を搭載しているデバイスでアプリがこのセンサーを使用するには、ACTIVITY_RECOGNITION
権限を宣言する必要があります。
次のコードは、デフォルトの歩数計センサーのインスタンスを取得する方法を示しています。
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
アプリを実行しているデバイスのバッテリーを節約するには、JobScheduler
クラスを使用して、特定の間隔で歩数計センサーから現在の値を取得します。アプリの種類によって必要なセンサーの読み取り間隔は異なりますが、アプリがセンサーからのリアルタイム データを必要とする場合を除き、この間隔はできるだけ長くする必要があります。
歩行検出センサーを使用する
歩数検出センサーは、ユーザーが 1 歩歩くたびにイベントをトリガーします。レイテンシは 2 秒未満と予想されます。
注: Android 10(API レベル 29)以降を搭載しているデバイスでアプリがこのセンサーを使用するには、ACTIVITY_RECOGNITION
権限を宣言する必要があります。
次のコードは、デフォルトの歩数検出センサーのインスタンスを取得する方法を示しています。
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
元データを操作する
以下のセンサーは、デバイスに適用されている線形力と回転力に関する生データをアプリに提供します。これらのセンサーの値を効果的に使用するには、環境から重力などの要因を除外する必要があります。ノイズを低減するために、値の傾向に平滑化アルゴリズムを適用する必要がある場合もあります。
加速度計を使用する
加速度センサーは、重力を含むデバイスに適用される加速度を測定します。次のコードは、デフォルトの加速度センサーのインスタンスを取得する方法を示しています。
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
注: Android 12(API レベル 31)以降をターゲットとするアプリの場合、このセンサーはレート制限されます。
概念的には、加速度センサーは、次の関係を使用してセンサー自体に適用される力(Fs)を測定することで、デバイスに適用される加速度(Ad)を決定します。
![A_D=-(1/質量)ΣF_S](https://developer.android.com/static/images/guide/topics/sensors/acceleration.png?authuser=19&hl=ja)
ただし、重力は、次の関係に従って、測定される加速度に常に影響します。
![A_D=-g-(1/質量)ΣF_S](https://developer.android.com/static/images/guide/topics/sensors/acceleration_with_grav.png?authuser=19&hl=ja)
このため、デバイスがテーブルの上に載っているとき(加速していないとき)には、加速度計は g = 9.81 m/s2 の大きさを読み取ります。同様に、デバイスが自由落下状態にあり、9.81 m/s2 で地面に向かって急速に加速している場合、加速度計は g = 0 m/s2 の大きさを読み取ります。したがって、デバイスの実際の加速度を測定するには、加速度計のデータから重力の寄与を除去する必要があります。これは、ハイパス フィルタを適用することで実現できます。逆に、ローパス フィルタを使用して重力を分離することもできます。次の例はその方法を示しています。
Kotlin
override fun onSensorChanged(event: SensorEvent) { // In this example, alpha is calculated as t / (t + dT), // where t is the low-pass filter's time-constant and // dT is the event delivery rate. val alpha: Float = 0.8f // Isolate the force of gravity with the low-pass filter. gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0] gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1] gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2] // Remove the gravity contribution with the high-pass filter. linear_acceleration[0] = event.values[0] - gravity[0] linear_acceleration[1] = event.values[1] - gravity[1] linear_acceleration[2] = event.values[2] - gravity[2] }
Java
public void onSensorChanged(SensorEvent event){ // In this example, alpha is calculated as t / (t + dT), // where t is the low-pass filter's time-constant and // dT is the event delivery rate. final float alpha = 0.8; // Isolate the force of gravity with the low-pass filter. gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]; gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]; gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]; // Remove the gravity contribution with the high-pass filter. linear_acceleration[0] = event.values[0] - gravity[0]; linear_acceleration[1] = event.values[1] - gravity[1]; linear_acceleration[2] = event.values[2] - gravity[2]; }
注: センサーデータのフィルタリングには、さまざまな手法を使用できます。上記のコードサンプルでは、単純なフィルタ定数(alpha)を使用してローパス フィルタを作成しています。このフィルタ定数は、フィルタがセンサー イベントに追加するレイテンシの大まかな表現である時定数(t)と、センサーのイベント配信率(dt)から導出されます。このコードサンプルでは、デモの目的でアルファ値 0.8 を使用しています。このフィルタリング方法を使用する場合、別のアルファ値の選択が必要になることがあります。
加速度計は、標準のセンサー座標系を使用します。実際には、デバイスが自然な向きのテーブルの上に平らに置かれている場合、次の条件が適用されます。
- デバイスを左側を押すと(右側に移動する)、x 加速度値は正になります。
- デバイスを下に押す(つまり遠ざかる方向に移動する)と、y 加速度値は正になります。
- 加速度 A m/s2 でデバイスを上空に押すと、z 加速度値は A + 9.81 に等しくなり、デバイスの加速度(+A m/s2)から重力(-9.81 m/s2)を引いた値になります。
- 静止したデバイスの加速度値は +9.81 で、デバイスの加速度(0 m/s2 から重力を引いた値 -9.81 m/s2)に相当します。
一般に、デバイスの動きを監視する場合は、加速度計のほうが適しています。Android を搭載したほぼすべてのスマートフォンとタブレットに加速度計が搭載されており、消費電力は他のモーション センサーの約 10 分の 1 です。欠点の 1 つは、重力を排除し、ノイズを低減するために、ローパス フィルタとハイパス フィルタの実装が必要になる可能性があることです。
ジャイロスコープを使用する
ジャイロスコープは、デバイスの x 軸、y 軸、z 軸を中心とした回転速度を rad/s で測定します。次のコードは、デフォルトのジャイロスコープのインスタンスを取得する方法を示しています。
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
注: Android 12(API レベル 31)以降をターゲットとするアプリの場合、このセンサーはレート制限されます。
センサーの座標系は、加速度センサーに使用される座標系と同じです。回転は反時計回りに正です。つまり、原点に配置されたデバイスを x、y、z 軸上の正の位置から見ると、デバイスが反時計回りに回転しているように見える場合、オブザーバーは正の回転を報告します。これは正の回転の標準的な数学的定義であり、方向センサーで使用されるロールの定義とは異なります。
通常、ジャイロスコープの出力は経時的に積分され、時間ステップでの角度の変化を表す回転を計算します。次に例を示します。
Kotlin
// Create a constant to convert nanoseconds to seconds. private val NS2S = 1.0f / 1000000000.0f private val deltaRotationVector = FloatArray(4) { 0f } private var timestamp: Float = 0f override fun onSensorChanged(event: SensorEvent?) { // This timestep's delta rotation to be multiplied by the current rotation // after computing it from the gyro sample data. if (timestamp != 0f && event != null) { val dT = (event.timestamp - timestamp) * NS2S // Axis of the rotation sample, not normalized yet. var axisX: Float = event.values[0] var axisY: Float = event.values[1] var axisZ: Float = event.values[2] // Calculate the angular speed of the sample val omegaMagnitude: Float = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ) // Normalize the rotation vector if it's big enough to get the axis // (that is, EPSILON should represent your maximum allowable margin of error) if (omegaMagnitude > EPSILON) { axisX /= omegaMagnitude axisY /= omegaMagnitude axisZ /= omegaMagnitude } // Integrate around this axis with the angular speed by the timestep // in order to get a delta rotation from this sample over the timestep // We will convert this axis-angle representation of the delta rotation // into a quaternion before turning it into the rotation matrix. val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f val sinThetaOverTwo: Float = sin(thetaOverTwo) val cosThetaOverTwo: Float = cos(thetaOverTwo) deltaRotationVector[0] = sinThetaOverTwo * axisX deltaRotationVector[1] = sinThetaOverTwo * axisY deltaRotationVector[2] = sinThetaOverTwo * axisZ deltaRotationVector[3] = cosThetaOverTwo } timestamp = event?.timestamp?.toFloat() ?: 0f val deltaRotationMatrix = FloatArray(9) { 0f } SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector); // User code should concatenate the delta rotation we computed with the current rotation // in order to get the updated rotation. // rotationCurrent = rotationCurrent * deltaRotationMatrix; }
Java
// Create a constant to convert nanoseconds to seconds. private static final float NS2S = 1.0f / 1000000000.0f; private final float[] deltaRotationVector = new float[4](); private float timestamp; public void onSensorChanged(SensorEvent event) { // This timestep's delta rotation to be multiplied by the current rotation // after computing it from the gyro sample data. if (timestamp != 0) { final float dT = (event.timestamp - timestamp) * NS2S; // Axis of the rotation sample, not normalized yet. float axisX = event.values[0]; float axisY = event.values[1]; float axisZ = event.values[2]; // Calculate the angular speed of the sample float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ); // Normalize the rotation vector if it's big enough to get the axis // (that is, EPSILON should represent your maximum allowable margin of error) if (omegaMagnitude > EPSILON) { axisX /= omegaMagnitude; axisY /= omegaMagnitude; axisZ /= omegaMagnitude; } // Integrate around this axis with the angular speed by the timestep // in order to get a delta rotation from this sample over the timestep // We will convert this axis-angle representation of the delta rotation // into a quaternion before turning it into the rotation matrix. float thetaOverTwo = omegaMagnitude * dT / 2.0f; float sinThetaOverTwo = sin(thetaOverTwo); float cosThetaOverTwo = cos(thetaOverTwo); deltaRotationVector[0] = sinThetaOverTwo * axisX; deltaRotationVector[1] = sinThetaOverTwo * axisY; deltaRotationVector[2] = sinThetaOverTwo * axisZ; deltaRotationVector[3] = cosThetaOverTwo; } timestamp = event.timestamp; float[] deltaRotationMatrix = new float[9]; SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector); // User code should concatenate the delta rotation we computed with the current rotation // in order to get the updated rotation. // rotationCurrent = rotationCurrent * deltaRotationMatrix; }
標準的なジャイロスコープは、ノイズやドリフト(バイアス)のフィルタリングや補正を行うことなく、未加工の回転データを提供します。実際には、ジャイロスコープのノイズやドリフトにより誤差が生じます。この誤差は補正する必要があります。通常は、重力センサーや加速度計などの他のセンサーをモニタリングすることで、ドリフト(バイアス)とノイズを判断します。
未調整のジャイロスコープを使用する
未調整のジャイロスコープはジャイロスコープと似ていますが、回転速度にジャイロドリフト補正が適用されていない点が異なります。工場出荷時の調整と温度補正は引き続き回転速度に適用されます。未調整のジャイロスコープは、向きデータの後処理やメルディングを行う場合に便利です。一般に、gyroscope_event.values[0]
は uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3]
に近くなります。つまり
calibrated_x ~= uncalibrated_x - bias_estimate_x
注: 未調整のセンサーは、未加工の結果が多くなり、ある程度のバイアスが含まれることがありますが、その測定値では、調整によって適用された調整からのジャンプが少なくなります。アプリケーションによっては、これらの未調整の結果をより滑らかで信頼できるものとみなすことがあります。たとえば、アプリが独自のセンサー フュージョンを実行しようとしている場合、キャリブレーションを導入すると、実際に結果が歪む可能性があります。
未調整のジャイロスコープは、回転速度だけでなく、各軸の周囲の推定ドリフトも提供します。次のコードは、調整されていないデフォルトのジャイロスコープのインスタンスを取得する方法を示しています。
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);
他のサンプルコード
このページで説明した API の使用方法の詳細については、BatchStepSensor サンプルをご覧ください。