彭 淼 , 陈国丽 , 姜 瑜
(重庆工业赋能创新中心有限公司,重庆 401123)
近年来,随着虚拟现实技术的迅速发展,人们愈加感受到体感交互设备所带来的便利,如侦测手臂肌电信号Myo智能臂环、Google 眼镜等可穿戴式交互设备[1]。Leap Motion公司推出了Leap Motion Controller小型运动控制系统,能以200帧/秒的速度追踪双手,追踪精度可达0.01mm,可用于捕捉视场范围达150。 ,空间范围可达8平方英尺的交互式空间中的多个物体,并且其对手势及手部细节的识别亦是一种新的突破[2]。
Leap Motion Controller目前的主要应用分为两个方面:一方面是在三维虚拟游戏方面的应用,作为虚拟交互设备提升玩家游戏体验;另一方面主要用于三维演示项目的虚拟设备。在演示项目的使用中有一种使用方法是用手势代替数字来进行控制项选择[3]。在中国,手势早已成为人们生活中简单方便的交流手段。而Leap Motion的手势识别能力可达到很高的精度,因而可以将手势作为一种设备控制的输入方式,帮助人们完成项目选择,如物体抓取、移动,控制小车,操作机械臂等[4]。
本文以研究Leap Motion设备获取手部位置信息的具体实现方法为目标,首先深入了解分析Leap Motion的工作原理和应用方法,开发基于VS2017的Leap Motion手部位置获取算法;然后编写基于MFC的应用程序界面,设定输出所需位置信息的窗口,用于显示手部位置和姿态数据;最后通过实验分析采集算法的精度以及程序的稳定性[5-9]。针对试验的具体结果,分析该输出方法的优点,并且提出一定的改进方案,为后续研究提供依据。
Leap Motion作为手部信息捕获设备,使用光学传感器和红外线扫描获取处于设备上方大约150°视野范围内的对象。Leap Motion的有效可视范围大约在设备上方25mm~600mm,形状呈尖端位于设备中心的倒漏斗形[10]。图1展示了Leap Motion如何监视用户的手。
图1 Leap Motion设备监视用户双手
在了解Leap Motion控制器所使用的坐标系统之后,将要用到手掌的中心坐标,即如图2所示圆点的坐标位置,然后给出中心坐标的某一具体坐标值编写输出方法。需要注意的是,在输出手掌中心位置坐标时,需要判别手掌类型,即对左右手进行判断。因此,首先输出手掌类型。
图2 手掌坐标位置及方向
在源代码中给出了各种类以及类的相互之间的调用关系,这里仅对HandList下的C++代码进行更改,在代码中,程序设定了一个Controller对象,用于连接到Leap Motion后台,从而可以通过命令Controller.frame()获取数据。另外,程序添加了一个Listener类,来设定一些回调函数。代码中的onFrame回调函数用于定义当获取到一帧数据时需要处理的事件,比如通过对frame的再调用来获取一些手部的位置数据信息,再调用的方法主要是通过语句HandList hands= frame.hands()来将帧中关于手部的数据信息传递给手列表中的手对象。在代码中设定了四个float型变量,以及一个初始化为“未检测到手”的字符变量,由于将其设定为全局变量,因此,可以在对工作状态进行判别后,通过对变量进行赋值,来输出手部的位置数据。
尖端对象是指在视野范围中出现的对象中可以指出方向的东西,可以被表示为手指或者工具,可以通过手对象中特定的手,来获取其相关的信息。即在获取hand.palmPosition的同时,获取尖端坐标,在视野中部出现工具的情况下,便可以通过两者之间的物理模型关系,确定视野中手部的位置以及方向。在检测时,程序会自动检测手指尖端距离手掌中心最远点的坐标并且进行输出。与输出手掌中心位置坐标的方式类似,程序通过onFrame回调函数获取每一帧的数据,通过命令const Pointable pointable = frame.pointables().frontmost()来获取帧数据中所保存的尖端数据信息,并将获取的坐标信息赋给程序开始所定义的float型变量tipx,tipy,tipz,进行输出。
在Leap Motion模型中,手指利用Finger类对象进行描述,是一种可指向性对象,物理特性包括Width(宽度)、Length(长度)、Direction(方向)、tipPosition(尖端坐标)以及tipVelocity(尖端速率)等。如图3所示,圆点处的位置表示为尖端坐标,箭头的方向表示尖端方向。
图3 尖端坐标及方向向量
这里选择尖端坐标来描述手指属性,并基于Pointable类获取指尖位置。与描述手掌中心坐标的方法相似,利用double型全局变量对获取的结果进行输出。
Leap Motion控制器可以获取用户十指的数据。在LeapSDK中提供了所检测到的用户手指物理特征数据的C++代码,通过运行可以对相关数据进行输出。所有的手指都包含有四根骨头,每根骨头可以获取指根和指尖的坐标位置,即如图4中小球处的坐标位置。定义的骨头名称如上述代码中boneNames[]字符串所包含的四个成员。拇指模型为了便于编程的统一多定义了一个掌骨,长度设定为零。
图4 Leap Motion手指指骨模型
在Leap Motion SDK中把一些特定的运动模式定义为手势,通过这个运动模式来对用户的意图进行猜测。对于每一个设备观察到的手势,都可以在frame.gestures列表中找到相关的数据信息。Leap Motion系统会将获取到的手势对象不断更新在获取的帧中。通过对手势的判别,更多的是可以获取到用户手部在坐标系中的一个动作姿态特征,在检测到用户的一个手势时,可以通过手部模型关联得到用户手部的一个大致位置。在手势识别启用后,便可以从帧中获取一些数据信息。检测手势的代码首先将帧中的关于手势的数据信息传递给手势类,采用了switch开关语句对手势的基本类型进行判别,如果符合case命令下的手势类型,则执行该case命令并且最终结束switch循环。识别手势基本类型的算法封装在gesture项目中。在程序运行过程中,程序通过调用相关的函数,将一些基本的信息填入进去,通过计算进行转换,在某些数据发生了重复时,便开始判断其是否为手势,以及为何种类型手势。
基于可视化的用户程序界面生成系统是当前编程发展的方向。如浏览器支持界面实时刷新显示,一些软件运行时可以实时的更新显示类似于时间、坐标等数值。由于在Windows中不可能完全由用户设计这些可能需要用到的控件,因此,微软公司推出的MFC提供了类库,用户可以基于此基础,通过重载它的各种消息处理函数来对所需要的程序界面进行编辑修改[11]。
本文所用的VS2017是微软公司推出的最新版集成开发工具,提供了MFC类库[12]。MFC的框架包含有一个应用程序所需的所有可能控件,用户只需要对其进行使用[13]。
应用程序界面的生成包括两个过程,首先是进行界面设计,即在编辑窗口中加入控件,并为控件添加成员变量。之后便是功能设计,用户需要基于各成员变量,编写功能函数来得到所需要的结果。下图5简单介绍了如何创建一个基于对话框类型的应用程序。
图5 基于对话框的应用程序工作流程图
在本文中所编写的应用程序界面需要完成的一个功能是:在Leap Motion检测到用户手部位置数据信息时,将存放在frame中的各结构的三维坐标分别实时动态显示出来,在用户改变所需要的位置信息时输出也会随之发生改变[14]。因此,本文本框必要的一些控件为:手掌类型、手指个数、手掌中心、尖端、手掌角度以及基本手势。前面四个坐标值可以表示用户手部的基本信息以及一个大致空间位置,第五个坐标值则主要用于描述手掌的一个方向,第四个为字符串输出,用于展示用户手部可能组成的基本手势。
基于以上分析,在进行Leap Motion数据信息与MFC界面进行共享的过程中,涉及多个坐标信息,以及三个字符串类型输出。在进行应用程序界面设计时,为使界面信息明了,采用Group Box控件,将位置坐标输出分别设置在不同的组合框控件中。在进行数据显示时,控件需要有自己的一个成员变量,该变量的定义类型可能是值,或者字符串。针对本文的应用程序,需要定义至少三个数值型成员变量以及三个字符串型成员变量[15]。
该对话框包含了手掌中心坐标、尖端坐标、手掌姿态角的数据输出,手势、手类型的判别,通过这些信息,可以具体地刻画出手部在空间中的位置信息,并对其进行一定的美化,如添加图片控件以及作者姓名信息等。完整的人机交互对话框如图6所示。
图6 人机交互对话框
在构建好MFC应用程序界面的对话框基本结构之后,比较关键的是将Leap Motion程序与搭建的MFC框架联系起来,获取到数据并且进行动态显示[16]。
至此,添加的成员变量有:float型变量m_palmX,m_palmY,m_palmZ,用于描述手掌位置坐标;int型变量m_pitch,m_yaw,m_roll,用于描述手掌角度;CString型变量m_rightorleft,用于描述手掌类型;int型变量m_fingerscount,用于描述手指数;float型变量m_tipX,m_tipY,m_tipZ,用于描述尖端位置坐标;CString型变量m_gesturetype,描述特征手势类型。
在Leap Motion的SDK文档中设定了一个Listener类用于监听设备状态以及获取帧数据并且将帧中的数据调用给手部各姿态用于输出。因此,为实现数据传递,在MFC项目中新建一个名为myListener的类,用于与Leap设备建立联系并且获取一些数据信息。关于MFC项目对各头文件路径的识别,可以按照Leap Motion SDK在C++环境下的配置,在MFC项目中重新配置一次即可[17-19]。
由于输出信息仅需要调用onConnect函数以及onFrame函数,因此,在MyListener的声明中,仅需添加这两个函数即可。拷贝获取位置信息的C++代码到myListener.cpp中,将获取的位置数据赋给所设定的各全局变量,以方便在交互界面进行输出[20]。在程序运行myListener.cpp后,手部位置数据信息便保存在了定义的各全局变量中。对话框的主程序代码需要实现的功能是将全局变量中的数据显示在相应的编辑框中,并且实现数据的动态更新显示。对于更新要求,采用的是添加一个onTimer函数,通过定时器定时执行某条语句,从而实现数据的更新显示。
数据通过全局变量传递到了每个编辑框所对应的成员变量中,并且可以通过编写的MFC人机交互界面进行动态显示。实时显示的输出交互界面如图7所示。
图7 MFC交互界面输出结果
对于一个信息采集设备,需要了解其所用坐标系统,以及其可检测范围,之后才可以对其采集精度进行检测。因此,需要对Leap Motion设备的可视范围进行测定,如可采集的三维最大长度,随后标定坐标空间的大致形状,基于此进行Leap Motion的采集精度实验。
在进行测定时,首先在Leap Motion设备旁边固定一根1m长的直尺,然后由低至高依次抬高手掌,观察屏幕输出的手掌中心坐标,直到输出为0,记录此时的手掌高度,如图8所示。
图8 利用直尺测定Y方向上设备可视范围
测定Y正方向上,当手掌移动到距离设备上表面约1 000mm的位置时,屏幕输出为0;最下方距离设备上表面约20mm的位置时,屏幕输出为0。可以看出,与官方给出的可视范围基本吻合。
之后,借助另一根直尺测定相隔一段Y方向上的距离,相应的X与Z轴在正负方向上的极限可视范围。测定方法与标准基本同上文相似。手掌随直尺方向平移,观察屏幕输出。在输出为0时则表示该方向到达可视范围的边界。如图9所示。
图9 测定X与Z方向上极限可视范围
结果如表1所示。
表1 三维坐标测量结果汇总表
Leap Motion坐标系统的方向设定,分别作X-Y图及Z-Y图,如图10所示。
将图10与官方给定的坐标系统图示进行比较,可知系统的实际坐标区域可分为两片不同类型。首先是由坐标原点到Y轴正向约620mm之间区域,大概为一个倒金字塔形。由Y轴正向距原点620mm至检测顶点之间的区域大概是一个倒球形。
在交互界面上可显示手掌姿态角数据信息,具体包括俯仰角(绕X轴)、侧倾角(绕Z轴)以及横摆角(绕Y轴)。在设备检测到用户手部位置信息时,界面可以实时显示数据信息。相关手部位置及输出信息如图11所示。
图10 坐标系实际可视范围图
图11 手掌姿态角数据信息获取
在设备的可视范围测定完成后,需要沿各轴测定手掌的实际坐标值,并与程序输出坐标值进行对比,对设备的检测精度进行评定。本文仅对Y轴正向的检测数据进行精度检测。测定方式:使用一根刻度为1m、精度为1cm的直尺。在进行检测时,首先将数据的输出频率降低,以方便读数。将使用直尺测定的数据与程序输出的坐标值进行对比,如表2所示。
表2 沿Y轴正向测定坐标与输出坐标数据表
基于上表所测数据,使用直尺测定值与程序输出值数据信息,绘制两者的数据对比线图如图12所示。
图12 手掌位置数据信息对比图
根据折线图中,两根曲线之间的拟合情况,结合数据采集过程中可能出现的误差,可知在距离Leap Motion设备较近位置时,如500mm范围内,设备的检测精度很高,与实际测量基本无偏差;在距离设备较远处出现数据的较大波动,原因可能是设备所用传感器的灵敏度随距离的增加逐渐降低。整体来说,其检测精度相比较其他传感器要更高,数据输出更为稳定,数据偏差范围较小。
另外,通过实际检测可以得知,Leap Motion设备的实际检测范围可能会比官方给出的标准范围要更大一些。在运行程序时屏幕输出的数据精度可以达到0.000 1,精度相比以往更高。这就为用户提供了更大的操作空间,同时其更高的检测精度也为手部位置的获取提供了更多的便利。
关于空间物体的坐标位置获取一直都是一个不断发展、不断进步的前沿研究方向,相比较以往推出的三维加速度传感器、超声波传感器、温度传感器、雷达等检测设备,Leap Motion具有更高的精度,可以适应更多的使用环境。本文基于Leap公司供用户参考学习的SDK文档,开发设计出一种相对来说更为实用、比较简便的手部位置信息获取方法,并且利用VS设计出了一个简单的MFC程序界面对数据进行输出显示。本文主要完成了以下工作:
1)根据Leap公司提供的用户参考SDK文档,对Leap Motion获取手部位置信息的C++程序进行了解读并且编写了更为直接以及简单的输出代码。
2)使用VS2017建立了一个MFC应用程序,并且对该应用程序的控件进行了设计,使其可以显示所需的一些位置坐标信息。
3)通过对其他MFC程序的参考,经过调试将编写的Leap Motion程序与新建的MFC程序之间建立了链接,使MFC控件的成员变量可以获得不断更新的位置数据信息并且进行动态显示,完成了手部位置信息获取方法的探究。
4)本文虽然对手部位置信息获取方法进行了探究,但是对其可能应用的方向并没有进行一个很好的描述,希望可以在后续工作中对该方面进行探究,并且提出一些可能应用的场景,为其更好地与现实结合提供参考。