Skip to content

Commit 3c87def

Browse files
authored
Merge pull request #3 from YanjieZe/main
[add hand isActive state]
2 parents 5485b4e + e36a2ac commit 3c87def

File tree

4 files changed

+196
-0
lines changed

4 files changed

+196
-0
lines changed

CLAUDE.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This project provides Python bindings for the XRoboToolkit PC Service SDK, enabling Python applications to extract XR state data including controller poses, hand tracking, and body motion capture from XR devices (primarily PICO headsets).
8+
9+
## Architecture
10+
11+
The project consists of:
12+
13+
- **Core C++ Bindings** (`bindings/py_bindings.cpp`): Pybind11-based C++ module that wraps the PXREARobotSDK
14+
- **SDK Integration**: Uses the XRoboToolkit-PC-Service SDK (cloned from external repository)
15+
- **Build System**: CMake-based build with Python setuptools integration
16+
- **Multi-platform Support**: Linux (x86_64/aarch64) and Windows
17+
18+
Key components:
19+
- `PXREARobotSDK.h`: Main SDK header providing device connectivity and data parsing
20+
- `py_bindings.cpp`: Thread-safe C++ wrapper with mutex-protected global state variables
21+
- JSON parsing using nlohmann/json for device state data
22+
- Callback-based data updates from the SDK
23+
24+
## Build Commands
25+
26+
### Ubuntu/Linux Setup and Build
27+
```bash
28+
# Full setup (downloads dependencies and builds)
29+
bash setup_ubuntu.sh
30+
31+
# Manual build after setup
32+
python setup.py install
33+
34+
# Clean build artifacts
35+
python setup.py clean
36+
```
37+
38+
### Windows Setup and Build
39+
```batch
40+
# Full setup (downloads dependencies and builds)
41+
setup_windows.bat
42+
43+
# Manual build after setup
44+
python setup.py install
45+
```
46+
47+
### Development Commands
48+
```bash
49+
# Uninstall existing package
50+
pip uninstall -y xrobotoolkit_sdk
51+
52+
# Install pybind11 dependency
53+
conda install -c conda-forge pybind11
54+
# or
55+
pip install pybind11
56+
57+
# Build and install
58+
python setup.py install
59+
```
60+
61+
## Data Flow and Threading
62+
63+
The SDK uses a callback-based architecture:
64+
- `OnPXREAClientCallback`: Main callback function that receives JSON data from connected devices
65+
- Global state variables (poses, button states, etc.) are updated in real-time
66+
- Thread-safe access via mutex locks for each data category
67+
- Data parsing from comma-separated pose strings to arrays
68+
69+
## Key Functions and Data Types
70+
71+
### Controller Data
72+
- Poses: `std::array<double, 7>` (x,y,z,qx,qy,qz,qw)
73+
- Buttons: Menu, Primary, Secondary, Axis Click
74+
- Analog: Trigger, Grip, Axis (x,y)
75+
76+
### Hand Tracking
77+
- 26 joints per hand with 7 values each (position + quaternion)
78+
- Hand scale factor
79+
80+
### Body Tracking
81+
- 24 body joints with pose, velocity, acceleration data
82+
- IMU timestamps for each joint
83+
- Availability flag for body tracking system
84+
85+
## Dependencies
86+
87+
### Required
88+
- pybind11 (Python binding framework)
89+
- CMake (build system)
90+
- XRoboToolkit-PC-Service SDK (automatically downloaded during setup)
91+
92+
### Platform-specific Libraries
93+
- Linux: `libPXREARobotSDK.so`
94+
- Windows: `PXREARobotSDK.dll` and `PXREARobotSDK.lib`
95+
96+
## Testing
97+
98+
No formal test suite is included. Test functionality using the example scripts in `examples/`:
99+
- `example.py`: Basic controller and headset pose testing
100+
- `example_body_tracking.py`: Body tracking functionality
101+
- `run_binding_continuous.py`: Continuous data capture
102+
103+
## Important Notes
104+
105+
- The SDK requires active XR device connection (PICO headset)
106+
- Body tracking requires at least two Pico Swift devices
107+
- All data access is thread-safe but real-time dependent on device connectivity
108+
- The project builds a Python extension module that must be installed to site-packages

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,19 @@ xrt.init()
102102
# Left Hand State
103103
left_hand_tracking_state = xrt.get_left_hand_tracking_state()
104104
print(f"Left Hand State: {left_hand_tracking_state}")
105+
106+
# Left Hand isActive
107+
left_hand_is_active = xrt.get_left_hand_is_active()
108+
print(f"Left Hand isActive: {left_hand_is_active}")
109+
105110
# Right Hand State
106111
right_hand_tracking_state = xrt.get_right_hand_tracking_state()
107112
print(f"Right Hand State: {right_hand_tracking_state}")
108113

114+
# Right Hand isActive
115+
right_hand_is_active = xrt.get_right_hand_is_active()
116+
print(f"Right Hand isActive: {right_hand_is_active}")
117+
109118
xrt.close()
110119
```
111120

bindings/py_bindings.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ std::array<double, 7> HeadsetPose;
1717

1818
std::array<std::array<double, 7>, 26> LeftHandTrackingState;
1919
double LeftHandScale = 1.0;
20+
int LeftHandIsActive = 0;
2021
std::array<std::array<double, 7>, 26> RightHandTrackingState;
2122
double RightHandScale = 1.0;
23+
int RightHandIsActive = 0;
2224

2325
// Whole body motion data - 24 joints for body tracking
2426
std::array<std::array<double, 7>, 24> BodyJointsPose; // Position and rotation for each joint
@@ -148,6 +150,7 @@ void OnPXREAClientCallback(void* context, PXREAClientCallbackType type, int stat
148150
std::lock_guard<std::mutex> lock(leftHandMutex);
149151

150152
LeftHandScale = leftHand["scale"].get<double>();
153+
LeftHandIsActive = leftHand["isActive"].get<int>();
151154
for (int i = 0; i < 26; i++) {
152155
LeftHandTrackingState[i] = stringToPoseArray(leftHand["HandJointLocations"][i]["p"].get<std::string>());
153156
}
@@ -158,6 +161,7 @@ void OnPXREAClientCallback(void* context, PXREAClientCallbackType type, int stat
158161
{
159162
std::lock_guard<std::mutex> lock(rightHandMutex);
160163
RightHandScale = rightHand["scale"].get<double>();
164+
RightHandIsActive = rightHand["isActive"].get<int>();
161165
for (int i = 0; i < 26; i++) {
162166
RightHandTrackingState[i] = stringToPoseArray(rightHand["HandJointLocations"][i]["p"].get<std::string>());
163167
}
@@ -324,6 +328,11 @@ int getLeftHandScale() {
324328
return LeftHandScale;
325329
}
326330

331+
int getLeftHandIsActive() {
332+
std::lock_guard<std::mutex> lock(leftHandMutex);
333+
return LeftHandIsActive;
334+
}
335+
327336
std::array<std::array<double, 7>, 26> getRightHandTrackingState() {
328337
std::lock_guard<std::mutex> lock(rightHandMutex);
329338
return RightHandTrackingState;
@@ -334,6 +343,11 @@ int getRightHandScale() {
334343
return RightHandScale;
335344
}
336345

346+
int getRightHandIsActive() {
347+
std::lock_guard<std::mutex> lock(rightHandMutex);
348+
return RightHandIsActive;
349+
}
350+
337351
// Body tracking functions
338352
bool isBodyDataAvailable() {
339353
std::lock_guard<std::mutex> lock(bodyMutex);
@@ -388,6 +402,8 @@ PYBIND11_MODULE(xrobotoolkit_sdk, m) {
388402
m.def("get_time_stamp_ns", &getTimeStampNs, "Get the timestamp in nanoseconds.");
389403
m.def("get_left_hand_tracking_state", &getLeftHandTrackingState, "Get the left hand state.");
390404
m.def("get_right_hand_tracking_state", &getRightHandTrackingState, "Get the right hand state.");
405+
m.def("get_left_hand_is_active", &getLeftHandIsActive, "Get the left hand tracking quality (0 = low, 1 = high).");
406+
m.def("get_right_hand_is_active", &getRightHandIsActive, "Get the right hand tracking quality (0 = low, 1 = high).");
391407

392408
// Body tracking functions
393409
m.def("is_body_data_available", &isBodyDataAvailable, "Check if body tracking data is available.");

examples/test_hand_isactive.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test script for hand tracking isActive functionality.
4+
5+
This script demonstrates how to use the new isActive functions to check
6+
hand tracking quality for both left and right hands.
7+
"""
8+
9+
import xrobotoolkit_sdk as xrt
10+
import time
11+
12+
def main():
13+
try:
14+
# Initialize the SDK
15+
print("Initializing XRoboToolkit SDK...")
16+
xrt.init()
17+
18+
print("Testing hand tracking isActive functionality...")
19+
print("isActive values: 0 = low quality, 1 = high quality")
20+
print("Press Ctrl+C to stop\n")
21+
22+
while True:
23+
# Get hand tracking states
24+
left_hand_state = xrt.get_left_hand_tracking_state()
25+
right_hand_state = xrt.get_right_hand_tracking_state()
26+
27+
# Get hand tracking quality (isActive)
28+
left_hand_active = xrt.get_left_hand_is_active()
29+
right_hand_active = xrt.get_right_hand_is_active()
30+
31+
32+
print(f"Left Hand: isActive={left_hand_active}")
33+
print(f"Right Hand: isActive={right_hand_active}")
34+
35+
# Show hand tracking quality status
36+
left_quality = "HIGH" if left_hand_active == 1 else "LOW"
37+
right_quality = "HIGH" if right_hand_active == 1 else "LOW"
38+
39+
print(f"Left Hand Quality: {left_quality}")
40+
print(f"Right Hand Quality: {right_quality}")
41+
42+
# Example of first joint position for reference
43+
if len(left_hand_state) > 0:
44+
left_wrist_pos = left_hand_state[0][:3] # x, y, z
45+
print(f"Left Wrist Position: ({left_wrist_pos[0]:.3f}, {left_wrist_pos[1]:.3f}, {left_wrist_pos[2]:.3f})")
46+
47+
if len(right_hand_state) > 0:
48+
right_wrist_pos = right_hand_state[0][:3] # x, y, z
49+
print(f"Right Wrist Position: ({right_wrist_pos[0]:.3f}, {right_wrist_pos[1]:.3f}, {right_wrist_pos[2]:.3f})")
50+
51+
print("-" * 50)
52+
time.sleep(1)
53+
54+
except KeyboardInterrupt:
55+
print("\nStopping hand tracking test...")
56+
except Exception as e:
57+
print(f"Error: {e}")
58+
finally:
59+
print("Closing SDK...")
60+
xrt.close()
61+
62+
if __name__ == "__main__":
63+
main()

0 commit comments

Comments
 (0)