Leap Motion 入门二:官方Sample个人解读

来源:互联网 发布:飚风打印软件 编辑:程序博客网 时间:2024/06/10 09:19

刚刚入门Leap Motion, 跑了LeapSDK中的Sample。这个Sample结构十分好,Leap Motion的API简洁而又强大。

我将注意力集中在onFrame()中,以下是我对这个例子的粗略解读。代码片后有一些调用方法和类的Reference,便于理解。

如有什么理解不当的地方,还请多多指正,互相交流学习。


<span style="font-family:Courier New;">/******************************************************************************\* Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved.               ** Leap Motion proprietary and confidential. Not for distribution.              ** Use subject to the terms of the Leap Motion SDK Agreement available at       ** https://developer.leapmotion.com/sdk_agreement, or another agreement         ** between Leap Motion and you, your company or other organization.             *\******************************************************************************/#include <iostream>#include <string.h>#include "Leap.h"#include <windows.h> // 打开Sleep(3000); using namespace Leap;// 生命周期class SampleListener : public Listener {  public:    virtual void onInit(const Controller&);    virtual void onConnect(const Controller&);    virtual void onDisconnect(const Controller&);    virtual void onExit(const Controller&);    virtual void onFrame(const Controller&);    virtual void onFocusGained(const Controller&);    virtual void onFocusLost(const Controller&);    virtual void onDeviceChange(const Controller&);    virtual void onServiceConnect(const Controller&);    virtual void onServiceDisconnect(const Controller&);  private:};const std::string fingerNames[] = {"Thumb", "Index", "Middle", "Ring", "Pinky"};const std::string boneNames[] = {"Metacarpal", "Proximal", "Middle", "Distal"};const std::string stateNames[] = {"STATE_INVALID", "STATE_START", "STATE_UPDATE", "STATE_END"};// 初始化时void SampleListener::onInit(const Controller& controller) {  std::cout << "Initialized" << std::endl;}// 连接时void SampleListener::onConnect(const Controller& controller) {  std::cout << "Connected" << std::endl;  // 打开对所有手势的识别  controller.enableGesture(Gesture::TYPE_CIRCLE);  controller.enableGesture(Gesture::TYPE_KEY_TAP);  controller.enableGesture(Gesture::TYPE_SCREEN_TAP);  controller.enableGesture(Gesture::TYPE_SWIPE);}// 断开连接时void SampleListener::onDisconnect(const Controller& controller) {  // Note: not dispatched when running in a debugger.  std::cout << "Disconnected" << std::endl;}// 退出时void SampleListener::onExit(const Controller& controller) {  std::cout << "Exited" << std::endl;}// 核心,获取一帧时void SampleListener::onFrame(const Controller& controller) {  // Get the most recent frame and report some basic information  // 首先获取frame信息  const Frame frame = controller.frame();  std::cout << "Frame id: " << frame.id()            << ", timestamp: " << frame.timestamp()            << ", hands: " << frame.hands().count()            << ", fingers: " << frame.fingers().count()            << ", tools: " << frame.tools().count()            << ", gestures: " << frame.gestures().count() << std::endl;  // 从frame中获取所有手的HandList, 遍历HandList获取每只手Hand  HandList hands = frame.hands();  for (HandList::const_iterator hl = hands.begin(); hl != hands.end(); ++hl) {    // Get the first hand    const Hand hand = *hl;    std::string handType = hand.isLeft() ? "Left hand" : "Right hand";    std::cout << std::string(2, ' ') << handType << ", id: " << hand.id()              << ", palm position: " << hand.palmPosition() << std::endl;    // Get the hand's normal vector and direction// 获取手掌法向量和手掌的方向, 其中 hand.direction() is expressed as a unit vector pointing in the same direction as the directed line from the palm position to the finger.    const Vector normal = hand.palmNormal();    const Vector direction = hand.direction();    // Calculate the hand's pitch, roll, and yaw angles// pitch yaw roll的介绍在代码片后方    std::cout << std::string(2, ' ') <<  "pitch: " << direction.pitch() * RAD_TO_DEG << " degrees, "              << "roll: " << normal.roll() * RAD_TO_DEG << " degrees, "              << "yaw: " << direction.yaw() * RAD_TO_DEG << " degrees" << std::endl;    // 通过 Hand 获取 Arm 信息    Arm arm = hand.arm();    std::cout << std::string(2, ' ') <<  "Arm direction: " << arm.direction()              << " wrist position: " << arm.wristPosition()              << " elbow position: " << arm.elbowPosition() << std::endl;    // 通过 Hand 获取 FingerList, 遍历 FingerList 获取每个 Finger 的信息    const FingerList fingers = hand.fingers();    for (FingerList::const_iterator fl = fingers.begin(); fl != fingers.end(); ++fl) {      const Finger finger = *fl;      std::cout << std::string(4, ' ') <<  fingerNames[finger.type()]                << " finger, id: " << finger.id()                << ", length: " << finger.length()                << "mm, width: " << finger.width() << std::endl;      // 手指骨的介绍见其他本博客的Leap Motion的入门简介      for (int b = 0; b < 4; ++b) {        Bone::Type boneType = static_cast<Bone::Type>(b);        Bone bone = finger.bone(boneType);        std::cout << std::string(6, ' ') <<  boneNames[boneType]                  << " bone, start: " << bone.prevJoint()                  << ", end: " << bone.nextJoint()                  << ", direction: " << bone.direction() << std::endl;      }    }  }  // 从 frame 中获取 ToolList, 遍历 ToolList 获取每个 Tool的信息  const ToolList tools = frame.tools();  for (ToolList::const_iterator tl = tools.begin(); tl != tools.end(); ++tl) {    const Tool tool = *tl;    std::cout << std::string(2, ' ') <<  "Tool, id: " << tool.id()              << ", position: " << tool.tipPosition()              << ", direction: " << tool.direction() << std::endl;  }  // 从Frame 中获取 Gesture信息  const GestureList gestures = frame.gestures();  for (int g = 0; g < gestures.count(); ++g) {    Gesture gesture = gestures[g];    switch (gesture.type()) {  // 如果是 CIRCLE 的手势      case Gesture::TYPE_CIRCLE:      {        CircleGesture circle = gesture;        std::string clockwiseness;/* circle.pointable() 代表画圈的那个 finger, finger继承自Pointable objects circle.pointable().direction() 返回的是手指三维空间上所指方向的向量 Vector circle.normal()  返回的是追踪到的圈的法向量。如果追踪到的是顺时针的圈,那么法向量是跟手指的方向是同一朝向的。逆时针则法向量朝内。 circle.pointable().direction().angleTo(circle.normal()) 通过计算圈的法向量和手指指向向量的夹角(此处返回的是弧度,PI相当于180°), 则可以推出是顺时针还是逆时针的。*/        if (circle.pointable().direction().angleTo(circle.normal()) <= PI/2) {          clockwiseness = "clockwise"; // 顺时针        } else {          clockwiseness = "counterclockwise"; // 逆时针        }        // 计算上一帧到这一帧经过的角度        float sweptAngle = 0;/*STATE_START 表示第一帧。circle.state() != Gesture::STATE_START: 第一帧不能计算        frame(0)表示的是当前帧,而frame(1)表示当前帧的前一帧,以此类推*/        if (circle.state() != Gesture::STATE_START) {          CircleGesture previousUpdate = CircleGesture(controller.frame(1).gesture(circle.id()));          sweptAngle = (circle.progress() - previousUpdate.progress()) * 2 * PI;        }        std::cout << std::string(2, ' ')                  << "Circle id: " << gesture.id()                  << ", state: " << stateNames[gesture.state()]                  << ", progress: " << circle.progress()                  << ", radius: " << circle.radius()                  << ", angle " << sweptAngle * RAD_TO_DEG                  <<  ", " << clockwiseness << std::endl;        break;      }  // 如果是SWIPE的手势      case Gesture::TYPE_SWIPE:      {        SwipeGesture swipe = gesture;        std::cout << std::string(2, ' ')          << "Swipe id: " << gesture.id()          << ", state: " << stateNames[gesture.state()]          << ", direction: " << swipe.direction()          << ", speed: " << swipe.speed() << std::endl;        break;      }  // 如果是KEY_TAP的手势      case Gesture::TYPE_KEY_TAP:      {        KeyTapGesture tap = gesture;        std::cout << std::string(2, ' ')          << "Key Tap id: " << gesture.id()          << ", state: " << stateNames[gesture.state()]          << ", position: " << tap.position()          << ", direction: " << tap.direction()<< std::endl;        break;      }  // 如果是SCREEN_TAP的手势      case Gesture::TYPE_SCREEN_TAP:      {        ScreenTapGesture screentap = gesture;        std::cout << std::string(2, ' ')          << "Screen Tap id: " << gesture.id()          << ", state: " << stateNames[gesture.state()]          << ", position: " << screentap.position()          << ", direction: " << screentap.direction()<< std::endl;        break;      }      default:        std::cout << std::string(2, ' ')  << "Unknown gesture type." << std::endl;        break;    }  }  if (!frame.hands().isEmpty() || !gestures.isEmpty()) {    std::cout << std::endl;  }  Sleep(3000); // 睡眠3秒钟以减慢输出。可以注释掉。}void SampleListener::onFocusGained(const Controller& controller) {  std::cout << "Focus Gained" << std::endl;}void SampleListener::onFocusLost(const Controller& controller) {  std::cout << "Focus Lost" << std::endl;}void SampleListener::onDeviceChange(const Controller& controller) {  std::cout << "Device Changed" << std::endl;  const DeviceList devices = controller.devices();  for (int i = 0; i < devices.count(); ++i) {    std::cout << "id: " << devices[i].toString() << std::endl;    std::cout << "  isStreaming: " << (devices[i].isStreaming() ? "true" : "false") << std::endl;  }}void SampleListener::onServiceConnect(const Controller& controller) {  std::cout << "Service Connected" << std::endl;}void SampleListener::onServiceDisconnect(const Controller& controller) {  std::cout << "Service Disconnected" << std::endl;}int main(int argc, char** argv) {  // 创建 listener 和 controller  SampleListener listener;  Controller controller;  // 把 Sample listener 加到 controller中  controller.addListener(listener);  // 此处改变请求策略  /*    // 设置单个策略controller.setPolicyFlags(Controller::PolicyFlag::POLICY_BACKGROUND_FRAMES);// 设置多个策略controller.setPolicyFlags(static_cast<Controller::PolicyFlag>(Controller::PolicyFlag::POLICY_IMAGES | Controller::PolicyFlag::OPTIMIZE_HMD));// 关闭所有策略controller.setPolicyFlags(Controller::PolicyFlag::POLICY_DEFAULT);  */  if (argc > 1 && strcmp(argv[1], "--bg") == 0)    controller.setPolicyFlags(Leap::Controller::POLICY_BACKGROUND_FRAMES);  // 输入 Enter结束  std::cout << "Press Enter to quit..." << std::endl;  std::cin.get();  // 去除listener  controller.removeListener(listener);  return 0;}</span>



手势介绍(Gesture)


class Leap::CircleGesture


代表了画圆的手势。

在用这个手势之前,必须在onConnect()中激活此手势。

Controller.enableGesture(Gesture::TYPE_CIRCLE);

状态:

State::STATE_START(刚开始), State::STATE_UPDATE(正在执行), State::STATE_STOP(结束)

可以设置最小的弧度和最小的角度来判断是否为一个TYPE_CIRCLE

controller.config().setFloat("Gesture.Circle.MinRadius", 10.0);

controller.config().setFloat("Gestrure.Circle.MinArc", .5):

controller.config().save();


Vector normal(): 返回被追踪圆的法向量

Pointable pointable(): 返回画圆的Finger


class Leap::SwipeGesture


代表横扫的手势。

在用这个手势之前,必须在onConnect()中激活此手势。

Controller.enableGesture(Gesture::TYPE_SWIPE);


可以设置最小的距离和最小的速度来判断是否为一个TYPE_SWIPE

controller.config().setFloat("Gesture.Swipe.MinLength", 200.0); //单位是mm

controller.config().setFloat("Gestrure.Circle.MinVelocity", 750): //单位是mm/s

controller.config().save();


class Leap::ScreenTapGesture


代表手指或者工具点击屏幕的动作。手指是不动的。


在用这个手势之前,必须在onConnect()中激活此手势。

Controller.enableGesture(Gesture::TYPE_SCREEN_TAP);


可以设置最小的手指移动距离,一段时间的最小移动速度来判断是否为一个TYPE_SCREEN_TAP

controller.config().setFloat("Gesture.ScreenTap.MinForwardVelocity", 30.0); // 单位是mm/s,Default value为50

controller.config().setFloat("Gesture.ScreenTap.HistorySeconds", .5): //单位是s, Default value为0.1

controller.config().setFloat("Gesture.ScreenTap.MinDistance", 1.0); // 单位是mm, Default value为5.0

controller.config().save();


class Leap::KeyTapGesture

代表手指点击键的动作。手指需要上下移动。

在用这个手势之前,必须在onConnect()中激活此手势。

Controller.enableGesture(Gesture::TYPE_KEY_TAP);


可以设置最小的手指移动距离,一段时间的最小手指按下键的运动速度来判断是否为一个TYPE_KEY_TAP

controller.config().setFloat("Gesture.KeyTap.MinDownVelocity", 40.0); // 单位是mm/s,Default value为50

controller.config().setFloat("Gesture.KeyTap.HistorySeconds", .2): //单位是s, Default value为0.1

controller.config().setFloat("Gesture.KeyTap.MinDistance", 8.0); // 单位是mm, Default value为3.0

controller.config().save();


PolicyFlag 介绍


void setPolicyFlags(PolicyFlag flags)

用于改变请求策略。

请求策略的分类:

1.POLICY_BACKGROUND_FRAMES == (1<<0)

程序在后台运行时leap motion依然获取数据信息。在leap motion的控制面板中也要将“允许后台应用程序”的设置开启。

2.POLICY_IMAGES == (1<<1)

程序在执行时同时也获取image信息。默认是不获取的,请求这项时同时也要在控制面板中将“允许图像”打勾。

3.POLICY_OPTIMIZE_HMD == (1<<2)

程序在执行时对头戴设备进行优化。首先你要有一个头戴式设备。

4.POLICY_DEFAULT== 0

默认,关闭所有请求。  


pitch, yaw, roll 介绍

在LeapMotion中,坐标的表示如下:

pitch() 俯仰。 代表物体绕X轴上下旋转。 想象一个飞机,机头指向Z的方向,pitch就代表飞机上升下降的角度。

yaw() 航向。 指物体绕Y轴旋转。机头左右偏转的角度,代表飞行的方向。

roll() 横滚。 指物体绕Z轴旋转。机身旋转的角度,或者说机翼水平方向上偏转的角度。

0 0