Unity3D中使用Leap Motion进行手势控制[通俗易懂]

Unity3D中使用Leap Motion进行手势控制

大家好,又见面了,我是全栈君。

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能检測到毫米级别是没错的,可是有时候会把你伸直的手指识别成弯曲的。所以要做好最坏的打算。

二、实际的须要

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

有一些原则:

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中执行会展示了一个手掌的轮廓,蓝色表示手指的方向。红色表示手指骨根到掌心和指尖的连线,黄色表示掌心到指尖的连线:

Unity3D中使用Leap Motion进行手势控制[通俗易懂]

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/115406.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • win10的pagefile.sys是什么文件?pagefile.sys文件太大如何移动到D盘中?

    win10的pagefile.sys是什么文件?pagefile.sys文件太大如何移动到D盘中?    在C盘系统下,有一个命名为pagefile.sys的文件占用C盘太大的空间,不少用户怕删除pagefile.sys文件之后会对系统造成影响,而不少用户想要将pagefile.sys文件移动到D盘中。那么pagefile.sys是什么文件?Win10系统下pagefile.sys文件太大如何移动到D盘中?pagefile.sys是什么文件?pagefile.sys文件是操作系…

    2022年7月25日
    31
  • Python中的多线程「建议收藏」

    Python中的多线程「建议收藏」什么是多线程:进程:正在运行的程序,QQ360……线程:就是进程中一条执行程序的执行路径,一个程序至少有一条执行路径。(360中的杀毒电脑体检电脑清理同时运行的话就需要开启多条路

    2022年7月3日
    31
  • pycharm界面颜色设置_Excel护眼色打印

    pycharm界面颜色设置_Excel护眼色打印首先打开菜单file下的setting设置:然后找到editor中的general3.然后点击图标最上面的saveas创建第二个默认设置:4.接下来再下面的对话框中找到defaulttext并将background和foreground对话框打勾:5.对background进行颜色设定:6.对foreground进行设计:7.最后点击最下面的apply和ok即…

    2022年8月26日
    7
  • Linux操作系统基础(完结)

    Linux操作系统基础(完结)一、Linux操作系统概述二、Linux操作系统安装三、Linux文件系统及文件基础四、Linux操作系统命令使用基础五、Linux应用程序的安装与卸载基础五、用户及进程六、相关信息查询七、网络配置八、Linux应用程序的安装与卸载基础九、vim

    2022年5月9日
    40
  • Android学习(简单使用Bottom Navigation Activity来实现底部导航栏)

    Android学习(简单使用Bottom Navigation Activity来实现底部导航栏)在我们实际编写程序时,不必每一个activity都要从零开始,利用好系统自带的模板往往可以起到事半功倍的效果。下面我们就来看看如何使用BottomNavigationActivity来完成简单的底部导航栏功能。先来看一下效果图吧:创建activity首先在创建面板,我们选择然后next,finish就OK了。创建成功以后我们来运行一下,发现已经基本实现了底部导航栏的功能了!但是还没有结…

    2025年6月16日
    0
  • 生物化学与分子生物学分析技术 Analytical Techniques in Biochemistry and Molecular Biology 英文原版[通俗易懂]

    生物化学与分子生物学分析技术 Analytical Techniques in Biochemistry and Molecular Biology 英文原版[通俗易懂]长期以来,生物化学被定义为生命科学的核心学科,现在它使我们能够以十年前甚至做梦都想不到的方式控制生命系统。随着植物生物学和生物技术在科学议程上不可阻挡地向前发展,其相关性也与日俱增。这本综合卷探讨了植物生物学、生物化学和生物技术研究人员必须掌握的众多实验技术。随着人们对调查生理过程的生化和分子方法的兴趣上升,新的、更快、更敏感的实验程序也在同步发展,使我们能够探索动物和植物有机体的内部运作。ebook获取<<<…

    2022年7月11日
    15

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号