Windows Phoneアプリケーション開発入門

第32回Mangoで追加されたSensorを使ってみよう!

はじめに

6月8日に、日本市場を視野に置いたという意味では日本では初めてとなる「Windows Phone Developer Day」が開催されました。Microsoft社の大場氏が、Windows Phoneの日本語版の最新ビルドを使ってデモをしていたのが印象的でした。

画像

MIX11では、次期アップデートの⁠Mango⁠が発表された際に、韓国と日本語が表示され多言語対応が大きく報じられていましたが、最新ビルドでも大きな変更はないようでした。ロック画面に漢数字の縦書きで日時が表示されているのが判ります。

画像

日本のチームが主力になって開発が進んでいるIMEの「カーブフリック」についても最新版を使ってのデモでした。Windows Phone Developer Tools 7.1 Betaに付属のWindows Phone Emulatorでもカーブフリックは使用できますので、インストール済みの方は是非お試しください。

画像

今回のイベントで一番大きな出来事としては、日本独自のプログラムとして開発・評価デバイスの無償提供が発表されました。現段階ではキャリアとメーカーとの調整中ということで、まだ評価デバイスの準備が整っていないようでした。残念ながらその場で配布という形は取られませんでしたが、端末が用意でき次第配布を行っていくことを約束されていました。

画像

「キャリアとの調整」という言葉が出てきており、そろそろ日本での発売も近いのではと推測させてくれるイベントでした。さて、今回はその⁠Mango⁠デバイスから追加されたセンサーについてのお話を書かせて頂きたいと思います。

Windows Phone OS 7.0でのセンサーのサポート

Windows Phone OS 7.0を動かすにあたって、デバイスメーカーが必ず搭載しないといけないとされているセンサーは以下の通りです。

  • 加速度センサー
  • 環境光センサー
  • 接近センサー

(端末間の差別化をしにくくなるというのはありますが)端末側のハードウェア仕様を一律にすることで、OSとしてハードウェアとソフトウェアをシームレスに統合することで、ユーザービリティをより高めることができます。

例えば、環境光センサーがあると周りの明るさに合わせて液晶の明るさを変えて視認性を上げることができますし、接近センサーがあると物体が近付いているのかが分かるので、通話中に耳や頬が液晶に触れて誤操作するのを防ぐことができます。

開発者からの視点で考えた場合には、沢山のセンサーが付いていることで現実世界のデータを取り込み、アプリケーションで処理させることができるので表現の幅が広がります。センサーの仕様が統一されていると端末間での互換性を考える必要がなくなるので、アプリケーションを作りやすくなります。

“Mango”アップデートにてサポートされるセンサー

“Mango⁠アップデートでOSによってサポートされるセンサーが追加されます。以下の3センサーです。

  • コンパスセンサー(地磁気センサー)
  • ジャイロスコープ
  • モーションセンサー

Windows Phone OS 7.0からサポートされていたAccelerometer(加速度センサー)クラスはSensorBase<T>を継承したクラスでした。新しく増えた3つのセンサーに関しても同様にSensorBase<T>を継承しています。もちろん取得できるデータに違いはありますが、これによってセンサー制御の基本的なインターフェースが統一されます。

CurrentValueセンサーの現在値を取得
CurrentValueChanged値が変わった時に通知されるイベント
TimeBetweenUpdates値の更新間隔の設定/取得
Startセンサーから値を取得開始
Stopセンサーから値を取得終了

後ほど詳細な解説を書かせて頂きますが、一律して以下のようなコードでセンサーからのデータ取得を開始することが可能になります。

var motion = new Microsoft.Devices.Sensors.Motion();
motion.TimeBetweenUpdates= TimeSpan.FromMilliseconds(20);
motion.CurrentValueChanged+= motion_CurrentValueChanged;
motion.Start();

これはモーションセンサーを使う場合のコードですが、⁠Microsoft.Devices.Sensors.Motion」の部分を「Microsoft.Devices.Sensors.Compass」に変更するだけで、コンパスセンサーを扱うことができます。

次にそれぞれのセンサーの使い方についてご紹介いたします。

コンパス(地磁気)センサー

コンパスセンサーはデバイスの向きを検出するセンサーデバイスです。CurrentValueChangedイベントでは、検出された磁界についての情報がCompassReadingオブジェクトで通知されます。CompassReading型は以下のプロパティを持っています。

HeadingAccuracy精度。コンパスの測定値に対する正確さの取得
MagneticHeadingデバイスの向き。磁北から時計回りの向きを取得(単位:度)
TrueHeadingデバイスの向き。地球の地理的な北から時計回りでの向きを取得(単位:度)
MagnetometerReadingXYZ軸の磁束密度の取得(単位:マイクロテスラ)
Timestampセンサーから読み取りが行われた時刻を取得

コンパスセンサーのデータ取得を開始する

コンパスセンサーが搭載されているデバイスかどうかをあらかじめ調べます。Compass.IsSupportedメソッドでコンパスセンサーがサポートされているデバイスかどうかを判定することが可能です。

private void Button_Click(object sender, RoutedEventArgs e)
{
    // 実行する端末でコンパスセンサーが利用可能か調べる
    if (!Microsoft.Devices.Sensors.Compass.IsSupported)
    {
        txtResult.Text = "Not Supported";
        return;
    }

    // センサーのオブジェクト生成
    compass = new Compass();
    // センサーの値を更新の間隔を100ミリ秒に設定
    compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(100);
    // CurrentValueChangedイベントのハンドラを設定
    compass.CurrentValueChanged += compass_CurrentValueChanged;
    // コンパスセンサーのデータ取得を開始
    compass.Start();
}

コンパスセンサーの向きを取得する

日本では大体5~10度のズレがあることを前提に、HeadingAccuracyプロパティで取得した地理的な北と地磁気北との誤差が15度以上の場合は、センサーから取得してきた値を採用しないようにガードを入れています。デバイスの周りに磁石や金属があるとセンサーが干渉してしまい、正確な値が取得できなくなってしまうようなので注意が必要です(端末を8の字を振らせるようにユーザーに注意を施してもよいかもしれません⁠⁠。

コンパスセンサーは、北極点の方向を示す地理的な北(真北)と方位磁石が指す北(磁北)を取得することができます。CurrentValueChangedイベントのハンドラには、引数としてSensorReadingEventArgs<CompassReading>オブジェクトが渡されます。TrueHeadingプロパティで前者の地理的な北に対する相対的な向きを、MagneticHeadingプロパティで後者の地磁気北に対する相対的な向きを度単位で取得することができます。

MagnetometerReadingプロパティでXYZ軸の磁束密度をマイクロテスラ単位で取得することができます。

void compass_CurrentValueChanged(object sender, SensorReadingEventArgs<CompassReading> e)
{
    // 地理的な北と地磁気北との誤差が15度以上であれば処理しない
    if (e.SensorReading.HeadingAccuracy > 15)
    {
        return;
    }

    this.Dispatcher.BeginInvoke(() => 
    {
        // 地理的な北に対する向き(単位:度)
        var trueHeading = e.SensorReading.TrueHeading;
        // 地磁気北に対する向き(単位:度)
        var magneticHeading = e.SensorReading.MagneticHeading;
        // XYZ軸の磁束密度(単位:マイクロテスラ)
        var magnetometerX = e.SensorReading.MagnetometerReading.X;
        var magnetometerY = e.SensorReading.MagnetometerReading.Y;
        var magnetometerZ = e.SensorReading.MagnetometerReading.Z;

        // コンパスセンサーの値を表示する
        txtResult.Text = string.Format("TrueHeading:{0:0.000}", trueHeading);
        txtResult.Text += string.Format("MagneticHeading:{0:0.000}", magneticHeading);
        txtResult.Text += string.Format("MagnetometerReading x:{0:0.000} y:{1:0.000} z:{2:0.000}",
            magnetometerX, magnetometerY, magnetometerZ);            
    });
}

ジャイロスコープ

ジャイロスコープは、デバイスの各軸周りの回転速度を取得できるセンサーデバイスです。CurrentValueChangedイベントで、検出された角速度の情報がGyroscopeReadingオブジェクトで通知されます。GyroscopeReading型は以下のプロパティを持っています。

RotationRate角速度。デバイスの各軸周りの回転速度の取得(単位:ラジアン毎秒)
Timestampセンサーから読み取りが行われた時刻を取得

ジャイロスコープのデータ取得開始

ジャイロスコープが搭載されているデバイスかどうかをあらかじめ調べます。Gyroscope.IsSupportedメソッドでジャイロスコープがサポートされているデバイスかどうかを判定することが可能です。
private void Button_Click(object sender, RoutedEventArgs e)
{
    // 実行する端末でジャイロスコープが利用可能か調べる
    if (!Microsoft.Devices.Sensors.Gyroscope.IsSupported)
    {
        txtResult.Text = "Not Supported";
        return;
    }

    // センサーのオブジェクト生成
    gyro = new Gyroscope();
    // センサーの値を更新の間隔を100ミリ秒に設定
    gyro.TimeBetweenUpdates = TimeSpan.FromMilliseconds(100);
    // CurrentValueChangedイベントのハンドラを設定
    gyro.CurrentValueChanged += gyro_CurrentValueChanged;
    // ジャイロスコープのデータ取得を開始
    gyro.Start();
}

ジャイロスコープの角速度を取得する


void gyro_CurrentValueChanged(object sender, SensorReadingEventArgs<GyroscopeReading> e)
{
    // センサーの値を表示させたいのでUIスレッドで実行
    Dispatcher.BeginInvoke(() =>
    {
        // ジャイロスコープの値を表示する
        var rotX = e.SensorReading.RotationRate.X;
        var rotY = e.SensorReading.RotationRate.Y;
        var rotZ = e.SensorReading.RotationRate.Z;

        txtResult.Text = string.Format("x:{0:0.000} y:{1:0.000} z:{2:0.000}",
            rotX, rotY, rotZ);
    });
}

モーションセンサー

“Mango⁠アップデートに対応したWindows Phone端末は、コンパス・ジャイロスコープ・加速度センサーを含む、デバイスの向きと角度などを提供する複数のセンサーをサポートしています。

これらの個別のセンサーからの生データを取得することは前記の通り簡単ですが、デバイスの実際の向きや動きを判断するためには物理的な制限があります。

例えば、加速度センサーからの測定値にはデバイスの動きから生じる力に加えて、デバイスに掛かる重力の力が含まれてしまいます。センサーからの生データからデバイスの本当の向きを求めるには、難しい計算が必要になります。

CurrentValueChangedイベントで、検出された角速度の情報がMotionReadingオブジェクトで通知されます。MotionReading型は以下のプロパティを持っています。

Attitudeラジアンでのデバイスの姿勢(ヨー、ピッチ、ロール)の取得
DeviceAccelerationデバイスの直線加速の取得(単位:重力)
DeviceRotationデバイスの回転加速度の取得(単位:ラジアン毎秒)
GravityMotionReadingに関連付けられている重力ベクトルの取得
Timestampセンサーから読み取りが行われた時刻を取得

端末がモーションセンサーに対応しているか調べる

モーションセンサーが搭載されているデバイスかどうかをあらかじめ調べます。Gyroscope.IsSupportedメソッドでモーションセンサーがサポートされているデバイスかどうかを判定することが可能です。

private void Button_Click(object sender, RoutedEventArgs e)
{
    // 実行する端末でモーションセンサーが利用可能か調べる
    if (!Microsoft.Devices.Sensors.Motion.IsSupported)
    {
        txtResult.Text = "Not Supported";
        return;
    }

    // センサーのオブジェクト生成
    motion = new Motion();
    // センサーの値を更新の間隔を100ミリ秒に設定
    motion.TimeBetweenUpdates = TimeSpan.FromMilliseconds(100);
    // CurrentValueChangedイベントのハンドラを設定
    motion.CurrentValueChanged += motion_CurrentValueChanged;
    // モーションセンサーのデータ取得を開始
    motion.Start();
}

モーションセンサーの各種値を取得する

Motionクラスは、低レベルのセンサー計算を処理して、アプリケーション側から容易にデバイスの姿勢(ヨー、ピッチ、ロール⁠⁠、回転加速度や重力情報を扱えるようにラップしてくれています。このMotionクラスを使用するとよいと思われる代表的なアプリケーションの例としては、カメラプレビューの上で位置情報と向きからオブジェクトを描画するAR(拡張現実)アプリケーションでしょうか。

定型的にセンサーの測定値を利用するのであればMotionクラスで十分かと思いますが、独自の動きと姿勢を認識する場合には、各センサークラスを組み合わせて実装する必要がありそうです。

void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
{
    // センサーの値を表示させたいのでUIスレッドで実行
    Dispatcher.BeginInvoke(() =>
    {
        // デバイスの角速度の値を表示する
        var rotX = e.SensorReading.DeviceRotation.RotationRate.X;
        var rotY = e.SensorReading.DeviceRotation.RotationRate.Y;
        var rotZ = e.SensorReading.DeviceRotation.RotationRate.Z;

        // デバイスの加速度の値を表示する
        var accX = e.SensorReading.DeviceAcceleration.Acceleration.X;
        var accY = e.SensorReading.DeviceAcceleration.Acceleration.Y;
        var accZ = e.SensorReading.DeviceAcceleration.Acceleration.Z;

        txtResult.Text = string.Format("RotationRate x:{0:0.000} y:{1:0.000} z:{2:0.000}",
            rotX, rotY, rotZ);
        txtResult.Text += string.Format("Acceleration x:{0:0.000} y:{1:0.000} z:{2:0.000}",
            accX, accY, accZ);
    });
}

さいごに

今回ご紹介したセンサーAPIは、Windows Phone Developer Tools 7.1 BetaのWindows Phone Emulatorでも使用することができません(IsSupportedプロパティがFalseで返ってきます)。現段階でセンサーのテストが可能なのは、Windows Phone OS 7.0からある加速度センサーだけとなっています。

Emulatorに付いているAdditional Toolsの実装はまだ完全と言える状態ではありませんので、実際にコードを動かすには開発ツールの完成版を待つか⁠Mango⁠用の開発・評価デバイスの配布を待つ必要があります。さて、どちらのリリースが先になるでしょうか……。

以上で今回は終わりです。ありがとうございました。

おすすめ記事

記事・ニュース一覧