EMF & Hand Tracking
EMF Poses
sub = glove.emf_poses().subscribe()
poses = await sub.recv_async()
for pose in poses.poses:
pos = pose.pose.position
print(f"Position: [{pos[0]:+.3f}, {pos[1]:+.3f}, {pos[2]:+.3f}], confidence: {pose.confidence:.2f}")EMF poses are expressed in the emf_tx (back-of-hand transmitter coil) frame, with frame_id set to l_hand_emf_tx / r_hand_emf_tx. This differs from the wrist frame used by the hand tracking products — see Coordinate Frames for the transform between them.
EmfPose
Single-finger EMF pose.
| Field | Type | Description |
|---|---|---|
pose | Pose | Pose (position + orientation) |
confidence | float | Positioning confidence (0.0~1.0, higher is more reliable. Values below 0.9 can be treated as unreliable) |
EmfPoseArray
5-finger EMF pose array. Finger order: thumb, index, middle, ring, pinky.
| Field | Type | Description |
|---|---|---|
header | FrameHeader | Frame header |
poses | List[EmfPose] | 5-finger pose list |
{
"header": {
"seq": 100,
"timestamp_us": 1709876543210,
"frame_id": "l_hand_emf_tx"
},
"poses": [
{
"pose": {
"position": [0.041, 0.066, 0.022],
"orientation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": 1.0
}
},
"confidence": 0.95
}
]
}Hand Tracking Products
# Joint angles
sub = glove.hand_joint_angles().subscribe()
angles = await sub.recv_async()
for i, finger in enumerate(angles.fingers):
print(f"Finger {i}: angles={finger.angles}, confidence={finger.confidence:.2f}")
# Hand skeleton
sub = glove.hand_skeleton().subscribe()
skeleton = await sub.recv_async()
for joint in skeleton.joints:
print(f"{joint.name}: position={joint.pose.position}")Hand tracking products (joint angles hand_joint_angles, skeleton hand_skeleton, and fingertip poses tip_poses) are computed from EMF pose frames, and their timestamps match the source EMF frame — use this to align the products with their source data. See the SDK Time Synchronization page for full timestamp semantics.
For skeleton and fingertip poses, frame_id is the wrist frame (l_wrist / r_wrist).
The fingertip-pose seq is an independent stream counter that does not continue the source EMF seq.
FingertipPose
Single-finger fingertip pose.
| Field | Type | Description |
|---|---|---|
pose | Pose | Pose |
confidence | float | Confidence |
FingertipPoses
5-finger fingertip poses.
| Field | Type | Description |
|---|---|---|
header | FrameHeader | Frame header |
poses | List[FingertipPose] | 5-finger fingertip poses |
{
"header": {
"seq": 100,
"timestamp_us": 1709876543210,
"frame_id": "l_wrist"
},
"poses": [
{
"pose": {
"position": [0.041, 0.066, 0.022],
"orientation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": 1.0
}
},
"confidence": 0.95
},
... // 5 fingers total
]
}FingerJointAngles
Single-finger joint angles.
| Field | Type | Description |
|---|---|---|
angles | List[float] | Fixed array of 5 joint-angle slots (radians), accessed by index. The thumb uses all 5 values. The other four fingers use only the first 4, with the 5th value fixed at 0 as padding |
confidence | float | IK solver confidence |
HandJointAngles
Full-hand 21 DoF joint angles, where 21 DoF refers to the degrees of freedom of the hand kinematic model used by the hand tracking algorithm.
Returned per finger. The thumb maps to CMC3 + MCP1 + IP1, so all 5 slots of angles are used. The other four fingers map to MCP2 + PIP1 + DIP1, so only the first 4 slots are used, with the 5th slot as padding. Total 21 DoF for the whole hand. To keep the data structure uniform, the SDK returns a fixed-length 5 angles array per finger, so fingers contains 5 × 5 slots in total.
| Field | Type | Description |
|---|---|---|
header | FrameHeader | Frame header. frame_id carries over from the source EMF frame and has no meaning for joint angles |
fingers | List[FingerJointAngles] | 5-finger joint angles in thumb, index, middle, ring, pinky order |
{
"header": {
"seq": 100,
"timestamp_us": 1709876543210,
"frame_id": "l_hand_emf_tx"
},
"fingers": [
{
"angles": [0.12, 0.34, 0.56, 0.78, 0.91],
"confidence": 0.92
},
{
"angles": [0.11, 0.22, 0.33, 0.44, 0.0],
"confidence": 0.88
},
... // 5 fingers total. For non-thumb fingers, the 5th value is 0 padding
]
}SkeletonJoint
Hand skeleton keypoint.
| Field | Type | Description |
|---|---|---|
name | str | Keypoint name (such as index_finger_mcp) |
pose | Pose | Pose |
confidence | float | Confidence |
HandSkeleton
21-keypoint hand skeleton in the MediaPipe topology.
| Field | Type | Description |
|---|---|---|
header | FrameHeader | Frame header |
joints | List[SkeletonJoint] | 21 keypoints |
{
"header": {
"seq": 100,
"timestamp_us": 1709876543210,
"frame_id": "l_wrist"
},
"joints": [
{
"name": "wrist",
"pose": {
"position": [0.0, 0.0, 0.0],
"orientation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": 1.0
}
},
"confidence": 0.95
},
... // 21 MediaPipe keypoints total
]
}Output Rate Tuning
emf_poses and its derived streams publish at the EMF input rate (120 Hz) by default. To cut IK/FK compute, call glove.emf_poses_rate_divider() to set divider N — the output rate becomes input rate / N.
Calling
glove.emf_poses_rate_divider().set(4) # 120 Hz → 30 Hz
n = glove.emf_poses_rate_divider().get()Values and Default
| Value | Behavior |
|---|---|
1 (default) | No decimation. Output rate equals input rate |
≥ 2 integer | Output rate = input rate / N, not restricted to divisors of 120 (for example, N=5 yields 24 Hz) |
< 1, non-numeric, or unset | Treated as 1. No error, no stream interruption |
The change takes effect within 10 input frames (≤ ~83 ms at 120 Hz).
Affected Streams
| Follows divider | Unaffected |
|---|---|
emf_poses | imu_* (IMU streams) |
tip_poses | tf, tf_static |
hand_joint_angles | tactile, tactile_zones |
hand_skeleton | tactile_binary, tactile_residual |
tactile_point_cloud | — |
EMF solving still runs per input frame. Only the publish step decimates, so tracking continuity stays intact.
Interaction with Calibration
When you call glove.calibrate() or calibrate_blocking(), the SDK temporarily forces the divider to 1 for the capture phase and restores the prior value when capture ends — including error and cancellation paths. Pre-setting N=4 won't stretch the calibration runtime by 4×. See Calibration.