SIN@SAPPOROWORKSの覚書

C#を中心に、夜な夜な試行錯誤したコードの記録です。

Xamarin.Android 方位の取得2(磁気センサー/加速度センサー)

【 Xamarin 記事一覧 】

1 方位の取得方法

Androidで方位を取得する方法は、次の2つがあります。
(1) TYPE_ORIENTATION(傾きセンサー)による方法
(2) TYPE_MAGNETIC_FIELD(磁気センサー)とTYPE_ACCELEROMETER(加速度センサー)による方法

今回は、(2)の磁気センサーと加速度センサーを使用してみます。
なお、API Level8 で「方位角」及び「傾き」を求める場合は、TYPE_ORIENTATIONは非推奨になっているようです。

2 磁気センサー(TYPE_MAGNETIC_FIELD)及び加速度センサー(TYPE_ACCELEROMETER)

センサー自体の使用方法は、傾きセンサーの時と同じですが、「方位角」と「傾き」は、SensorManagerのメソッドで計算しています。
http://developer.android.com/reference/android/hardware/SensorManager.html

// 加速度センサーと磁気センサーの値から回転行列を求める
public static bool GetRotationMatrix(float[] R, float[] I, float[] gravity, float[] geomagnetic)
// 端末の画面設定に合わせる変換行列を求める(以下は, 縦表示で画面を上にした場合)
public static bool RemapCoordinateSystem(float[] inR, Android.Hardware.Axis X, Android.Hardware.Axis Y, float[] outR)
// 方位角及び傾きを求める
public static float[] GetOrientation(float[] R, float[] values)

サンプルは、傾きセンサーの値も合わせて表示して違いを比べて見ましたが、ほとんど同じでした。
※回転角の符号が逆転しているのに注意が必要です。

[Activity(Label = "AndroidApplication1", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity, ISensorEventListener{

    private SensorManager _senseManager;
    private TextView _textView;

    protected override void OnCreate(Bundle bundle){
        base.OnCreate(bundle);
        SetContentView(Resource.Layout.Main);

        _senseManager = (SensorManager) GetSystemService(SensorService);
        _textView = FindViewById<TextView>(Resource.Id.textView);
    }

    protected override void OnResume(){
        base.OnResume();
        //リスナー登録
        _senseManager.RegisterListener(this, _senseManager.GetDefaultSensor(SensorType.MagneticField),SensorDelay.Ui);
        _senseManager.RegisterListener(this, _senseManager.GetDefaultSensor(SensorType.Accelerometer),SensorDelay.Ui);
        _senseManager.RegisterListener(this, _senseManager.GetDefaultSensor(SensorType.Orientation), SensorDelay.Ui);
    }

    protected override void OnPause(){
        base.OnPause();
        //リスナー解除            
        _senseManager.UnregisterListener(this);
    }

    //ISensorEventListenerで必須
    public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy){ }

    float [] _aVal;
    float [] _mVal;
    float[] _oVal;
    public void OnSensorChanged(SensorEvent e) {
        //センサー値の取得
        switch (e.Sensor.Type){
            case SensorType.Accelerometer: //加速度センサー
                _aVal = new float[3];
                _aVal = e.Values.ToArray();
                break;
            case SensorType.MagneticField: //磁気センサー
                _mVal = new float[3];
                _mVal = e.Values.ToArray();
                break;
            case SensorType.Orientation: //傾きセンサー
                _oVal = new float[3];
                _oVal = e.Values.ToArray();
                break;
        }
 
        var sb = new System.Text.StringBuilder();
  
        if (_oVal != null) {
            sb.Append(String.Format("傾きセンサー\n"));
            sb.Append(String.Format("方位角:{0:F1}\n", _oVal[0]));
            sb.Append(String.Format("傾斜角:{0:F1}\n", _oVal[1]));
            sb.Append(String.Format("回転角:{0:F1}\n", _oVal[2]));
            sb.Append(String.Format("\n"));
        }

        if (_aVal != null && _mVal != null) {
            var R1 = new float[16];
            var R2= new float[16];
            var I = new float[16];
            var val = new float[3];

            // 加速度センサーと磁気センサーの値から回転行列を求める
            SensorManager.GetRotationMatrix(R1, I, _aVal, _mVal);
            // 端末の画面設定に合わせる変換行列を求める(以下は, 縦表示で画面を上にした場合)
            SensorManager.RemapCoordinateSystem(R1, Axis.X, Axis.Y, R2);
            // 方位角及び傾きを求める
            SensorManager.GetOrientation(R2, val);
            //ラジアンを角度に変換
            for (var i = 0; i < 3; i++) {
                val[i] = (float)(val[i] * 180 / Math.PI);
            }

            sb.Append(String.Format("磁気センサー + 加速度センサー\n"));
            sb.Append(String.Format("方位角:{0:F1}\n", (val[0] < 0) ? val[0] + 360 : val[0]));
            sb.Append(String.Format("傾斜角:{0:F1}\n", val[1]));
            sb.Append(String.Format("回転角:{0:F1}\n", val[2]));
        }
        //表示
        _textView.Text = sb.ToString();
    }
}

【 Xamarin 記事一覧 】

Xamarin.Android 方位の取得(傾きセンサー)

【 Xamarin 記事一覧 】


1 方位の取得方法

Androidで方位を取得する方法は、次の2つがあります。

(1) TYPE_ORIENTATION(傾きセンサー)による方法
(2) TYPE_MAGNETIC_FIELD(磁界センサー)とTYPE_ACCELEROMETER(加速度センサー)による方法

今回は、(1)の傾きセンサーを使用してみます。

針の指している方向がちょっと?となりますが・・・
磁北は、真北(地図の真上)より、左(西)に少しずれています。(偏角

2 傾きセンサー(TYPE_ORIENTATION)


まずは、OnCreate()で、SensorManagerのインスタンスや、表示のための初期化を行っています。
リスナー(ISensorEventListener)は、自分自身として実装し、登録及び解除は、OnResume()及びOnPause()で行っています。

OnSensorChanged()で取得できるのは、「方位角」「傾斜角」「回転角」の3つですが、
この方位角は、現在の角度(0〜360の左回り)です。

サンプルでは、この値(角度)を1回分保存し、前回との差分で画像を回転させています。

[Activity(Label = "AndroidApplication1", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity,ISensorEventListener{

    private SensorManager _senseManager;
    private ImageView _imageView;
    private TextView _textView;
    private Matrix _matrix;
    private float _centerX; //回転の中心座標
    private float _centerY; //回転の中心座標

    protected override void OnCreate(Bundle bundle){
       base.OnCreate(bundle);
       SetContentView(Resource.Layout.Main);

       _senseManager = (SensorManager) GetSystemService(SensorService);
       _imageView = FindViewById<ImageView>(Resource.Id.image);
       _textView = FindViewById<TextView>(Resource.Id.textView);
      _matrix = new Matrix();

       //ディスプレイのサイズ取得
       var display = WindowManager.DefaultDisplay;
       var dispWidth = display.Width;
       var dispHeight = display.Height;

       //画像のサイズ取得
       var d = Resources.GetDrawable(Resource.Drawable.magnet);
       var imageWidth = d.IntrinsicWidth;
       var imageHeight = d.IntrinsicHeight;

       //画像を画面の中央に移動する
       _matrix.PostTranslate((dispWidth - imageWidth) / 2, (dispHeight - imageHeight) / 2);
       //回転させる際の中心座標を保存しておく
       _centerX = dispWidth/2;
       _centerY = dispHeight/2;
    }

    protected override void OnResume(){
        base.OnResume();
        //傾きセンサー取得
        var list = _senseManager.GetSensorList(SensorType.Orientation);
        //取得に成功した場合のみ処理
        if (list.Count > 0){
            //リスナーに登録(第3引数は感度)
            _senseManager.RegisterListener(this,list[0], SensorDelay.Normal);
        }
    }
    protected override void OnPause(){
        base.OnPause();
        //リスナー解除
        _senseManager.UnregisterListener(this);
    }

    //ISensorEventListenerで必須
    public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy){ }

    private float _keepValue;//前回の角度

    public void OnSensorChanged(SensorEvent e) {
        //傾きセンサーのみ処理
        if (e.Sensor.Type == SensorType.Orientation){

            //e.Values[0] 方位角
            //e.Values[1] 傾斜角
            //e.Values[2] 回転角

            //現在の角度を取得
            var value = e.Values[0];

            //中心座標と回転角(前回角度ー現在角度)をセットする
            _matrix.PostRotate(_keepValue - value, _centerX, _centerY);
            //画像を回転させる
            _imageView.ImageMatrix = _matrix;
            //テキスト表示
            _textView.Text = string.Format("{0} - {1} = {2}", _keepValue,value,_keepValue - value);

            //次回用に角度を保存する
            _keepValue = value;
        }
    }

}

【 Xamarin 記事一覧 】