当前位置:   article > 正文

Leap Motion 探究 【第一篇】_历动

历动

 

       由于团队开发需要,今天拿到了Leap Motion做测试开发,也就是历动,一款手部识别传感器。
       拿到历动之前已经对它有所了解,然而拿到手后发现确实不如想象中的那么没好,由于基础图像识别,肯定有一些弊端,例如手部遮盖部分识别出错,应用体验一般,应用也比较少等,给我的感觉好像这是一款还处于最后优化的产品,并不能代替现有的交互操作体验。不过,在一些简单的交互上,Leap还是给我了一个很好的反馈,比如手掌的左右倾斜,手指简单的点击操作等。
       结合VR交互,这款产品应该是一个颠覆性体验,抛弃了传统的遥控器式手柄,完全手部操纵,所见即所得的感觉,冲击还是很大。所以打算开一个专题,记录下开发Leap的点滴。

 
       万事当先,Leap提供了很友好的SDK!这对开发者极其重要,不知道牛长什么样怎么去喂牛?Leap支持的平台有:C, C#, Unity, Object-C, Java, Python, JavaScript, Unreal Engine。 非常庞大的支持了,然而百度搜索到的开发文档还是寥寥无几,谷歌到的东西也不多,不知道是体验不够优秀,还是什么原因,这么好的一款产品应该大家一起来优化才对。
       由于最近.net平台开发比较多,所以还是以CS做例子,看一下官方的源码,然后写一个自己的例子看看怎么处理消息。
 
  1. class Sample
  2. {
  3. public static void Main ()
  4. {
  5. // Create a sample listener and controller
  6. SampleListener listener = new SampleListener ();
  7. Controller controller = new Controller ();
  8. // Have the sample listener receive events from the controller
  9. controller.AddListener (listener);
  10. // Keep this process running until Enter is pressed
  11. Console.WriteLine ("Press Enter to quit...");
  12. Console.ReadLine ();
  13. // Remove the sample listener when done
  14. controller.RemoveListener (listener);
  15. controller.Dispose ();
  16. }
  17. }
 
       主程序段很简单, 首先实例化两个类,SampleListener(这个类后面详细讲,是Leap的核心部分)和Controller,然后给Controller增加一个监听,监听SampleListener反馈的消息。关闭时注意需要停止监听,并且丢弃Controller。
       下面开始讲SampleListener,即Leap中的Listener,不过由于Listener内消息都要自己编写,所以必须创建一个新的Listener去override Leap中的Listener。
 
  1. class SampleListener : Listener
  2. {
  3. private Object thisLock = new Object ();
  4. private void SafeWriteLine (String line)
  5. {
  6. lock (thisLock) {
  7. Console.WriteLine (line);
  8. }
  9. }
  10. public override void OnInit (Controller controller)
  11. {
  12. SafeWriteLine ("Initialized");
  13. }
  14. public override void OnConnect (Controller controller)
  15. {
  16. SafeWriteLine ("Connected");
  17. controller.EnableGesture (Gesture.GestureType.TYPE_CIRCLE);
  18. controller.EnableGesture (Gesture.GestureType.TYPE_KEY_TAP);
  19. controller.EnableGesture (Gesture.GestureType.TYPE_SCREEN_TAP);
  20. controller.EnableGesture (Gesture.GestureType.TYPE_SWIPE);
  21. }
  22. public override void OnDisconnect (Controller controller)
  23. {
  24. //Note: not dispatched when running in a debugger.
  25. SafeWriteLine ("Disconnected");
  26. }
  27. public override void OnExit (Controller controller)
  28. {
  29. SafeWriteLine ("Exited");
  30. }
  31. public override void OnFrame (Controller controller)
  32. {
  33. // Get the most recent frame and report some basic information
  34. Frame frame = controller.Frame ();
  35. SafeWriteLine ("Frame id: " + frame.Id
  36. + ", timestamp: " + frame.Timestamp
  37. + ", hands: " + frame.Hands.Count
  38. + ", fingers: " + frame.Fingers.Count
  39. + ", tools: " + frame.Tools.Count
  40. + ", gestures: " + frame.Gestures ().Count);
  41. foreach (Hand hand in frame.Hands) {
  42. SafeWriteLine (" Hand id: " + hand.Id
  43. + ", palm position: " + hand.PalmPosition);
  44. // Get the hand's normal vector and direction
  45. Vector normal = hand.PalmNormal;
  46. Vector direction = hand.Direction;
  47. // Calculate the hand's pitch, roll, and yaw angles
  48. SafeWriteLine (" Hand pitch: " + direction.Pitch * 180.0f / (float)Math.PI + " degrees, "
  49. + "roll: " + normal.Roll * 180.0f / (float)Math.PI + " degrees, "
  50. + "yaw: " + direction.Yaw * 180.0f / (float)Math.PI + " degrees");
  51. // Get the Arm bone
  52. Arm arm = hand.Arm;
  53. SafeWriteLine (" Arm direction: " + arm.Direction
  54. + ", wrist position: " + arm.WristPosition
  55. + ", elbow position: " + arm.ElbowPosition);
  56. // Get fingers
  57. foreach (Finger finger in hand.Fingers) {
  58. SafeWriteLine (" Finger id: " + finger.Id
  59. + ", " + finger.Type.ToString()
  60. + ", length: " + finger.Length
  61. + "mm, width: " + finger.Width + "mm");
  62. // Get finger bones
  63. Bone bone;
  64. foreach (Bone.BoneType boneType in (Bone.BoneType[]) Enum.GetValues(typeof(Bone.BoneType)))
  65. {
  66. bone = finger.Bone(boneType);
  67. SafeWriteLine(" Bone: " + boneType
  68. + ", start: " + bone.PrevJoint
  69. + ", end: " + bone.NextJoint
  70. + ", direction: " + bone.Direction);
  71. }
  72. }
  73. }
  74. // Get tools
  75. foreach (Tool tool in frame.Tools) {
  76. SafeWriteLine (" Tool id: " + tool.Id
  77. + ", position: " + tool.TipPosition
  78. + ", direction " + tool.Direction);
  79. }
  80. // Get gestures
  81. GestureList gestures = frame.Gestures ();
  82. for (int i = 0; i < gestures.Count; i++) {
  83. Gesture gesture = gestures [i];
  84. switch (gesture.Type) {
  85. case Gesture.GestureType.TYPE_CIRCLE:
  86. CircleGesture circle = new CircleGesture (gesture);
  87. // Calculate clock direction using the angle between circle normal and pointable
  88. String clockwiseness;
  89. if (circle.Pointable.Direction.AngleTo (circle.Normal) <= Math.PI / 2) {
  90. //Clockwise if angle is less than 90 degrees
  91. clockwiseness = "clockwise";
  92. } else {
  93. clockwiseness = "counterclockwise";
  94. }
  95. float sweptAngle = 0;
  96. // Calculate angle swept since last frame
  97. if (circle.State != Gesture.GestureState.STATE_START) {
  98. CircleGesture previousUpdate = new CircleGesture (controller.Frame (1).Gesture (circle.Id));
  99. sweptAngle = (circle.Progress - previousUpdate.Progress) * 360;
  100. }
  101. SafeWriteLine (" Circle id: " + circle.Id
  102. + ", " + circle.State
  103. + ", progress: " + circle.Progress
  104. + ", radius: " + circle.Radius
  105. + ", angle: " + sweptAngle
  106. + ", " + clockwiseness);
  107. break;
  108. case Gesture.GestureType.TYPE_SWIPE:
  109. SwipeGesture swipe = new SwipeGesture (gesture);
  110. SafeWriteLine (" Swipe id: " + swipe.Id
  111. + ", " + swipe.State
  112. + ", position: " + swipe.Position
  113. + ", direction: " + swipe.Direction
  114. + ", speed: " + swipe.Speed);
  115. break;
  116. case Gesture.GestureType.TYPE_KEY_TAP:
  117. KeyTapGesture keytap = new KeyTapGesture (gesture);
  118. SafeWriteLine (" Tap id: " + keytap.Id
  119. + ", " + keytap.State
  120. + ", position: " + keytap.Position
  121. + ", direction: " + keytap.Direction);
  122. break;
  123. case Gesture.GestureType.TYPE_SCREEN_TAP:
  124. ScreenTapGesture screentap = new ScreenTapGesture (gesture);
  125. SafeWriteLine (" Tap id: " + screentap.Id
  126. + ", " + screentap.State
  127. + ", position: " + screentap.Position
  128. + ", direction: " + screentap.Direction);
  129. break;
  130. default:
  131. SafeWriteLine (" Unknown gesture type.");
  132. break;
  133. }
  134. }
  135. if (!frame.Hands.IsEmpty || !frame.Gestures ().IsEmpty) {
  136. SafeWriteLine ("");
  137. }
  138. }
  139. }
       SampleListener中前几个事件不做说明,要点在于onFrame这个东西。这个东西和OpenCV中Frame概念一致,即“在每一帧上的数据”,关于“Frame”这个东西,我们能获取到以下数据:
  1. Console.WriteLine("Frame id: " + frame.Id
  2. + ", timestamp: " + frame.Timestamp
  3. + ", hands: " + frame.Hands.Count
  4. + ", fingers: " + frame.Fingers.Count
  5. + ", tools: " + frame.Tools.Count
  6. + ", gestures: " + frame.Gestures ().Count);
1. Frame ID
2.手个数
3.手指个数
4.工具个数(Leap可以识别出棍子这样的东西)
5.手势个数
 
       这些都是些统计数据,没有什么特别的意义,官方example在Frame中提供了上面五个玩意的详细使用方法。第一个就是手的细节参数。
       细读程序,可以发现,手包括了:手心,手方向,手臂,手指,骨头(关节翻译的比较合适一些),图示是官方的一个配置程序,可以看到,Hand包含的所有要素。
 
 
           手的角度,采用了PitchRowYaw坐标系定义,这样减轻了很多计算负担,只以手自身姿态为参照,确实给开发省去不少事情。手臂提供了关节和手腕的方向。手指提供了手指编号(即手指类型),长度(精度竟然是毫米!可以做为解锁用了)。骨头提供了类型,开始结束(即先后关节),方向。很明显,Leap程序是以人关节位置作为参考点处理的,非常聪明,一般做OpenCV时候,我们只是处理外手形状,找明显分割点来处理的,精度还行,不过极易被外部环境干扰,比如胖子的脖子2333333。以Leap这样处理,精细,稳定,对开发者来说,提供了友好的方式处理手势,为开发提供了方便。
 
      工具,简单,只有ID,position,direction,没有其他东西,够用就好,乔布斯说过,手指是最好的工具,要触摸笔干嘛。
 
       手势,内置了几种手势操作,在激发时候系统能够自动识别的,分别是 画圈,横扫,点击,向屏幕点击。 在操作时,记得按照官方给的属性操作就好。
 
  • Circle — A single finger tracing a circle.
  • Swipe — A long, linear movement of a finger.
  • Key Tap — A tapping movement by a finger as if tapping a keyboard key.
  • Screen Tap — A tapping movement by the finger as if tapping a vertical computer screen.
 
       值得一提的是,Leap提供了一个单独的 Touch Emulation,可以模仿平时大家熟悉的手机触摸操作。这点单独拿出来,看来Leap也是动了心思的,识别阈值甚至可以达到毫米级别,当然要这么用,你开心就好。
       分析了官方提供的Example后,自己写一个简单的参数获取就异常简单了,今天早上拿到的Leap,下午上课+调试飞机,晚上就将测试程序写了出来,有个几个小点要注意,一是记得引用LeapCSharp.NET3.5.dll或者LeapCSharp.NET4.0.dll,然后添加Leap.dll  Leap.lib  LeapCSharp.dll到项目中,二是不知为何,需要在application startup文件夹下,把LeapCSharp.NETx.0.dll添加进去,否则实例化时会出错。
       简单的Leap就这样创建好了,这几天试试做一个简单的东西来利用Leap的这些数据。
自己实例化的样例:http://download.csdn.net/detail/prius0304/9506700
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/128022
推荐阅读
相关标签
  

闽ICP备14008679号