开发

手势检测

更新时间: 2026年3月13日
手势由形状和变换定义。形状是关于手指关节所需位置的布尔条件。变换是关于手部在世界空间中所需方向的布尔条件。当被追踪的手部与特定姿势所需的形状和变换相匹配时,该姿势即被检测到。
本主题解释了姿势、形状和变换识别,以及用于确定姿势是否被检测到的标准。此外,它还介绍了可用于可视化调试姿势的调试组件。要创建自己的自定义手势,请参阅构建自定义手势

姿势 Prefab

互动 SDK 包含六个即插即用的示例姿势 Prefab。您可以使用这些 Prefab 中定义的模式来定义自己的姿势。您可以在 PoseExamples 示例场景中使用这些姿势 Prefab 来尝试姿势检测。
  • RockPose
  • PaperPose
  • ScissorsPose
  • ThumbsUpPose
  • ThumbsDownPose
  • StopPose
每个姿势 Prefab 都有以下组件:
Thumbs up pose detection image
一个姿势包括必需的变换特征,这些变换特征包含在 TransformRecognizerActiveState 中,还包括形状特征,这些形状特征包含在 ShapeRecognizerActiveState 中。这些变换和形状特征通过 ActiveStateGroup 进行组合,以定义姿势。

形状识别

姿势包括一个或多个形状。形状是关于一个或多个手指位置的一组布尔条件。条件使用手指特征状态来定义。如果有追踪的手符合这些条件,形状就会变为启用。如果姿势中的所有形状都已启用,并且变换特征也已启用,则该姿势就会被检测到。姿势的形状存储为姿势的 ShapeRecognizerActiveState 组件中的 ShapeRecognizer 资源。

ShapeRecognizerActiveState

ShapeRecognizerActiveState 会将构成姿态的形状与每根手指的状态进行比对。如果它们全部匹配,ShapeRecognizerActiveState 就会变为启用。

ShapeRecognizer

ShapeRecognizer 是一个用于定义形状的 ScriptableObject。为了定义形状,它使用一组称为“特征配置”的规则。“特性配置”为五根手指中的每一根都指定了一个必需的位置(状态)。它通过使用至少一个手指特征(卷曲度、屈曲度、外展度和对掌度)来定义状态。ShapeRecognizerShapeRecognizerActiveState 组件引用,以确定某个姿势是否处于启用状态。

FingerFeatureStateProvider

FingerFeatureStateProvider 提供追踪到的手部各手指的状态,并包含每个手指状态转换的阈值。它由 ShapeRecognizerActiveState 组件引用。

FingerFeatureStateThresholds

FingerFeatureStateThresholds 是一个 ScriptableObject,用于定义每个手指特征的状态阈值。状态阈值是一组边界值,用于确定手指何时从一个状态过渡到另一个状态。例如,卷曲特征有三种状态:张开、中立和闭合。卷曲特征的状态阈值使用角度(以度为单位)来定义手指状态从张开变为中立、从中立变为闭合,或者相反的情况。
互动 SDK 提供四组默认的状态阈值,它们位于 DefaultSettings/PoseDetection 下:
  • DefaultThumbFeatureStateThresholds(对于拇指)
  • IndexFingerFeatureStateThresholds(对于食指)
  • MiddleFingerFeatureStateThresholds(对于中指)
  • DefaultFingerFeatureStateThresholds(对于无名指和小指)
Finger feature image
拇指的卷曲状态阈值。对于卷曲特征,该值是以度为单位的角度。

FingerFeatureStateThresholds 示例

考虑到两种状态之间的转换,A <> B:
如果当前状态是“A”,要转换到“B”,则角度必须在该对的中点以上至少上升(宽度 / 2.0)度,并保持“处于状态的最小时间”秒。
如果当前状态是“B”,要转换到“A”,则角度必须在该对的中点以下至少下降(宽度 / 2.0)度,并保持“处于状态的最小时间”秒。
因此,对于卷曲特征的转换来说:
  • 张开 > 中立:值必须在 0.0222 秒内保持在 195 度以上
  • 中立 > 张开:值必须在 0.0222 秒内保持在 185 度以下
  • 中立 > 闭合:值必须在 0.0222 秒内保持在 210 度以上
  • 闭合 > 中立:值必须在 0.0222 秒内保持在 200 度以下

手指特征

手指特性是指特定的手指位置,这些位置允许您定义一个形状。共有四个特征:

卷曲

代表手指或拇指顶部两个关节的弯曲程度。这个特征不考虑近指(指根)关节。
状态:
  • 张开:手指完全伸直。
  • 中立:手指微微向内卷曲,就像它们环绕着一个咖啡杯一样。
  • 闭合(如图所示):手指紧紧地向内卷曲,以至于指尖几乎触碰到手掌。
The first two joints of all five fingers curled inwards
关节用于测量卷曲特征。

屈曲

近指(指根)关节相对于手掌的弯曲程度。屈曲特征仅适用于四根手指。对于拇指可能会产生误报。
状态:
  • 张开:手指的第一块骨头完全伸展,与手掌平行。
  • 中立:有点弯曲。
  • 闭合:指根关节完全弯曲(如图所示)。
屈曲特征仅适用于 4 根手指。对于拇指可能会产生误报。
Four fingers bent perpendicular to the palm
一个指根关节处于闭合状态的屈曲示例。

外展

外展指两个相邻手指在手指基部之间所成的角度。也即给定手指与其靠近小指的相邻手指之间的角度。例如,食指的外展是指食指与中指之间的角度。
状态:
  • 张开:这两个手指是分开的(以食指为例,如图所示)。
  • 闭合:这两个手指紧紧贴合在一起(以拇指、中指、无名指为例,如图所示)。
  • :目前未使用。
注意:不支持测量小指的外展。
All five fingers are parallel to the palm. The index and middle fingers are spread apart, forming a v shape
外展的示例。食指处于张开状态。拇指、中指和无名指处于闭合状态。

对指

指给定手指的指尖与拇指指尖之间的接近程度。此特征仅适用于食指、中指、无名指和小指。
状态:
  • 触摸:指尖关节之间的距离在 1.5 厘米以内(以食指为例,如图所示)。
  • 靠近:指尖关节之间的距离在 1.5 厘米至 15 厘米之间。
  • :指尖关节之间的距离超过 15 厘米。
Index finger touching the tip of the thumb
对指的示例。食指处于触摸状态。

变换识别

姿势由一个或多个变换组成。手的变换只代表方向和位置。方向的评估仅相对于以下变换进行:手腕向上、手腕向下、手掌向下、手掌向上、手掌朝向面部、手掌背向面部、手指向上、手指向下以及捏合清除。
一个姿势所需的变换列在该姿势的 TransformRecognizerActiveState 组件中。在手势追踪过程中,会将手部的变换与姿势的变换进行比较。如果两组变换都匹配,并且姿势中的所有形状都处于启用状态,那么就会检测到该姿势。
Transform recognition image定义手部手指、手腕和手掌的轴线。

TransformRecognizerActiveState

TransformRecognizerActiveState 接收一个 TransformFeatureStateProvider、一组变换特征配置以及一个变换配置。该状态提供器会读取原始特征值,并使用您在此组件中提供的 TransformConfig 将它们量化为 TransformFeatureStates

TransformFeatureStateProvider

TransformFeatureStateProvider 提供被追踪手部的变换状态。它被 TransformRecognizerActiveState 组件所引用。
注意:注册特定的配置后,RegisterConfig 方法就可以查询该配置下追踪的每个特征的状态。它借助 FeatureStateProvider 来驱动状态变更逻辑。

速度识别

速度识别组件检测运动,而形状识别仅检测静态姿势。例如,形状识别可以检测到一只手摆出大拇指朝上的姿势,但无法确定在该姿势下这只手是否正在向上移动。

JointDeltaProvider

JointDeltaProvider 是一个组件,用于追踪各帧之间的手部关节的位置,并计算帧到帧的位置和旋转差异。它会缓存关节姿态数据,以避免当多个组件需要同一关节的差异信息时进行重复计算。
JointVelocityActiveStateJointRotationActiveState 均需引用 JointDeltaProvider 以访问关节运动数据。提供器仅追踪由活跃使用者注册的关节,尽量减少性能开销。
属性说明
手部
追踪手部,每帧从中读取关节姿态数据

JointVelocityActiveState

JointVelocityActiveState 是一个 IActiveState,用于追踪一系列手部关节的位置速度,并将其与目标方向进行比较。当目标轴向上的速度超过阈值并持续达到最短设定时长时,该状态将会激活。
目标方向可基于手部、世界空间或头部进行定义,支持检测向前滑动(相对于手掌)或向上移动(相对于世界)等手势。
属性说明
手部
提供关节位置的追踪手部
关节增量提供器
JointDeltaProvider 用于检索缓存的位置增量
功能配置
需评估的关节及其目标轴列表
最小速度
状态激活所需超过的单位(每秒)速度阈值
阈值范围
为速度阈值设置的迟滞缓冲范围,避免在临界值附近频繁切换状态
状态最短持续时间
状态变化前,速度超过阈值的最短持续时间(单位:秒)

JointRotationActiveState

JointRotationActiveState 是一个 IActiveState,用于追踪一系列手部关节的角速度,并将其与目标旋转轴进行比较。当围绕目标轴的角速度超过阈值并持续达到最短设定时长时,该状态将会激活。
目标旋转轴可基于手部或世界进行定义。手部相对轴包含旋前、旋后、桡偏、尺偏、伸展及屈曲。
属性说明
手部
提供关节旋转的追踪手部
关节增量提供器
JointDeltaProvider 用于检索缓存的旋转增量
功能配置
需评估的关节及其目标旋转轴列表
每秒度数
状态激活所需超过的角速度阈值(单位:度/秒)
阈值范围
为角速度阈值设置的迟滞缓冲范围,避免在临界值附近频繁切换状态
状态最短持续时间
状态变化前,角速度必须超过阈值的最短持续时间(单位:秒)

Sequence 组件

IActiveState 组件可以使用 Sequence 组件串联在一起。由于 Sequence 组件可识别一段时间内的一系列 IActiveState,因此可用于组成复杂的手势。有关复杂手势的示例,请参阅手势示例示例场景。

Sequence 类

Sequence 接收一个 ActivationSteps 列表,并在它们启用时逐个执行。每个 ActivationStep 包含 IActiveState、最小启用时长和最大步骤时长。这些步骤的运作如下:
  • IActiveState 的启用时长必须至少达到最小启用时长,Sequence 才会进入下一步。
  • 如果 IActiveState 的启用时长超过最大步骤时长,则步骤会失败,Sequence 会重新开始。
一旦 Sequence 中的最后一个 ActivationStep 完成,Sequence 就会启用。如果提供了一个可选的 RemainActiveWhile IActiveState,则只要 RemainActiveWhile 保持启用,Sequence 就会保持启用。
Sequence 的最后一个阶段是可选的冷却阶段,其持续时间可在 RemainActiveCooldown 字段中设置。停用中的 Sequence 会等待这个冷却计时器结束,然后才最终变为非启用状态。
SequenceActiveStateSequenceIActiveState 包装函数,它可以在序列开始、Sequence 步骤完成或两者都发生时报告为激活状态。

调试姿势

ActiveStateDebugTreeUI 组件会在世界空间用户界面画布上实时渲染任何 IActiveState 层级的可视化树状图。树中的每个节点代表一个 IActiveState 组件,并显示其当前是处于激活还是未激活状态。通过该组件,您可以直观看到姿势或手势序列中哪些部分已被识别,哪些部分还未被识别。
注意:由于 ActiveStateDebugTreeUI 基于 IActiveState 接口运行,因此可用于调试项目中的任何 IActiveState 层级,而不仅仅是姿势。例如,您可以指定 ActiveStateGroupSequence 或任何自定义 IActiveState 作为根,来实现状态树可视化。
如要使用调试树,请执行以下操作:
  1. Packages/com.oculus.integration.interaction/Runtime/Prefabs/Debug/ 中的 ActiveStateDebugTree prefab 添加到您的场景。
  2. ActiveStateDebugTreeUI 组件中,指定要调试的根 IActiveState,例如 ActiveStateGroupSequence
  3. 进入运行模式。调试树会自动展开,以显示每个子 IActiveState 节点,并通过颜色来区分当前的激活或未激活状态。
调试树每帧更新,您可在手部执行手势时实时观察状态切换。该组件提供两种布局模式:水平布局(从左至右)和垂直布局(从上至下,更为紧凑)。
属性说明
激活状态
根节点 IActiveState,其树状结构将可视化
节点 prefab
用于渲染树中每个节点的 prefab
有关调试树的可用示例,请参阅手势示例示例场景。如要了解如何通过调试可视化功能构建自定义姿势,请参阅构建自定义手势

内部实现类

这里描述的类实现了底层功能,但在使用姿势识别时,无需对其进行修改。

FeatureStateProvider

FeatureStateProvider 是一个通用辅助类,用于跟踪功能的当前状态,并使用阈值来确定状态变化。该类使用缓冲区来处理状态变化,以确保特征不会在状态之间快速切换。

FeatureStateThresholdsEditor

一个泛型编辑器类,它定义了依赖编辑器类(如 FingerFeatureStateThresholdsEditorTransformStatesThresholdsEditor)的外观和感觉。

FeatureDescriptionAttribute

用于定义在编辑器中可见的说明、提示和值,以帮助用户为特征设置阈值。

IFeatureStateThreshold

IFeatureStateThreshold 是一个泛型接口,它定义了手部姿态检测中使用的所有阈值的功能。

IFeatureStateThresholds

IFeatureStateThresholds 定义了一个特定于特征类型(无论是手指特征还是变换特征)的 IFeatureStateThreshold 集合。

IFeatureThresholds

IFeatureThresholds 提供了一个接口,该接口包含一组 IFeatureStateThreshold 以及 MinTimeInState(即特征在过渡到另一个状态之前可以处于某个状态的最小时间阈值)。

ColliderContainsHandJointActiveState

一个 IActiveState,用于检测手部关节是否位于碰撞器内部。如果指定 SphereCollider,则使用其半径进行检查;否则,脚本将依赖于碰撞器的边界。如果开发者希望在姿势处于启用状态时检查手部关节是否存在于某个体积内,那么这个类就很有用。

HmdOffset

可以附加到需要从中心眼锚定的对象上。可以指定位置和旋转偏移量,以及用于切换后者的横滚、俯仰和偏航的选项。可以将此值与 ColliderContainsHandJointActiveState 相结合,以将碰撞器相对于中心眼进行定位。

详细了解

设计准则