Leap Motion作为一款手势识别设备,相比于Kniect,优点在于精确度。

在我的毕业设计《场景漫游器》的开发中,Leap Motion的手势控制作为重要的一个环节。以此,谈谈开发中使用Leap Motion进行手势识别的实现方式以及需要注意的地方。

一、对Leap Motion的能力进行评估

在设定手势之前,我们必须知道Leap Motion能做到哪种程度,以免在设定方案之后发现很难实现。这个评估依靠实际对设备的使用体验,主要从三个方面:

1.Leap Motion提供的可视化的手势识别界面

2.SDK文档说明

3.Leap商店中的APP

基本可以的得出:

1.Leap Motion的识别对于水平方向或者以水平方向为基础手势能够较好的识别。

2.对于握拳或者垂直的行为识别会出现误差,这种误差和具体的手势行为有关。

3.不应该过分依赖高精确度,Leap Motion能检测到毫米级别是没错的,但是有时候会把你伸直的手指识别成弯曲的,所以要做好最坏的打算。

二、实际的需要

移动、旋转、点击按钮、缩放和旋转物体、关闭程序、暂停,基本的功能需求是这样。

有一些原则:

1.相同环境下的手势应该接近和方便的转换。旋转和移动的之间的转换应该设计的很自然。

2.手势避免冲突,手势过于相似不是什么好事。比如三个伸直的手指和四个伸直的手指不应该被设计成两个手势。当然这不是绝对的,如果你进行一个缓慢的动作并且动作是面向Leap Motion的摄像头,这时候应该相信它,至少要针对这个手势做一个单独的测试。

三、考虑基本的数据结构和算法的轮廓

Leap Motion的SDK在第一部分的时候已经浏览过,最起码能知道Leap Motion可以包含的信息,从SDK看来这是非常丰富的,既然设计自己的手势,那么最好不要依赖于SKD开发包的炫酷的手势。很可能,这些手势只是官方用来演示或者炫耀的。自己设计手势的基本数据结构也有另外的好处,比如更换了体感设备,但是功能是相似的,这时候只需要更改获取数据的方式就可以了(从一个SDK更换到另一个SDK),而不要修改算法。

算法的轮廓与基本数据有很大的关系。所以数据结构一定要尽量的精简并且允许修改(可能某个算法占据了决定性因素,但是开始没考虑到)。

public class HandAndFingersPoint : MonoBehaviour
{const int BUFFER_MAX=5;Controller m_LeapCtrl;<span style="white-space:pre">  </span>public E_HandInAboveView m_AboveView = E_HandInAboveView.None;//手指-数据 ,[0]表示左手,[1]表示右手private Dictionary<Finger.FingerType,FingerData>[] m_FingerDatas = new Dictionary<Finger.FingerType, FingerData>[2];//buffer,[0]表示左手,[1]表示右手,[,n](n属于0,3,表示第n次缓存)private Dictionary<Finger.FingerType,FingerData>[,] m_FingerDatasBuffer=new Dictionary<Finger.FingerType, FingerData>[2,BUFFER_MAX];private int m_CurBufIndex=0;//palm 0:左手 和1:右手private PointData[] m_PalmDatas = new PointData[2];private readonly PointData m_DefaultPointData = new PointData(Vector.Zero, Vector.Zero);private readonly FingerData m_DefaultFingerData = new FingerData(Vector.Zero,Vector.Zero,Vector.Zero);

HandAndFingersPoint类中剩下的部分是对数据的填充、清除、刷新等方法。E_HandInAboveView记录哪只手先进入Leap Motion的视野,用于设定优先级。
另外两个基本的数据结构PointData和FingerData:

//一个手指的数据包含一个指尖点数据和手指根骨的位置数据
public struct FingerData
{public PointData m_Point;//指尖的位置和指向public Vector m_Position;//手指根骨的位置,对于拇指来说是Proximal phalanges近端指骨的位置public FingerData(PointData pointData, Vector pos){m_Point = pointData;m_Position = pos;}public FingerData(Vector pointPos, Vector pointDir, Vector pos){m_Point.m_Position = pointPos;m_Point.m_Direction = pointDir;m_Position = pos;}public void Set(FingerData fd){m_Point = fd.m_Point;m_Position = fd.m_Position;}
}
//一个点的数据,包括方向和位置
public struct PointData
{public Vector m_Position;//位置public Vector m_Direction;//方向public PointData(Vector pos,Vector dir){m_Position = pos;m_Direction = dir;}public void Set(PointData pd){m_Position = pd.m_Position;m_Direction = pd.m_Direction;}public void Set(Vector pos,Vector dir){m_Position = pos;m_Direction = dir;}
}//先被看到的手
public enum E_HandInAboveView
{None,Left,Right
}

基本数据定义好之后,最好确认数据的填充是没问题的,实际通过Frame frame = Leap.Controller.Frame();来获取最新的数据。这时候并不急着写完和基本数据相关的方法,现在最终要的是手势算法的合理性。要判断是否合理,最好先写一个算法。

最简单的是伸掌手势,在控制中水平的伸掌用于漫游,垂直的伸掌用于暂停。我发现手掌依赖于手指,而手指包括两个状态——伸直和弯曲。另外,其他的手势,也都是手指的伸直或者弯曲,外加方向的判定累积出各种效果。理所当然的,应该单独写出手指的弯曲和伸直判定算法:

/// <summary>
/// 该方法提供对于单个手指匹配的算法,如伸直,弯曲
/// 以后可能的改变:对于不同的场景可能要求有所不同,这里的阈值也许会随之改变
/// </summary>
public class FingerMatch
{//弯曲状态的角度阈值static readonly float FingerBendState_Radian = Mathf.PI*4f / 18 ;//40度//伸直状态的角度阈值static readonly float FingerStrightState_Radian = Mathf.PI/12;//15度/// <summary>/// 手指伸直的状态,当根骨-指尖的方向和指向的偏差小于阀值时,判定手指为伸直状态。/// 注意无效的方向为零向量,先判定是零向量/// </summary>/// <param name="adjustBorder">对阈值做的微调</param>/// <returns></returns>public static bool StrightState(FingerData fingerData, float adjustBorder=0f){bool isStright =false;Vector disalDir = fingerData.m_Point.m_Direction;//如果指尖方向为0向量,表示无效的数据if (!disalDir.Equals(Vector.Zero)) {Vector fingerDir = fingerData.m_Point.m_Position - fingerData.m_Position;//指尖位置减去指根位置,由指根指向指尖的向量            float radian = fingerDir.AngleTo(disalDir);if (radian < FingerStrightState_Radian + adjustBorder){isStright = true;}}return isStright;}/// <summary>/// 判断一根手指是否处于弯曲状态/// </summary>/// <param name="fingerData">需要判定的手指数据</param>/// <param name="bandBorder">弯曲的阈值</param>/// <returns></returns>public static bool BendState(FingerData fingerData, float adjustBorder=0f)//,out float eulerAugle){bool isBend = false;//eulerAugle = -1f;Vector disalDir = fingerData.m_Point.m_Direction;if( !disalDir.Equals(Vector.Zero) ){Vector fingerDir = fingerData.m_Point.m_Position - fingerData.m_Position;//指尖位置减去指根位置,指跟到指尖的向量float radian = fingerDir.AngleTo(disalDir);//eulerAugle = radian*180/Mathf.PI;    //夹角超过定义的阈值时,认定为弯曲状态if (radian > FingerBendState_Radian + adjustBorder){isBend = true;}}return isBend;}}

上面包含了一个重要的概念——阈值。它是描述到底何种程度算是伸直,何种程度算是弯曲。阈值的确定是需要实际测试来决定的。写到这里也是时候进行一次简单的测试了,毕竟算法的轮廓已经确定。我甚至没写出手掌伸直的判定算法,就确定是可行的。

基本数据结构相关的操作——HandAndFingersPoint类:源代码GitHub链接

该类使用基本数据,在Unity Editor中运行会展示了一个手掌的轮廓,蓝色表示手指的方向,红色表示手指骨根到掌心和指尖的连线,黄色表示掌心到指尖的连线:

四、手势实现中简要的概括

其他代码都可以在我的GitHub:Leap Motion In Unity3D仓库中获取,在手势的实现中,也包含了一些小的技巧,比如对于动作的匹配要防止手指的颤抖引起的误差,采用离散的数据取样——每隔一定时间做一次取样。

使用和观察这些脚本的方式:可以把这些脚本放在一个GameObject中,通过Leap Motion会看到脚本的属性在匹配成功时会发生变化。另外,脚本中包含了事件的注册功能,换句话说,外部可以向任意的手势注册一个事件,以便手势完成匹配或者到达某种匹配状态时做一些额外的处理。这些脚本现在并不能直接完成我们的需求,如暂停。我们需要在这些手势状态或者动作上做进一步的限定,如根据掌心的方向设定垂直向前的手掌为暂停,水平的手掌为平移之类的。

Unity3D中使用Leap Motion进行手势控制相关推荐

  1. Unity3D中使用Joystick Pack实现摇杆控制

    学更好的别人, 做更好的自己. --<微卡智享> 本文长度为2342字,预计阅读6分钟 前言 花了几个小时重新根据Unity3D的初学者教程做了一个简单的Demo,做完后确实对新人来说是个 ...

  2. Unity3D与Leap motion连接学习记录

     https://www.leapmotion.com/partnerships (Leapmotion官方网站) 把这个leapmotion手势识别相机看一下,这个开发互动非常方便,有开发sdk ...

  3. 【新手入门】全网最全Leap Motion技术总结

    不好意思有点标题党了hhh~ 本科毕设使用了Leap Motion完成了一个双手交互体验的小程序,在查阅相关资料时发现网上的资料良莠不齐.不够系统,常常是花费了很多精力搜集资料却得不到想要的答案.正好 ...

  4. leap motion原理

    这就是 Motion Visualizer,它将由 Leap 传感器捕捉到的手部运动信息映射出来: &amp;lt;img src="https://pic1.zhimg.com/f ...

  5. 手势控制 Python !Leap Motion Python 开发教程 (一)

    标题手势控制 Python !Leap Motion Python 开发教程 最近需要使用 Leap Motion 控制机械灵巧手,所以在 Python 的平台上做了一些 Leap Motion 的开 ...

  6. Leap Motion开发(三)Unity3D API总结

    主要参考b站教程 鉴于网上没有Unity3D和Leap Motion的API的文档,所以自己做一个总结. 配置 B站教程采用的配置是Unity2018.4.7 + Leap unity包4.6.0 我 ...

  7. 基于Leap Motion设备及Unity3D引擎的自定义手势识别

    1.Leap Motion 官网下载SDK并导入Unity3D,官网地址:https://developer.leapmotion.com/  本次测试适用unity版本为2017.1以上 下载内容: ...

  8. 【Unity3D小技巧】Unity3D中Animation和Animator动画的播放、暂停、倒放控制

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦. 一.前言 ...

  9. 【虚拟仿真】Unity3D中实现控制物体的旋转、移动、缩放

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦. 一.前言 ...

最新文章

  1. EyeQ进展The Evolution of EyeQ
  2. Machine Learning Algorithm
  3. 如何实现每个周期4个FLOP的理论最大值?
  4. SQL Server 2000安装指南及数据创建
  5. 扑克牌排序_巧用扑克牌搞定孩子的数学思维启蒙,聪明的妈妈都在玩这样的游戏...
  6. python硬件测试开发_用python实现高性能测试工具(一)
  7. gis如何加入emf图片_当GIS运用于建筑遗产保护
  8. ServiceMix部署自定义开发程序(ActiveMQ和Kafka实现)
  9. SAP UI5 extend debug
  10. 课时55.详情和概要标签(理解)
  11. Js中Currying的应用
  12. 计算机审计 报告哦,计算机审计实训报告
  13. js扁平数组对象转成树结构
  14. 设计模式 GOF23 模式比较
  15. cadence 617工艺库安装以及相关问题解决
  16. 使用Exchange Server 2010搭建多域名邮件系统
  17. 前后端分离,不在同一服务器上部署,报错“strict-origin-when-cross-origin”解决
  18. bilibili视频批量下载
  19. Allegro中anti-etch的作用
  20. 设置LINUX启动时的液晶屏参数

热门文章

  1. 测绘技能大赛-无人机航测虚拟仿真(内业部分)
  2. php首字母改大写,php实现字符串首字母转换成大写的方法
  3. 5G推动下,XR的需求“爆发”会来自B端还是C端?
  4. RuntimeError: size mismatch
  5. Creo4.0安装与VS2015环境下的开发配置
  6. android项目中使用的服务器上,android 开发中使用okhttp上传文件到服务器
  7. 基于BERT的新闻文本分类
  8. java基础猜拳游戏
  9. 写字机器人软件_被误解的写字机器人应该如何为自己正名?
  10. 农信计算机资料录入试题,农村信用社计算机考试试题.docx