Windows Phone 7 - 了解Sensor - Combined Motion API

Windows Phone 7 - 了解Accelerometer Sensor概念

最近看到很多App多少都有加上一些體感的東西,讓App充滿樂趣與新的玩法,因此,我也想要來了解一下,

怎麼在WP7提供那些Sensor來協助開發人員實作體感的操作。在那之前照舊一樣要把原理跟可用的SDK做一個說明。

 

〉WP7目前支援的Sensor類型

     根據<Sensors Overview for Windows Phone>整理下來,目前支援Sensor的類型,如下表:

類型 說明
Accelerometer 加速度感應器用於測量設備在時間裡的運動,識別用戶如何移動設備、移動的方向
acceleration value的值來自三個維度:X、Y、Z軸。透過這三個值識別設備的方向性。
例如:Z軸 = –1g時,代表手機螢幕向上;Y軸 = -1g時,代表手機直立於桌面上。

透過Accelerometer類別以取得手機的運作所產生的三個維度與重力。
Compass 指南針或稱磁力儀(magnetometer),用於確定設備相對地球的北磁極所旋轉的角度(angle)。可透過該原始的磁力計數值(raw magnetometer readings)檢測設備周圍的磁力。

注意該Sensor並非Windows Phone手機必備的元件,如果開發的Application必需用的它時,需在程式裡進行判斷以確保可用性,或提供其他方式輸入值來完成功能。
Gyroscope 陀羅儀用於測量設備在每一個軸的旋轉速度。可用測量值獲得設備在空間裡的方向。

如果對Gyroscope測量的值怎麼算出角度,可透過Motion類別以取代Gyroscope API所測量的結果。

注意該Sensor並非Windows Phone手機必備的元件,如果開發的Application必需用的它時,需在程式裡進行判斷以確保可用性,或提供其他方式輸入值來完成功能。
Combined Motion 該API提供了收集所有Sensor回傳的資料,協助進行處理,包括了:方向、運動的資訊。
上述三種API的值,均可以透過整合的API一併取得。

例如:透過Motion類別,可同時取得設備中多個Sensors的值,往下透過acceleration的值去分離重力加速度,進一步計算設備目前的偏航、俯仰和滾轉角度。

注意Motion API使用二種不同的Sensor配置
一般運動模式:使用Compass與Accelerometer
進階運動模式:使用Compass、Accelerometer與Gyroscope
同樣的,如果您需要用進階運動模式,記得檢查設備是否具有Gyroscope Sensor。

透過上表的功能,可協助識別目前手機的方向與運動的方式,進一步將這些Sensor的結果當成另一種Input的方式,

啟動對應的功能與服務增加用戶體驗。

另外,這些Sensor APIs均基於SensorBase<TSensorReading>類別往下實作,遵守一個簡單的模式,以啟動並運用該API。

其中,Combined Motion的好處是將上述三個API所收到最原始數據,透過Motion類別加以處理,讓開發人員可以直接

取得需要的數據,包括:設備的屬性(偏航,俯仰和滾轉)、旋轉加速度、線性加速度…等,所以如果您跟我一樣對空間

概念或計算物理性不是很懂得的話,建議使用Motion API會節省不少時間,進階的可直接使用另外三個API。

 

[注意]

‧Sensor APIs不支援在鎖定螢幕下(under the lock screen)進行。<Idle Detection for Windows Phone>

‧Sensor APIs不能使用於Background Agents。<Background Agents Overview for Windows Phone>

 

======

上述大致說明了WP7在Sensor上支援的類型後,該篇先針對Combined Motion API做一個使用上的說明:

〉Combined Motion API

     透過上表說明,對於Motion API有了一定的了解,那麼Motion API究竟包括了那些屬性與方法,該怎麼來使用它呢。

(1) Motion API

     該類別主要提供應用程式,目前設備的方向、運動資訊。它收集Accelerometer、Compass、Gyroscope的原始資訊,

將這些資料透過easy-to-use的屬性、事件,讓開發人員得以取得資料並且邏輯的處理,形成另一種Input mechanism。

 

(A) 重點屬性

屬性 說明
IsSupported 設定/取得設備是否支援Motion執行時所需要的感應器。該屬性不需要實例化Motion類型可直接使用。
IsDataValid 取得感應器的資料是否具有效性。
CurrentValue 取得一個實作ISensorReading的物件,該物件包含感應器所收集的當前值。該物件可能是下列類型的其中一種:
AccelerometerReading
CompassReading
GyroscopeReading
MotionReading
TimeBetweenUpdates 設定/取得CurrentValueChanged事件之間發生的首選時間(preferred time)。

上述這些屬性均是在實作應用程式時,一定會使用的,包括檢查設備是否支援Motion,是否取得的資料是有效的,

進一步使用當前的值去進行處理。

 

(B) 重點事件與方法

方法 說明
Start 開始從感應器收集數據資料。要擷取資料記得註冊CurrentValueChanged事件。
Stop 停止從感應器收集數據資料。
Dispose 釋放託管或未託管的感應器資源。
Finalize 在Object被GC釋放前,允許Object嘗試去釋放資源並執行其他清理操作。

由於Sensor會更新頻率太頻繁會造成電池大量消耗,因此Dispose與Finalize可搭配使用。

 

事件 說明
Calibrate 該事件發生於Compass需要校準(calibration)時,會被觸發。
CurrentValueChanged 該事件發生當有新的數據資料被Sensor擷取到時。
發生的頻率,根據TimeBetweenUpdates屬性設定時間而定。
不同的Sensor在該事件取得的參數有所不同:
‧SensorReadingEventArgs<AccelerometerReading>
‧SensorReadingEventArgs<CompassReading>
‧SensorReadingEventArgs<GyroscopeReading>
‧SensorReadingEventArgs<MotionReading>

 

(C) 重點類別

SensorBase<TSensorReading>

     在上述說明屬性、方法與事件時,一直看到這些功能很多均繼承SensorBase<TSensorReading>類別,那麼它到底是什麼呢?

它是所有Sensor的最基礎類別,實作了驗證數據資料的有效性、取得數據資料、發生數值改變的擷取時機…等,因此,當使

用Accelerometer或其他Sensor時,它們則實作了Reading的內容,才能讓開發人員取得實際的數據。

 

‧SensorReadingEventArgs<MotionReading>

     SensorReadingEventArgs<T> Class該類別提供CurrentValueChanged事件主要觸發來源的參數資料,不同的Sensor來源帶的T

有所不同。以該篇而言,T則為:MotionReading。往下進一步看MotionReding的結構:

屬性 說明
Attitude 取得設備的弧度,包括:偏航(yaw)、俯仰(pitch)、滾動(roll)弧度。
DeviceAcceleration 取得設備的線性加速度(linear acceleration),以G力(gravitational)為單位。
DeviceRotationRate 取得設備的旋轉速度,以每秒的弧度為單位。
Gravity 取得與MotionReading相關的重力載體物件。
Timestamp 取得MotionReading計算的時間戳的時間,這可用於關聯整個Sensor取得數據與計算原始資料的演算法。

 

〉實作範例:<擷錄How to: Use the Combined Motion API for Windows Phone的內容>

由於要使用Motion API需要實體設備,並且最少要支援Compass的Sensor,所以HTC Radar沒有支援Compass,因此,建議台灣

的開發者可用HTC Mozart來測試。以下擷取How to: Use the Combined Motion API for Windows Phone的內容功能加以說明。

 

(1) 註冊Motion API

   1: Motion gMotion = null;
   2:  
   3: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
   4: {
   5:     base.OnNavigatedTo(e);
   6:     //在進入程式時識別,如果不支援Motion API則不可以使用。
   7:     if (Motion.IsSupported == false)
   8:     {
   9:         MessageBox.Show("the Motion API is not supported on this device.");
  10:         return;
  11:     }
  12:  
  13:     if (gMotion == null)
  14:     {
  15:         gMotion = new Motion();
  16:         //設定TimeBetweenUpdates的時間
  17:         gMotion.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
  18:         gMotion.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<MotionReading>>(Motion_CurrentValueChanged);
  19:     }
  20:  
  21:     try
  22:     {
  23:         //啟動Motion API
  24:         gMotion.Start();
  25:     }
  26:     catch (Exception ex)
  27:     {
  28:         MessageBox.Show("unable to start the Motion API.");
  29:     }
  30: }

 

(2) 加上要呈現的Attitude與DeviceAcceleration的XAML畫面

   1: <!--ContentPanel - place additional content here-->
   2: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="0,0,0,0">
   3: <StackPanel>
   4:     <!-- 設定顯示Motion Attitude效果的範圍 -->
   5:     <TextBlock Text="attitude" Style="{StaticResource PhoneTextLargeStyle}"/>
   6:     <Grid Margin="12 0 12 0">
   7:         <TextBlock Height="30" HorizontalAlignment="Left"  Name="yawTextBlock" Text="YAW: 000" VerticalAlignment="Top" Foreground="Red" FontSize="25" FontWeight="Bold"/>
   8:         <TextBlock Height="30" HorizontalAlignment="Center"  Name="pitchTextBlock" Text="PITCH: 000" VerticalAlignment="Top" Foreground="Green" FontSize="25" FontWeight="Bold"/>
   9:         <TextBlock Height="30" HorizontalAlignment="Right"   Name="rollTextBlock" Text="ROLL: 000" VerticalAlignment="Top"  Foreground="Blue" FontSize="25" FontWeight="Bold"/>
  10:     </Grid>
  11:     <Grid Height="200">
  12:         <!-- 繪製三角型 -->
  13:         <Polygon Name="yawtriangle"
  14:               Points="205,135 240,50 275,135"
  15:               Stroke="Red"
  16:               StrokeThickness="2">
  17:             <Polygon.Fill>
  18:                 <SolidColorBrush Color="Red" Opacity="0.3"/>
  19:             </Polygon.Fill>
  20:             <Polygon.RenderTransform>
  21:                 <RotateTransform CenterX="240" CenterY="100"></RotateTransform>
  22:             </Polygon.RenderTransform>
  23:         </Polygon>
  24:         <Polygon Name="pitchtriangle"
  25:               Points="205,135 240,50 275,135"
  26:               Stroke="Green"
  27:               StrokeThickness="2">
  28:             <Polygon.Fill>
  29:                 <SolidColorBrush Color="Green" Opacity="0.3"/>
  30:             </Polygon.Fill>
  31:             <Polygon.RenderTransform>
  32:                 <RotateTransform CenterX="240" CenterY="100"></RotateTransform>
  33:             </Polygon.RenderTransform>
  34:         </Polygon>
  35:         <Polygon Name="rolltriangle"
  36:               Points="205,135 240,50 275,135"
  37:               Stroke="Blue"
  38:               StrokeThickness="2" >
  39:             <Polygon.Fill>
  40:                 <SolidColorBrush Color="Blue" Opacity="0.3"/>
  41:             </Polygon.Fill>
  42:             <Polygon.RenderTransform>
  43:                 <RotateTransform CenterX="240" CenterY="100"></RotateTransform>
  44:             </Polygon.RenderTransform>
  45:         </Polygon>
  46:     </Grid>
  47:     <!-- 設定顯示Motion DeviceAcceleration效果的範圍 -->
  48:     <TextBlock Text="acceleration" Style="{StaticResource PhoneTextLargeStyle}"/>
  49:     <Grid Margin="12 0 12 0">
  50:         <TextBlock Height="30" HorizontalAlignment="Left"  Name="xTextBlock" Text="X: 000" VerticalAlignment="Top" Foreground="Red" FontSize="25" FontWeight="Bold"/>
  51:         <TextBlock Height="30" HorizontalAlignment="Center"  Name="yTextBlock" Text="Y: 000" VerticalAlignment="Top" Foreground="Green" FontSize="25" FontWeight="Bold"/>
  52:         <TextBlock Height="30" HorizontalAlignment="Right"   Name="zTextBlock" Text="Z: 000" VerticalAlignment="Top"  Foreground="Blue" FontSize="25" FontWeight="Bold"/>
  53:     </Grid>
  54:     <Grid Height="300">
  55:         <!-- 繪製代表accelerometer運作的三個軸 -->
  56:         <Line x:Name="xLine" X1="240" Y1="150" X2="340" Y2="150" Stroke="Red" StrokeThickness="4"></Line>
  57:         <Line x:Name="yLine" X1="240" Y1="150" X2="240" Y2="50" Stroke="Green" StrokeThickness="4"></Line>
  58:         <Line x:Name="zLine" X1="240" Y1="150" X2="190" Y2="200" Stroke="Blue" StrokeThickness="4"></Line>
  59:     </Grid>
  60: </StackPanel>
  61: </Grid>

 

(3) 實作處理Attitude屬性的事件

   1: void Motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
   2: {
   3:     // This event arrives on a background thread. Use BeginInvoke to call
   4:     // CurrentValueChanged on the UI thread.
   5:     Dispatcher.BeginInvoke(() => CurrentValueChanged(e.SensorReading));
   6: }
   7:  
   8: private void CurrentValueChanged(MotionReading e)
   9: {
  10:     // Check to see if the Motion data is valid.
  11:     if (gMotion.IsDataValid)
  12:     {
  13:         //處理Attitude
  14:         HandleAttitude(e.Attitude);        
  15:     }
  16: }
  17:  
  18: private void HandleAttitude(AttitudeReading pReading)
  19: {
  20:     // Show the numeric values for attitude.
  21:     yawTextBlock.Text = "YAW: " + MathHelper.ToDegrees(pReading.Yaw).ToString("0") + "°";
  22:     pitchTextBlock.Text = "PITCH: " + MathHelper.ToDegrees(pReading.Pitch).ToString("0") + "°";
  23:     rollTextBlock.Text = "ROLL: " + MathHelper.ToDegrees(pReading.Roll).ToString("0") + "°";
  24:  
  25:     // Set the Angle of the triangle RenderTransforms to the attitude of the device.
  26:     ((RotateTransform)yawtriangle.RenderTransform).Angle = MathHelper.ToDegrees(pReading.Yaw);
  27:     ((RotateTransform)pitchtriangle.RenderTransform).Angle = MathHelper.ToDegrees(pReading.Pitch);
  28:     ((RotateTransform)rolltriangle.RenderTransform).Angle = MathHelper.ToDegrees(pReading.Roll);
  29: }

根據取得的Attitude去調整畫面三角型的三個弧度類型,透過設定RenaderTransform的Angle屬性進行呈現。

 

(4) 實作處理DeviceAcceleration屬性的事件

   1: void Motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
   2: {
   3:     // This event arrives on a background thread. Use BeginInvoke to call
   4:     // CurrentValueChanged on the UI thread.
   5:     Dispatcher.BeginInvoke(() => CurrentValueChanged(e.SensorReading));
   6: }
   7:  
   8: private void CurrentValueChanged(MotionReading e)
   9: {
  10:     // Check to see if the Motion data is valid.
  11:     if (gMotion.IsDataValid)
  12:     {
  13:         //處理DeviceAcceleration
  14:         HandleDeviceAcceleration(e.DeviceAcceleration);
  15:     }
  16: }
  17:  
  18: private void HandleDeviceAcceleration(Vector3 pDeviceVector3)
  19: {
  20:     // Show the numeric values for acceleration.
  21:     xTextBlock.Text = "X: " + pDeviceVector3.X.ToString("0.00");
  22:     yTextBlock.Text = "Y: " + pDeviceVector3.Y.ToString("0.00");
  23:     zTextBlock.Text = "Z: " + pDeviceVector3.Z.ToString("0.00");
  24:  
  25:     // Show the acceleration values graphically.
  26:     xLine.X2 = xLine.X1 + pDeviceVector3.X * 100;
  27:     yLine.Y2 = yLine.Y1 - pDeviceVector3.Y * 100;
  28:     zLine.X2 = zLine.X1 - pDeviceVector3.Z * 50;
  29:     zLine.Y2 = zLine.Y1 + pDeviceVector3.Z * 50;
  30: }

根據取得的DeviceAcceleration去調整畫面三軸線長度,透過調整三條軸線的X或Y屬性進行呈現。

 

(5) 執行畫面

000111

第一張圖:檢查設備Mozart支援Accelerometer、Compass與Motion;第二圖顯示透過Motion API調整YAW、PITCH、ROLL

三種角度與Acceleration的值,不過Motion API取得Acceleration的值會根據Motion API所整理過的結果,值會比直接取得

Accelerometer API的有些許差異。

 

[補充]

〉應用程式使用Sensor API,對於上架至Marketplace的影響

     當用戶想下載一個應用程式時,該應用程式用到的Sensor該設備所沒有的,在Marketplace會出現如下圖:

     N

因此,對於Marketplace怎麼檢查設備是否有支援應用程式裡所使用到的Sensor有幾個依據:

(a) 如果應用程式使用Motion API,那至少該設備需要支援Compass Sensor;(如果有Gyroscope會有更好數據)

(b) 如果應用程式直接使用的Accelerometer、Compass或Gyroscope Sensor,只要缺一就會出現

 

〉檢查設備支援那些Sensor的方法

   1: #region 檢查設備支援那些Sensor的方法
   2: /// <summary>
   3: /// 確認設備支援那些Sensor。
   4: /// </summary>
   5: private void CheckSensor()
   6: {
   7:     string tMsg = string.Format("Accelerometer: {0}\nCompass: {1}\nGyroscope: {2}\nMotion: {3}",
   8:                         Accelerometer.IsSupported, 
   9:                         Compass.IsSupported,
  10:                         Gyroscope.IsSupported,
  11:                         Motion.IsSupported);
  12:     MessageBox.Show(tMsg, "Sensor Supports", MessageBoxButton.OK);
  13: }
  14: #endregion

 

======

以上是分享Combined Motion API的使用,可惜我的HTC Radar只有支援Accelerometer感應器,所以範例借別人的手機玩。

說實在話,如果在撰寫過程中,遇到像我一樣沒有什麼空間概念的話,建議拿出一個實體物直接練習,有助於快速了解空

間的概念,進一步理解Accelerometer 中的x, y ,z三軸的應用。希望該篇對大家有些幫助,謝謝。

 

References:

Sensors for Windows Phone

Sensors Overview for Windows Phone

How to: Get Data from the Accelerometer Sensor for Windows Phone

How to: Get Data from the Compass Sensor for Windows Phone

How to: Get Data from the Gyroscope Sensor for Windows Phone

How to: Use the Combined Motion API for Windows Phone

Windows Phone > 邊做邊學 Windows Phone 7 開發 > 手機操控方式

Windows Phone 7 Motion Sensor 使用指南

什么是Motion Sensor? & 你不可不知的Mango — 开发者篇(1)

 

Dotblogs Tags: