Data channels & read methods
How pose, IMU, Skeleton and output are read, and how to feed an external input source.
Data channels & read methods
The two data families
The SDK publishes two families of data: raw values produced by the device, and values the SDK computes. They are read the same way; only how the data is produced differs:
| Raw data | SDK-processed data | |
|---|---|---|
| Typical API | pose(), imu(), status() | skeleton(), output() |
| Algorithm package | Not required | Required for dexterous hand output, see Algorithm package configuration; Skeleton is provided by the SDK and needs none |
| Run mode | Not required | Required, see Runtime modes |
Raw data is covered in Reading raw data; SDK-processed data (retargeting) in Reading retargeting data.
Data read methods
The read methods deliver the same Sample<T>. Callback subscription is the recommended default: each sample is handled as it is produced, with no polling; use latest() when you only need the current value, and see Runtime modes for blocking waits and coroutines.
Pose, IMU, Skeleton, dexterous hand output, status, and calibration progress all use the same
read methods: register a callback before start() to receive continuously, read the latest value
with latest() after starting, wait for the next item with the blocking next(), or await it with
coro::next. The executor, stop token and cancellation rules of the blocking and coroutine paths
are in Runtime modes and
C++20 coroutines.
Each read returns Sample<T>; for the fields, types and the constraints on the two timestamps, see Sample<T>.
Callback subscriptions
subscribe() invokes the callback each time new data arrives:
auto subscription = device.status().subscribe(
[](const qnbot::Sample<qnbot::GloveStatus>& sample) {
std::cout << sample.value.connected << '\n';
});Callbacks of the same subscription execute in order and never concurrently. Callbacks should stay
short; an exception thrown inside one does not terminate the subscription. The application must
keep the returned Subscription; call subscription.unsubscribe() when that callback needs to be
stopped early. Do not synchronously cancel or destroy a subscription from inside its own callback.
For blocking on the next data item and C++20 coroutine usage, see Runtime modes.
Getting the latest value
latest() immediately returns the latest data currently saved; it returns std::nullopt when
there is no data yet:
if (const auto sample = device.pose().latest()) {
std::cout << sample->sequence << '\n';
}External input
When there is no physical glove, or when the frames come from your own device or a simulated hand, ExternalConnection and push_frame() feed external hand frames as the input source. External frames join the same data path as a physical glove: raw poses are read directly, and the SDK computes the retargeting result.
Configuration and usage
External input has no device identity handshake, so the side must be configured explicitly in GloveConfig:
qnbot::GloveConfig glove_config{
qnbot::Side::right, qnbot::ExternalConnection{}};After configuring the target, subscribe to the output and start the SDK first, then have the application keep submitting frames:
auto device = glove.device();
auto output = device.output("hand");
auto subscription = output.subscribe(
[](const qnbot::Sample<qnbot::HandJointCommand>& sample) {
std::cout << sample.value.joints.size() << '\n';
});
device.start();
glove.run_background();
qnbot::GlovePose pose = device.push_frame(frame);push_frame() synchronously returns the raw GlovePose of the current frame; the retargeting result is read through output(). The example uses background mode.
External input uses the background or manual runtime: keep calling push_frame(frame) in background mode; in manual mode, submit each frame before calling update(). If the first run requires calibration, complete Capture and Calibration before expecting target output. See the complete background and manual update examples.
Frame constraints
- The five fingertip poses (
thumb,index,middle,ring,pinky) are required, and each value is aGloveNodePose. positionis 3 finite floating-point numbers, in meters.quaternion_xyzwis 4 finite XYZW components with a non-zero overall norm; the SDK normalizes it before use.timestamp_msmay be omitted; when provided it must be a finite number of milliseconds and strictly increasing for the same device; when omitted, the current shared time reference is used. See Timestamps & external time sync for the time unit, default reference, and synchronization flow.timestamp_secis retained only for compatibility with existing code. The SDK multiplies it by1000to convert it to milliseconds; do not set it together withtimestamp_ms.
The public contract unifies units and the quaternion component order, but the specific coordinate origin, axis directions, and rotation reference frame must still match the product documentation of the selected algorithm package. The side is not passed with the frame; the device identity and sequence are determined by the selected device and the SDK. For the complete fields, see GloveNodePose and ExternalGloveFrame.