桌面应用程序中的超极本传感器






4.86/5 (4投票s)
在桌面应用程序中使用所有超极本传感器
目录
- 引言
- 背景
- 从 C# 桌面应用程序调用 WinRT API
- 传感器概述
- 加速度计
- 陀螺仪
- 指南针
- 光传感器
- 方向传感器
- 简化方向传感器
- 倾角计
- 使用代码
引言
在为 Windows 8 和英特尔设备开发应用程序时,您有一个独特的机会和决定,决定要开发哪种类型的 Windows 8 应用程序。以下文章帮助开发人员为 Windows 8 桌面编写传感器应用程序 使用 WinRT 传感器 (API)。 现在出现了很多问题:-
为什么要在 Windows 8 桌面应用程序中使用 WinRT api?
如何在 Windows 8 桌面应用程序中使用 WinRT api 来处理传感器?
背景
我试图访问这款运行 Windows 8 的英特尔超极本中内置的一些传感器。然而,虽然 Windows 7 及更高版本的 .NET 4 库中内置了对位置传感器的支持,但我希望访问 Windows 8 本身内置的完整传感器和位置平台。这些 API 可通过 COM 获得,我可以通过 COM 调用它们,但通过 WinRT 层调用它们要好得多。此外,这有点是 WinRT 存在的原因。
这让我开始思考 WinRT。就像 C 语言有 C 运行时提供一堆支持函数并为其定义调用约定一样,Windows 运行时 (WinRT) 也为 Windows 及其语言提供。这些 API 和运行时包括有关调用约定的元数据,这些元数据使 WinRT API 比 COM 更容易调用。
从 C# 桌面应用程序调用 WinRT API
在桌面项目中,默认情况下不显示“核心”选项卡。用户可以通过打开项目节点的快捷菜单,选择“卸载项目”,添加以下代码段,再次打开项目节点的快捷菜单,然后选择“重新加载项目”来选择针对 Windows 运行时进行编码。
现在,当用户从项目调用“引用管理器”对话框时,“核心”选项卡将出现。
<PropertyGroup> <TargetPlatformVersion>8.0</TargetPlatformVersion> </PropertyGroup>
但是,当我编译应用程序时,我在尝试连接事件处理程序的行上收到错误。用于添加多播委托的“+=”语言糖不起作用。
为了解决这个问题并使我的应用程序中加载的相应程序集支持从我的桌面应用程序调用 WinRT,我需要添加对 System.Runtime 和 System.Runtime.InteropServices.WindowsRuntime.dll 的引用。它在我的系统上的 C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5 中。
添加引用 > 浏览 > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
传感器概述
基本上,英特尔超极本附带四个传感器设备:
- 环境光传感器
- 加速度计
- 陀螺仪
- 磁力计
鉴于这四个传感器在硬件层面的存在,我们可以在应用程序层面以各种方式利用它们:
- 加速度计
- 陀螺仪
- 指南针
- 光传感器
- 方向传感器
- 简化方向传感器
- 倾角计
在 3D 笛卡尔坐标系中定位
每当我们打算感知与设备在我们物理真实世界中的实际定位相关的数值时,我们需要将某个几何模型作为我们可以参考的基础。例如,我们可能想要区分设备是从左到右移动还是向上和向下移动。这意味着我们需要将设备与我们三维空间中的一组轴对齐。
无论我们想到哪种 3D 传感器接口,我们都可以始终以三个轴为基础
- X:长边(左/右)
- Y:短边(上/下)
- Z:“进入”屏幕
传感器接口和功能
MSDN 已经提供了一些 示例
C# 代码,说明如何使用 WinRT 的传感器 API,这看起来相当简单,本节中我们将专门关注特定传感器的实际功能。我们知道此代码示例仅考虑加速度计接口的使用(源代码包含所有传感器),但由于下面列出的所有传感器都可以类似地应用,因此我们相信我们的受众也可以应用其他传感器,而无需任何可观的开销。
加速度计
返回 |
测量 |
单位 |
类型 |
X |
加速 |
G 力 |
双精度浮点型 |
Y |
加速 |
G 力 |
双精度浮点型 |
Z |
加速 |
G 力 |
双精度 |
加速度计感知设备相对于自由落体的加速度;这意味着,我们不能(至少在地球上或没有零重力模拟的情况下)同时实现所有三个轴的结果都为零的状态。例如,假设我们的 Surface 平板电脑平躺着,返回的 z 值将为 -1,因为 1g 是将我们的设备拉向地心的标准 g 力(类似地,如果设备正面朝上,z 值将为 +1)。
陀螺仪
返回 |
测量 |
单位 |
类型 |
X |
Angular |
度 |
双精度 |
Y |
Angular |
度 |
双精度浮点型 |
Z |
Angular |
度 |
双精度浮点型 |
当陀螺仪(也称为回转仪)感应我们设备在移动时的角速度时,当我们静止不动地握持它时,所有三个轴的值都将为零。同时,只有当我们打开和关闭 Surface 屏幕而不将其移动到任何其他方向时,x 值才会改变(因为我们正在绕其 x 轴旋转设备)。
指南针
返回 |
测量 |
单位 |
类型 |
磁性 |
Angle |
度 |
双精度浮点型 |
True |
Angle |
度 |
双精度? |
虽然磁北(以及设备当前到该方向的角度)可以通过内置磁力计确定,但这个单一的传感器设备无法同时感知地理(或真)北,因为磁北和真北通常不同。由于 Surface RT 平板电脑没有 GPS 传感器,我们最初假设指南针接口可能会利用平板电脑的互联网连接来近似真北——不幸的是,我们无法接收到数值。
光传感器
请注意,Surface 的光传感器位于前置摄像头左侧。将手放在传感器前面,您可以看到传感器会产生不同的结果。
方向传感器
返回 |
测量 |
单位 |
类型 |
四元数 |
Orientation |
4×1 |
SensorQuaternion |
旋转 |
Orientation |
3×3 |
SensorRotationMatrix |
方向传感器接口公开了一个四元数和一个旋转矩阵,指示设备的当前方向,两者都可以在(类似地)旋转视图元素时使用。由于深入研究计算机图形学超出了本文的范围,我们将避免详细解释底层数学概念(尽管如此,请随意自行查阅这些概念)。
简单方向传感器
返回 |
类型 |
成员 |
简单 |
枚举 |
未旋转 |
逆时针旋转 90 度 |
||
逆时针旋转 180 度 |
||
逆时针旋转 270 度 |
||
面朝上 |
||
面朝下 |
该传感器通过将复杂数字传感器值(由实际方向传感器提供)自动转换为一组离散的、人类可读的状态,简化了对设备当前方向的感知。
倾角计
返回 |
测量 |
单位 |
类型 |
Pitch |
Angle |
度 |
双精度浮点型 |
Roll(滚动) |
Angle |
度 |
双精度浮点型 |
偏航 |
Angle |
度 |
双精度浮点型 |
由于倾角计传感器提供有关设备相对于其各自轴的角度的信息,因此设备假设的初始位置问题随之产生:为了获得独特的初始位置,倾角计利用磁力计。因此,承认一定的差异,我们可以观察到,如果我们的设备与磁北完美对齐,则 Pitch=Roll=Yaw=0(类似地,如果 MagneticNorth=0,则 Yaw=0)。
由于倾角计传感器提供有关设备相对于其各自轴的角度的信息,因此设备假设的初始位置问题随之产生:为了获得独特的初始位置,倾角计利用磁力计。因此,承认一定的差异,我们可以观察到,如果我们的设备与磁北完美对齐,则 Pitch=Roll=Yaw=0(类似地,如果 MagneticNorth=0,则 Yaw=0)。
使用代码
出于教学目的,我在单个应用程序中使用了所有 7 个传感器。 代码可以分为以下几个部分:-
- 创建一个类来初始化传感器对象。
- 继承控件类,使其可以用作用户控件。
- 描述 DependencyProperty 以通知属性更改。
- 绑定到传感器的 Reading Change 事件。
- 异步更新 UI 随更改而改变。
步骤 1
Sensorbase.cs
它是一个抽象类,继承控件类并定义依赖属性。
public abstract class SensorBase : Control { protected static readonly DependencyProperty ReportIntervalProperty = DependencyProperty.Register( "ReportInterval", typeof (int), typeof (SensorBase), new PropertyMetadata(1, ReportIntervalPropertyPropertyChanged)); public int ReportInterval { get { return (int) GetValue(ReportIntervalProperty); } set { SetValue(ReportIntervalProperty, value); } } protected abstract void OnReportIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e); private static void ReportIntervalPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var sender = d as SensorBase; sender.OnReportIntervalChanged(d, e); } }
第二步
AccelerometerSensor.cs
AccelerometerSensor 类继承 SensorBase 类。在这个类中,我首先创建传感器
对象并将其附加到 ReadingChanged 事件。
private readonly Accelerometer _accelerometer; _accelerometer = Accelerometer.GetDefault(); if (_accelerometer != null) { _accelerometer.ReadingChanged += AccelerometerReadingChanged; }
步骤 3
描述加速度计的 X、Y、Z 轴的依赖属性。public static readonly DependencyProperty XProperty = DependencyProperty.Register( "X", typeof (double), typeof (AccelerometerSensor), new PropertyMetadata(0.0)); public static readonly DependencyProperty YProperty = DependencyProperty.Register( "Y", typeof (double), typeof (AccelerometerSensor), new PropertyMetadata(0.0)); public static readonly DependencyProperty ZProperty = DependencyProperty.Register( "Z", typeof (double), typeof (AccelerometerSensor), new PropertyMetadata(0.0));
这里 X、Y、Z 描述如下
public double X { get { return (double) GetValue(XProperty); } set { SetValue(XProperty, value); } } public double Y { get { return (double) GetValue(YProperty); } set { SetValue(YProperty, value); } } public double Z { get { return (double) GetValue(ZProperty); } set { SetValue(ZProperty, value); } }
步骤 4:
是时候异步读取传感器数据了。private async void AccelerometerReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args) { await this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() => { X = args.Reading.AccelerationX; Y = args.Reading.AccelerationY; Z = args.Reading.AccelerationZ; })); }
在上述代码中,方法 AccelerometerReadingChanged 被描述为 async,读取代码被描述为 await,以异步处理任务。并且 Dispatcher.BeginInvoke 用于将加速度值写入其父线程。
步骤 5:
现在,加速度计传感器控制已就绪,我们可以将其放置在 UI 上,并在程序的任何位置使用其 X、Y、Z 轴数据。 xmlns:local="clr-namespace:SensorsUltrabook" <local:AccelerometerSensor Template="{StaticResource AccelerometerSensorTemplate}" />不要忘记将样式应用于加速度计控件。
<ControlTemplate x:Key="AccelerometerSensorTemplate" TargetType="local:AccelerometerSensor"> <StackPanel Orientation="Vertical" Margin="15,15,15,15"> <TextBlock Text="ACCELEROMETER" /> <Image Source="{StaticResource AccelerometerImage}" Width="288" Height="300"/> <StackPanel Orientation="Horizontal"> <TextBlock Text=" x:" /> <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=X}" /> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text=" y:" /> <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Y}" /> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text=" z:" /> <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Z}" /> </StackPanel> </StackPanel> </ControlTemplate>
在代码中,每个传感器都被描述为单独的 CS 文件中的一个控件。