Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 50 additions & 0 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Wheels

on:
workflow_dispatch:
pull_request:
push:
tags: ["v*"]

jobs:
build_sdist:
name: Build sdist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv build --sdist
- uses: actions/upload-artifact@v4
with:
name: sdist
path: dist/*.tar.gz

build_wheels:
name: Wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: pypa/cibuildwheel@v3.2
- uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.os }}
path: wheelhouse/*.whl

publish:
name: Publish to PyPI
needs: [build_sdist, build_wheels]
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@ Thumbs.db
*.egg-info/
.vscode/

include/
lib/

tmp/

*.pkl
*.pkl

# Vendored prebuilt SDK binaries are intentionally tracked
!vendor/**
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
55 changes: 20 additions & 35 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,41 +23,27 @@ Key components:

## Build Commands

### Ubuntu/Linux Setup and Build
```bash
# Full setup (downloads dependencies and builds)
bash setup_ubuntu.sh

# Manual build after setup
python setup.py install

# Clean build artifacts
python setup.py clean
```
The project is uv-first and uses scikit-build-core as its build backend.
Prebuilt PXREARobotSDK binaries are vendored in `vendor/` (Linux x86_64,
Linux aarch64, Windows amd64), so no SDK download or build step is needed.
Building the extension is only possible on Linux and Windows; macOS is
unsupported (no vendor binaries exist).

### Windows Setup and Build
```batch
# Full setup (downloads dependencies and builds)
setup_windows.bat

# Manual build after setup
python setup.py install
```

### Development Commands
```bash
# Uninstall existing package
pip uninstall -y xrobotoolkit_sdk
# Create .venv (Python from .python-version), build and install the package
uv sync

# Install pybind11 dependency
conda install -c conda-forge pybind11
# or
pip install pybind11
# Run an example inside the project venv
uv run examples/example.py

# Build and install
python setup.py install
# Build sdist + wheel
uv build
```

Release wheels are built by cibuildwheel in `.github/workflows/wheels.yml`
(manylinux_2_34 for both Linux arches, dictated by the vendored libraries'
glibc requirements — see `vendor/README.md`).

## Data Flow and Threading

The SDK uses a callback-based architecture:
Expand All @@ -84,13 +70,12 @@ The SDK uses a callback-based architecture:

## Dependencies

### Required
- pybind11 (Python binding framework)
- CMake (build system)
- XRoboToolkit-PC-Service SDK (automatically downloaded during setup)
### Required (build time)
- CMake >= 3.18 and a C++17 compiler
- pybind11 and scikit-build-core (fetched automatically by the build backend)

### Platform-specific Libraries
- Linux: `libPXREARobotSDK.so`
### Platform-specific Libraries (vendored in `vendor/`)
- Linux: `libPXREARobotSDK.so` (x86_64 and aarch64 variants)
- Windows: `PXREARobotSDK.dll` and `PXREARobotSDK.lib`

## Testing
Expand Down
75 changes: 32 additions & 43 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,54 +1,43 @@
cmake_minimum_required(VERSION 3.14)
cmake_minimum_required(VERSION 3.18)

project(MyPybind11Project LANGUAGES CXX)
project(xrobotoolkit_sdk LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

cmake_host_system_information(RESULT ISA_NAME QUERY OS_PLATFORM) # Added: Important for UNIX specific logic
message(STATUS "OS_PLATFORM (ISA_NAME): ${ISA_NAME}")
find_package(pybind11 CONFIG REQUIRED)

include(GNUInstallDirs) # Add this line
find_package(pybind11 REQUIRED)

# Python Bindings for py_bindings.cpp
pybind11_add_module(xrobotoolkit_sdk MODULE bindings/py_bindings.cpp)

# Link xrobotoolkit_sdk module against pybind11
target_link_libraries(xrobotoolkit_sdk PRIVATE pybind11::module)

# Add include directories and link libraries for PXREARobotSDK to xrobotoolkit_sdk target
# --- Select the vendored prebuilt PXREARobotSDK for the target platform ---
# Binaries are checked into vendor/ (see vendor/README.md for provenance).
if(WIN32)
target_include_directories(xrobotoolkit_sdk PUBLIC
${PROJECT_SOURCE_DIR}/include
)
target_link_directories(xrobotoolkit_sdk PUBLIC ${PROJECT_SOURCE_DIR}/lib)
target_link_libraries(xrobotoolkit_sdk PUBLIC
PXREARobotSDK.dll # Assuming this is how PXREARobotSDK is linked, mirroring ConsoleDemo
)
endif()

if(UNIX)
# ISA_NAME is set by cmake_host_system_information above
if(ISA_NAME STREQUAL "aarch64")
target_include_directories(xrobotoolkit_sdk PUBLIC
${PROJECT_SOURCE_DIR}/include/aarch64
)
target_link_directories(xrobotoolkit_sdk PUBLIC ${PROJECT_SOURCE_DIR}/lib/aarch64)
set(XRT_VENDOR_DIR "${PROJECT_SOURCE_DIR}/vendor/windows_amd64")
set(XRT_VENDOR_IMPLIB "${XRT_VENDOR_DIR}/PXREARobotSDK.lib")
set(XRT_VENDOR_RUNTIME "${XRT_VENDOR_DIR}/PXREARobotSDK.dll")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)$")
set(XRT_VENDOR_DIR "${PROJECT_SOURCE_DIR}/vendor/linux_aarch64")
else()
target_include_directories(xrobotoolkit_sdk PUBLIC
${PROJECT_SOURCE_DIR}/include
)
target_link_directories(xrobotoolkit_sdk PUBLIC ${PROJECT_SOURCE_DIR}/lib)
set(XRT_VENDOR_DIR "${PROJECT_SOURCE_DIR}/vendor/linux_x86_64")
endif()
target_link_libraries(xrobotoolkit_sdk PUBLIC
PXREARobotSDK
)
set(XRT_VENDOR_IMPLIB "${XRT_VENDOR_DIR}/libPXREARobotSDK.so")
set(XRT_VENDOR_RUNTIME "${XRT_VENDOR_DIR}/libPXREARobotSDK.so")
else()
message(FATAL_ERROR
"Unsupported platform '${CMAKE_SYSTEM_NAME}'. PXREARobotSDK ships prebuilt "
"binaries for Linux (x86_64, aarch64) and Windows (amd64) only.")
endif()

pybind11_add_module(_core MODULE bindings/py_bindings.cpp)

target_include_directories(_core PRIVATE "${PROJECT_SOURCE_DIR}/vendor/include")
target_link_libraries(_core PRIVATE "${XRT_VENDOR_IMPLIB}")

# The extension and the vendored shared library are installed side by side in
# the package directory; $ORIGIN makes the dynamic linker look there first.
# (On Windows, __init__.py calls os.add_dll_directory instead.)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set_target_properties(_core PROPERTIES INSTALL_RPATH "$ORIGIN")
endif()

# Install the Python module
install(TARGETS xrobotoolkit_sdk
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # Installs to <prefix>/lib
# You might want a Python-specific path like:
# DESTINATION lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages
)
install(TARGETS _core LIBRARY DESTINATION xrobotoolkit_sdk)
install(FILES "${XRT_VENDOR_RUNTIME}" DESTINATION xrobotoolkit_sdk)
66 changes: 31 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,45 @@ This project provides a python interface to extract XR state using XRoboToolkit-

## Requirements

- [`pybind11`](https://github.com/pybind/pybind11)
- [`XRoboRoolkit PC Service`](https://github.com/XR-Robotics/XRoboToolkit-PC-Service#)
- Linux (x86_64 or aarch64, e.g. NVIDIA Orin) or Windows (x64), Python >= 3.10
- A running [XRoboToolkit PC Service](https://github.com/XR-Robotics/XRoboToolkit-PC-Service) with a connected PICO headset (at runtime)

## Building the Project
### Ubuntu 22.04
The prebuilt `PXREARobotSDK` binaries are vendored in `vendor/`, so no manual
SDK download or build is needed.

## Installation

### From PyPI (once published)

```bash
uv add xrobotoolkit-sdk
# or
pip install xrobotoolkit-sdk
```
conda remove --name xr --all
conda create -n xr python=3.10
conda activate xr

mkdir -p tmp
cd tmp
git clone https://github.com/XR-Robotics/XRoboToolkit-PC-Service.git
cd XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK
bash build.sh
cd ../../../..

mkdir -p lib
mkdir -p include
cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/PXREARobotSDK.h include/
cp -r tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/nlohmann include/nlohmann/
cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/build/libPXREARobotSDK.so lib/
# rm -rf tmp

# Build the project
conda install -c conda-forge pybind11

pip uninstall -y xrobotoolkit_sdk
python setup.py install
```
### Linux Ubuntu 22.04 arm64 version (Nvidia orin supported)
```
bash setup_orin.sh
```
### Windows

**Ensure pybind11 is installed before running the following command.**
### From source

If you do not have [uv](https://docs.astral.sh/uv/) installed, run:

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
setup_windows.bat

Then (requires CMake and a C++17 compiler):

```bash
git clone https://github.com/XR-Robotics/XRoboToolkit-PC-Service-Pybind.git
cd XRoboToolkit-PC-Service-Pybind
uv sync
```

`uv sync` creates `.venv/` with the Python version pinned in `.python-version`,
builds the extension, and installs the package into it. Run scripts with e.g.
`uv run examples/example.py`.

To build distributable artifacts locally: `uv build` (wheels for Linux/Windows
are also built in CI via cibuildwheel, see `.github/workflows/wheels.yml`).

## Using the Python Bindings

**1. Get Controller and Headset Poses**
Expand Down
2 changes: 1 addition & 1 deletion bindings/py_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ int SendBytesToDeviceWrapper(const std::string& dev_id, pybind11::bytes blob) {
}


PYBIND11_MODULE(xrobotoolkit_sdk, m) {
PYBIND11_MODULE(_core, m) {
m.def("init", &init, "Initialize the PXREARobot SDK.");
m.def("close", &deinit, "Deinitialize the PXREARobot SDK.");
m.def("get_left_controller_pose", &getLeftControllerPose, "Get the left controller pose.");
Expand Down
53 changes: 53 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[build-system]
requires = ["scikit-build-core>=0.11", "pybind11>=2.13"]
build-backend = "scikit_build_core.build"

[project]
name = "xrobotoolkit-sdk"
version = "1.1.0"
description = "Python bindings for the XRoboToolkit PC Service SDK (PICO XR controller, hand, and body tracking)."
readme = "README.md"
license = "MIT"
license-files = ["LICENSE"]
authors = [{ name = "Zhigen Zhao", email = "zhigen.zhao@bytedance.com" }]
requires-python = ">=3.10"
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: C++",
"Operating System :: POSIX :: Linux",
"Operating System :: Microsoft :: Windows",
"Topic :: Scientific/Engineering",
]

[project.urls]
Homepage = "https://github.com/XR-Robotics/XRoboToolkit-PC-Service-Pybind"
Upstream = "https://github.com/XR-Robotics/XRoboToolkit-PC-Service"

[tool.scikit-build]
minimum-version = "build-system.requires"
wheel.packages = ["src/xrobotoolkit_sdk"]

# Rebuild the extension when any of these change (uv caches builds otherwise).
[tool.uv]
cache-keys = [
{ file = "pyproject.toml" },
{ file = "CMakeLists.txt" },
{ file = "bindings/**" },
{ file = "vendor/**" },
]

[tool.cibuildwheel]
archs = ["auto64"]
# Skip musl (no vendored musl lib) and free-threaded CPython (pybind11
# free-threading support is experimental; Windows link also fails on it).
skip = "*-musllinux* cp3*t-*"
test-command = 'python -c "import xrobotoolkit_sdk"'

[tool.cibuildwheel.linux]
# The vendored aarch64 libPXREARobotSDK.so requires glibc >= 2.34, the x86_64
# one glibc >= 2.29, so manylinux_2_34 is the oldest tag both can claim.
manylinux-x86_64-image = "manylinux_2_34"
manylinux-aarch64-image = "manylinux_2_34"
# libPXREARobotSDK.so is already bundled in the package with rpath $ORIGIN;
# exclude it so auditwheel does not graft a second copy into .libs.
repair-wheel-command = "auditwheel repair --exclude libPXREARobotSDK.so -w {dest_dir} {wheel}"
Loading