zoukankan      html  css  js  c++  java
  • LeapMotion(1):环境配置、简单测试、理解对象

    关注Leap Motion很长时间了,很早就想入手。可是,一方面,一直忙着其它的比赛,没时间顾及;二是缺钱,钱都垫在比赛上了。

    好不容易,11月18日,下定决心买进了,这么长时间,也就是再给贵阳职业学院的学生上课的时候显摆了一次。

     

    周末休息,总算是强迫自己摆弄一下Leap Motion了。

    那么做点什么呢?不放就先从简单的开始吧,就在窗口上显示一个红色的圆,以表示追踪到的某一个手指头(指尖)。

     

    要开发Leap Motion应用,我觉得官方的文档必须要仔细的看看。

    文档1:开发者首页,这里可以下载SDK。

    文档2:理解C#例程,以便开发我们自己的应用程序。

    文档3:Leap概述,获取开发的必要知识。

     

    Step1:下载SDK,并解压缩。在“LeapSDK”中有我们需要用的源代码和类库。

    Step2:创建一个C# WPF应用程序,不妨就叫“LeapMotion1_WPF”。

    Step3:在项目中添加“lib/LeapCSharp.NET4.0.dll”引用(如果是.net framework 3.5的话,则添加LeapCSharp.NET3.5.dll),如图。

    Step4:在项目中添加现有项,“lib/x86/Leap.dll”、“lib/x86/Leap.lib”和“lib/x86/LeapCSharp.dll”三个文件,并设置三个文件“始终复制”到输出目录,如图。

    Step5:创建用户界面,在MainWindow上,放一个Canvas,两个Button,一个Ellipse,如图。

     

    设置好相应的属性。Canvas的大小和窗口一样。

    Step6:编写“连接设备”按钮的单击事件。在该事件中,我们需要让Leap Motion工作起来。不放看看“samples/Sample.cs”中是如何实现的。

    该类实现的是一个控制台应用程序。我们从main开始看。代码如下:

     1     public static void Main ()
     2     {
     3         // Create a sample listener and controller
     4         SampleListener listener = new SampleListener ();
     5         Controller controller = new Controller ();
     6 
     7         // Have the sample listener receive events from the controller
     8         controller.AddListener (listener);
     9 
    10         // Keep this process running until Enter is pressed
    11         Console.WriteLine ("Press Enter to quit...");
    12         Console.ReadLine ();
    13 
    14         // Remove the sample listener when done
    15         controller.RemoveListener (listener);
    16         controller.Dispose ();
    17     }

    文档2中说,“The Controller class provides the main interface between the Leap and your application. When you create a Controller object, it connects to the Leap software running on the computer and makes hand tracking data available through Frame objects. You can access these Frame objects by instantiating a Controller object and calling the Controller.Frame method”,即应用程序使用Controller对象来访问Leap硬件,追踪到的数据封装在Frame对象中的,这个对象可以通过Controller.Frame获取。 

    文档2中还说,“If your application has a natural update loop or frame rate, then you can call Controller.Frame as part of this update. Otherwise, you can add a listener to the controller object. The controller object invokes the callback methods defined in your Listener subclass whenever a new frame of tracking data is available (and also for a few other Leap events)”。如果开发的应用程序本身有个大循环的话(类似XNA应用程序的update,或是单片机程序的loop),可以在这个大循环中通过Controller.Frame获取Frame对象。否则,我们可以给Controller对象提供一个Listener,当Controller准备好新的Frame时会回调Listener,之后我们在进行处理。

    这样来看,前三行代码就比较好理解了。

    在我们的项目中,“连接设备”按钮的单击事件的代码如下:

    1         private void Button_Click_1(object sender, RoutedEventArgs e)
    2         {
    3             listener = new MyLeapListener();
    4             controller = new Controller();
    5             controller.AddListener(listener);
    6 
    7             btn1.IsEnabled = false;//btn1表示“连接设备”
    8             btn2.IsEnabled = true;//btn2表示“断开设备”
    9         }

    Step7:“断开设备”的点击事件的代码自然也比较容易写出,代码如下:

    1         private void Button_Click_2(object sender, RoutedEventArgs e)
    2         {
    3             controller.RemoveListener(listener);
    4 
    5             btn1.IsEnabled = true;
    6             btn2.IsEnabled = false;
    7         }

    Step8:仿照例程的Main方法,我们还需要销毁Controller对象。如下:

    1         private void Window_Closing_1(object sender, System.ComponentModel.CancelEventArgs e)
    2         {
    3             controller.Dispose();
    4         }

    Step9:实现自己的监听器。先来看看SampleListener是如何实现的呢?其骨架如下。

     1 class SampleListener : Listener
     2 {
     3     public override void OnInit (Controller controller)
     4     {
     5     }
     6 
     7     public override void OnConnect (Controller controller)
     8     {
     9     }
    10 
    11     public override void OnDisconnect (Controller controller)
    12     {
    13     }
    14 
    15     public override void OnExit (Controller controller)
    16     {
    17     }
    18 
    19     public override void OnFrame (Controller controller)
    20     {
    21     }
    22 }

    同样是在文档2中有详细的说明,我就不粘贴原文了。我们自己创建的监听器需要继承Listener,可以根据需要重写上面几个方法。对于我们而言,需要关注的是OnFrame方法,因为当一个新的Frame准备好后会调用这个方法。

    在LeapMotion1_WPF命名空间中添加一个新类MyLeapListener,其实现如下:

    1     class MyLeapListener : Listener
    2     {
    3         public override void OnFrame(Controller ctl)
    4         {
    5         }
    6     }

    Ok,非常好。我们可以在OnFrame方法中获得Frame对象,然后获取关于指尖的数据,然后进行一些操作,就如例程中那样。但是,在这个方法中,我们是无法改变Ellipse的位置的。我们需要多做一步操作,在MyLeapListener的OnFrame方法触发的时候,通知调用者进行处理。显然,使用事件可以完美的解决我们的问题。于是,我们修改了MyLeapListener的代码:

     1     class MyLeapListener : Listener
     2     {
     3         public event EventHandler OnFrameEvent = null;
     4         public override void OnFrame(Controller ctl)
     5         {
     6             if (OnFrameEvent != null)
     7             {
     8                 OnFrameEvent.Invoke(ctl, null);
     9             }
    10         }
    11     }

    在OnFrame方法中触发OnFrameEvent事件。那么,相应的,在实例化MyLeapListener的时候,就应该加一个事件处理方法进去。在Button_Click_1方法中作如下修改:

     1         private void Button_Click_1(object sender, RoutedEventArgs e)
     2         {
     3             listener = new MyLeapListener();
     4             listener.OnFrameEvent += listener_OnFrameEvent;
     5 
     6             controller = new Controller();
     7             controller.AddListener(listener);
     8 
     9             btn1.IsEnabled = false;
    10             btn2.IsEnabled = true;
    11         }

    Step10:实现listener_OnFrameEvent方法。先把方法声明写出来。

    1         void listener_OnFrameEvent(object sender, EventArgs e)
    2         {
    3         }

    按照之前的说法,追踪的数据是封装在Frame对象中的,因此,我们首先获取该对象。

    1             //获取帧数据
    2             LeapFrame frame = controller.Frame();//using LeapFrame = Leap.Frame;

    再往下就需要用到文档3的知识了。Leap可以同时检测到若干只手,并用Hand对象封装数据,建议最多2只手进入Leap的检测区域。需要注意的是Leap无法区分左右手。我们判断一下Leap是否检测到手,如果检测到的话,就获取第一个。

    1             //如果能够获取手部数据
    2             if (!frame.Hands.IsEmpty)
    3             {
    4                 //获取手部数据
    5                 Hand hand = frame.Hands[0];
    6             }

    有了Hand对象,我们就能够知道手相对于Leap的位置。

    1                 //获取手部的位置,判断检测的范围
    2                 LeapVector palmPosition = hand.PalmPosition;//using LeapVetcor = Leap.Vector;
    3                 float palmHeight = palmPosition.y;

    这里的高度为什么是y,不是z?文档3中有介绍的啊。Leap的坐标系如下图所示。

    我们要在窗口中绘制Ellipse表示指尖在Leap检测区域中的位置,Leap检测的坐标是毫米,窗口绘制的时候是像素,这就涉及到了坐标的转换。

    我曾看到一篇文章,其说Leap的检测角度为150度,因此,在不考虑z轴的情况下,我们可以计算出来手所在的高度检测的宽度是多少,并将指尖的位置映射到窗口上。文档3说明了有效的检测区域是设备上方25毫米-600毫米的范围,故可以将指尖的高度映射到窗口上。

    1                 float detectionWidth = (float)(palmHeight * Math.Tan(75.0 / 180.0 * Math.PI) * 2);

    有了手的数据,我们接下来获取指尖的信息。Leap检测时,并不是每一帧都包括指尖的信息,例如当握拳时就无法得到指尖的数据。所以,在使用指尖数据时,我们需要判断一下。

    1                 //获取指尖的数据
    2                 FingerList fingers = hand.Fingers;
    3                 if (!fingers.IsEmpty)
    4                 {
    5                     Finger f1 = fingers[0];
    6                 }

    获取指尖的位置数据,并计算其在窗口上的位置。

     1                     LeapVector position = f1.TipPosition;
     2 
     3                     float x = position.x;
     4                     float y = position.y;
     5 
     6                     //下面将x、y映射到屏幕上
     7                     double screenWidth = canvas1.ActualWidth;
     8                     double screenHeight = canvas1.ActualHeight;
     9 
    10                     x = x / detectionWidth * screenWidth + (screenWidth / 2);
    11                     y = screenHeight - y / (60025) * screenHeight;

    最后,需要设置Ellipse的位置。

    1                     this.Dispatcher.BeginInvoke(new Action(delegate {
    2                        Canvas.SetLeft(ellipse, x);
    3                        Canvas.SetTop(ellipse, y);
    4                     }), null);

    注意,不能直接调用Canvas.SetLeft会出错的。

    好了,运行程序,看看效果吧。

     

    注意:

    1、using Leap;

    2、注意代码的缩进。

  • 相关阅读:
    换零钞
    空心菱形
    生成回文数
    机器人数目
    胡同门牌号
    七星填数
    阶乘位数
    打印数字
    平方末尾
    数位和
  • 原文地址:https://www.cnblogs.com/yinyy/p/3487567.html
Copyright © 2011-2022 走看看