2012年10月22日月曜日

Androidの方位センサーについて


Androidで端末の傾きや方位を知りたい時についてのメモ

Android端末にはたくさんのセンサーが搭載されている.

  • TYPE_ACCELEROMETER 加速度センサー
  • TYPE_ALL 全部のセンサーを指定
  • TYPE_GRAVITY 重力センサー
  • TYPE_GYROSCOPE ジャイロスコープ
  • TYPE_LIGHT 照度センサー
  • TYPE_LINEAR_ACCELERATION 線形の加速度センサー
  • TYPE_MAGNETIC_FIELD 地磁気センサー
  • TYPE_ORIENTATION 傾きセンサー(非推奨定数)
  • TYPE_PRESSURE 加圧センサー
  • TYPE_PROXIMITY 接近センサー
  • TYPE_ROTATION_VECTOR 回転ベクトルセンサー
  • TYPE_TEMPERATURE 温度センサー

アプリなどで端末の傾きや方位を使いたい場合は方位センサーを使うことが考えられるが,Android developersでは以下のようにTYPE_ORIENTATIONは非推奨となっている.
Note: This sensor type exists for legacy reasons, please use getRotationMatrix() in conjunction with remapCoordinateSystem() and getOrientation() to compute these values instead.
Android developersより.


またAndroid developersではTYPE_ORIENTATIONの代わりにgetRotationMatrix(), remapCoordinateSystem(), getOrientation()を使用しろと書いてあるのでそれらを利用する.
この方法では加速度と地磁気を用いて傾きを算出しているので,それらを取得する必要がある.


まずはセンサーマネージャの取得とイベントリスナーの登録.
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);        //ウィンドウタイトルバーを非表示

    setContentView(R.layout.activity_main);

    tv1 = (TextView)findViewById(R.id.textView1);

    //センサーマネージャの取得
    sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);

    list = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
    if(list.size()>0) accelerometer = list.get(0);
    list = sensorManager.getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
    if(list.size()>0) magneticField = list.get(0);
}


@Override
protected void onResume(){
    super.onResume();

    //センサの処理の開始
    if(accelerometer != null)
        sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
    if(magneticField != null)
        sensorManager.registerListener(this, magneticField, SensorManager.SENSOR_DELAY_FASTEST);
}
リスナーの登録は,取得するセンサーのタイプをSensor.TYPE_ALLにした方がよりスマートなコードになるかも.
次にセンサーでの値の取得.
@Override
    public void onSensorChanged(SensorEvent event) {
        //加速度の取得
        if(event.sensor == accelerometer){
            accelerometerValues[0] = event.values[0];
            accelerometerValues[1] = event.values[1];
            accelerometerValues[2] = event.values[2];
        }
        //地磁気の取得
        if(event.sensor == magneticField){
            magneticValues[0] = event.values[0];
            magneticValues[1] = event.values[1];
            magneticValues[2] = event.values[2];
        }

        //傾きの算出
        if (magneticValues != null && accelerometerValues != null) {

            SensorManager.getRotationMatrix(inR, I, accelerometerValues, magneticValues);

            //画面の向きによって軸の変更可
            SensorManager.remapCoordinateSystem(inR, SensorManager.AXIS_X, SensorManager.AXIS_Y, outR);
            SensorManager.getOrientation(outR, orientationValues);

            //ラジアンから度への変換 及び方位の範囲を-180~180度から0~359度に変換
            float angle = radianToDegree(orientationValues[0]);
            if(angle >= 0)
                orientationValues[0] = angle;
            else if(angle < 0)
                orientationValues[0] = 360 + angle;
            orientationValues[1] = radianToDegree(orientationValues[1]);
            orientationValues[2] = radianToDegree(orientationValues[2]);
        }

        //出力するための配列に格納
        values[0] = accelerometerValues[0];
        values[1] = accelerometerValues[1];
        values[2] = accelerometerValues[2];
        values[3] = orientationValues[0];
        values[4] = orientationValues[1];
        values[5] = orientationValues[2];

        String text = "Xaccel:"+values[0]+"\nYaccel:"+values[1]+"\nZaccel:"+values[2]+
                "\n方位:"+values[3]+"\nピッチ:"+values[4]+"\nロール:"+values[5];
        tv1.setText(text);        //描画

    }

    /* ***** ラジアンから度への変換 ***** */
    int radianToDegree(float rad){
        return (int) Math.floor( Math.toDegrees(rad) ) ;
    }


onSensorChanged(SensorEvent event)はセンサーで取得した値が変更されたときに呼ばれる.
加速度と地磁気のセンサーで値が取得できているときに傾きの算出を行う.

まずgetRotationMatrix()で回転行列を生成する.
次に上で得た回転行列をremapCoordinateSystem()で回転させて画面の座標系に直す
最後にgetOrientation()で回転角を取得している.

要するに行列に加速度と地磁気の値を入れて,回転させることで軸を決めている.

getRotationMatrix()の第1引数は生成される回転行列,第3・第4引数は加速度と地磁気を格納したfloat型配列
remapCoordinateSystem()の第1引数は回転行列,第2・第3引数は端末のX軸,Y軸がそれぞれ指している世界座標系の方向を指定する.これにより端末を横に傾けたとしても端末に対する軸の向きは変わるが取得可能になる.第4引数は生成される端末の座標系行列.
getOrientation()の第1引数はremapCoordinateSystem()で生成した座標系行列,第2引数は算出した傾きを格納するfloat型配列である.

 radianToDegree()はラジアンから度への変換.


アプリを走らせると下のようになる.




 とりあえずこれで端末に搭載されているTYPE_ORIENTATION(非推奨)を使わないで傾きを取得することができた.
今回は端末を地面と水平にすることを前提としているので,端末が大きく傾くと正しく算出できない.
次回は端末が大きく傾いていても正しく取得できるようにしたり,取得した傾きを元に補正をかけたりできるようにしたいと思う.

2012年10月18日木曜日

google sitesにソースコードを貼る

google sitesで作ったページにソースコードを貼りたいけど、テキストボックスにコピペしたらシンタックスハイライトが全部消えて見づらいという話。

いくつか試した結果、見せるだけならOnline syntax highlightingが一番シンプルで使いやすい。
ソースコード貼って、言語とスタイルを指定して「Highlight」を押すとHTMLコードとプレビューが出る。
sitesで使う場合、プレビューの方をコピーしてテキストボックスに貼ればOK
こんな感じにハイライトされる(BloggerはHTMLモードにしてコード貼ったほうが良さそう)
package com.example.helloandroid;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {

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

}