Windows Phone 7.1 Sensor プログラミング基礎
MSC D1-401セッションのフォローアップ第一弾です。
この投稿では、Windows Phone 7.1 Mangoに搭載されているSensorの使い方を解説します。
Windows Phone 7.1のアプリケーションで利用可能なセンサーは以下の4種類です。
- Accelerometer
加速度センサー - Gyroscope
ジャイロセンサー - Compass
コンパス - Motion
モーション
それぞれMicrosoft.Devices.Sensorsという名前空間の下に対応する同じ名前のクラスが用意されています。これらのクラスはすべて主要なメンバーとして、以下が用意されています。
メソッド:
- Start()
値の計測開始 - Stop()
値の計測終了 - Dispose()
センサーのDispose。もう使わなくなったらお方付け
プロパティ:
- CurrentValue
センサー計測値の現在の値 - IsDataValid
計測データの妥当性 - IsSupported
センサーサポート状況 - State
センサーデバイスの状態 - TimeBetweenUpdates
CurrentValueChangedイベントを発生する時間間隔
イベント:
- CurrentValueChanged
センサー計測値を受信するためのイベント
4つのセンサークラスは全て上のリストにあるメンバーを持っています。CurrentValueだけ名前は一緒でも、Accelerometerは、AccelerometerReading、GyroscopeはGyroscopeReading、CompassはCompassReading、MotionはMotionReadingという異なる型になっていて、それぞれのセンサーの種別に応じた値群を保持しています。
センサーにアクセスするプログラムは非常に簡単です。
先ずは、プロジェクトの参照設定に、Microsoft.Devices.SensorsとMicrosoft.Xna.Frameworkという2つのコンポーネントを追加します。前者はAccelerometerをはじめとする4つのクラスが入ったコンポーネントです。後者は、センサーの計測値がXNA FrameworkのVector3をはじめとするクラスを使っているので、必要です。
センサーの値の取得は、同期的な取得と、非同期的な取得の二つが用意されています。同期的なスタイルは、処理の流れの中でセンサーの計測値が直ぐ必要な時に使い、非同期的なスタイルは、センサーの値が計測されるたびに、それをトリガーにして処理を行う時に使います。
先ず、同期的なスタイルを説明します。流れは以下のようになります。
using (var sensor = new Accelerometer())
{
sensor.Start();
var currentValue = sensor.CurrentValue;
... 処理
sensor.Stop();
}
最後にDispose()メソッドをコールする必要があることから、usingブロックで囲んでいます。コードの中のAccelerometerの部分をGyroscopeやCompass、Motionに変えても問題なく動作します。currentValueのプロパティは、それぞれのセンサー種別で異なりますが、基本骨格はまったく同じです。
次に、非同期的なスタイルを説明します。この場合は、同一のセンサーインスタンスが方々のメソッドでアクセスされるので、センサーインスタンスは、クラスメソッドとして定義しておきます。
private Accelerometer sensor; // クラスのメンバー変数として定義しておく
そして、PageのコンストラクタやPageのLoadやNavigationで開かれるタイミング、もしくはセンサーからのイベントを受ける必要のあるロジックの初期化処理で、インスタンスを生成し、イベントハンドラーを登録します。
sensor = new Accelerometer();
sensor.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(sensor_CurrentValueChanged);
そして、いよいよセンサーからの計測情報が必要になるトリガー処理で、センサーを起動します。
sensor.Start();
これでセンサーデバイスの計測処理が開始され、計測値が取得されるたびに、sensro_CurrentValueChangedメソッドがコールされます。
センサーからのデータが必要な一連の処理が終わったら、センサーの計測処理を止めます。センサーを動かしていると、電池の減りが早いので、必要の無いところではなるべくとめておきます。
sensor.Stop();
そして、もうアプリケーション上必要ないのなら、
sensor.Dispose();
でセンサーを片付けておきます。
CurrentValueChangedに登録したハンドラーの処理のパターンは、以下のようになります。
void sensro_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
{
Deployment.Current.Dispatcher.BeginInvoke(
() => {
var currentValue = e.SensorReading;
... ロジック
});
}
イベントハンドラーをコールするスレッドは、アプリケーションのスレッドとは異なるため、そのままSilverlightのコントロールをアクセスしようとするとExceptionが発生します。なので、アプリケーションのDispatcherに処理を委譲しなければなりません。これはパターンなので、このままこういうものだと覚えておきましょう。
また、イベント引数eに格納されている計測値と、このハンドラーメソッドがコールされた時にsensorのCurrentValueプロパティで取得した値は、計測する時点が異なるので違う可能性があります。
基本パターンはこれだけです。例では、Accelerometerを使っていますが、その部分をGyroscopeやCompass、Motionセンサーに変えれば、そのままそれぞれのセンサーを扱うコードを書くことが可能です。
さて、次に、各センサーのCurrentValueとSensorReadingを紹介していきます。
4つのセンサーともにTimestampという名前のプロパティを持っています。型はDateTimeOffsetで、計測値を取得した時間が格納されています。例えば、計測した値の時間的変化を扱いたい場合などは、このプロパティを使って計測時間を管理することが可能です。他のプロパティは、それぞれのセンサーで違うので、それぞれを順に説明していきます。
Accelerometer:
先ず加速度センサーです、このクラスのCurrentValue、e.SensorReadingの型はAccelerometerReadingです。Accelerationというプロパティが用意されていて、デバイスにかかっている加速度が保持されています。型は、Vector3という三次元ベクトルをあらわす型で、空間におけるデバイスが加速されている方向を示しています。地球上には重力があるので、デバイスが静止状態の時には、重力の方向を向いたベクトルが計測されます。単位は地球の重力が基準です。例えば、デバイスの表面を上にして机の上においておけば、計測値は、ほぼ、X=0、Y=0、Z=-1の値を示します。振り回したり振ったりすればデバイスにかかった圧力が重力込みで計測された値を得ることができます。
Gyroscope:
次はジャイロスコープです、こちらは型がGyroscopeReadingで、Vector3型のRotateRateというプロパティが用意されています。デバイスのX軸、Y軸、Z軸周りの角速度を保持します。単位はラジアン/秒です。(πラジアン=180度)
デバイスを動かした時に、どれだけの角速度で動いているかを計測可能です。
Compass:
コンパスは、デバイスが向いている方向を図ることが出来ます。方はCompassReadingで、以下の4つのプロパティが用意されています。
- HeadingAccuracy
地軸の北極からの角度(0~359度) - MagneticHeading
磁極の北極からの角度(0~359度) - MagnetometerReeading
デバイスに対する磁極の北極への方向 - TrueHeading
デバイスに対する地軸の北極への方向
実は北極は二つあるんですねぇ地球には。皆さんご存知の通り地球は自転しているわけですが、その軸の日本やアメリカがある方が北極でこれが地軸の北極です。また地球は磁気を帯びた巨大な磁石で、地軸の北極付近にS極があるのですが、地軸の北極から少しだけ位置がずれています。それが磁極の北極です。Compassセンサーはそれら二つをサポートしているわけです。
Motion:
Motionセンサーは他の3つのセンサーと一味違います。何が違うかというと、他の3つのセンサーは対応するセンサーデバイスが実在してその値を取得できますが、Motionセンサーは、それら3つのセンサーデバイスからの値を料理してより使いやすい値セットに変換して提供してくれるありがたいセンサークラスです。提供されるプロパティは、
- Attitude
デバイスの姿勢 - DeviceAcceleration
加速度 - DeviceRotation
- 各速度
- Gravity
重力の方向
の4つです。2番目のDeviceAccelerationの値は、最初に説明したAccelerometerの計測値と同じ加速度ですが、こちらは重力をさっぴいた値を取得できます。純粋にデバイスがどちらに加速したかを計測するにはMotionセンサーが便利。
3番目のDeviceRotationは、基本Gyroscopeと同じだと思う。(現在調査中)
4番目のGravityは、重力の方向です。Accelerometerから重力成分を取り出したと理解すればよいですね。
で、一番最初のAttitudeですが、こちらは、デバイスの姿勢を測ったパラメータが入っています。他の3つのプロパティの型がVector3なのに対し、このプロパティは、AttitudeReadingという型になっています。このプロパティには更に以下のプロパティが格納されています。
- Yaw
デバイスのY軸を中心とした角度(ラジアン) - Pitch
デバイスのX軸を中心とした角度(ラジアン) - Roll
デバイスのZ軸を中心とした角度(ラジアン) - RotationMatrix
地球に対するデバイスの姿勢をあらわした3×3の行列 - Quaternion
地球に対するデバイスの姿勢を現した4変数のQuaternion
Yaw、Pitch、Rolllは、float型のデバイスの角度を表しています。RotationMatrixはXNA FrameworkのMatrix型をもった行列です。このプロパティはXNAで作成した3Dモデルの回転行列に代入すると直接使えるような凄く便利な行列値なので、非常に使い手があります。Quaternionは、CGをやっている人には超おなじみの変換用変数らしいっすね。
ちなみに各種計測値と座標を絵にするとこんな感じ。
Mangoの全センサーを使ったサンプルは、http://mangosensorchecker.codeplex.com/ から公開しているので、参考にしてみてください。
以上、センサーの使い方について説明してきましたが、センサーの値を取得すること自体は凄く簡単なので、あとは取得した値をどんな風に活用するかがポイント。
あ、コードを書く時は、名前空間のUsing宣言を忘れずにね。
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;