您的位置:首页 > 其它

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

2014-11-03 18:11 190 查看
刚刚入门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轴旋转。机身旋转的角度,或者说机翼水平方向上偏转的角度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: