Gyroscopeを使った回転量の計算|Android開発

      2018/06/15

AndroidのセンサGyroscopeを使用して回転量を取る方法です。

解説

Gyroscopeからは各軸を中心とした角速度(rad/sec)が通知されます。
角速度は前回通知~今回通知までの値なので、通知時間を掛けてやると前回通知から今回通知までに移動した角度(rad)が算出されます。
角速度(rad/sec) × 時間(sec) = 角度(rad)
軸は以下の図のようになっています。

axis_device

使用例

下記では、start時を0(360)とし、通知毎に移動角度を加算していくことで、時計回りなら360~0へ、反時計回りなら0~360へと値が変化します。
※移動量なので、続けると負数にもなるし、360を超えます。

public class GyroscopeTest implements SensorEventListener {

    private static final float NS2S = 1.0f / 1000000000.0f;
    private float mLastTimestamp;

    private float mRadianLandscape;
    private float mRadianPortrait;

    protected int mCurrentDegreeLandscape;
    protected int mCurrentDegreePortrait;
    protected int mDirection;
    protected boolean isLandscape;

    public static final Object SynchronizedObject = new Object();

    public void start(int rotation, int direction) {
        isLandscape = (rotation == 0 || rotation == 180);
        synchronized (SynchronizedObject) {
            mLastTimestamp = 0;
            mRadianLandscape = 0;
            mRadianPortrait = 0;
            mDirection = direction;
        }
    }

    protected int currentDegree() {
        return isLandscape ? mCurrentDegreeLandscape : mCurrentDegreePortrait;
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() != Sensor.TYPE_GYROSCOPE) {
            return;
        }

        synchronized (SynchronizedObject) {
            if (mLastTimestamp != 0) {
                final float dT = (event.timestamp - mLastTimestamp) * NS2S;
                float axisX = event.values[0];
                float axisY = event.values[1];
                float axisZ = event.values[2];

                mRadianLandscape += (axisX * dT);
                mRadianPortrait += (axisY * dT);
            }

            mLastTimestamp = event.timestamp;
            int degreeLandscape = radianToDegree(mRadianLandscape);
            int degreePortrait = radianToDegree(mRadianPortrait);

            if (degreeLandscape < 0) {
                degreeLandscape += 360;
            }
            if (degreePortrait < 0) {
                degreePortrait += 360;
            }

            mCurrentDegreeLandscape = degreeLandscape;
            mCurrentDegreePortrait = degreePortrait;
            //Log.v("GyroscopeTest", String.format("Gyroscope: %d %d", mCurrentDegreeLandscape, mCurrentDegreePortrait));
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // NOP
    }

    protected static int radianToDegree(float radian) {
        return (int) Math.floor(Math.toDegrees(radian));
    }
}

 - Android