Skip to content
FORSENSE

How to use the SDK ​

The pico motion-capture host fuses data from the G2-L gloves and tracker strap locators (full body) into a unified MocapFrame (1,860 bytes), then sends it to your computer over UDP. The SDK packages on this page receive, parse, display, and support secondary development with this frame.

There are four packages. Choose one for your machine and programming language:

Your setupPackage
Windows computerG2L_Mocap_SDK_1.0.0_windows_x86_64.zip
Linux PC / x86 industrial computerG2L_Mocap_SDK_1.0.0_linux_x86_64.tar.gz
ARM64 board (Raspberry Pi, Jetson, RK series, etc.)G2L_Mocap_SDK_1.0.0_linux_aarch64.tar.gz
Python only, without compilingG2L_Mocap_SDK_Python_1.0.0.zip

The first three are C++ SDKs containing headers, dynamic/static libraries, and ready-to-run programs. The Python package is a pure-Python, cross-platform implementation; the same wheel works on all three platforms.

  1. Write no code yet. Run the included program to confirm that data arrives and the visualization moves.
  2. After that works, connect the library to your own application and continuously read 64 joint poses.

Choose what you need ​

Your situationGo to
You have just unpacked the SDK and do not know the filesFiles in each package
You want to verify the data visually before writing codeStep 1: view the motion
You want to inspect joint values in a terminalStep 2: terminal data
You want to record and replay dataRecording and playback
You want to integrate the data into your applicationIntegrating into your application
You want to understand the received valuesData format
It does not run or no data arrivesTroubleshooting

Extracting the packages ​

Windows

Right-click and extract in File Explorer, or use:

powershell
Expand-Archive -Path "G2L_Mocap_SDK_1.0.0_windows_x86_64.zip" -DestinationPath .

The result is one directory, Forsense_Mocap_SDK_1.0.0_windows_x86_64, containing bin, include, lib, demo, and README.md directly inside it.

Linux

bash
tar -xzf G2L_Mocap_SDK_1.0.0_linux_x86_64.tar.gz     # x86_64
tar -xzf G2L_Mocap_SDK_1.0.0_linux_aarch64.tar.gz    # ARM64

The extracted directory is Forsense_Mocap_SDK_1.0.0_linux_x86_64 (note that it differs from the archive name). Paths below are relative to this directory.

Python

bash
# Extract G2L_Mocap_SDK_Python_1.0.0.zip
# Enter the extracted dist directory. Windows "Extract All" may add another
# same-named directory; use the level containing the wheel below.
pip install forsense_mocap-1.0.0-py3-none-any.whl

Python 3.8 or later is required, with no third-party dependencies. Installation is mandatory for the Python menu and forsense-recv; otherwise, Python reports No module named 'forsense_mocap'.

Files in each package ​

C++ SDK (same structure on Windows, Linux x86_64, and Linux aarch64) ​

text
Forsense_Mocap_SDK_1.0.0_linux_x86_64/
├── README.md             In-package guide
├── include/forsense/     Public headers for integration
│   ├── protocol/         Frame format and MocapFrame definition
│   ├── codec/            Decoder.h / Encoder.h
│   ├── adapter/          UDP / BLE / WebSocket / LocalSocket / Binary adapters
│   ├── types/            Joint enums and Pose types
│   ├── pose_vizer/       Skeleton-pose WebSocket interface
│   └── tui/              Terminal UI tools
├── lib/
│   ├── linux/x86_64/     SDK shared libraries, Qt runtime, and CMake package
│   ├── linux/aarch64/    Same for ARM64
│   └── windows/x86_64/   Static import libraries (*.lib) and CMake package
├── bin/                  Ready-to-run programs with runtime paths configured
└── demo/
    ├── README.md         Example guide
    ├── docs/             Data-format and integration documentation
    └── examples/         Example programs
FileHow to use it
bin/forsense_mocap_viewerDesktop window listing 64 joint poses. The Windows build currently lacks the Qt platform plugin and cannot start.
bin/forsense_bin_recorderRecords received UDP frames to a .bin file.
bin/forsense_bin_playerReplays a .bin recording.
bin/forsense_mocap_pose_vizerForwards MocapFrame data to the PoseVizer visualization service.
demo/examples/recv_mocap_udp.cppSelf-contained C++ example using protocol headers and native sockets only; no Qt, SDK library, or CMake required.
demo/examples/compile.shCompiles the C++ example into recv_mocap_udp.
demo/examples/recv_mocap_udpPrebuilt example included in Linux packages.
demo/examples/visual / visual.exeLocal visualization service with an HTTP server, Three.js page, and FBX model. Start this before viewing motion.
demo/examples/udp_receiver.pyData bridge: receives UDP, converts coordinates, and reports them to visual. It does not draw by itself.
demo/docs/DataFormat.mdLayout of the 1,860-byte frame and CRC calculation.
demo/docs/Integration.mdC++ integration guide.
include/ + lib/The two parts needed for secondary development.
bin/*_adapter.dll (Windows)SDK runtime libraries required by programs in bin; do not delete them.

Python SDK ​

text
G2L_Mocap_SDK_Python_1.0.0/
├── README.md                     In-package guide
├── dist/forsense_mocap-1.0.0-py3-none-any.whl   Cross-platform wheel
└── demo/
    ├── recv_mocap_udp.py         Interactive menu example
    ├── udp_receiver.py           Drives the web visualization
    ├── docs/DataFormat.md        Data format
    └── visual / visual_linux_x86_64 / visual_linux_arrch64 / visual.exe
FileHow to use it
dist/*.whlInstall this before running the Python menu. Otherwise, Python reports No module named 'forsense_mocap'. Installation is described under Step 2.
demo/recv_mocap_udp.pyInteractive menu. The in-package script fails in option 3 (Diagnostics); replace it with the corrected recv_mocap_udp.py provided here.
demo/udp_receiver.pyWorks with visual to display the model in a web page.
demo/visual*Local visualization service. Run it and open http://127.0.0.1:8765/.

Differences between package demos ​

  • The Windows package has no C++ source or compile.sh in demo/examples/; it contains only Python scripts and visual.exe. To compile C++ on Windows, use include/ and lib/windows/x86_64/*.lib (see C++ integration).
  • The two Linux packages (x86_64 / ARM64) contain the C++ source and prebuilt recv_mocap_udp, but not the Python recv_mocap_udp.py. Copy the cross-platform corrected script provided on this page if you need the Python menu.

Before the first run ​

MocapFrame travels over the network. The pico motion-capture host sends data to UDP port 15000 on your computer by default.

SituationWhat to do
WindowsWhen the firewall prompt appears, choose Allow and enable both private and public networks, or manually allow inbound UDP 15000. Otherwise, no packets arrive.
LinuxPrograms in bin/ already have runtime library paths configured. Programs you compile need LD_LIBRARY_PATH as described below.
All platformsThe pico host and your computer must be on mutually reachable networks, and UDP must not be blocked by a firewall.
Python menu (recv_mocap_udp.py or forsense-recv)Install the wheel from the Python package's dist/ directory first.
Web visualization (Step 1)No Python package is required. visual / visual.exe is standalone, and udp_receiver.py uses only the standard library.

Step 1: view the motion without writing code ​

The shortest validation path uses the demo web visualization to render data from the pico host as a 3D model. This path does not depend on Qt and works the same way on Windows and Linux.

It requires two programs:

ProgramPurpose
visual / visual.exeRenderer: embedded HTTP server, Three.js page, and FBX model
udp_receiver.pyData bridge: receives UDP, converts coordinates, and POSTs poses to the renderer

udp_receiver.py does not draw anything itself. By default, it sends poses to http://127.0.0.1:8765/api/state. Running it without the renderer produces repeated State update failed messages.

1. Start the renderer (visual.exe on Windows):

bash
cd demo/examples
./visual

Open http://127.0.0.1:8765/. You can rotate the model manually even without incoming data.

2. Start the data bridge:

bash
python udp_receiver.py

The model now follows the live data.

Optional udp_receiver.py arguments:

ArgumentDefaultMeaning
--host0.0.0.0UDP bind address
--port15000UDP listening port
--urlhttp://127.0.0.1:8765/api/stateRenderer state endpoint
--moderelativeRotation space; absolute aligns to the first frame
--coordmocap-to-threeCoordinate conversion; raw performs none

The default relative mode receives relative rotations and does not require calibration.

A UDP port can be bound by only one process at a time. While udp_receiver.py owns port 15000, the terminal menu and desktop viewer cannot bind it. Stop the first receiver, use another port, or forward the data.

Step 2: inspect data in a terminal ​

The example provides Full body, Hands, Diagnostics, and Exit options:

text
===== Forsense Mocap CLI =====
  1) Full-body data   # all 64 joint poses
  2) Hand data        # hand joints only (15-34 left + 44-63 right)
  3) Diagnostics      # connection, frames, rejections, length/id/crc categories
  4) Exit
===============================
Select [1-4]:
  • Press Ctrl+C in a data screen to return to the main menu; press it in the main menu to exit.
  • Enter m, 0, or q in a sub-screen to return to the main menu.
  • Diagnostics refreshes every 0.5 seconds and reports an interruption after more than two seconds without data.

Linux: compile, then run ​

bash
cd demo/examples
./compile.sh                     # creates recv_mocap_udp
./recv_mocap_udp                # default port 15000
./recv_mocap_udp --port 15001   # another port

This example uses no Qt, SDK library, or CMake—only SDK protocol headers and native sockets. Manual compilation:

bash
# Linux / macOS
g++ recv_mocap_udp.cpp -std=c++17 -I <sdk>/include -o recv_mocap_udp
bat
:: Windows (MSVC, from an x64 Native Tools command prompt)
cl /std:c++17 /EHsc /I <sdk>\include recv_mocap_udp.cpp ws2_32.lib
:: Windows (MinGW)
g++ recv_mocap_udp.cpp -std=c++17 -I <sdk>/include -lws2_32 -o recv_mocap_udp

Any platform: Python menu ​

recv_mocap_udp.py is pure Python and works on Windows, Linux x86_64, and Linux ARM64.

Install the library first. The script imports forsense_mocap; otherwise it immediately reports:

text
ModuleNotFoundError: No module named 'forsense_mocap'

Enter the extracted Python package's dist directory and run:

bash
pip install forsense_mocap-1.0.0-py3-none-any.whl

Windows may add another same-named directory during extraction. Use the level containing dist.

Verify the installation:

bash
python -c "import forsense_mocap; print(forsense_mocap.__version__)"

Output 1.0.1 is expected: the wheel filename says 1.0.0, but its internal version is 1.0.1.

The Step 1 udp_receiver.py uses only the Python standard library and does not need this wheel. The wheel is required only for recv_mocap_udp.py and forsense-recv.

Use the corrected recv_mocap_udp.py provided here, not the one inside the package. The package copy calls a nonexistent recv.diagnostics() method in option 3. The corrected script uses recv.stats, prints live FPS, and has been tested with all four menu options.

Replace demo\examples\recv_mocap_udp.py in the Windows package or demo\recv_mocap_udp.py in the Python package. Linux packages do not contain the Python script, so copy this file into them or run it from any directory.

The menu matches the C++ version (1 Full body / 2 Hands / 3 Diagnostics / 4 Exit). Diagnostics refreshes once per second and categorizes rejections as short packet, header, frame ID, or CRC.

Windows

powershell
python recv_mocap_udp.py                 # default port 15000
python recv_mocap_udp.py --port 15001

Linux x86_64 / ARM64

bash
python3 recv_mocap_udp.py                # default port 15000
python3 recv_mocap_udp.py --port 15001

If the port is occupied, close the process using it or choose another port.

Recording and playback ​

The bin/ directory contains two tools for recording a live UDP stream and analyzing it later:

ProgramPurpose
forsense_bin_recorderRecords UDP MocapFrame data to a .bin file
forsense_bin_playerReplays a .bin recording

The recording is a continuous raw protocol-frame stream without an additional container header. Playback accepts data frames beginning with 0xAA 0x55 and reproduces timing from timestamps in each frame.

Integrating into your application ​

Integrate only after the visualization or terminal-data path works.

C++ integration ​

Files to ship

  • include/forsense/
  • SDK libraries under lib/<platform>/
  • On Windows, the *_adapter.dll, forsense_tui.dll, and Qt 6 DLLs from bin/

Prerequisite: Qt 6 development environment with Core and Network.

CMake (recommended)

cmake
find_package(forsense_sdk CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE forsense::udp_adapter)

Available targets: forsense::udp_adapter, forsense::ble_adapter, forsense::websocket_adapter, forsense::local_socket_adapter, forsense::binary_adapter, forsense::pose_vizer, forsense::tui, and forsense::math, plus dependency targets for fmt, magic_enum, and eigen.

If CMake cannot find the package, set -Dforsense_sdk_DIR=<sdk>/lib/<platform>/<architecture>/cmake/forsense_sdk.

Minimal receiver

cpp
#include <forsense/adapter/UdpAdapter.h>
#include <forsense/codec/Decoder.h>
#include <forsense/protocol/human/MocapFrame.h>

fss::UdpAdapter udp;
udp.initialize();
udp.connect(QHostAddress::AnyIPv4, 15000);

fss::codec::DataDecoderT<fss::protocol::MocapFrame,
                         fss::protocol::MOCAP_DATA_FRAME_ID> decoder;

QObject::connect(&udp, &fss::UdpAdapter::data_received,
    [&](const QByteArray &data, const QHostAddress &, quint16) {
        for (char b : data)
        {
            if (decoder.parse(static_cast<std::uint8_t>(b)) !=
                fss::codec::DataDecoderT<fss::protocol::MocapFrame,
                    fss::protocol::MOCAP_DATA_FRAME_ID>::Result::FRAME_READY)
                continue;
            const auto &frame = decoder.frame();   // CRC already verified
            // frame.payload.time_us         : timestamp in microseconds
            // frame.payload.joint_poses[i]  : joint i
            //   .position[0..2]   : (x, y, z)
            //   .quaternion[0..3] : (w, x, y, z)
        }
    });

Only one process can bind a UDP port at a time. If another receiver already owns port 15000, use another port or forward the data.

For a simpler solution without Qt, copy demo/examples/recv_mocap_udp.cpp. It uses only Decoder.h, MocapFrame.h, and native sockets.

InformationHeader
Frame structure / payload layoutinclude/forsense/protocol/human/MocapFrame.h
Frame constants and CRC algorithminclude/forsense/protocol/FrameFormat.h
64-joint enuminclude/forsense/types/SkeletonJoint.h
Pose structureinclude/forsense/types/Pose.h
Streaming decoderinclude/forsense/codec/Decoder.h
Encoder for test/replay framesinclude/forsense/codec/Encoder.h
UDP input/outputinclude/forsense/adapter/UdpAdapter.h

Python integration ​

Install the wheel from the Python package's dist/ directory:

bash
pip install forsense_mocap-1.0.0-py3-none-any.whl

Minimal callback example:

python
from forsense_mocap import UdpMocapReceiver, JOINT_NAMES

def on_frame(frame):
    head = frame.joint("Head")
    print("Head:", head.position, head.quaternion)

UdpMocapReceiver(port=15000, on_frame=on_frame).start()

Polling example:

python
from forsense_mocap import UdpMocapReceiver

recv = UdpMocapReceiver(port=15000)
recv.start()
while True:
    frame = recv.poll(timeout=1.0)
    if frame is None:
        continue
    print(frame.timestamp_us, frame.joints[0].position)
    # recv.stats.frames / recv.stats.rejected / recv.stats.rejected_causes
NameDescription
JOINT_NAMES / JOINT_INDEX64 joint names / name-to-index mapping matching the C++ SkeletonJoint order
MocapFrameOne frame: timestamp_us, source, joints[64], and joint(name)
MocapJointposition(x,y,z) and quaternion(w,x,y,z)
parse_frame(data)Parses and CRC-checks a frame; returns None on failure
make_frame(joints)Builds a frame for tests or playback
crc32_forsense(data)Forsense CRC-32
UdpMocapReceiver(port, on_frame, on_error, on_status, timeout)UDP receiver with callback and poll() modes
RecvResultStatistics: frames, bytes, rejected, and rejected_causes

The receiver also detects disconnections. After no data for longer than timeout (default two seconds), on_status receives {"connected": False, "elapsed": ...}. When data resumes, it receives {"connected": True, "recovered": True}. If the port is occupied, start() raises RuntimeError with an explanation.

Command-line tools installed with the wheel:

bash
forsense-recv --port 15000
forsense-recv --port 15000 --joints Head Waist LeftHand RightHand

Data format ​

Frame structure ​

MocapFrame is fixed at 1,860 bytes, begins with AA 55, and has ID 0. All multibyte integers and float values are little-endian.

text
AA 55 + id(u16 LE) + length(u16 LE) + payload(1850) + crc32(u32 LE)
FieldLengthDescription
header1 / header21 + 10xAA / 0x55 synchronization bytes
id20 (Mocap data frame)
length21850 (payload length)
payload1850Described below
crc324Forsense CRC-32

Payload layout (1,850 bytes) ​

OffsetLengthTypeMemberDescription
08uint64time_usSystem timestamp in microseconds
81792float[64][7]joint_poses64 joint poses
18009uint8 + float[2]left_controllerLeft controller / glove state
18099uint8 + float[2]right_controllerRight controller / glove state
181832uint64[4]reservedReserved

Each joint occupies 28 bytes: position[3] at offset 0 (x,y,z) and quaternion[4] at offset 12 in w, x, y, z order.

64 joint indices ​

#Joint#Joint#Joint#Joint
0Head16LeftThumbMid32LeftPinkyMid48RightIndexNear
1Neck17LeftThumbFar33LeftPinkyFar49RightIndexMid
2Chest18LeftThumbEnd34LeftPinkyEnd50RightIndexFar
3Spine119LeftIndexNear35RightCollar51RightIndexEnd
4Spine220LeftIndexMid36RightShoulder52RightMiddleNear
5Waist21LeftIndexFar37RightElbow53RightMiddleMid
6LeftCollar22LeftIndexEnd38RightWrist54RightMiddleFar
7LeftShoulder23LeftMiddleNear39RightHand55RightMiddleEnd
8LeftElbow24LeftMiddleMid40RightHip56RightRingNear
9LeftWrist25LeftMiddleFar41RightKnee57RightRingMid
10LeftHand26LeftMiddleEnd42RightAnkle58RightRingFar
11LeftHip27LeftRingNear43RightFoot59RightRingEnd
12LeftKnee28LeftRingMid44RightThumbNear60RightPinkyNear
13LeftAnkle29LeftRingFar45RightThumbMid61RightPinkyMid
14LeftFoot30LeftRingEnd46RightThumbFar62RightPinkyFar
15LeftThumbNear31LeftPinkyNear47RightThumbEnd63RightPinkyEnd
  • Full-body skeleton: 0-14 and 35-43 (24 joints)
  • Left hand: 15-34 (20 finger joints from the G2-L glove)
  • Right hand: 44-63 (20 finger joints)

Array indices are fixed; do not reorder by joint name.

Position units, axis directions, and the quaternion reference frame are defined by the pico motion-capture host. The protocol does not carry unit metadata; align these settings with the host before integrating with your engine.

Controller / glove state ​

left_controller and right_controller each occupy 9 bytes: one-byte button_state, four-byte joystick_x, and four-byte joystick_y.

BitMeaning when setLeftRight
0Input-device modeGlove (0 means controller)Glove (0 means controller)
1Joystick pressedJoystickJoystick
2Primary buttonXA
3Secondary buttonYB
4Trigger pressedTriggerTrigger
5Grip pressedGripGrip
6 / 7ReservedIgnoreIgnore

Both joystick axes range from -1 to 1. Left X is positive up and negative down; right X is positive right and negative left. Y is positive up and negative down on both sides.

CRC-32 ​

Custom CRC-32: initial value 1, polynomial 0xEDB88320 (reflected), processed byte-by-byte and bit-by-bit, with no final XOR. Coverage begins at header1 and ends at the final payload byte; it excludes the CRC field itself.

python
def crc32_forsense(data: bytes) -> int:
    crc = 1
    for b in data:
        crc ^= b
        for _ in range(8):
            crc = (crc >> 1) ^ 0xEDB88320 if crc & 1 else crc >> 1
    return crc & 0xFFFFFFFF

Troubleshooting ​

SymptomSolution
Windows receives no packetsAllow inbound UDP 15000 in Windows Firewall.
recv_mocap_udp.py or forsense-recv reports No module named 'forsense_mocap'Install the wheel from the Python package's dist directory.
Bind fails / port in useOnly one process can bind the port. Stop the receiver using it or select --port 15001.
Linux server has no graphical environmentRun xvfb-run ./bin/forsense_mocap_viewer.
A compiled program cannot find libQt6*.soSet export LD_LIBRARY_PATH=<sdk>/lib/linux/<architecture>:$LD_LIBRARY_PATH, or run the prebuilt program in bin/.
find_package(forsense_sdk) failsAdd -Dforsense_sdk_DIR=<sdk>/lib/<platform>/<architecture>/cmake/forsense_sdk.
Data repeatedly reports an interruptionCheck that the pico host is sending, the networks are mutually reachable, and UDP is not blocked.
Joint values look wrongConfirm host-side units and coordinates, and use the fixed 64-joint index order.
The re-downloaded Windows archive extracts into three nested foldersEnter the innermost folder containing bin, include, lib, and demo; the file provided here has the extra nesting removed.
Extraction reports an overlong ...\Eigen\...\Transform.h pathExtract to a shorter path such as D:\sdk, or use the non-nested archive provided here.
Package demo/recv_mocap_udp.py fails in Diagnostics with AttributeErrorReplace it with the corrected recv_mocap_udp.py provided here.
Web visualization is blank / repeatedly reports State update failedStart visual or visual.exe before udp_receiver.py; confirm that the service is listening at the configured --url on port 8765.
Two programs need the same dataOnly one process can bind the UDP port. Let one receiver forward the data, for example with forsense_mocap_pose_vizer.

Copyright © 2026 Forsense. All rights reserved.