Skip to content
Get SDK Access

Solving glove data onto a hand skeleton

The Smartgloves stream per-sensor orientations, not joint angles or a finished pose. To drive a rigged hand you have to solve those orientations onto a skeleton every frame. This guide recaps the steps that solve involves, independent of any particular language or engine, so you can implement it on your own stack. The RGMP Viewer sample is a complete, working reference for everything described here.

The SDK is deliberately low-level: it hands you the raw sensor data and stays out of your way, so the solver is yours to build and tune for your own rig, accuracy, and performance needs. Our sample is here to show one approach and get you started; treat it as a learning reference rather than a drop-in, production-grade implementation, and don’t be shy about doing things differently.

If you would rather not implement any of it, the SDK also ships a ready-made solver you can run as a separate process — see Hand solver below.

rkk-hand-solver is a sidecar binary, included in the SDK package, that performs the solve described on this page for you. It consumes the driver’s RGMP stream, solves every connected glove into a 26-joint hand, and re-emits the result as RGMP on a port of its own:

flowchart LR
    Driver["SDK Driver<br/>rokoko-sdk"]
    Solver["rkk-hand-solver<br/>(solve)"]
    App["Your application"]

    Driver -->|"TCP :12276 (RGMP)"| Solver
    Solver -->|"TCP :12277 (RGMP)"| App

Re-emitting RGMP is the whole point: a solved hand is just another RGMP device, so you read it with the client you already have. Point it at the solver’s port instead of the driver’s and nothing else changes:

Terminal window
python3 rgmp_stream.py --host 127.0.0.1 --port 12277

By default the solver starts the driver itself, so this is the whole setup:

Terminal window
rkk-hand-solver

If something else already manages the driver, tell the solver to use the running one instead:

Terminal window
rkk-hand-solver --no-driver
FlagDefaultPurpose
--listen127.0.0.1:12277Address to serve solved hands on. Use :0 to let the OS pick a free port; the startup banner reports what was bound.
--rgmp-addr127.0.0.1:12276The driver’s RGMP endpoint to consume.
--emitlocalWhat each hand carries: local, world, or both. See below.
--no-driverAssume the driver is already running.
--driver-pathrokoko-sdkDriver executable to launch.
--driver-argExtra argument passed to the driver, after the defaults. Repeatable.
--driver-quietDiscard the driver’s output instead of sharing the solver’s.
--driver-timeout15Seconds to wait for the driver to open its RGMP port.

One synthetic device per solved hand, with device_type of solved_hand. Its device_id is the source glove’s, and timestamp_epoch and every timestamp_us are passed through unchanged, so solved hands stay correlated with the raw gloves they came from and remain monotonic.

The skeleton is 26 joints: forearm → hand → five fingers, with the thumb carrying metacarpal, proximal, and distal joints and the other fingers adding a medial one. Because a solve only ever changes joint rotations, the rest pose — bone lengths and rest orientations, fixed for the session — is sent once in static_data as one TRANSFORM per joint, and the per-frame groups carry only what actually moves.

--emit chooses that per-frame shape:

--emitGroupPer frame, per handPick it when
local (default)joints_local444 bytesYou are retargeting onto your own skeleton. Local rotations against a known rest pose are what that takes.
worldjoints_world744 bytesYou want to draw the hand or attach props directly, with no forward kinematics on your side.
bothboth of the above1188 bytesYou need each of the above for different consumers.

A frame that cannot be decoded, or whose solve produces a non-finite joint, is dropped rather than emitted, and each cause is logged once per device rather than once per frame. The solve is stateless, so the next good frame recovers. If the upstream stream drops, every solved device gets a Disconnect frame and the solver reconnects with backoff; a consumer that connects mid-session receives every current definition before any data.

Values arrive in the glove’s own right-handed sensor frame (+X right, +Y toward the wrist, +Z down). There is no conversion to Y-up, no handedness flip, and no yaw offset — so both frame cautions below still apply to the solved output, and rebasing onto your engine’s frame remains a single rotation you apply to the whole skeleton. This is deliberate: yaw is referenced to magnetic north with an arbitrary per-session zero, so no fixed rotation could cancel it and choosing one here would be guessing on your behalf.

Each glove exposes 8 sensors over the RGMP stream:

  • one per finger (5),
  • a hub on the back of the hand,
  • a forearm sensor, and
  • a world-anchored forearm pose, available only when a Coil Pro is tracking.

Every sensor streams an orientation quaternion; some also carry a position and status flags (data-valid, Coil-present). The orientations live in a right-handed, gravity-referenced frame: one axis points along gravity, and the hub references its heading to magnetic north. The finger and forearm sensors are expressed relative to the hub, so you compose them with the hub orientation to get a hand-space pose. See the Smartgloves reference for the exact frames and sensor list.

  • A rigged hand skeleton with a known bone hierarchy and a neutral (rest) pose.
  • A sensor-to-bone mapping: which bone each sensor drives, and that sensor’s mounting offset: where it physically sits relative to the bone.
  • Quaternion math (compose, rotate a vector, build a rotation that aims one axis at a target) and forward kinematics to turn local rotations into world transforms.

Run this each frame, from the latest sample:

  1. Reset to the rest pose. Start from a known neutral pose (fingers slightly fanned, joints at identity) so the result is deterministic and doesn’t drift.
  2. Anchor the hand. Orient the hand from the hub sensor and offset the hand root by the hub’s mounting position on the back of the hand.
  3. Apply calibration. Compose each sensor’s orientation with its per-sensor calibration offset (identity if you aren’t calibrating).
  4. Aim each finger. For each finger, aim the driven bone’s parent at a target derived from the sensor orientation and that sensor’s mounting offset, then set the driven bone from the sensor orientation itself.
  5. Constrain to anatomy. Fingers (except the thumb) bend on a hinge: clamp backward hyper-extension to a small limit, and distribute the remaining curl onto the fingertip joint so the chain reads naturally.
  6. Orient the forearm from the forearm sensor (composed through the hub).
  7. World-anchor (optional). When a Coil Pro is tracking, re-anchor the whole arm in world space from the world-anchored forearm pose: a single rotation + translation alignment.
  8. Propagate transforms. Run forward kinematics so every local rotation becomes a world transform your renderer can consume.
  • RGMP Viewer — a complete reference implementation of this solve, with a live 3D hand. Start here, then read its source for the concrete math.
  • Web apps overview — how the browser samples connect to the driver through the bridge.
  • Streaming data and the RGMP specification — the stream these sensors arrive on.