#include #include #include #include #include #include #include #include #include #include "PXREARobotSDK.h" using json = nlohmann::json; std::array LeftControllerPose; std::array RightControllerPose; std::array HeadsetPose; std::array, 26> LeftHandTrackingState; double LeftHandScale = 1.0; int LeftHandIsActive = 0; std::array, 26> RightHandTrackingState; double RightHandScale = 1.0; int RightHandIsActive = 0; // Whole body motion data - 24 joints for body tracking std::array, 24> BodyJointsPose; // Position and rotation for each joint std::array, 24> BodyJointsVelocity; // Velocity and angular velocity for each joint std::array, 24> BodyJointsAcceleration; // Acceleration and angular acceleration for each joint std::array BodyJointsTimestamp; // IMU timestamp for each joint int64_t BodyTimeStampNs = 0; // Body data timestamp bool BodyDataAvailable = false; // Flag to indicate if body data is available std::array, 3> MotionTrackerPose; // Position and rotation for each joint std::array, 3> MotionTrackerVelocity; // Velocity and angular velocity for each joint std::array, 3> MotionTrackerAcceleration; // Acceleration and angular acceleration for each joint std::array MotionTrackerSerialNumbers; // Serial numbers of the motion trackers int64_t MotionTimeStampNs = 0; // Motion data timestamp int NumMotionDataAvailable = 0; // number of motion trackers bool LeftMenuButton; double LeftTrigger; double LeftGrip; std::array LeftAxis{0.0, 0.0}; bool LeftAxisClick; bool LeftPrimaryButton; bool LeftSecondaryButton; bool RightMenuButton; double RightTrigger; double RightGrip; std::array RightAxis{0.0, 0.0}; bool RightAxisClick; bool RightPrimaryButton; bool RightSecondaryButton; int64_t TimeStampNs; std::mutex leftMutex; std::mutex rightMutex; std::mutex headsetPoseMutex; std::mutex timestampMutex; std::mutex leftHandMutex; std::mutex rightHandMutex; std::mutex bodyMutex; // Mutex for body tracking data std::mutex motionMutex; std::array stringToPoseArray(const std::string& poseStr) { std::array result{0}; std::stringstream ss(poseStr); std::string value; int i = 0; while (std::getline(ss, value, ',') && i < 7) { result[i++] = std::stod(value); } return result; } std::array stringToVelocityArray(const std::string& velocityStr) { std::array result{0}; std::stringstream ss(velocityStr); std::string value; int i = 0; while (std::getline(ss, value, ',') && i < 6) { result[i++] = std::stod(value); } return result; } void OnPXREAClientCallback(void* context, PXREAClientCallbackType type, int status, void* userData) { switch (type) { case PXREAServerConnect: std::cout << "server connect\n" << std::endl; break; case PXREAServerDisconnect: std::cout << "server disconnect\n" << std::endl; break; case PXREADeviceFind: std::cout << "device found\n" << (const char*)userData << std::endl; break; case PXREADeviceMissing: std::cout << "device missing\n" << (const char*)userData << std::endl; break; case PXREADeviceConnect: std::cout << "device connect\n" << (const char*)userData << status << std::endl; break; case PXREADeviceStateJson: auto& dsj = *((PXREADevStateJson*)userData); try { json data = json::parse(dsj.stateJson); if (data.contains("value")) { auto value = json::parse(data["value"].get()); if (value["Controller"].contains("left")) { auto& left = value["Controller"]["left"]; { std::lock_guard lock(leftMutex); LeftControllerPose = stringToPoseArray(left["pose"].get()); LeftTrigger = left["trigger"].get(); LeftGrip = left["grip"].get(); LeftMenuButton = left["menuButton"].get(); LeftAxis[0] = left["axisX"].get(); LeftAxis[1] = left["axisY"].get(); LeftAxisClick = left["axisClick"].get(); LeftPrimaryButton = left["primaryButton"].get(); LeftSecondaryButton = left["secondaryButton"].get(); } } if (value["Controller"].contains("right")) { auto& right = value["Controller"]["right"]; { std::lock_guard lock(rightMutex); RightControllerPose = stringToPoseArray(right["pose"].get()); RightTrigger = right["trigger"].get(); RightGrip = right["grip"].get(); RightMenuButton = right["menuButton"].get(); RightAxis[0] = right["axisX"].get(); RightAxis[1] = right["axisY"].get(); RightAxisClick = right["axisClick"].get(); RightPrimaryButton = right["primaryButton"].get(); RightSecondaryButton = right["secondaryButton"].get(); } } if (value.contains("Head")) { auto& headset = value["Head"]; { std::lock_guard lock(headsetPoseMutex); HeadsetPose = stringToPoseArray(headset["pose"].get()); } } if (value.contains("timeStampNs")) { std::lock_guard lock(timestampMutex); TimeStampNs = value["timeStampNs"].get(); } if (value["Hand"].contains("leftHand")) { auto& leftHand = value["Hand"]["leftHand"]; { std::lock_guard lock(leftHandMutex); LeftHandScale = leftHand["scale"].get(); LeftHandIsActive = leftHand["isActive"].get(); for (int i = 0; i < 26; i++) { LeftHandTrackingState[i] = stringToPoseArray(leftHand["HandJointLocations"][i]["p"].get()); } } } if (value["Hand"].contains("rightHand")) { auto& rightHand = value["Hand"]["rightHand"]; { std::lock_guard lock(rightHandMutex); RightHandScale = rightHand["scale"].get(); RightHandIsActive = rightHand["isActive"].get(); for (int i = 0; i < 26; i++) { RightHandTrackingState[i] = stringToPoseArray(rightHand["HandJointLocations"][i]["p"].get()); } } } // Parse Body data for whole body motion capture if (value.contains("Body")) { auto& body = value["Body"]; { std::lock_guard lock(bodyMutex); if (body.contains("timeStampNs")) { BodyTimeStampNs = body["timeStampNs"].get(); } if (body.contains("joints") && body["joints"].is_array()) { auto joints = body["joints"]; int jointCount = std::min(static_cast(joints.size()), 24); for (int i = 0; i < jointCount; i++) { auto& joint = joints[i]; // Parse pose (position and rotation) if (joint.contains("p")) { BodyJointsPose[i] = stringToPoseArray(joint["p"].get()); } // Parse velocity and angular velocity if (joint.contains("va")) { BodyJointsVelocity[i] = stringToVelocityArray(joint["va"].get()); } // Parse acceleration and angular acceleration if (joint.contains("wva")) { BodyJointsAcceleration[i] = stringToVelocityArray(joint["wva"].get()); } // Parse IMU timestamp if (joint.contains("t")) { BodyJointsTimestamp[i] = joint["t"].get(); } } BodyDataAvailable = true; } } } //parse individual tracker data if (value.contains("Motion")) { auto& motion = value["Motion"]; { std::lock_guard lock(motionMutex); if (motion.contains("timeStampNs")) { MotionTimeStampNs = motion["timeStampNs"].get(); } if (motion.contains("joints") && motion["joints"].is_array()) { auto joints = motion["joints"]; NumMotionDataAvailable = std::min(static_cast(joints.size()), 3); for (int i = 0; i < NumMotionDataAvailable; i++) { auto& joint = joints[i]; // Parse pose (position and rotation) if (joint.contains("p")) { MotionTrackerPose[i] = stringToPoseArray(joint["p"].get()); } // Parse velocity and angular velocity if (joint.contains("va")) { MotionTrackerVelocity[i] = stringToVelocityArray(joint["va"].get()); } // Parse acceleration and angular acceleration if (joint.contains("wva")) { MotionTrackerAcceleration[i] = stringToVelocityArray(joint["wva"].get()); } if (joint.contains("sn")) { MotionTrackerSerialNumbers[i] = joint["sn"].get(); } } } } } } } catch (const json::exception& e) { std::cerr << "JSON parsing error: " << e.what() << std::endl; } break; } } void init() { if (PXREAInit(NULL, OnPXREAClientCallback, PXREAFullMask) != 0) { throw std::runtime_error("PXREAInit failed"); } } void deinit() { PXREADeinit(); } std::array getLeftControllerPose() { std::lock_guard lock(leftMutex); return LeftControllerPose; } std::array getRightControllerPose() { std::lock_guard lock(rightMutex); return RightControllerPose; } std::array getHeadsetPose() { std::lock_guard lock(headsetPoseMutex); return HeadsetPose; } double getLeftTrigger() { std::lock_guard lock(leftMutex); return LeftTrigger; } double getLeftGrip() { std::lock_guard lock(leftMutex); return LeftGrip; } double getRightTrigger() { std::lock_guard lock(rightMutex); return RightTrigger; } double getRightGrip() { std::lock_guard lock(rightMutex); return RightGrip; } bool getLeftMenuButton() { std::lock_guard lock(leftMutex); return LeftMenuButton; } bool getRightMenuButton() { std::lock_guard lock(rightMutex); return RightMenuButton; } bool getLeftAxisClick() { std::lock_guard lock(leftMutex); return LeftAxisClick; } bool getRightAxisClick() { std::lock_guard lock(rightMutex); return RightAxisClick; } std::array getLeftAxis() { std::lock_guard lock(leftMutex); return LeftAxis; } std::array getRightAxis() { std::lock_guard lock(rightMutex); return RightAxis; } bool getLeftPrimaryButton() { std::lock_guard lock(leftMutex); return LeftPrimaryButton; } bool getRightPrimaryButton() { std::lock_guard lock(rightMutex); return RightPrimaryButton; } bool getLeftSecondaryButton() { std::lock_guard lock(leftMutex); return LeftSecondaryButton; } bool getRightSecondaryButton() { std::lock_guard lock(rightMutex); return RightSecondaryButton; } int64_t getTimeStampNs() { std::lock_guard lock(timestampMutex); return TimeStampNs; } std::array, 26> getLeftHandTrackingState() { std::lock_guard lock(leftHandMutex); return LeftHandTrackingState; } int getLeftHandScale() { std::lock_guard lock(leftHandMutex); return LeftHandScale; } int getLeftHandIsActive() { std::lock_guard lock(leftHandMutex); return LeftHandIsActive; } std::array, 26> getRightHandTrackingState() { std::lock_guard lock(rightHandMutex); return RightHandTrackingState; } int getRightHandScale() { std::lock_guard lock(rightHandMutex); return RightHandScale; } int getRightHandIsActive() { std::lock_guard lock(rightHandMutex); return RightHandIsActive; } // Body tracking functions bool isBodyDataAvailable() { std::lock_guard lock(bodyMutex); return BodyDataAvailable; } std::array, 24> getBodyJointsPose() { std::lock_guard lock(bodyMutex); return BodyJointsPose; } std::array, 24> getBodyJointsVelocity() { std::lock_guard lock(bodyMutex); return BodyJointsVelocity; } std::array, 24> getBodyJointsAcceleration() { std::lock_guard lock(bodyMutex); return BodyJointsAcceleration; } std::array getBodyJointsTimestamp() { std::lock_guard lock(bodyMutex); return BodyJointsTimestamp; } int64_t getBodyTimeStampNs() { std::lock_guard lock(bodyMutex); return BodyTimeStampNs; } int numMotionDataAvailable() { std::lock_guard lock(motionMutex); return NumMotionDataAvailable; } std::vector> getMotionTrackerPose() { std::lock_guard lock(motionMutex); std::vector> result; for (int i = 0; i < NumMotionDataAvailable; i++) { result.push_back(MotionTrackerPose[i]); } return result; } std::vector> getMotionTrackerVelocity() { std::lock_guard lock(motionMutex); std::vector> result; for (int i = 0; i < NumMotionDataAvailable; i++) { result.push_back(MotionTrackerVelocity[i]); } return result; } std::vector> getMotionTrackerAcceleration() { std::lock_guard lock(motionMutex); std::vector> result; for (int i = 0; i < NumMotionDataAvailable; i++) { result.push_back(MotionTrackerAcceleration[i]); } return result; } std::vector getMotionTrackerSerialNumbers() { std::lock_guard lock(motionMutex); std::vector result; for (int i = 0; i < NumMotionDataAvailable; i++) { result.push_back(MotionTrackerSerialNumbers[i]); } return result; } int64_t getMotionTimeStampNs() { std::lock_guard lock(motionMutex); return MotionTimeStampNs; } int DeviceControlJsonWrapper(const std::string& dev_id, const std::string& json_str) { const int rc = PXREADeviceControlJson(dev_id.c_str(), json_str.c_str()); if (rc != 0) { throw std::runtime_error("device_control_json failed"); } return rc; // 0 } int SendBytesToDeviceWrapper(const std::string& dev_id, pybind11::bytes blob) { std::string s = blob; // copy Python bytes to std::string const int rc = PXREASendBytesToDevice(dev_id.c_str(), s.data(), static_cast(s.size())); if (rc != 0) { throw std::runtime_error("send_bytes_to_device failed"); } return rc; // 0 } PYBIND11_MODULE(xrobotoolkit_sdk, 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."); m.def("get_right_controller_pose", &getRightControllerPose, "Get the right controller pose."); m.def("get_headset_pose", &getHeadsetPose, "Get the headset pose."); m.def("get_left_trigger", &getLeftTrigger, "Get the left trigger value."); m.def("get_left_grip", &getLeftGrip, "Get the left grip value."); m.def("get_right_trigger", &getRightTrigger, "Get the right trigger value."); m.def("get_right_grip", &getRightGrip, "Get the right grip value."); m.def("get_left_menu_button", &getLeftMenuButton, "Get the left menu button state."); m.def("get_right_menu_button", &getRightMenuButton, "Get the right menu button state."); m.def("get_left_axis_click", &getLeftAxisClick, "Get the left axis click state."); m.def("get_right_axis_click", &getRightAxisClick, "Get the right axis click state."); m.def("get_left_axis", &getLeftAxis, "Get the left axis values (x, y)."); m.def("get_right_axis", &getRightAxis, "Get the right axis values (x, y)."); m.def("get_X_button", &getLeftPrimaryButton, "Get the left primary button state."); m.def("get_A_button", &getRightPrimaryButton, "Get the right primary button state."); m.def("get_Y_button", &getLeftSecondaryButton, "Get the left secondary button state."); m.def("get_B_button", &getRightSecondaryButton, "Get the right secondary button state."); m.def("get_time_stamp_ns", &getTimeStampNs, "Get the timestamp in nanoseconds."); m.def("get_left_hand_tracking_state", &getLeftHandTrackingState, "Get the left hand state."); m.def("get_right_hand_tracking_state", &getRightHandTrackingState, "Get the right hand state."); m.def("get_left_hand_is_active", &getLeftHandIsActive, "Get the left hand tracking quality (0 = low, 1 = high)."); m.def("get_right_hand_is_active", &getRightHandIsActive, "Get the right hand tracking quality (0 = low, 1 = high)."); // Body tracking functions m.def("is_body_data_available", &isBodyDataAvailable, "Check if body tracking data is available."); m.def("get_body_joints_pose", &getBodyJointsPose, "Get the body joints pose data (24 joints, 7 values each: x,y,z,qx,qy,qz,qw)."); m.def("get_body_joints_velocity", &getBodyJointsVelocity, "Get the body joints velocity data (24 joints, 6 values each: vx,vy,vz,wx,wy,wz)."); m.def("get_body_joints_acceleration", &getBodyJointsAcceleration, "Get the body joints acceleration data (24 joints, 6 values each: ax,ay,az,wax,way,waz)."); m.def("get_body_joints_timestamp", &getBodyJointsTimestamp, "Get the body joints IMU timestamp data (24 joints)."); m.def("get_body_timestamp_ns", &getBodyTimeStampNs, "Get the body data timestamp in nanoseconds."); // Motion tracker functions m.def("num_motion_data_available", &numMotionDataAvailable, "Check if motion tracker data is available."); m.def("get_motion_tracker_pose", &getMotionTrackerPose, "Get the motion tracker pose data (3 trackers, 7 values each: x,y,z,qx,qy,qz,qw)."); m.def("get_motion_tracker_velocity", &getMotionTrackerVelocity, "Get the motion tracker velocity data (3 trackers, 6 values each: vx,vy,vz,wx,wy,wz)."); m.def("get_motion_tracker_acceleration", &getMotionTrackerAcceleration, "Get the motion tracker acceleration data (3 trackers, 6 values each: ax,ay,az,wax,way,waz)."); m.def("get_motion_tracker_serial_numbers", &getMotionTrackerSerialNumbers, "Get the serial numbers of the motion trackers."); m.def("get_motion_timestamp_ns", &getMotionTimeStampNs, "Get the motion data timestamp in nanoseconds."); // send json bytes functions m.def("device_control_json", &DeviceControlJsonWrapper, "Send a JSON control command to a device"); m.def("send_bytes_to_device", &SendBytesToDeviceWrapper, "Send raw bytes to a device"); m.doc() = "Python bindings for PXREARobot SDK using pybind11."; }