1. 项目概述:从吃灰的体感设备到Unity的交互利器
几年前,Kinect v2作为一款革命性的体感设备,曾让无数开发者和玩家为之兴奋。它不仅能捕捉全身骨骼动作,还能识别手势、语音,甚至进行面部表情分析。但随着微软官方逐步停止对其消费级硬件的支持,很多人的Kinect v2都成了柜子里的“收藏品”。然而,在特定的开发领域,尤其是需要低成本、高精度人体动作捕捉的场景里,Kinect v2依然是一块“宝”。它那套包含深度、彩色、红外和骨骼数据的完整传感器套件,至今仍能提供远超普通摄像头的交互信息。
这个项目的核心,就是让这块“老宝贝”在当下最流行的实时内容创作引擎——Unity中重新焕发生机。我们不再依赖那些可能已经过时或不再维护的第三方插件,而是直接使用微软官方提供的Kinect for Windows SDK 2.0,将其原生能力与Unity引擎进行深度集成。这听起来可能有点“硬核”,但实际走下来,你会发现这条路不仅更稳定、功能更完整,而且能让你对底层数据流有完全的控制权,这对于需要定制化交互逻辑的应用(比如数字孪生中的虚拟人驱动、医疗康复训练模拟、沉浸式艺术装置)来说至关重要。
简单来说,我们要做的是搭建一座桥梁:桥的一头是Kinect v2硬件通过USB 3.0接口源源不断吐出的原始数据流,另一头是Unity场景中等待被驱动的GameObject。这座桥的基石,就是Microsoft Kinect SDK。通过这个项目,你将掌握如何配置开发环境、如何将SDK的C++/C#库与Unity的C#脚本进行交互、如何处理多线程数据流,并最终实现一个稳定、低延迟的骨骼跟踪与可视化应用。无论你是想做一个体感游戏原型,还是为严肃的行业应用构建交互模块,这套流程都是坚实的第一步。
2. 环境准备与SDK集成:搭建稳固的开发地基
在开始写任何代码之前,搭建一个正确且干净的环境是成功的一半。Kinect v2对系统环境的要求比较特定,任何一步的疏漏都可能导致后续莫名其妙的错误。
2.1 硬件与系统环境核查
首先,确保你的硬件和系统符合最低要求,这绝非走过场:
- Kinect v2设备:确认是Xbox One版或Windows版Kinect v2,注意它需要一个独立的电源适配器。
- 主机接口:必须是USB 3.0接口。Kinect v2的数据吞吐量巨大,USB 2.0的带宽完全无法满足,连接后会直接报错。如果你用的是台式机,优先使用主板原生的USB 3.0接口(通常是蓝色的);使用笔记本或扩展坞时,务必确认其USB口是真正的3.0标准。
- 操作系统:Windows 8、8.1或10(64位)。Windows 11理论上可以,但可能需要以兼容模式运行安装程序。强烈建议使用Windows 10,这是经过最广泛测试的平台。
- 开发环境:Visual Studio 2015或更高版本(社区版即可),用于编译SDK中的示例和可能需要的本地插件。Unity版本建议使用2019.4 LTS或2021.3 LTS等长期支持版,稳定性优先。
注意:在连接Kinect之前,先安装好SDK和驱动。不要先插上设备让Windows自动寻找驱动,这很可能安装上不兼容的通用驱动,导致后续SDK无法识别设备。
2.2 安装Kinect for Windows SDK 2.0
这是最核心的一步。你需要从微软的官方页面下载“Kinect for Windows SDK 2.0”。安装过程基本是“下一步”到底,但有几个关键点:
- 安装路径:默认路径即可(通常是
C:\Program Files\Microsoft SDKs\Kinect\v2.0_xxx)。记住这个路径,稍后我们需要从这里引用必要的库文件。 - 安装内容:SDK会同时安装开发所需的头文件、库文件、运行时组件(Kinect Service)以及一系列宝贵的工具和示例代码。特别是
KinectStudio v2.0这个工具,它可以直接录制和回放Kinect的数据流,对于调试和离线开发无比重要。 - 环境变量:安装程序通常会为你设置好系统环境变量。安装完成后,可以打开命令提示符,输入
kinect并按Tab键,如果能自动补全为KinectStudio等命令,说明环境变量已生效。
安装完成后,建议重启一次电脑,确保Kinect服务(Kinect Service)正常启动。之后,再使用USB 3.0数据线连接Kinect v2主机和电源适配器。正常情况下,设备上的白色状态灯会亮起,在Windows的设备管理器中,你会在“声音、视频和游戏控制器”以及“图像设备”下看到“Kinect for Windows”的相关设备,且没有黄色叹号。
2.3 在Unity项目中集成SDK库
Unity本身不能直接调用Kinect SDK的C++库,我们需要通过一个“桥接”层。最直接的方式是使用SDK提供的Microsoft.Kinect.dll。这个DLL是一个托管库(Managed DLL),Unity的C#脚本可以直接引用它。
具体操作步骤如下:
- 在你的Unity项目Assets目录下,创建一个名为
Plugins的文件夹(如果不存在)。这是Unity识别外部库的标准位置。 - 导航到SDK的安装目录(例如
C:\Program Files\Microsoft SDKs\Kinect\v2.0_1409\Assemblies),找到Microsoft.Kinect.dll和Microsoft.Kinect.Face.dll(如果你需要面部追踪)。 - 将这两个DLL文件复制到Unity项目的
Assets/Plugins文件夹中。 - 回到Unity编辑器,Unity会自动导入这些DLL。你可能会看到一些关于.NET版本的提示,通常保持默认设置即可。
现在,你可以在任何C#脚本的开头通过using Microsoft.Kinect;来引入Kinect的命名空间,并开始调用其API了。但是,这仅仅是托管代码部分。Microsoft.Kinect.dll底层仍然依赖于一系列C++编写的原生DLL(如Kinect20.dll)。这些原生DLL在SDK安装时已经被注册到系统,或者位于SDK的Redist目录下。为了确保我们的Unity打包成exe后能在其他电脑上运行,我们需要将这些原生DLL也一并打包。
- 在
Assets/Plugins下,根据你的目标平台(x86或x64)创建子文件夹。对于大多数现代64位系统,创建Assets/Plugins/x86_64文件夹。 - 从SDK安装目录的
Redist文件夹中,找到对应的DLL文件(如Kinect20.dll,Kinect20.Face.dll等),复制到Assets/Plugins/x86_64中。这样,在构建项目时,Unity会将这些DLL包含在输出目录中。
至此,Unity与Kinect SDK的集成环境就搭建完毕了。你可以通过编写一个简单的测试脚本,尝试初始化KinectSensor对象来验证集成是否成功。
3. 核心数据流处理与多线程架构设计
Kinect v2同时提供多种数据流:彩色流(1920x1080)、深度流(512x424)、红外流(512x424)以及最重要的骨骼(Body)流(最多同时追踪6个人,每人25个关节点)。高效、稳定地处理这些并发数据流,是保证应用流畅度的关键。Unity的主循环运行在单一线程(主线程)上,而Kinect的数据采集是另一个线程。我们必须妥善处理这种跨线程的数据交换。
3.1 初始化与数据读取器配置
一切始于KinectSensor对象。通常我们会设计一个单例管理器(如KinectManager)来统一管理传感器的生命周期。
using Microsoft.Kinect; using System.Threading; using UnityEngine; public class KinectManager : MonoBehaviour { private static KinectManager _instance; public static KinectManager Instance => _instance; private KinectSensor _kinectSensor; private MultiSourceFrameReader _multiSourceFrameReader; private Body[] _bodies = null; // 用于存储骨骼数据 private object _bodyLock = new object(); // 用于线程安全的锁 void Awake() { if (_instance != null && _instance != this) { Destroy(this.gameObject); return; } _instance = this; DontDestroyOnLoad(this.gameObject); InitializeKinect(); } private void InitializeKinect() { // 获取默认的Kinect传感器 _kinectSensor = KinectSensor.GetDefault(); if (_kinectSensor == null) { Debug.LogError("未检测到Kinect v2设备!"); return; } // 打开多源帧读取器,指定我们需要的数据类型 _multiSourceFrameReader = _kinectSensor.OpenMultiSourceFrameReader(FrameSourceTypes.Body | FrameSourceTypes.Color | FrameSourceTypes.Depth); // 订阅帧到达事件 _multiSourceFrameReader.MultiSourceFrameArrived += MultiSourceFrameReader_MultiSourceFrameArrived; // 打开传感器 _kinectSensor.Open(); Debug.Log("Kinect v2 初始化成功。"); } }这里的关键是OpenMultiSourceFrameReader方法,它允许我们订阅多种数据流。事件MultiSourceFrameArrived会在新数据帧到达时触发,这个事件是在Kinect SDK的内部线程中触发的,而非Unity主线程。
3.2 跨线程数据安全与Unity主线程更新
在事件回调中,我们获取并处理原始数据,但绝对不能在回调中直接调用任何Unity的API(如Transform.position,GameObject.Instantiate),这会导致崩溃或不可预知的行为。正确的做法是将处理好的数据暂存起来,在Unity的Update()主循环中再去使用。
private void MultiSourceFrameReader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e) { var frameReference = e.FrameReference; using (var multiSourceFrame = frameReference.AcquireFrame()) { if (multiSourceFrame == null) return; // 处理骨骼帧 using (var bodyFrame = multiSourceFrame.BodyFrameReference.AcquireFrame()) { if (bodyFrame != null) { if (_bodies == null) { _bodies = new Body[bodyFrame.BodyCount]; } // 将数据复制到_bodies数组中 bodyFrame.GetAndRefreshBodyData(_bodies); // 现在_bodies里有了最新的骨骼数据,但先不进行Unity操作 // 可以在这里进行一些数据预处理,比如坐标转换计算 } } // 类似地,可以处理彩色帧、深度帧... // using (var colorFrame = multiSourceFrame.ColorFrameReference.AcquireFrame()) { ... } } } void Update() { // 在主线程中安全地使用_bodies数据 if (_bodies != null) { lock (_bodyLock) // 使用锁确保线程安全 { foreach (var body in _bodies) { if (body.IsTracked) { // 现在可以安全地调用Unity API,例如更新虚拟骨骼的位置 // UpdateSkeletonVisualization(body); } } } } }这里使用了C#的lock关键字来确保在Update中读取_bodies数组时,不会被事件回调中的写入操作打断,避免数据竞争。对于简单的应用,如果只有主线程读取、工作线程写入,且写入是原子操作(一次数组整体替换),有时可以省略锁,但加上锁是最安全的做法。
3.3 坐标空间转换:从Kinect空间到Unity世界
Kinect返回的骨骼关节点的坐标位于“相机空间”(Camera Space),单位是米。原点在Kinect红外摄像头的光心,X轴向右,Y轴向上,Z轴向前(指向用户方向)。而Unity的世界坐标系通常是Y轴向上,但Z轴向前还是向前取决于你的视图。我们需要进行转换。
一个常见的转换是将Kinect的坐标映射到Unity的世界坐标,同时进行缩放和镜像(因为Kinect看到的你是镜像的,而Unity中的虚拟人通常正面朝向屏幕内)。
private Vector3 KinectToUnityVector3(CameraSpacePoint kinectPoint) { // Kinect坐标:X右,Y上,Z前(单位:米) // 假设我们希望虚拟人站在Unity世界原点,面向Z轴正方向 float scaleFactor = 10.0f; // 缩放因子,将米转换为Unity单位(例如1米=10单位) Vector3 unityPosition = new Vector3( kinectPoint.X * scaleFactor, kinectPoint.Y * scaleFactor, kinectPoint.Z * scaleFactor ); // 镜像处理:因为Kinect看到的是镜像,如果我们希望虚拟人和用户动作一致,通常需要镜像X轴 unityPosition.x *= -1.0f; // 可能需要的轴向调整:例如,如果Unity中角色默认朝向是Z轴负方向,可能需要旋转 // 这取决于你的具体场景和模型 return unityPosition; }在Update中,对于每个被追踪的Body,你可以获取其25个关节点的CameraSpacePoint,通过上述函数转换后,赋值给代表虚拟骨骼的GameObject的transform.localPosition。
4. 骨骼跟踪与虚拟人驱动实战
有了稳定的数据流和坐标转换,我们就可以驱动一个虚拟角色了。这里我们分两步:首先是创建虚拟骨骼的视觉表示,然后是实时更新这些骨骼的位置和旋转。
4.1 创建虚拟骨骼表示
最简单的方式是使用立方体(Cube)或球体(Sphere)来代表每一个关节点,并用线(LineRenderer)或圆柱体(Cylinder)来连接它们,形成骨架。我们可以预先创建一个骨架预制体(Prefab)。
- 在Unity中创建一个空GameObject,命名为“SkeletonRoot”。
- 为其添加25个子GameObject,分别对应Kinect的25个关节(
JointType枚举)。每个子对象可以挂一个简单的MeshRenderer(如Sphere)用于显示。 - 为需要连接的关节之间创建连接线。可以写一个脚本,根据关节间的父子关系(例如脊柱中点到肩部,肩部到肘部),动态生成LineRenderer或实例化圆柱体模型,并设置其两端位置。
更高级的做法是使用人形角色模型(Humanoid Avatar)。这需要:
- 一个带骨骼(Rig)的3D人物模型,并将其Rig类型设置为“Humanoid”。
- 编写脚本,将Kinect追踪到的关节数据,映射到Unity的Mecanim动画系统的
Animator组件中的骨骼上。这通常通过Animator.GetBoneTransform获取特定人形骨骼的Transform,然后直接设置其位置和旋转。这种方法视觉效果更专业,但需要处理IK(反向动力学)来修正脚部与地面接触等细节,否则容易产生“滑步”。
4.2 实时更新关节位置与旋转
Kinect v2不仅提供关节位置(Position),还提供关节方向(Orientation),这是一个四元数(Quaternion)。直接使用方向数据可以驱动模型的旋转,实现更自然的姿态。
public class JointVisualizer : MonoBehaviour { public JointType jointType; // 在Inspector中指定这个可视化对象代表哪个关节 private GameObject _jointObject; void Start() { _jointObject = this.gameObject; // 假设这个脚本就挂在与关节对应的GameObject上 } void Update() { var body = KinectManager.Instance.GetPrimaryTrackedBody(); // 假设管理器提供了获取主要追踪身体的方法 if (body != null && body.IsTracked) { var joint = body.Joints[jointType]; if (joint.TrackingState != TrackingState.NotTracked) { // 转换位置 Vector3 unityPos = KinectManager.Instance.KinectToUnityVector3(joint.Position); _jointObject.transform.localPosition = unityPos; // 应用旋转(如果关节方向被追踪) if (body.JointOrientations.TryGetValue(jointType, out var jointOrientation)) { // Kinect的四元数可能需要根据坐标系差异进行调整 Quaternion kinectQuaternion = new Quaternion( jointOrientation.Orientation.X, jointOrientation.Orientation.Y, jointOrientation.Orientation.Z, jointOrientation.Orientation.W ); // 进行可能的轴向转换(类似于位置转换) Quaternion unityQuaternion = ConvertKinectQuaternion(kinectQuaternion); _jointObject.transform.localRotation = unityQuaternion; } } } } private Quaternion ConvertKinectQuaternion(Quaternion kinectQuat) { // 这是一个示例转换,实际转换取决于你的坐标系设定 // 可能需要绕某个轴旋转90度或180度 return new Quaternion( -kinectQuat.x, // 镜像X轴 kinectQuat.y, -kinectQuat.z, // 镜像Z轴?取决于你的设定 kinectQuat.w ); } }实操心得:直接应用关节旋转驱动一个标准的Humanoid模型往往效果不佳,因为Kinect估算的关节旋转可能存在误差,且与模型自身的骨骼朝向不匹配。一个更鲁棒的方法是只使用关节位置数据,通过IK(反向动力学)来求解骨骼旋转。例如,使用Unity的Final IK或开源的IK库,根据手掌、脚掌等末端效应器(End Effector)的目标位置(来自Kinect),来自动计算手臂和腿部的关节旋转,这样得到的动作通常更贴合模型,且能自动处理约束(如肘部不能反向弯曲)。
5. 彩色与深度流处理及其应用拓展
除了骨骼,彩色和深度流也是强大的数据源,可以用于背景抠像、创建点云、实现简单的AR遮挡等效果。
5.1 获取与处理彩色/深度帧
在MultiSourceFrameArrived事件中,我们可以类似地获取彩色和深度帧。
// 在MultiSourceFrameArrived事件处理函数内 using (var colorFrame = multiSourceFrame.ColorFrameReference.AcquireFrame()) { if (colorFrame != null) { // 彩色帧格式通常是BGRA FrameDescription colorFrameDescription = colorFrame.FrameDescription; byte[] colorData = new byte[colorFrameDescription.LengthInPixels * 4]; // 4 bytes per pixel (BGRA) colorFrame.CopyConvertedFrameDataToArray(colorData, ColorImageFormat.Bgra); // 现在colorData里是原始的BGRA字节数组 // 可以将其转换为Texture2D在Unity中显示 UpdateColorTexture(colorData, colorFrameDescription.Width, colorFrameDescription.Height); } } using (var depthFrame = multiSourceFrame.DepthFrameReference.AcquireFrame()) { if (depthFrame != null) { FrameDescription depthFrameDescription = depthFrame.FrameDescription; ushort[] depthData = new ushort[depthFrameDescription.LengthInPixels]; // 每个像素是16位深度值 depthFrame.CopyFrameDataToArray(depthData); // depthData中的每个值代表该像素点到相机的距离,单位是毫米 // 可以用于创建深度图纹理或点云 UpdateDepthTexture(depthData, depthFrameDescription.Width, depthFrameDescription.Height); } }5.2 在Unity中创建动态纹理
为了在Unity中显示彩色图像,我们需要将字节数组转换为Texture2D。
private Texture2D _colorTexture; private byte[] _latestColorData; private object _colorTextureLock = new object(); private void UpdateColorTexture(byte[] data, int width, int height) { lock (_colorTextureLock) { _latestColorData = data; // 暂存数据 } // 标记需要更新纹理,实际更新在主线程进行 } void Update() { // 在主线程中更新纹理 if (_latestColorData != null) { lock (_colorTextureLock) { if (_colorTexture == null || _colorTexture.width != width || _colorTexture.height != height) { _colorTexture = new Texture2D(width, height, TextureFormat.BGRA32, false); } _colorTexture.LoadRawTextureData(_latestColorData); _colorTexture.Apply(); // 将_texture赋值给某个RawImage的texture属性,即可在UI上显示 } } }深度数据的显示稍微复杂,因为它是单通道的16位数据。通常我们需要将其映射到8位的RGB纹理以便显示,例如将深度值(毫米)归一化到0-255的范围。
5.3 应用实例:基于深度数据的简单背景分割
结合深度数据和骨骼数据,可以实现实时的背景分割(抠像)。基本原理是:对于彩色图像中的每个像素,查找其对应的深度值。如果该深度值在距离人体骨骼点一定范围内,则认为是前景(用户),否则是背景。
- 坐标映射:这是最关键的步骤。彩色图像(1920x1080)和深度图像(512x424)的分辨率和视场角都不同。Kinect SDK提供了
CoordinateMapper类,可以将深度空间坐标映射到彩色空间坐标,反之亦然。你需要使用它来建立深度像素与彩色像素之间的对应关系。 - 创建遮罩:根据骨骼关节点的位置(在相机空间中),估算出用户身体的大致空间范围。对于彩色图像的每个像素,通过
CoordinateMapper找到对应的深度位置,判断该位置是否在用户所在的距离区间内。如果是,则在遮罩纹理中标记为白色(前景),否则为黑色(背景)。 - 应用遮罩:在Unity的Shader中,使用这个遮罩纹理来混合前景(原始彩色图像)和背景(虚拟场景或另一张图像)。这可以实现用户“融入”虚拟场景的效果。
注意事项:基于深度抠像在用户与背景距离较远、背景平整时效果较好。如果背景杂乱或有人从旁经过,可能会被误识别为前景。更高级的方法可以结合人体索引帧(Body Index Frame,Kinect v2也提供,用于标记每个像素属于哪个用户),能获得更精确的分割结果。
6. 性能优化与常见问题排查
一个稳定的Kinect Unity应用,必须考虑性能和多线程带来的复杂性。以下是一些关键的优化和排错点。
6.1 性能优化要点
- 降低帧率:Kinect默认输出频率是30FPS。对于很多应用来说,15FPS甚至更低就已足够流畅。你可以在初始化读取器时,通过配置
FrameSourceTypes并配合定时器,来主动降低数据拉取频率,大幅降低CPU开销。 - 选择性订阅数据流:
OpenMultiSourceFrameReader时,只订阅你真正需要的数据流。如果不需要彩色图像,就不要订阅FrameSourceTypes.Color。 - 减少Update中的计算:在
Update中,只做必要的、与渲染相关的操作。复杂的数据处理(如点云生成、手势识别算法)尽量放在接收到数据的后台线程中完成,只将最终结果传递给主线程。 - 纹理更新优化:频繁创建
Texture2D或调用LoadRawTextureData开销很大。尽量复用纹理对象,并使用Texture2D.LoadRawTextureData的异步版本(如果可用),或者将纹理更新限制在一定的帧率。 - 使用对象池:对于用于显示骨骼的关节小球和连接线,使用对象池进行管理,避免频繁的
Instantiate和Destroy。
6.2 常见问题与解决方案速查表
| 问题现象 | 可能原因 | 排查与解决步骤 |
|---|---|---|
初始化失败,KinectSensor.GetDefault()返回null | 1. Kinect未连接或USB不是3.0。 2. Kinect for Windows服务未运行。 3. 驱动未正确安装。 | 1. 检查设备管理器,确认Kinect设备正常识别且无叹号。 2. 在服务管理器中找到“Kinect for Windows”服务,确保其正在运行。 3. 重新安装SDK 2.0,并确保在安装时Kinect未连接到电脑。 |
| 运行时卡顿、延迟高 | 1. USB带宽不足或干扰。 2. 主线程处理数据过载。 3. 垃圾回收(GC)频繁。 | 1. 尝试更换USB 3.0接口,避免使用集线器。 2. 优化代码,降低处理帧率,将繁重计算移出主线程。 3. 避免在每帧中分配新的大型数组(如 new byte[]),尽量复用缓存数组。 |
| 骨骼追踪抖动或不稳定 | 1. 环境光线过强(红外干扰)。 2. 用户穿着过于宽松或纯黑/纯红外反射率低的衣物。 3. 用户距离Kinect太近或太远(最佳距离1.2-3.5米)。 | 1. 避免阳光直射或强红外光源(如某些灯具)。 2. 建议用户穿着日常合身衣物。 3. 确保用户在Kinect的视野和有效距离内。 |
| 彩色/深度图像显示为绿色或扭曲 | 数据流处理或纹理格式错误。 | 1. 检查CopyConvertedFrameDataToArray时指定的ColorImageFormat是否与创建的Texture2D的TextureFormat匹配(如BGRA32对应Bgra)。2. 确认纹理的宽度和高度与帧描述( FrameDescription)一致。 |
| 打包成exe后在其他电脑上无法运行 | 缺少Kinect运行时或原生DLL。 | 1. 确保目标电脑已安装“Kinect for Windows Runtime 2.0”。 2. 检查Unity构建时, Assets/Plugins/x86_64下的原生DLL是否被正确包含。你可以手动将这些DLL复制到exe同级目录下试试。 |
| “DllNotFoundException: Kinect20.dll” | Unity未找到原生Kinect DLL。 | 1. 确认DLL已放置在正确的Plugins子文件夹下。2. 对于开发期,确保系统已安装SDK(即DLL已在系统路径中)。 3. 尝试将 Kinect20.dll等核心DLL直接复制到Unity项目根目录或Assets根目录下(不推荐为最终方案,仅用于测试)。 |
6.3 调试利器:Kinect Studio v2.0
这是SDK自带的最强大的调试工具,没有之一。你可以在开发阶段用它录制一段包含骨骼、彩色、深度数据的“.xef”文件。然后在你的Unity应用中,不连接真实的Kinect,而是改为从录制的文件读取数据。这能让你:
- 离线开发:在没有Kinect硬件的电脑上也能开发和调试代码。
- 场景复现:精准复现某个特定动作或bug出现时的数据流。
- 数据标注:为机器学习准备数据集。
在代码中,你需要使用KinectSensor的EnableKinectStudio方法并指定文件路径来启用回放模式。这能极大提升开发效率。
集成Kinect v2到Unity的过程,本质上是一个标准的软硬件交互和多线程编程问题。遵循“环境配置 -> 数据获取(多线程)-> 线程安全传递 -> 主线程消费与渲染”这个核心链路,并善用官方SDK和工具,就能构建出稳定、响应迅速的体感交互应用。这套流程不仅适用于Kinect,其处理外部传感器数据、跨线程协作的思想,对于集成Leap Motion、VR设备等其它硬件也有很高的参考价值。