Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ce72fab
refactor(deviceio): generic FullBodyTracker + session-level vendor se…
aristarkhovNV Jul 15, 2026
3419f2f
feat(teleop-session): per-source tracker vendor selection
aristarkhovNV Jul 16, 2026
fc2ef92
docs(device): document FullBodyTracker rename and vendor selection
aristarkhovNV Jul 16, 2026
3e63d73
fix(teleop-session): default vendor_config to empty VendorConfig, not…
aristarkhovNV Jul 16, 2026
45ced69
fix(live-trackers): validate full vendor mapping in get_required_exte…
aristarkhovNV Jul 16, 2026
94c4b3f
compat(deviceio): add deprecated aliases for pre-rename full-body names
aristarkhovNV Jul 16, 2026
0fd9246
refactor(deviceio): unify live tracker dispatch on a single vendor table
aristarkhovNV Jul 17, 2026
b0939b9
refactor(live-trackers): derive vendor-support check from dispatch table
aristarkhovNV Jul 17, 2026
487d120
test(live-trackers): cover validate_vendor_selections outcomes
aristarkhovNV Jul 17, 2026
af408eb
fix(live-trackers): reject empty vendor id and restore compat aliases
aristarkhovNV Jul 17, 2026
d68f34c
refactor(schema): rename full-body Pico schema types to generic names
aristarkhovNV Jul 16, 2026
472eb5d
refactor(teleop-session): carry tracker vendor on the DeviceIO source
aristarkhovNV Jul 20, 2026
0b82ba2
refactor(live-trackers): default to first dispatch row per tracker
aristarkhovNV Jul 20, 2026
dcd35cc
refactor(live-trackers): give dispatch rows an explicit type predicate
aristarkhovNV Jul 20, 2026
bfe8c0f
fix(live-trackers): validate vendor id against the tracker's own type
aristarkhovNV Jul 20, 2026
0222b1c
refactor(deviceio-session): unify not-in-list wording with the factory
aristarkhovNV Jul 20, 2026
b7ddc9b
refactor(live-trackers): name the tracker in vendor-error messages
aristarkhovNV Jul 20, 2026
7a9b776
feat(deviceio-session): expose get_tracker_vendors on VendorConfig bi…
aristarkhovNV Jul 20, 2026
cf0927e
docs(deviceio-base): correct TrackerVendor id comment on vendor selec…
aristarkhovNV Jul 20, 2026
5cff822
fix(live-trackers): reject non-empty vendor params until a consumer e…
aristarkhovNV Jul 21, 2026
dfb2028
fix(teleop-session): reject source vendor selection in replay mode
aristarkhovNV Jul 21, 2026
93fc267
refactor(deviceio): make FullBodyTrackerPico deprecation machine-read…
aristarkhovNV Jul 21, 2026
591a06a
refactor(python): make schema/tensor Pico aliases machine-readable de…
aristarkhovNV Jul 21, 2026
ad3aeb0
style(python): apply ruff-format line wrapping
aristarkhovNV Jul 21, 2026
d1e1ca2
fix(vendored-trackers): resolve code-review nits
aristarkhovNV Jul 21, 2026
aebf9e4
clang format fix
aristarkhovNV Jul 21, 2026
01887e8
fix(teleop-session): revalidate replay vendor guard on context entry
aristarkhovNV Jul 21, 2026
8bb4fdc
refactor(deviceio-session): own vendor-config validation in the session
aristarkhovNV Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/source/device/body_tracking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Body Tracking

Isaac Teleop supports streaming full-body tracking data from an XR headset
through the CloudXR WebXR client to the teleop server. The server exposes the
body skeleton to applications through the ``FullBodyTrackerPico`` tracker and
body skeleton to applications through the ``FullBodyTracker`` tracker and
the OpenXR ``XR_BD_body_tracking`` extension.

Body tracking support currently targets the **PICO 4 Ultra Enterprise** with
Expand Down Expand Up @@ -183,10 +183,10 @@ Server-side access
~~~~~~~~~~~~~~~~~~

On the server, body tracking data is consumed through the
``FullBodyTrackerPico`` tracker (see :doc:`trackers` for the full tracker
``FullBodyTracker`` tracker (see :doc:`trackers` for the full tracker
reference). The tracker exposes a ``get_body_pose()`` method that returns the
24-joint skeleton each frame (or null when body tracking is not available).
Joint data follows the ``FullBodyPosePico`` FlatBuffers schema defined in
Joint data follows the ``FullBodyPose`` FlatBuffers schema defined in
``src/core/schema/fbs/full_body.fbs``.

The ``all_joint_poses_tracked`` quality flag indicates whether every joint was
Expand Down
76 changes: 68 additions & 8 deletions docs/source/device/trackers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ APIs (``xrLocateSpace``, ``xrSyncActions``, etc.):
- :code-file:`HeadTracker <src/core/deviceio_trackers/cpp/inc/deviceio_trackers/head_tracker.hpp>` -- HMD head pose
- :code-file:`HandTracker <src/core/deviceio_trackers/cpp/inc/deviceio_trackers/hand_tracker.hpp>` -- articulated hand joints (left and right)
- :code-file:`ControllerTracker <src/core/deviceio_trackers/cpp/inc/deviceio_trackers/controller_tracker.hpp>` -- controller poses and button/axis inputs (left and right)
- :code-file:`FullBodyTrackerPico <src/core/deviceio_trackers/cpp/inc/deviceio_trackers/full_body_tracker_pico.hpp>` -- 24-joint full body pose (PICO ``XR_BD_body_tracking``)
- :code-file:`FullBodyTracker <src/core/deviceio_trackers/cpp/inc/deviceio_trackers/full_body_tracker.hpp>` -- vendor-agnostic 24-joint full body pose; default vendor reads the PICO ``XR_BD_body_tracking`` extension (see `Vendor Selection`_)

**SchemaTracker-based trackers** -- create new device type by defining a FlatBuffer schema and
reading it from OpenXR tensor collections via the
Expand Down Expand Up @@ -185,22 +185,31 @@ axis inputs. Uses standard OpenXR action bindings.
- :code-file:`examples/teleop/python/locomotion_retargeting_example.py`
- :code-file:`examples/teleop/python/gripper_retargeting_example_simple.py`

FullBodyTrackerPico
~~~~~~~~~~~~~~~~~~~
FullBodyTracker
~~~~~~~~~~~~~~~

Tracks 24 body joints on PICO devices using the ``XR_BD_body_tracking``
extension.
Tracks 24 body joints through a vendor-selected backend. The tracker itself is
a vendor-agnostic marker and carries no vendor or live/replay state: a live
session picks the backend via ``VendorConfig`` (see `Vendor Selection`_), and
replay reads the recorded ``full_body`` channel regardless of which vendor
produced it. When no vendor is selected, the default vendor ``body.pico-xr``
reads the PICO ``XR_BD_body_tracking`` extension directly.

- Schema: :code-file:`src/core/schema/fbs/full_body.fbs`
- C++ header: ``#include <deviceio/full_body_tracker_pico.hpp>``
- Python import: ``from isaacteleop.deviceio import FullBodyTrackerPico``
- Record channels: ``full_body`` | MCAP schema: ``core.FullBodyPosePicoRecord``
- C++ header: ``#include <deviceio_trackers/full_body_tracker.hpp>``
- Python import: ``from isaacteleop.deviceio import FullBodyTracker``
- Record channels: ``full_body`` | MCAP schema: ``core.FullBodyPoseRecord``
- Tests:

- :code-file:`src/core/schema_tests/cpp/test_full_body.cpp`
- :code-file:`src/core/schema_tests/python/test_full_body.py`
- :code-file:`examples/oxr/python/test_full_body_tracker.py`

.. note::

``FullBodyTrackerPico`` remains available as a deprecated alias for
``FullBodyTracker`` so existing scripts run unchanged.

FrameMetadataTrackerOak
~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -248,6 +257,57 @@ utility internally.
The Python method is named ``get_pedal_data()`` (instead of the C++
``get_data()``).

.. _vendor-selection:

Vendor Selection
----------------

Some trackers are **vendor-agnostic markers**: the tracker declares *what*
device data it represents, while a live session chooses *which* backend
("vendor") produces that data. This mirrors how live-vs-replay is chosen at the
session level -- the same tracker instance works across vendors and across live
and replay. ``FullBodyTracker`` is currently the only vendored tracker; its
default vendor ``body.pico-xr`` reads the PICO ``XR_BD_body_tracking``
extension.

Select a vendor by passing a ``VendorConfig`` to both
``DeviceIOSession.get_required_extensions()`` and ``DeviceIOSession.run()``. A
``VendorConfig`` maps tracker instances to a ``TrackerVendor(id, params)``,
where ``id`` selects the backend from the live factory's vendor registry and
``params`` carries free-form string key/value options for it. Trackers left out
of the config use their default vendor. Vendor selections on non-vendored
trackers, and unknown vendor ids, are rejected at session construction.

.. code-block:: python

import isaacteleop.deviceio as deviceio

body = deviceio.FullBodyTracker()

# Select the backend for the vendored tracker (default shown explicitly).
vendor_config = deviceio.VendorConfig([
(body, deviceio.TrackerVendor("body.pico-xr")),
])

required_extensions = deviceio.DeviceIOSession.get_required_extensions(
[body], vendor_config
)
with deviceio.DeviceIOSession.run(
[body], handles, None, vendor_config
) as session:
...

Replay is always vendor-neutral: the replay full-body impl reads the recorded
``full_body`` channel regardless of which live vendor produced it, so
``VendorConfig`` applies to live sessions only. The vendor registry is open for
additional pre-built plugin vendors without changing the tracker marker.

When driving devices through the higher-level teleop session manager, vendor
selection is carried on the DeviceIO source itself via its ``vendor`` argument
(e.g. ``FullBodySource(name="full_body", vendor=deviceio.TrackerVendor("body.pico-xr"))``),
so it travels with the pipeline into both extension discovery and session
construction; see :doc:`../getting_started/teleop_session`.

.. _tracker-usage-example:

Usage Examples
Expand Down
31 changes: 30 additions & 1 deletion docs/source/getting_started/teleop_session.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,30 @@ argument is a ``uint64`` handle value.
oxr_handles=handles, # Skip internal OpenXR session creation
)
Tracker vendor selection
""""""""""""""""""""""""

In live mode, a vendored source selects the backend ("vendor") for its tracker
-- for example which backend drives a ``FullBodySource``. The selection is
carried **on the source** via its ``vendor`` argument, a
``deviceio.TrackerVendor(id, params)``; sources left at the default use their
tracker's default vendor. Because the vendor travels with the source, it is part
of the pipeline: ``TeleopSession`` picks it up automatically, and
``get_required_oxr_extensions_from_pipeline(pipeline)`` reports the matching
OpenXR extensions with no extra argument (important for the external-``oxr_handles``
flow). See :ref:`vendor-selection` for the underlying DeviceIO mechanism and the
available vendor ids.

.. code-block:: python
import isaacteleop.deviceio as deviceio
from isaacteleop.retargeting_engine.deviceio_source_nodes import FullBodySource
# Select the backend on the source; it flows through the pipeline into the session.
full_body = FullBodySource(
name="full_body", vendor=deviceio.TrackerVendor("body.pico-xr")
)
Retargeting execution
"""""""""""""""""""""

Expand Down Expand Up @@ -382,7 +406,12 @@ The module also exports two utility functions:
- ``get_required_oxr_extensions_from_pipeline(pipeline) -> List[str]`` --
Discover the OpenXR extensions needed by a retargeting pipeline by
traversing its DeviceIO source leaf nodes. Returns a sorted, deduplicated
list of extension name strings.
list of extension name strings. Extensions are vendor-dependent, but each
source carries its own vendor selection, so the result already reflects them
-- when you create the OpenXR session yourself and feed the handles in via
``oxr_handles``, the enabled extensions match the session that gets built
with no extra argument. Select a vendor on the source (e.g.
``FullBodySource(name="full_body", vendor=deviceio.TrackerVendor("body.pico-xr"))``).

- ``create_standard_inputs(trackers) -> Dict[str, IDeviceIOSource]`` --
Convenience function that creates ``HandsSource``, ``ControllersSource``,
Expand Down
6 changes: 3 additions & 3 deletions examples/deviceio_live_view/python/deviceio_viser.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
from isaacteleop.retargeting_engine.interface import OutputCombiner
from isaacteleop.retargeting_engine.tensor_types import HandInputIndex
from isaacteleop.retargeting_engine.tensor_types.indices import (
BodyJointPicoIndex,
BodyJointIndex,
ControllerInputIndex,
FullBodyInputIndex,
HeadPoseIndex,
)

HANDS_CHANNEL = "hands"
BODY_JOINT_NAMES = [joint.name for joint in BodyJointPicoIndex]
BODY_JOINT_NAMES = [joint.name for joint in BodyJointIndex]

# ---------------------------------------------------------------------------
# Color palette shared across all viz scripts
Expand Down Expand Up @@ -61,7 +61,7 @@ def build_all_human_pipeline():


# PICO body-joint connectivity (parent → child) for skeleton rendering.
# Indices follow BodyJointPicoIndex: 0=PELVIS, 1/2=LEFT/RIGHT_HIP, 3/6/9=SPINE1/2/3,
# Indices follow BodyJointIndex: 0=PELVIS, 1/2=LEFT/RIGHT_HIP, 3/6/9=SPINE1/2/3,
# 4/5=LEFT/RIGHT_KNEE, 7/8=LEFT/RIGHT_ANKLE, 10/11=LEFT/RIGHT_FOOT, 12=NECK,
# 13/14=LEFT/RIGHT_COLLAR, 15=HEAD, 16/17=LEFT/RIGHT_SHOULDER,
# 18/19=LEFT/RIGHT_ELBOW, 20/21=LEFT/RIGHT_WRIST, 22/23=LEFT/RIGHT_HAND — 24 total.
Expand Down
6 changes: 3 additions & 3 deletions examples/mcap_record_replay/python/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
HandInputIndex,
)
from isaacteleop.retargeting_engine.tensor_types.indices import (
BodyJointPicoIndex,
BodyJointIndex,
ControllerInputIndex,
)
from isaacteleop.retargeting_engine.tensor_types.ndarray_types import (
Expand All @@ -52,7 +52,7 @@


HANDS_CHANNEL = "hands"
BODY_JOINT_NAMES = [joint.name for joint in BodyJointPicoIndex]
BODY_JOINT_NAMES = [joint.name for joint in BodyJointIndex]

# ---------------------------------------------------------------------------
# Color palette shared across all viz scripts
Expand Down Expand Up @@ -149,7 +149,7 @@ def build_full_body_pipeline():


# PICO body-joint connectivity (parent → child) for skeleton rendering.
# Indices follow BodyJointPicoIndex: 0=PELVIS, 1/2=LEFT/RIGHT_HIP, 3/6/9=SPINE1/2/3,
# Indices follow BodyJointIndex: 0=PELVIS, 1/2=LEFT/RIGHT_HIP, 3/6/9=SPINE1/2/3,
# 4/5=LEFT/RIGHT_KNEE, 7/8=LEFT/RIGHT_ANKLE, 10/11=LEFT/RIGHT_FOOT, 12=NECK,
# 13/14=LEFT/RIGHT_COLLAR, 15=HEAD, 16/17=LEFT/RIGHT_SHOULDER,
# 18/19=LEFT/RIGHT_ELBOW, 20/21=LEFT/RIGHT_WRIST, 22/23=LEFT/RIGHT_HAND — 24 total.
Expand Down
28 changes: 14 additions & 14 deletions examples/oxr/python/test_full_body_tracker.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""
Test script for FullBodyTrackerPico with XR_BD_body_tracking extension.
Test script for FullBodyTracker with XR_BD_body_tracking extension.

Demonstrates:
- Getting full body pose data (24 joints from pelvis to hands)
- Requires PICO device with body tracking support
- Requires PICO device with body tracking support (default "body.pico-xr" vendor)
"""

import time
Expand All @@ -22,7 +22,7 @@

# Test 1: Create full body tracker
print("[Test 1] Creating full body tracker...")
body_tracker = deviceio.FullBodyTrackerPico()
body_tracker = deviceio.FullBodyTracker()
print(f"✓ {body_tracker.get_name()} created")
print()

Expand All @@ -33,10 +33,10 @@
print(f"Required extensions: {required_extensions}")
print()

# Test 3: Show joint names from schema (BodyJointPico enum)
print(f"[Test 3] Body joint names ({schema.BodyJointPico.NUM_JOINTS} joints):")
for i in range(schema.BodyJointPico.NUM_JOINTS):
print(f" [{i:2d}] {schema.BodyJointPico(i).name}")
# Test 3: Show joint names from schema (BodyJoint enum)
print(f"[Test 3] Body joint names ({schema.BodyJoint.NUM_JOINTS} joints):")
for i in range(schema.BodyJoint.NUM_JOINTS):
print(f" [{i:2d}] {schema.BodyJoint(i).name}")
print()

# Test 4: Initialize
Expand Down Expand Up @@ -70,10 +70,10 @@
if body_tracked.data is not None:
valid_count = sum(
1
for i in range(schema.BodyJointPico.NUM_JOINTS)
for i in range(schema.BodyJoint.NUM_JOINTS)
if body_tracked.data.joints.joints(i).is_valid
)
print(f" Valid joints: {valid_count}/{schema.BodyJointPico.NUM_JOINTS}")
print(f" Valid joints: {valid_count}/{schema.BodyJoint.NUM_JOINTS}")
print()

# Test 7: Run tracking loop
Expand All @@ -96,10 +96,10 @@

if body_tracked.data is not None:
pelvis_pos = body_tracked.data.joints.joints(
int(schema.BodyJointPico.PELVIS)
int(schema.BodyJoint.PELVIS)
).pose.position
head_pos = body_tracked.data.joints.joints(
int(schema.BodyJointPico.HEAD)
int(schema.BodyJoint.HEAD)
).pose.position
print(
f" [{elapsed:5.2f}s] Frame {frame_count:4d}"
Expand Down Expand Up @@ -128,9 +128,9 @@
if body_tracked.data is not None:
print()
print(" Joint positions:")
for i in range(schema.BodyJointPico.NUM_JOINTS):
for i in range(schema.BodyJoint.NUM_JOINTS):
joint = body_tracked.data.joints.joints(i)
name = schema.BodyJointPico(i).name
name = schema.BodyJoint(i).name
pos = joint.pose.position
rot = joint.pose.orientation
print(
Expand Down
14 changes: 7 additions & 7 deletions examples/teleop_ros2/cpp/integration_tests/mcap_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ using ControllerChannels = core::McapTrackerChannels<core::ControllerSnapshotRec
using HandChannels = core::McapTrackerChannels<core::HandPoseRecord, core::HandPose>;
using HeadChannels = core::McapTrackerChannels<core::HeadPoseRecord, core::HeadPose>;
using PedalChannels = core::McapTrackerChannels<core::Generic3AxisPedalOutputRecord, core::Generic3AxisPedalOutput>;
using FullBodyChannels = core::McapTrackerChannels<core::FullBodyPosePicoRecord, core::FullBodyPosePico>;
using FullBodyChannels = core::McapTrackerChannels<core::FullBodyPoseRecord, core::FullBodyPose>;

constexpr int kDefaultFrameCount = 1800;
constexpr int64_t kFramePeriodNs = 16'666'667;
Expand Down Expand Up @@ -113,12 +113,12 @@ std::shared_ptr<core::Generic3AxisPedalOutputT> make_pedal_sample(int frame)
return sample;
}

std::shared_ptr<core::FullBodyPosePicoT> make_full_body_sample(int frame)
std::shared_ptr<core::FullBodyPoseT> make_full_body_sample(int frame)
{
const float delta = kDriftRatePerFrameM * static_cast<float>(frame);
auto sample = std::make_shared<core::FullBodyPosePicoT>();
sample->joints = std::make_unique<core::BodyJointsPico>();
for (int joint = 0; joint < core::BodyJointPico_NUM_JOINTS; ++joint)
auto sample = std::make_shared<core::FullBodyPoseT>();
sample->joints = std::make_unique<core::BodyJoints>();
for (int joint = 0; joint < core::BodyJoint_NUM_JOINTS; ++joint)
{
// Per-joint offsets spread the joints into a plausible-looking body layout.
const float joint_f = static_cast<float>(joint);
Expand Down Expand Up @@ -157,15 +157,15 @@ void write_fixture(const std::filesystem::path& output_path, int frame_count)
const auto hand_names = to_strings(core::HandRecordingTraits::recording_channels);
const auto head_names = to_strings(core::HeadRecordingTraits::recording_channels);
const auto pedal_names = to_strings(core::PedalRecordingTraits::recording_channels);
const auto full_body_names = to_strings(core::FullBodyPicoRecordingTraits::recording_channels);
const auto full_body_names = to_strings(core::FullBodyRecordingTraits::recording_channels);

ControllerChannels controller_channels(
*writer, "controllers", core::ControllerRecordingTraits::schema_name, controller_names);
HandChannels hand_channels(*writer, "hands", core::HandRecordingTraits::schema_name, hand_names);
HeadChannels head_channels(*writer, "head", core::HeadRecordingTraits::schema_name, head_names);
PedalChannels pedal_channels(*writer, "pedals", core::PedalRecordingTraits::schema_name, pedal_names);
FullBodyChannels full_body_channels(
*writer, "full_body", core::FullBodyPicoRecordingTraits::schema_name, full_body_names);
*writer, "full_body", core::FullBodyRecordingTraits::schema_name, full_body_names);

for (int frame = 0; frame < frame_count; ++frame)
{
Expand Down
4 changes: 2 additions & 2 deletions examples/teleop_ros2/python/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from enum import Enum

from isaacteleop.retargeting_engine.tensor_types.indices import (
BodyJointPicoIndex,
BodyJointIndex,
HandJointIndex,
)

Expand All @@ -34,7 +34,7 @@ class TeleopMode(StrEnum):
FULL_BODY = "full_body"


BODY_JOINT_NAMES = [e.name for e in BodyJointPicoIndex]
BODY_JOINT_NAMES = [e.name for e in BodyJointIndex]
HAND_POSE_JOINT_INDICES = tuple(
HandJointIndex(i)
for i in range(HandJointIndex.WRIST, HandJointIndex.LITTLE_TIP + 1)
Expand Down
8 changes: 4 additions & 4 deletions examples/teleop_ros2/python/tests/test_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from isaacteleop.retargeting_engine.tensor_types import (
DLDataType,
NDArrayType,
NUM_BODY_JOINTS_PICO,
NUM_BODY_JOINTS,
NUM_HAND_JOINTS,
ControllerInput,
ControllerInputIndex,
Expand Down Expand Up @@ -73,11 +73,11 @@ def _active_controller() -> TensorGroup:

def _active_full_body() -> TensorGroup:
full_body = TensorGroup(FullBodyInput())
positions = np.zeros((NUM_BODY_JOINTS_PICO, 3), dtype=np.float32)
positions = np.zeros((NUM_BODY_JOINTS, 3), dtype=np.float32)
positions[0] = [1.0, 2.0, 3.0]
orientations = np.zeros((NUM_BODY_JOINTS_PICO, 4), dtype=np.float32)
orientations = np.zeros((NUM_BODY_JOINTS, 4), dtype=np.float32)
orientations[:, 3] = 1.0
valid = np.zeros(NUM_BODY_JOINTS_PICO, dtype=np.uint8)
valid = np.zeros(NUM_BODY_JOINTS, dtype=np.uint8)
valid[0] = 1

full_body[FullBodyInputIndex.JOINT_POSITIONS] = positions
Expand Down
3 changes: 3 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,7 @@ if(BUILD_TESTING)

# Replay DeviceIO session tests (C++)
add_subdirectory(replay_deviceio_session_tests/cpp)

# Live trackers tests (C++)
add_subdirectory(live_trackers_tests/cpp)
endif()
Loading
Loading