Android
Theta
THETAPlugin

THETAを振るだけで撮影してみよう【THETAプラグイン開発】

THETAを振るだけで撮影してみよう【THETAプラグイン開発】

はじめに

どーも、リコーのKAZ (@yokazuya) です。

RICOH THETA Vにはモーションセンサーが搭載されているのはご存知でしょうか?
従来の加速度センサーに加え、 新たにジャイロセンサーにも対応しました。
勘が鋭い方ならもうお気付きかと思いますが、THETAを動かすだけでほにゃらら出来るプラグインを作ることが出来ます。

この記事では、THETAを振るだけで撮影出来ちゃうプラグインを作りたいと思います。
theta-sensor

加速度の取得方法を整理してみた

THETAが動いているかどうか知るためには、加速度センサーやジャイロセンサーの値を取得する必要があります。

ここでは加速度の取得方法について説明します。

基本的な考えは、加速度センサーを搭載したAndroidデバイス(スマホ等)の加速度を取得するやり方と同じになります。

Android プラットフォームには、センサーを利用するためのクラスが用意されていますのでそちらを利用します。
クラスを利用するためには、下記のパッケージをインポートする必要があります。

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

おおまかにですが、下記を実装することで加速度を取得することが出来ます。

  • SensorEventListenerインターフェースの実装
  • イベントリスナーの登録と解除の設定
  • SensorManagerの取得
  • onResumeイベントでの加速度の制御

イベントリスナーの登録では、センサーに応じて下記を指定する必要があります。
今回は加速度を取得したいので、Sensor.TYPE_ACCELEROMETERを指定します。

タイプ名 センサー種類
Sensor.TYPE_ACCELEROMETER 加速度センサー
Sensor.TYPE_GYROSCOPE ジャイロセンサー

加速度を取得するコードを書いてみた

取得される加速度は、重力やノイズの影響を受けた生データであるため、そのままでは使えません。
ここでは生データから重力を除去したサンプルコードを書いてみます。

AccelerationGraSensor.java
package com.theta360.pluginapplication.AccelerationSensor;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

import java.util.List;

public class AccelerationGraSensor implements SensorEventListener {

    private static final float FILTER = 0.1f; // フィルタ係数

    private float[] currentGravityValues      = {0.0f, 0.0f, 0.0f};
    private float[] currentAccelerationValues = {0.0f, 0.0f, 0.0f};

    public synchronized float getX() {
        return this.currentAccelerationValues[0];
    }

    public synchronized float getY() {
        return this.currentAccelerationValues[1];
    }

    public synchronized float getZ() {
        return this.currentAccelerationValues[2];
    }

    public AccelerationGraSensor(SensorManager sensorManager) {
        // イベントリスナーの登録
        Sensor s = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        sensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_UI);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            // ローパスフィルタで重力値を抽出
            currentGravityValues[0] = event.values[0] * FILTER + currentGravityValues[0] * (1.0f - FILTER);
            currentGravityValues[1] = event.values[1] * FILTER + currentGravityValues[1] * (1.0f - FILTER);
            currentGravityValues[2] = event.values[2] * FILTER + currentGravityValues[2] * (1.0f - FILTER);

            synchronized (this) {
                // 重力値の除去
                currentAccelerationValues[0] = event.values[0] - currentGravityValues[0];
                currentAccelerationValues[1] = event.values[1] - currentGravityValues[1];
                currentAccelerationValues[2] = event.values[2] - currentGravityValues[2];
            }
        }
    }

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

    }
}

簡単に解説したいと思います。

MainActivityクラス内に実装するのではなく、分かりやすいように別クラス化しました。
加速度を制御出来るように、SensorEventListenerインターフェースを実装したクラスです。

コードの15~25行目では、他のクラスから加速度を参照出来るようにgetterを定義しています。

コードの27行目からのインスタンス生成時にイベントリスナーの登録を実施しています。
ここでどのセンサーを利用するか定義する必要があります。
加速度を取得したいので、Sensor.TYPE_ACCELEROMETERを定義しています。

コードの37行目からのonSensorChangedイベントは、センサーの値が変わると呼ばれます。
ここではローパスフィルタで重力値を抽出し、除去した値を保持しています。

コードの52行目からのonAccuracyChangedイベントは、センサーの精度が変わると呼ばれます。
ここでは特に何もしていません。

取得した加速度を確認してみた

実際に加速度が取得出来ているか確認してみたいと思います。
単純に値を確認するだけであれば、Android Studioのデバッグ機能を利用するのが簡単です。

MainActivityクラスにログを仕込むことで、加速度を参照することが出来るようにしてみます。

MainActivity.java
private SensorManager graSensorManager;
private AccelerationGraSensor accelerationGraSensor;

private static final int ACCELERATION_INTERVAL_PERIOD = 1000;
private Timer timer;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //加速度を取れる状態に設定
    graSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
    accelerationGraSensor = new AccelerationGraSensor(graSensorManager);
}

@Override
protected void onResume() {
    super.onResume();
    timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            // 加速度のログを出力
            Log.d("accelerX", String.valueOf(accelerationGraSensor.getX()));
            Log.d("accelerY", String.valueOf(accelerationGraSensor.getY()));
            Log.d("accelerZ", String.valueOf(accelerationGraSensor.getZ()));
        }
    }, 0, ACCELERATION_INTERVAL_PERIOD);
}

@Override
protected void onPause() {
    super.onPause();
    timer.cancel();
}

onCreateメソッドにて、SensorManagerクラスと作成したAccelerationGraSensorクラスを定義し、加速度を取得出来る準備をします。
新たにonResumeメソッドを定義し、タイマーを使って一定間隔で加速度をログに出力します。
加速度を取得する間隔はACCELERATION_INTERVAL_PERIODに定義しています。
ここでは1000ミリ秒毎に加速度を出力するようにしています。

Android StudioでTHETAプラグインをデバッグ実行するには下記の手順で行います。

  1. THETAとPCをUSBケーブルで接続

  2. Android Studioの実行->実行を選択
    debug-execute

  3. デプロイ対象の選択で接続しているTHETAを選択
    deploy-select

これでプラグインを実行している状態になります。

Android Studioのログキャットを見ると、約1000ミリ秒毎に取得した加速度が表示されることが確認出来ます。
THETAを動かすと加速度が大きくなっていることも分かります。

debug-log

THETAを振ると撮影する仕組みを作ってみた

上のサンプルコードで説明したonResumeメソッドの中に撮影処理を記述します。
runメソッドの中身をごっそり変えています。

MainActivity.java
private static final float ACCELERATION_THRESHOLD = 3.0f;

@Override
protected void onResume() {
    super.onResume();
    timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            // 指定した加速度を超えたタイミングで撮影する
            if (Math.abs(accelerationGraSensor.getX()) > ACCELERATION_THRESHOLD ||
                Math.abs(accelerationGraSensor.getY()) > ACCELERATION_THRESHOLD ||
                Math.abs(accelerationGraSensor.getZ()) > ACCELERATION_THRESHOLD) {
                    new TakePictureTask(mTakePictureTaskCallback).execute();
            }
        }
    }, 0, ACCELERATION_INTERVAL_PERIOD);
}

@Override
protected void onPause() {
    super.onPause();
    timer.cancel();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (graSensorManager != null) {
        // イベントリスナーの解除
        graSensorManager.unregisterListener(accelerationGraSensor);
    }
}

THETAを振ると自動で撮影するように実装したいので、タイマーを使って一定間隔で加速度のチェックを行い、閾値を超えたら撮影するようにしました。
ここでは方向関係なくTHETAを動かすと撮影する単純な判定式になっています。
閾値はACCELERATION_THRESHOLDに定義し、軽く振れば反応する程度の値を定義しています。
定義する値の単位はm/s2になります。
精度よく反応させたい場合は、アルゴリズムをしっかり検討する必要があるので決して真似しないでください。

あ、イベントリスナーの解除も忘れずに行いましょう。
ここではonDestroyメソッドで解除しています。

実際に動かしてみた

YouTube動画

サンプルコードのプラグインだと精度は良くないですが、加速度に反応して撮影出来ていることが分かります。

まとめ

センサーの値を取得するだけであればとても簡単です。
精度よく加速度や角速度を取得するためには専門的な知識や工夫が必要となりますが、センサーの値を取得して利用するという部分を分かりやすく説明するために、単純なサンプルコードにしてみました。
加速度センサーやジャイロセンサーをうまく利用することで、例えば、THETAをモーションコントローラにして、WiFi接続可能なロボットを動かすことも可能です。
そのあたりの記事も追々作成したいと思っています。

お知らせ

THETAプラグインをご存じない方はこちらをご覧ください。
興味を持たれた方はTwitterのフォローと
THETAプラグイン開発コミュニティ(Slack)への参加もよろしくおねがいします。