Skip to content
FORSENSE

UDP Reception – Joint Poses

This is the joint UDP sent by the HumanHand host and the SDK demo. Joint order and axes match the SDK arrays. Applications may skip UDP and read the SDK directly.

1. Overview

Output formatOriginJoints per handProtocolPacket size
Joint pose (wrist-relative)Palm root21FSNH version 4612 bytes
Joint pose (forearm-relative)Forearm module22FSNH version 4640 bytes
ItemDescription
SourceGlove joint poses; the forearm format requires a forearm module
TransportUDP unicast, connectionless
Rate120 Hz per hand by default
HandOne hand per packet. hand=1 left, hand=2 right
Two handsSame destination IP and port; one packet for left, one for right. The receiver splits on hand. The two packets need not arrive together

Host path: User Settings → Data Sending → Output format. SDK demo menu: send wrist-relative or forearm-relative joints.

2. Sending Configuration

ParameterDefaultDescription
Destination IP127.0.0.1Host: Data Sending. SDK demo: prompted at start
Destination port15001Both hands use this port
Byte orderLittle-endianAll multibyte fields

3. Coordinate System

ItemDescription
Wrist-relativeOrigin at the palm root (same as SDK hh_sdk_poll)
Forearm-relativeOrigin at the forearm (same as SDK hh_sdk_forearm_root); axes match the wrist format
Position unitmeters (m)
RotationQuaternion w, x, y, z
AxesLeft: right, forward, up; right: left, back, up
AxisLeft handRight hand
XRightLeft
YForward (along the fingers)Back
ZUp (dorsal)Up

The payload contains only that hand's joints. Use hand to select the side.

4. Joint Indices

4.1 Wrist-relative (21)

IndexPart
0Palm root (position ≈ 0)
1–4Thumb: MCP, proximal, distal, tip
5–8Index
9–12Middle
13–16Ring
17–20Little

Tips: 4, 8, 12, 16, 20.

4.2 Forearm-relative (22)

IndexPart
0Forearm (position always 0, identity quaternion)
1Wrist (the palm root above)
2–21Indices 1–20 of the wrist format (thumb MCP … little tip)

Tips: 5, 9, 13, 17, 21.

5. Packet Layout

24-byte header, then that hand's joints. Each joint is 28 bytes: xyz (12) + wxyz (16).

text
Wrist:   24 + 21×28 = 612
Forearm: 24 + 22×28 = 640

Send exactly that many bytes.

5.1 Header (24 bytes)

OffsetSizeTypeFieldDescription
04uint32magic0x464E4853 ("FSNH")
41uint8version4
51uint8hand1 left / 2 right
61uint8joint_count21 wrist-relative, 22 forearm-relative
71uint8statusSee 5.2
84uint32frame_indexPer-hand frame counter
128uint64timestamp_nsHost monotonic clock, nanoseconds
204uint32quat_order0x77787A79 (wxyz)

Packet length also distinguishes the two formats: 612 = wrist, 640 = forearm. It must match joint_count.

5.2 status

BitMaskMeaning
00x011 = new solve; 0 = repeat of the previous tick
10x0222-point packets only: 1 = forearm origin live this tick; 0 = last good forearm. 0 on 21-point packets
2–7Must be 0

5.3 One joint (28 bytes)

Relative offsetFieldType
0position.x/y/zfloat32×3
12quaternion.w/x/y/zfloat32×4

joint_count joints start at offset 24.

6. Receiving two hands

  1. Bind one UDP port (default 15001).
  2. On each datagram, read hand: 1 → left cache, 2 → right cache.
  3. Left and right frame_index / timestamp_ns are independent. Do not pair them as one instant.
  4. If one side has no new packet, keep that side's last packet. Do not copy the other side.

7. Timestamp and Rate

FieldMeaning
timestamp_nsHost monotonic clock, nanoseconds
frame_indexThat hand's solve counter; stops increasing if the solver stalls

Default interval is about 8.33 ms per hand (120 Hz). Two hands yield about 240 datagrams per second on that port.

8. Receiver Checklist

  1. magic == 0x464E4853 and version == 4
  2. Length 612 with joint_count==21, or length 640 with joint_count==22
  3. hand is 1 or 2
  4. quat_order == 0x77787A79
  5. status bits 2–7 are 0
  6. Parse only this packet's joint_count joints

9. C Struct Reference

Pack or unpack with the structs below.

c
#pragma pack(push, 1)

#define FSNH_MAGIC            0x464E4853u
#define FSNH_VERSION          4u
#define FSNH_HAND_LEFT        1u
#define FSNH_HAND_RIGHT       2u
#define FSNH_JOINTS_WRIST     21u
#define FSNH_JOINTS_FOREARM   22u
#define FSNH_STATUS_NEW       0x01u
#define FSNH_STATUS_FOREARM_LIVE 0x02u
#define FSNH_PKT_WRIST        612u
#define FSNH_PKT_FOREARM      640u
#define FSNH_QUAT_ORDER_WXYZ  0x77787A79u

typedef struct {
    float x, y, z;
    float qw, qx, qy, qz;
} FsnhJointPose;

typedef struct {
    uint32_t magic;
    uint8_t  version;
    uint8_t  hand;
    uint8_t  joint_count;
    uint8_t  status;
    uint32_t frame_index;
    uint64_t timestamp_ns;
    uint32_t quat_order;
} FsnhHeader;

typedef struct {
    FsnhHeader header;
    FsnhJointPose joints[FSNH_JOINTS_WRIST];
} FsnhPacketWrist;

typedef struct {
    FsnhHeader header;
    FsnhJointPose joints[FSNH_JOINTS_FOREARM];
} FsnhPacketForearm;

#pragma pack(pop)

For a wrist packet, send sizeof(FsnhPacketWrist) (612) bytes only.

10. Sample Receiver

The site provides udp_receiver.py on the host default port 15001:

bash
python3 udp_receiver.py --port 15001

Packet length selects the live table: 612/640 joints, 240 coil+IMU, 1860 Pico body. Two hands share the port; each hand is its own table, refreshed in place about once a second.

11. Comparison with EMF + Transmitter IMU

ItemJoint posesEMF + transmitter IMU
ProtocolFSNH version 4FNIM version 2
Packet size612 B (wrist) / 640 B (forearm)240 B
Content21 or 22 joints/hand6 slot poses + transmitter IMU
Position unitMetresMillimetres
FrameLeft RFU / right LBUMagnetic-transmitter tracking frame
Two handsSame port, separate packetsSeparate left and right packets
TimestampMonotonic-clock nanosecondsMonotonic-clock nanoseconds

Copyright © 2026 Forsense. All rights reserved.