加速度センサーやジャイロスコープセンサーを使った、3つのセンサーサンプル

2012年6月29日(金)
PROJECT KySS

前回までは、PROJECT KySSの薬師寺国安が、マーケットプレイスで公開しているアプリ(発行者名:kuniyasu)を例に出しながら、サンプルプログラムを解説しました。

最終回では、これまでの連載でお伝えしきれなかったWindows Phoneのセンサープログラミングについて、私、薬師寺聖から補足して解説します。3つのサンプルを紹介していきますので、ぜひアプリ制作の参考にしてください。

Windows Phone アプリ開発で使えるセンサー

まず、Windows Phone アプリ開発でセンサーからのデータを利用したい場合は、Microsoft.Devices.Sensors 名前空間のクラスを用います。次の4種類のデータを取得できます。

(1)加速度センサー(Accelerometer)
実機の傾きが分かります。
(2)コンパスセンサー(Compass)
磁気強度と方角が分かります。
(3)ジャイロスコープセンサー(Gyroscope)
どのくらいの速さで、どの方向に、どれだけの角度、デバイスの姿勢が変化したかが分かります。
(4)複合モーション(Combined Motion API)
(1)~(3)をまとめて扱える1つの複合APIです。

これらのセンサーにより取得できるデータは、表1のとおりです。

表1:4種類のセンサーで取得できるデータの一覧

クリックで拡大します。

本稿では、加速度センサーを使った「転がるボール」と、ジャイロスコープセンサーを使った「カウンター」、複合モーションを使った「回転する針」の3つのサンプルについて解説します。

このうち、加速度センサーの処理を応用すると、筆者(薬師寺聖)がマーケットプレイスで公開中の「ビジュアル傾斜計 Ver.0.8」のようなアプリになります(図1)。
→参照:ビジュアル傾斜計(Windows Phone/Marketplace)

ジャイロスコープセンサーと複合モーションの処理を応用すると、「センサー計測セット Ver.0.8」のようなアプリになります(図2)。
→参照:センサー計測セット(Windows Phone/Marketplace)

 図1:「ビジュアル傾斜計 Ver.0.8」の一画面(クリックで拡大)
 図2:「センサー計測セット Ver.0.8」の各計測ページ(クリックで拡大)

サンプル一式は、会員限定特典としてダウンロードできます。記事末尾をご確認ください。

加速度センサーを使った、転がるボールのサンプル

最初に、加速度センサーを使った「転がるボール」の作り方を解説します。サンプルの動作は以下の通りです。

  1. ページをロードした時、楕円の中央に、緑色のボールが表示されています。
  2. 「開始」ボタンをタップして実機を傾けると、ボールが楕円の中を移動します。
  3. 実機の頭側を下に傾けた時は、遠近法のようにボールが小さくなります。
  4. 「停止」ボタンをタップすると、ボールの移動は止まります(図3)。

このボールはExpression Designで描いた単純なpng画像ですが、回転させることによって転がるように見せかけています。

 図3:実機を傾けると、ボールが楕円の中を転がる。図は、エミュレーターでの動作確認画面(クリックで拡大)

プロジェクトの作成

VS 2010のメニューから[ファイル(F)/新規作成(N)/プロジェクト(P)]と選択します。次に、「Windows Phone アプリケーション」を選択して、「名前(N)」に任意のプロジェクト名を指定します。ここでは「AccelerometerSample」という名前を付けています。

Windows Phoneのバージョンは7.1を選択します。ソリューションエクスプローラー内にImageというフォルダを作って、PNG画像(Ball1.png)を追加しておきます。サンプルファイルには画像ファイルは追加済みです。

MainPage.xamlの編集とコントロールの追加

ツールボックスからButtonコントロールを1個、TextBlockコントロールを1個配置します。また、Ellipse(楕円)を描き、Imageコントロールを1個配置します。このImageコントロールの名前は「BallImage」としておき、SourceプロパティにはImageフォルダ内の画像を指定します。

書き出されるXAMLコードをリスト1のように編集します。

リスト1 編集されたXAMLコード(MainPage.xaml)

Nameが「BallImage」のImage要素の中に、Image.Projection要素を追加します。さらに、その子として、PlaneProjection要素を追加します。x:Nameは「MyAngleProjection」としておきます。

~前略~ <!--LayoutRoot は、すべてのページ コンテンツが配置されるルート グリッドです-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="0,0,0,0">
    <Button Content="開始" Height="100" HorizontalAlignment="Left" Margin="140,20,0,0" Name="Button1" VerticalAlignment="Top" Width="200" />
    <Ellipse Height="260" HorizontalAlignment="Left" Margin="60,200,0,0" Name="Ellipse1" Stroke="White" StrokeThickness="1" VerticalAlignment="Top" Width="360" Fill="Black" />
      <Image Height="60" HorizontalAlignment="Left" Margin="210,300,0,0" Name="BallImage" Stretch="Fill" VerticalAlignment="Top" Width="60" Source="/AccelerometerSample;component/Image/Ball1.png">
        <Image.Projection>
          <PlaneProjection x:Name="MyAngleProjection"/>
        </Image.Projection>
      </Image>
    <TextBlock Height="50" HorizontalAlignment="Left" Margin="15,530,0,0" Name="TextBlock1" Text="センサーの状態" VerticalAlignment="Top" Width="440" FontSize="24" />
  </Grid>
</Grid>
<!--ApplicationBar の使用法を示すサンプル コード--> ~後略~

ロジックコードを記述する前に、「プロジェクト/参照の追加」で、Microsoft.Devices.Sensors名前空間への参照を追加しておきます。また、Microsoft.Xna.Framework名前空間への参照も追加しておきます。

参照の追加ができたら、xaml.vbを開いて、ロジックコードを記述します。

ロジックコードを記述する

リスト2 (MainPage.xaml.vb)

名前空間のインポートと変数の宣言

加速度計にアクセスする API を提供する、Microsoft.Devices.Sensors名前空間をインポートします。また、Vector3型のセンサーデータを扱うために、Xna.Framework名前空間もインポートしておきます。

加速度センサーオブジェクトと、加速度センサーの生データを格納する変数、楕円の中心点の座標値と長軸と短軸、ボールの画像の幅と高さを代入する変数を宣言します。

Option Strict On
Imports Microsoft.Devices.Sensors
Imports Microsoft.Xna.Framework

Partial Public Class MainPage
  Inherits PhoneApplicationPage

  ' Constructor
  Public Sub New()
    InitializeComponent()
  End Sub
 
  Dim MyAccelerometer As Accelerometer '加速度センサー
  Dim MyAcceleration As Vector3 'センサーデータ
 
  Dim MyAreaLeft, MyAreaTop As Double '楕円の中心点の座標値
  Dim MajorAxis, MinorAxis As Double '楕円の長軸、短軸
  Dim MyBallWidth, MyBallHeight As Double 'ボールの幅、高さ

このページがロードされた時の処理

デバイスがセンサーをサポートしているかどうかをチェックして、サポートしていなければ、メッセージを表示します。

ボールの幅と高さ、楕円の長軸と短軸の半径を取得します。また、楕円のマージンを取得して、中心点の座標値をもとめておきます。このように、XAML中の値を利用するように記述しておくと、楕円のサイズなどを変更した場合でも、プログラムを変更する必要がないので、レイアウトの微調整が容易です。

  Private Sub MainPage_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
    '加速度センサーがサポートされていない機種への対応
    If Accelerometer.IsSupported = False Then
      MessageBox.Show("このデバイスは加速度センサーをサポートしていません。")
      Button1.IsEnabled = False
      Exit Sub
    Else
      Button1.IsEnabled = True
    End If
 
    'ボールの幅と高さ
    MyBallWidth = CDbl(BallImage.GetValue(FrameworkElement.ActualWidthProperty))
    MyBallHeight = CDbl(BallImage.GetValue(FrameworkElement.ActualHeightProperty))

    '楕円の長軸と短軸の半径
    MajorAxis = CDbl(Ellipse1.GetValue(FrameworkElement.ActualWidthProperty)) / 2
    MinorAxis = CDbl(Ellipse1.GetValue(FrameworkElement.ActualHeightProperty)) / 2
 
    '楕円の中心点の座標値
    MyAreaLeft = Ellipse1.Margin.Left + MajorAxis
    MyAreaTop = Ellipse1.Margin.Top + MinorAxis
  End Sub

ボタンがタップされた時の処理

このサンプルでは、1個のボタンをタップする都度、センシングの開始と停止を切り替えます。センサーオブジェクトが有効であれば、センサーを停止し、ボタンの文字列を「開始」にします。

一方、センサーオブジェクトが空であれば、加速度センサーの新しいインスタンスを作成して、ボタンの文字列を「停止」にしたうえで、センシングを実行します。

AddHandlerステートメントを用いて、センサーの情報が変化したときに発生するCurrentValueChanged イベントにイベント ハンドラーを追加します。非 UI スレッドのデータを UI スレッド上で処理するため、Dispatcher クラスを使います。

  Private Sub Button1_Tap(sender As Object, e As System.Windows.Input.GestureEventArgs) Handles Button1.Tap
 
    If MyAccelerometer IsNot Nothing AndAlso MyAccelerometer.IsDataValid Then
      Try
        MyAccelerometer.Stop()
        MyAccelerometer = Nothing
        TextBlock1.Text = "実行中の加速度センサーを停止しました。"
        Button1.Content = "開始"
      Catch
        MessageBox.Show("加速度センサーの停止に失敗しました。")
      End Try
    Else
      If MyAccelerometer Is Nothing Then
        MyAccelerometer = New Accelerometer
        AddHandler MyAccelerometer.CurrentValueChanged, Sub(senderValue As Object, eValue As SensorReadingEventArgs(Of AccelerometerReading))
  
Deployment.Current.Dispatcher.BeginInvoke(Sub() CurrentValueChanged(eValue.SensorReading))
                                                    End Sub
      End If
 
      Try
        Button1.Content = "停止"
        TextBlock1.Text = "加速度センサーを開始しました。"
        MyAccelerometer.Start()
      Catch
        MessageBox.Show("加速度センサーを開始できません。")
      End Try
    End If
  End Sub

センサーデータが更新された時の処理

センサーデータが更新された時、X、Y、Zの3つに分解して生データを取得します。この値は基本的に-1~1の範囲になりますが、念のために範囲外の値をカットしておきます。

実機が頭側に傾くほどボールを小さくして遠近感があるように見せるため、Yの値をもとに、ボールの幅と高さを算出して適用します。

楕円の中のボールの可動範囲と角度からボールの移動距離を計算し、楕円の中心点と幅と高さと移動距離から、ボールの表示位置の座標を計算します。

さらに、ボールそのものにも動きを付けます。XとYのセンサーデータをもとにMath.Atanを使って、ボールを回転させます。

ボールの画像に、計算した座標を適用して移動させます。

  Private Sub CurrentValueChanged(MyReading As AccelerometerReading)
    MyAcceleration = MyReading.Acceleration
 
    '加速度の値を取得
    Dim XValue As Double = MyAcceleration.X
    Dim YValue As Double = MyAcceleration.Y
    Dim ZValue As Double = MyAcceleration.Z
 
    '誤差を防ぐため上下をカット
    If MyAcceleration.X > 1 Then
      XValue = 1
    ElseIf MyAcceleration.X < -1 Then
      XValue = -1
    End If
 
    If MyAcceleration.Y > 1 Then
      YValue = 1
    ElseIf MyAcceleration.Y < -1 Then
      YValue = -1
    End If
 
    If MyAcceleration.Z > 1 Then
      ZValue = 1
    ElseIf MyAcceleration.Z < -1 Then
      ZValue = -1
    End If
 
    'ボールの拡大縮小率の計算と指定
    Dim MyBallWidth2, MyBallHeight2 As Double
    MyBallWidth2 = (MyBallWidth * (1 + (-YValue * 0.3)))
    MyBallHeight2 = (MyBallHeight * (1 + (-YValue * 0.3)))
    BallImage.Width = MyBallWidth2
    BallImage.Height = MyBallHeight2
 
    '楕円の可動範囲と角度からボールの移動距離を計算
    Dim MyMoveX2D As Integer = CInt(XValue * (MajorAxis - MyBallWidth2 / 2))
    Dim MyMoveY2D As Integer = CInt(YValue * (MinorAxis - MyBallHeight2 / 2))
 
    '楕円の中心点と幅高さと移動距離から、ボールの表示位置の座標を計算
    Dim MyXPos2D As Integer = CInt(MyAreaLeft - MyBallWidth2 / 2 + MyMoveX2D)
    Dim MyYPos2D As Integer = CInt(MyAreaTop - MyBallHeight2 / 2 - MyMoveY2D)
 
    'ボールを回転
    MyAngleProjection.RotationZ = (Math.Atan(YValue / XValue) * 360 / Math.PI)
 
    'ボールの画像に座標を適用して移動
    BallImage.Margin = New Thickness(MyXPos2D, MyYPos2D, 0, 0)
 
  End Sub
End Class
  • センサーを使ったアプリのサンプル

四国のSOHO。薬師寺国安(VBプログラマ)と、薬師寺聖(デザイナ、エンジニア)によるコラボレーション・ユニット。1997年6月、Dynamic HTMLとDirectAnimationの普及を目的として結成。共同開発やユニット名義での執筆活動を行う。XMLおよび.NETに関する著書や連載多数。最新刊は「Silverlight実践プログラミング」両名とも、Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。http://www.PROJECTKySS.NET/

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています