Windows Phone 7 Motion Sensor 使用指南
1. 为什么要引入Motion API?
在上一篇《Windows Phone 7 数字罗盘使用指南》中,我们已经对WP7中的传感器有所了解,事实上,WP7正是通过这些传感器来获取手机当前的姿态和位置等信息。但是,对于开发者来说,他们所关心的并不是这些数据的值本身,而是这些数据所表示的含义。举个例子,之前做Windows Mobile设备定位应用的时候,一般会涉及到GPS数据的获取。在WM2003平台上,开发者需要自己写串口通信类,实现NEMA数据的解析。分析这个过程,最终我们提供给应用的有意义的数据其实就是经纬度和时间等信息,而并不是原始的NEMA协议数据。因此,微软在WM5.0平台中引入了GPSID(即GPS中间层驱动),对于应用程序来说,GPSID为其提供经纬度等信息;而与GPS硬件打交道的过程,就交给了GPSID。这样一来,应用程序开发者就从中解脱出来了。
那么,在我看来,Motion API所做的事情,其实质就和GPSID类似。在应用程序开发过程中,如果我们开发者获取原始的传感器数据,然后对其数据进行分析,从而去判断手机的姿态和移动方向,这样一个处理流程确实有点复杂,而且有时候也很难处理,如加速度传感器的返回值中,不仅仅包含了手机移动的加速度,还包括了重力加速度(事实上,在Windows Phone OS 7.0上,处理加速度传感器时,我们的确是需要经历上面的过程)。
因此,在Windows Phone OS 7.1(Mango)中,引入了Motion API,用它来对底层的传感器数据进行分析和处理,从而得到开发者需要的信息,如设备的姿态(yaw, pitch, and roll)、旋转加速度和线性加速度。我们可以对原始的加速度传感器信息与Motion API中的加速度传感器信息进行对比,了解它们的区别。如下图1所示,左边是获取加速度传感器原始数据的页面,右边是获取Motion数据的页面。
图1:加速度传感器的原始数据与Motion获取的数据对比
从Y轴的数据来看,原始的加速度传感器数据包含了重力加速度与手机在外力作用下的加速度,而从Motion API获取到的加速度数据来看,它已经为我们排除了重力加速度的影响。因此,对于开发者而言,Motion API有助于简化应用程序的开发。
2. 使用Motion API的前提
首先,需要注意的是,一代的Windows Phone 7手机没有开放Motion API。因此,如果想要在Windows Phone OS 7.0上写基于Motion的应用程序,那是无法实现的。但是,自从这个月初微软提供了Mango升级以后,很多设备厂商都对其生产的WP7推送了Mango更新。在OS升级以后,有些设备就提供了对Motion API的支持。如我的三星Focus i917就支持,因此才有了以下的尝试。下文中的内容,参考了MSDN上的文章:How to: Use the Combined Motion API for Windows Phone。
3. 如何在应用程序中使用Motion API?
这里以silverlight应用程序为例,展示了在应用程序中使用Motion API的方法。
(1)添加对Microsoft.Devices.Sensors与Microsoft.Xna.Framework的引用,如下图2所示:
图2:添加Motion API相关的namespace
(2)在主页面的XAML中,加入6个Textblock,表示手机姿态的yall、pitch、roll和加速度传感器的三个返回值。为了使得表现形式更加得直观,引入三个三角形,用其旋转的角度来表征yall、pitch、roll的数值,同时引入三个相互垂直的轴线,用来表示加速度传感器的值,其代码如下:
1: <!--TitlePanel contains the name of the application and page title-->
2: <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
3: <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
4: <TextBlock x:Name="PageTitle" Text="Simple Motion" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
5: </StackPanel>
6:
7: <StackPanel>
8: <TextBlock Text="attitude" Margin="12,130,0,28" Style="{StaticResource PhoneTextLargeStyle}"/>
9: <Grid Margin="12 0 12 0">
10: <TextBlock Height="30" HorizontalAlignment="Left" Name="yawTextBlock" Text="YAW: 000" VerticalAlignment="Top" Foreground="Red" FontSize="25" FontWeight="Bold"/>
11: <TextBlock Height="30" HorizontalAlignment="Center" Name="pitchTextBlock" Text="PITCH: 000" VerticalAlignment="Top" Foreground="Green" FontSize="25" FontWeight="Bold"/>
12: <TextBlock Height="30" HorizontalAlignment="Right" Name="rollTextBlock" Text="ROLL: 000" VerticalAlignment="Top" Foreground="Blue" FontSize="25" FontWeight="Bold"/>
13: </Grid>
14: <Grid Height="200">
15: <Polygon Name="yawtriangle" Points="45,135 80,50 115,135" Stroke="Red" StrokeThickness="2" >
16: <Polygon.Fill>
17: <SolidColorBrush Color="Red" Opacity="0.3"/>
18: </Polygon.Fill>
19: <Polygon.RenderTransform>
20: <RotateTransform CenterX="80" CenterY="100"></RotateTransform>
21: </Polygon.RenderTransform>
22: </Polygon>
23: <Polygon Name="pitchtriangle" Points="205,135 240,50 275,135" Stroke="Green" StrokeThickness="2" >
24: <Polygon.Fill>
25: <SolidColorBrush Color="Green" Opacity="0.3"/>
26: </Polygon.Fill>
27: <Polygon.RenderTransform>
28: <RotateTransform CenterX="240" CenterY="100"></RotateTransform>
29: </Polygon.RenderTransform>
30: </Polygon>
31: <Polygon Name="rolltriangle" Points="365,135 400,50 435,135" Stroke="Blue" StrokeThickness="2" >
32: <Polygon.Fill>
33: <SolidColorBrush Color="Blue" Opacity="0.3"/>
34: </Polygon.Fill>
35: <Polygon.RenderTransform>
36: <RotateTransform CenterX="400" CenterY="100"></RotateTransform>
37: </Polygon.RenderTransform>
38: </Polygon>
39: </Grid>
40: <TextBlock Text="acceleration" Style="{StaticResource PhoneTextLargeStyle}"/>
41: <Grid Margin="12 0 12 0">
42: <TextBlock Height="30" HorizontalAlignment="Left" Name="xTextBlock" Text="X: 000" VerticalAlignment="Top" Foreground="Red" FontSize="25" FontWeight="Bold"/>
43: <TextBlock Height="30" HorizontalAlignment="Center" Name="yTextBlock" Text="Y: 000" VerticalAlignment="Top" Foreground="Green" FontSize="25" FontWeight="Bold"/>
44: <TextBlock Height="30" HorizontalAlignment="Right" Name="zTextBlock" Text="Z: 000" VerticalAlignment="Top" Foreground="Blue" FontSize="25" FontWeight="Bold"/>
45: </Grid>
46: <Grid Height="300">
47: <Line x:Name="xLine" X1="240" Y1="150" X2="340" Y2="150" Stroke="Red" StrokeThickness="4"></Line>
48: <Line x:Name="yLine" X1="240" Y1="150" X2="240" Y2="50" Stroke="Green" StrokeThickness="4"></Line>
49: <Line x:Name="zLine" X1="240" Y1="150" X2="190" Y2="200" Stroke="Blue" StrokeThickness="4"></Line>
50: </Grid>
51: </StackPanel>
(3)在主页面的MainPage.xaml.cs中,声明一个Motion类的对象。
(4)重写页面的OnNavigatedTo(NavigationEventArgs)方法,检查设备是否支持Motion,初始化Motion对象,添加CurrentValueChanged事件,代码如下:
1: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
2: {
3: // Check to see whether the Motion API is supported on the device.
4: if (! Motion.IsSupported)
5: {
6: MessageBox.Show("the Motion API is not supported on this device.");
7: return;
8: }
9:
10: // If the Motion object is null, initialize it and add a CurrentValueChanged
11: // event handler.
12: if (motion == null)
13: {
14: motion = new Motion();
15: motion.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
16: motion.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<MotionReading>>(motion_CurrentValueChanged);
17: }
18:
19: // Try to start the Motion API.
20: try
21: {
22: motion.Start();
23: }
24: catch (Exception ex)
25: {
26: MessageBox.Show("unable to start the Motion API.");
27: }
28: }
(5)通过周期性地调用CurrentValueChanged事件获得Motion数据,但是该事件是在后台进程中调用,无法对UI元素进行更改。因此,我们需要在UI线程中使用BeginInvoke来调用CurrentValueChanged。
(6)创建CurrentValueChanged方法,在其中设置6个Textblock和4个图的内容。使用XNA Framework中的MathHelper类实现弧度和角度的转换,代码如下:
1: private void CurrentValueChanged(MotionReading e)
2: {
3: // Check to see if the Motion data is valid.
4: if (motion.IsDataValid)
5: {
6: // Show the numeric values for attitude.
7: yawTextBlock.Text = "YAW: " + MathHelper.ToDegrees(e.Attitude.Yaw).ToString("0") + "°";
8: pitchTextBlock.Text = "PITCH: " + MathHelper.ToDegrees(e.Attitude.Pitch).ToString("0") + "°";
9: rollTextBlock.Text = "ROLL: " + MathHelper.ToDegrees(e.Attitude.Roll).ToString("0") + "°";
10:
11: // Set the Angle of the triangle RenderTransforms to the attitude of the device.
12: ((RotateTransform)yawtriangle.RenderTransform).Angle = MathHelper.ToDegrees(e.Attitude.Yaw);
13: ((RotateTransform)pitchtriangle.RenderTransform).Angle = MathHelper.ToDegrees(e.Attitude.Pitch);
14: ((RotateTransform)rolltriangle.RenderTransform).Angle = MathHelper.ToDegrees(e.Attitude.Roll);
15:
16: // Show the numeric values for acceleration.
17: xTextBlock.Text = "X: " + e.DeviceAcceleration.X.ToString("0.00");
18: yTextBlock.Text = "Y: " + e.DeviceAcceleration.Y.ToString("0.00");
19: zTextBlock.Text = "Z: " + e.DeviceAcceleration.Z.ToString("0.00");
20:
21: // Show the acceleration values graphically.
22: xLine.X2 = xLine.X1 + e.DeviceAcceleration.X * 100;
23: yLine.Y2 = yLine.Y1 - e.DeviceAcceleration.Y * 100;
24: zLine.X2 = zLine.X1 - e.DeviceAcceleration.Z * 50;
25: zLine.Y2 = zLine.Y1 + e.DeviceAcceleration.Z * 50;
26: }
27: }
4. 测试结果
对三星Focus i917(已升级到Mango,版本号为7720.68)进行Compass测试,获得的结果如下图3所示:
图3:Focus上的Motion API测试结果
参考链接: