Hand Solver Example (Python)
rgmp_hand_solver.py does the same job as the
rkk-hand-solver sidecar shipped in the SDK,
but in plain Python: it reads 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. Any RGMP client can then read solved hands without carrying a
solver.
flowchart LR
Driver["SDK Driver<br/>rokoko-sdk"]
Solver["rgmp_hand_solver.py<br/>(solve)"]
App["Your application"]
Driver -->|"TCP :12276 (RGMP)"| Solver
Solver -->|"TCP :12277 (RGMP)"| App
The ports, flags, and wire format match the binary, so the two are interchangeable. Where the binary is the one you ship against, this example is the one you read and change: it is the whole solve — quaternion math, rest pose, finger aiming, hinge constraints, forward kinematics — in readable Python, with no third-party packages and no rebuild between edits.
Running it
Section titled “Running it”Navigate to $ROKOKO_SDK_HOME/examples/python in a terminal. By default the
solver starts the driver itself, so this is the whole setup:
python3 rgmp_hand_solver.pyIf something else already manages the driver, point it at the running one instead:
python3 rgmp_hand_solver.py --no-driverThen read the solved hands with any RGMP client — the same one you already use against the driver, aimed at the solver’s port:
python3 rgmp_stream.py --host 127.0.0.1 --port 12277| Flag | Default | Purpose |
|---|---|---|
--listen | 127.0.0.1:12277 | Address to serve solved hands on. |
--rgmp-addr | 127.0.0.1:12276 | The driver’s RGMP endpoint to consume. |
--emit | local | What each hand carries: local, world, or both. |
--hand-width | 0 | Knuckle span in meters, widening the rest pose’s finger fan. 0 uses the model default. |
--no-hinges | — | Disable the hinge constraint, letting non-thumb fingers splay and twist off-axis. |
--no-driver | — | Assume the driver is already running. |
--driver-path | rokoko-sdk | Driver executable to launch. |
--driver-arg | — | Extra argument passed to the driver, after the defaults. Repeatable. |
--driver-quiet | — | Discard the driver’s output instead of sharing the solver’s. |
--driver-timeout | 15 | Seconds to wait for the driver to open its RGMP port. |
The four --driver-* flags are rejected, not ignored, when combined with
--no-driver — silently dropping them would hide a mistake.
The last two flags have no counterpart in the binary. They exist because this is
the copy you tune: --hand-width and --no-hinges are the two knobs worth
reaching for first when a resting pose or a finger’s motion doesn’t match your
rig.
What it emits
Section titled “What it emits”One synthetic device per solved hand, with a device_type of solved_hand,
identical in shape to the binary’s output — including the rest pose sent once in
static_data and the --emit group choice:
--emit | Group | Per frame, per hand |
|---|---|---|
local (default) | joints_local | 444 bytes |
world | joints_world | 744 bytes |
both | both of the above | 1188 bytes |
What the solver emits in the hand solving guide describes the skeleton, the group layouts, and the timestamp and disconnect behavior in full; all of it applies here.
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 — rebasing onto your engine’s frame stays a single rotation you apply to the whole skeleton. See Two things that trip people up.
Differences from the binary
Section titled “Differences from the binary”Same protocol, same defaults, but not the same coverage. Pick the binary for anything you ship:
- No
--emit-openxr. Thejoints_openxrgroup is the binary’s only. That makes this solver unusable as the source for the Isaac Lab glove device, which consumes exactly that group. - Pure Python performance. The solve is fast enough to keep up with two gloves on a normal machine, but it is interpreted math on a single thread — it has none of the binary’s headroom.
- The binary’s early-release caveats apply here too: calibration is not applied, sensor validity is read but not acted on, and Coil Pro world anchoring is the least exercised path. See the caveats in the guide.
How the source is laid out
Section titled “How the source is laid out”Unlike the other Python examples, this one is a small package tree rather than a
single script, so it isn’t reproduced inline here — read it in
$ROKOKO_SDK_HOME/examples/python. It splits into three layers, each usable on
its own:
rgmp_hand_solver.py the CLI: arguments in, hand_solver.run() outhand_solver/ solve -> emit -> fan outsolver_core/ the solve itself, standing apart from any transportrgmp_core/ the wire protocol: framing, definitions, layouts, clientsolver_core/ is the part to read if you are implementing the solve yourself in
another language — it has no transport code in it at all:
| Module | Role |
|---|---|
math.py | Vector and quaternion primitives |
transform.py | The bone transform hierarchy |
skeleton.py | The 26-joint skeleton and its rest pose, loaded from data/hand-skeleton.json |
finger_poser.py | One frame of sensor quaternions in, a posed skeleton out |
smartglove.py | Recognizing and decoding a glove’s sensors from an RGMP definition |
hand_solver/ is everything around the solve — the parts you would replace to
put the solver somewhere other than a TCP port:
| Module | Role |
|---|---|
upstream.py | Reads the driver’s stream, solves it, publishes the result |
pipeline.py | RGMP events in, framed solved-hand messages out. No I/O |
emit.py | Describing and encoding a solved hand as an RGMP device |
hub.py | Fan-out: one solved frame in, every connected consumer out |
server.py | The listener and accept loop |
driver.py | Launching and supervising rokoko-sdk |
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.
Where to go next
Section titled “Where to go next”- Hand solving guide — the solve step by step, and
the
rkk-hand-solversidecar this example mirrors. - RGMP Viewer — the same solve in TypeScript, with a live 3D hand.
- Isaac Lab glove device — solved hands consumed from NVIDIA Isaac Lab.
- RGMP specification — the wire format both ends of this example speak.