### Project and dependency setup Source: https://github.com/wuji-technology/wujihandpy/blob/main/CMakeLists.txt Initializes the project and includes the wujihandcpp subdirectory while disabling its installation for wheel builds. ```cmake project(wujihandpy) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(BUILD_STATIC_WUJIHANDCPP ON CACHE BOOL "" FORCE) # Wheel build: wujihandcpp is embedded into _core.so; do not also install it # (or its bundled spdlog) into the wheel. set(WUJIHANDCPP_INSTALL OFF CACHE BOOL "" FORCE) set(SPDLOG_INSTALL OFF CACHE BOOL "" FORCE) add_subdirectory(wujihandcpp EXCLUDE_FROM_ALL) ``` -------------------------------- ### Run quickstart script Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/introduction.mdx Execute the quickstart Python script from the terminal. ```bash python3 quickstart.py ``` -------------------------------- ### Install Wuji Hand SDK Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/introduction.mdx Use pip to install the library in your Python environment. ```bash python3 -m pip install wujihandpy ``` -------------------------------- ### Install target Source: https://github.com/wuji-technology/wujihandpy/blob/main/CMakeLists.txt Defines the installation path for the compiled module. ```cmake # The install directory is the output (wheel) directory install(TARGETS _core DESTINATION wujihandpy) ``` -------------------------------- ### TactileGlove Example Output Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Expected console output after running the full workflow example. ```text [xxxx-xx-xx xx:xx:xx.xxx] [wuji] [info] SDK vx.x.x initialized [xxxx-xx-xx xx:xx:xx.xxx] [wuji] [info] Log files can be found at ~/.wuji/log/_.log Serial: WTxxxxxxxxxxxxxx Hardware: vx.x.x, Firmware: vx.x.x (xxxxxxx) Handedness: left Host/device clock skew: -1779537846723254078 ns Device monotonic time: 142623339080000 ns Current sample rate: 60 Hz New sample rate: 60 Hz Streaming enabled: True Frame: seq=33917, ts=142623244 ms, hand=L, max=0.820 Collected 326 frames, CRC errors 0, dropouts 0, USB resets 0, uptime 142628802 ms Saved 326 frames to tactile_data.npy ``` -------------------------------- ### Define Installation Rules Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Configures installation paths for targets, headers, and CMake package configuration files. ```cmake if(WUJIHANDCPP_INSTALL) include(GNUInstallDirs) include(CMakePackageConfigHelpers) install(TARGETS ${PROJECT_NAME} EXPORT wujihandcppTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(EXPORT wujihandcppTargets FILE wujihandcppTargets.cmake NAMESPACE wujihandcpp:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wujihandcpp ) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/wujihandcppConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/wujihandcppConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/wujihandcppConfig.cmake" INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wujihandcpp ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/wujihandcppConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/wujihandcppConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wujihandcpp ) export(EXPORT wujihandcppTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/wujihandcppTargets.cmake" NAMESPACE wujihandcpp:: ) endif() ``` -------------------------------- ### Install Wujihandpy via pip Source: https://github.com/wuji-technology/wujihandpy/blob/main/README.md Command to install the Python package. ```bash pip install wujihandpy ``` -------------------------------- ### Batch Reading Examples Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Examples of retrieving multi-dimensional data arrays for joints and fingers. ```python positions = hand.read_joint_actual_position() ``` ```python >>> print(positions) [[ 0.975 0.523 0.271 -0.450] [ 0.382 0.241 -0.003 -0.275] [-0.299 0.329 0.067 -0.286] [-0.122 0.228 0.315 -0.178] [ 0.205 0.087 0.288 -0.149]] ``` ```python >>> print(hand.finger(1).read_joint_actual_position()) [ 0.382 0.241 -0.003 -0.275] ``` -------------------------------- ### Full Real-Time Control Loop Example Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx A complete example demonstrating how to enable joints, enter real-time control mode with a low-pass filter, and perform non-blocking read/write operations. ```python import wujihandpy import numpy as np hand = wujihandpy.Hand() try: hand.write_joint_enabled(True) # Enter real-time control mode using a first-order low-pass filter with 5 Hz cutoff frequency with hand.realtime_controller( enable_upstream=True, filter=wujihandpy.filter.LowPass(cutoff_freq=5.0) ) as controller: while True: # Real-time write target position (non-blocking) controller.set_joint_target_position(np.zeros((5, 4))) # Real-time read position and effort (non-blocking) position = controller.get_joint_actual_position() effort = controller.get_joint_actual_effort() print(f"pos: {position[1, :]} effort: {effort[1, :]}") finally: hand.write_joint_enabled(False) ``` -------------------------------- ### TactileGlove Full Workflow Example Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Demonstrates device discovery, clock synchronization, sample rate configuration, streaming, and data collection. The context manager ensures the device connection is properly closed upon exit. ```python import time import numpy as np from wujihandpy import ( TactileGlove, TactileHandedness, TactileError, TACTILE_BOOTLOADER_MAGIC, ) def on_disconnect(): print("[event] USB disconnect detected") # 1. Connect — context manager calls disconnect() on exit. # To target a specific device: TactileGlove(serial_number="ABC123"). with TactileGlove() as glove: if not glove.is_connected(): print("Not connected") exit(1) glove.set_disconnect_callback(on_disconnect) # 2. Device info info = glove.get_device_info() hw = ".".join(str(x) for x in info.hw_revision[:3]) fw = ".".join(str(x) for x in info.fw_version[:3]) build = glove.get_fw_build() print(f"Serial: {info.serial}") print(f"Hardware: v{hw}, Firmware: v{fw} ({build.git_short_sha})") # 3. Handedness hand = glove.get_handedness() side = "left" if hand == TactileHandedness.LEFT else "right" print(f"Handedness: {side}") # 4. Clock sync and device time sync = glove.sync_host_epoch(time.time_ns()) skew_ns = sync.device_ns_at_sync - sync.host_ns_echo dev_time = glove.get_device_time() print(f"Host/device clock skew: {skew_ns} ns") print(f"Device monotonic time: {dev_time.device_monotonic_ns} ns") # 5. Sample rate print(f"Current sample rate: {glove.get_sample_rate_hz()} Hz") glove.set_sample_rate_hz(60) print(f"New sample rate: {glove.get_sample_rate_hz()} Hz") # 6. Streaming toggle + single blocking read glove.set_streaming(True) print(f"Streaming enabled: {glove.get_streaming_enabled()}") try: frame = glove.read_frame(timeout_ms=200) side_str = "L" if frame.hand == TactileHandedness.LEFT else "R" print( f"Frame: seq={frame.sequence}, ts={frame.timestamp_ms} ms, " f"hand={side_str}, max={np.nanmax(frame.pressure):.3f}" ) except RuntimeError as e: print(f"Read failed (timeout or disconnect): {e}") except TactileError as e: print(f"Device protocol error: {e}") # 7. Reset counters, then stream and collect glove.reset_counters() frames = [] def collect(frame): frames.append(frame.pressure.copy()) glove.start_streaming(collect) time.sleep(5) # Collect for 5 seconds glove.stop_streaming() # 8. Diagnostics diag = glove.get_diagnostics() print( f"Collected {diag.frame_count} frames, CRC errors {diag.crc_err_count}, " f"dropouts {diag.dropout_count}, USB resets {diag.usb_reset_count}, " f"uptime {diag.uptime_ms} ms" ) # 9. Save data if frames: data = np.stack(frames) # shape: (N, 24, 32), dtype: float32 np.save("tactile_data.npy", data) print(f"Saved {len(frames)} frames to tactile_data.npy") else: print("No frames collected") # Destructive operations (commented by default; both invalidate the handle): # glove.reset_device() # glove.enter_bootloader(TACTILE_BOOTLOADER_MAGIC) ``` -------------------------------- ### Configure Installation Rules Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Defines the WUJIHANDCPP_INSTALL option to control whether install rules are generated, allowing for flexible embedding in Python wheels or standalone builds. ```cmake option(WUJIHANDCPP_INSTALL "Generate install rules for wujihandcpp" ON) ``` -------------------------------- ### Initialize Hand and Control Joints Source: https://github.com/wuji-technology/wujihandpy/blob/main/README.md Basic usage example for reading joint positions and writing target positions. ```python import wujihandpy hand = wujihandpy.Hand() # Read all joint positions positions = hand.read_joint_actual_position() # Write target position to a joint hand.finger(1).joint(0).write_joint_target_position(0.8) ``` -------------------------------- ### Setup Test Suite with GoogleTest Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Downloads and configures GoogleTest using FetchContent and filters test sources based on platform compatibility. ```cmake include(CTest) if(BUILD_TESTING) include(FetchContent) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) FetchContent_MakeAvailable(googletest) file(GLOB_RECURSE WUJIHANDCPP_TEST_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/tests/*.cpp ) list(FILTER WUJIHANDCPP_TEST_SOURCES EXCLUDE REGEX "/cpp11_compat/") if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") list(FILTER WUJIHANDCPP_TEST_SOURCES EXCLUDE REGEX "/tests/device/frame_demuxer_test\\.cpp$") endif() ``` -------------------------------- ### Upgrade pip for installation Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/introduction.mdx Use this command to resolve version satisfaction errors during package installation. ```bash python3 -m pip install --upgrade pip && python3 -m pip install wujihandpy ``` -------------------------------- ### Control dexterous hand joints Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/introduction.mdx A minimal example demonstrating how to enable joints, move a finger, and disable joints safely. ```python # quickstart.py import time import wujihandpy hand = wujihandpy.Hand() try: # Enable all joints (blocking, ensures success) hand.write_joint_enabled(True) # Move index finger joint 0 to 1.57 rad (approximately 90° downward) hand.finger(1).joint(0).write_joint_target_position(1.57) # Wait for motion to complete time.sleep(0.5) finally: # Always disable all joints when finished hand.write_joint_enabled(False) ``` -------------------------------- ### Single Value Reading Examples Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Examples of reading scalar data from the hand and specific joints. ```python time = hand.read_system_time() ``` ```python position = hand.finger(1).joint(0).read_joint_actual_position() ``` -------------------------------- ### Set Compile Definitions and Include Directories Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Sets project-wide definitions and configures include paths for both build and install interfaces. ```cmake target_compile_definitions(${PROJECT_NAME} PRIVATE BUILDING_WUJIHANDCPP) target_compile_definitions(${PROJECT_NAME} PUBLIC WUJIHANDCPP_VERSION="${PROJECT_VERSION}") # Public include split between BUILD and INSTALL interfaces so the install # tree (lib/cmake/wujihandcpp/wujihandcppTargets.cmake) records `include` # under the install prefix instead of leaking the source-tree path. target_include_directories(${PROJECT_NAME} PUBLIC $ $ ) target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/src) ``` -------------------------------- ### Python Bridge Execution Source: https://github.com/wuji-technology/wujihandpy/blob/main/bridge/README.md Commands to install dependencies and launch the Python-based bridge with specific configuration arguments. ```bash cd wujihandpy source .venv/bin/activate pip install eclipse-zenoh numpy # Start the bridge (device over USB; --pub-rate and --side are required) PYTHONPATH=. python -m bridge.python.hand_zenoh_bridge --pub-rate 1000 --side left # Full arguments PYTHONPATH=. python -m bridge.python.hand_zenoh_bridge \ --sn "DEVICE_SN" \ --pub-rate 1000 \ --side left \ --log-level DEBUG ``` -------------------------------- ### Asynchronous Operation Example Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Demonstrates using asyncio.gather to perform multiple joint movements and a sleep task in parallel. ```python import asyncio import wujihandpy async def main(): hand = wujihandpy.Hand() try: await hand.write_joint_enabled_async(True) # Execute the following tasks in parallel: # - Move index finger joint 0 to 1.57 rad (approximately 90° downward) # - Move index finger joint 2 to 1.57 rad (approximately 90° downward) # - Wait for 0.5 seconds simultaneously await asyncio.gather( hand.finger(1).joint(0).write_joint_target_position_async(1.57), hand.finger(1).joint(2).write_joint_target_position_async(1.57), asyncio.sleep(0.5), ) finally: await hand.write_joint_enabled_async(False) # Start the event loop and run the main task asyncio.run(main()) ``` -------------------------------- ### Perform Single Value Write Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Example of enabling a specific joint on the index finger. ```python hand.finger(1).joint(0).write_joint_enabled(True) ``` -------------------------------- ### Implement Multi-threaded Controller Access Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Example of using a threading lock to synchronize reader and writer threads when accessing a real-time controller. ```python import threading import time import wujihandpy import numpy as np import math hand = wujihandpy.Hand() # Disable thread safety check to allow multi-threaded access hand.disable_thread_safe_check() print("Thread safety check disabled, multi-threaded access enabled") # Create mutex lock to ensure thread safety lock = threading.Lock() def reader_thread(ctrl): """Periodically read joint positions""" while True: with lock: position = ctrl.get_joint_actual_position() print(f"[Reader] Position:\n{position}") time.sleep(0.01) def writer_thread(ctrl): """Periodically write target positions (cosine wave motion)""" x = 0 while True: y = (1 - math.cos(x)) * 0.8 target = np.array( [ [0, 0, 0, 0], # F1 [y, 0, y, y], # F2 [y, 0, y, y], # F3 [y, 0, y, y], # F4 [y, 0, y, y], # F5 ], dtype=np.float64, ) with lock: ctrl.set_joint_target_position(target) print(f"[Writer] Target: {y:.4f} rad") x += math.pi / 100.0 time.sleep(0.01) hand.write_joint_enabled(True) with hand.realtime_controller( enable_upstream=True, filter=wujihandpy.filter.LowPass(cutoff_freq=5.0) ) as ctrl: threads = [ threading.Thread(target=reader_thread, args=(ctrl,)), threading.Thread(target=writer_thread, args=(ctrl,)), ] for t in threads: t.start() for t in threads: t.join() ``` -------------------------------- ### Perform Batch Write Operations Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Examples of writing data to multiple joints simultaneously using scalar or array inputs. ```python hand.write_joint_enabled(True) ``` ```python hand.write_joint_enabled( np.array( [ # J1 J2 J3 J4 [False, False, False, False], # F1 [ True, True, True, True], # F2 [False, False, False, False], # F3 [False, False, False, False], # F4 [False, False, False, False], # F5 ], dtype=bool, ) ) ``` ```python hand.write_joint_target_position(0.0) # Equals to hand.write_joint_target_position( np.array( [ # J1 J2 J3 J4 [0.0, 0.0, 0.0, 0.0], # F1 [0.0, 0.0, 0.0, 0.0], # F2 [0.0, 0.0, 0.0, 0.0], # F3 [0.0, 0.0, 0.0, 0.0], # F4 [0.0, 0.0, 0.0, 0.0], # F5 ], dtype=np.float64, ) ) ``` -------------------------------- ### Read Glove Data via Serial Port Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Example usage of the parsing functions with the pyserial library to read from a device port. ```python import serial # python3 -m pip install pyserial with serial.Serial("/dev/ttyACM0", timeout=1.0) as port: frame = read_frame(port) print(f"seq={frame['sequence']}, ts={frame['timestamp_ms']} ms, " f"hand={frame['hand']}, max={np.nanmax(frame['pressure']):.3f}") ``` -------------------------------- ### Fetch External Dependencies Source: https://github.com/wuji-technology/wujihandpy/blob/main/bridge/cpp/CMakeLists.txt Uses FetchContent to download and configure specific versions of zenoh-c, zenoh-cpp, and nlohmann_json. ```cmake # --- FetchContent dependencies --- include(FetchContent) # Disable zenoh-cpp extras set(ZENOHCXX_ENABLE_TESTS OFF CACHE BOOL "" FORCE) set(ZENOHCXX_ENABLE_EXAMPLES OFF CACHE BOOL "" FORCE) set(ZENOHCXX_EXAMPLES_PROTOBUF OFF CACHE BOOL "" FORCE) # Disable nlohmann_json extras set(JSON_BuildTests OFF CACHE BOOL "" FORCE) # Pin released Zenoh versions so bridge builds stay reproducible. FetchContent_Declare( zenohc GIT_REPOSITORY https://github.com/eclipse-zenoh/zenoh-c.git GIT_TAG 1.8.0 GIT_SHALLOW TRUE ) FetchContent_Declare( zenohcxx GIT_REPOSITORY https://github.com/eclipse-zenoh/zenoh-cpp.git GIT_TAG 1.8.0 GIT_SHALLOW TRUE ) FetchContent_Declare( nlohmann_json GIT_REPOSITORY https://github.com/nlohmann/json.git GIT_TAG v3.11.3 GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(zenohc zenohcxx nlohmann_json) ``` -------------------------------- ### System Information Methods Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Methods for retrieving firmware, system time, temperature, and voltage information. ```APIDOC ## System Information Methods ### Description Methods to read system information synchronously, asynchronously, or via cached values. ### Methods - **read_firmware_version(timeout=0.5)** -> uint32 - **read_firmware_version_async(timeout=0.5)** -> Awaitable[uint32] - **read_firmware_version_unchecked(timeout=0.5)** -> None - **get_firmware_version()** -> uint32 - **read_firmware_date(timeout=0.5)** -> uint32 - **read_firmware_date_async(timeout=0.5)** -> Awaitable[uint32] - **read_firmware_date_unchecked(timeout=0.5)** -> None - **get_firmware_date()** -> uint32 - **read_handedness(timeout=0.5)** -> uint8 - **read_handedness_async(timeout=0.5)** -> Awaitable[uint8] - **read_handedness_unchecked(timeout=0.5)** -> None - **get_handedness()** -> uint8 - **read_system_time(timeout=0.5)** -> uint32 - **read_system_time_async(timeout=0.5)** -> Awaitable[uint32] - **read_system_time_unchecked(timeout=0.5)** -> None - **get_system_time()** -> uint32 - **read_temperature(timeout=0.5)** -> float32 - **read_temperature_async(timeout=0.5)** -> Awaitable[float32] - **read_temperature_unchecked(timeout=0.5)** -> None - **get_temperature()** -> float32 - **read_input_voltage(timeout=0.5)** -> float32 - **read_input_voltage_async(timeout=0.5)** -> Awaitable[float32] - **read_input_voltage_unchecked(timeout=0.5)** -> None - **get_input_voltage()** -> float32 ``` -------------------------------- ### Interact with WujiHand via Zenoh Source: https://github.com/wuji-technology/wujihandpy/blob/main/bridge/README.md Demonstrates how to discover devices, query capabilities, read sensor data, and send target positions using the Zenoh Python API. ```python import json import zenoh sn = "WUJIHAND_001" def callback(sample): payload = json.loads(bytes(sample.payload).decode("utf-8")) print("actual_position:", payload) session = zenoh.open(zenoh.Config()) target = [[0.1, 0.0, 0.1, 0.1] for _ in range(5)] sub = None try: # 1. Discover devices via liveliness replies = session.liveliness().get("wuji/**") # 2. Query capability replies = session.get(f"wuji/{sn}/@capability", timeout=5.0) # 3. Read data (GET) replies = session.get(f"wuji/{sn}/joint/actual_position", timeout=5.0) # 4. Write target position (low-latency fire-and-forget PUT) session.put( f"wuji/{sn}/joint/target_position", json.dumps(target).encode(), ) # 5. Subscribe to realtime data (rate controlled by --pub-rate) sub = session.declare_subscriber(f"wuji/{sn}/joint/actual_position", callback) finally: if sub is not None: sub.undeclare() session.close() ``` -------------------------------- ### Configure Linux USB Permissions Source: https://github.com/wuji-technology/wujihandpy/blob/main/README.md Commands to set up udev rules for USB device access on Linux. ```bash echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0483", MODE="0666"' | \ sudo tee /etc/udev/rules.d/95-wujihand.rules && \ sudo udevadm control --reload-rules && sudo udevadm trigger ``` -------------------------------- ### Fetch spdlog Dependency Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Uses FetchContent to download and make the spdlog library available for the project. ```cmake include(FetchContent) FetchContent_Declare( spdlog GIT_REPOSITORY https://github.com/gabime/spdlog.git GIT_TAG v1.16.0 GIT_SHALLOW TRUE ) # Ship spdlog only for static installs. Shared-library packages already link # the spdlog objects into libwujihandcpp.so, and no public header includes # spdlog, so installing it would unnecessarily conflict with system packages. if(WUJIHANDCPP_INSTALL AND BUILD_STATIC_WUJIHANDCPP) set(SPDLOG_INSTALL ON CACHE BOOL "" FORCE) else() set(SPDLOG_INSTALL OFF CACHE BOOL "" FORCE) endif() FetchContent_MakeAvailable(spdlog) ``` -------------------------------- ### realtime_controller Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Initializes a real-time controller instance for high-frequency control tasks. ```APIDOC ## realtime_controller(enable_upstream, filter) ### Description Creates a real-time controller for high-frequency control scenarios. ### Parameters - **enable_upstream** (bool) - Whether to enable upstream data flow. - **filter** (Any) - The filter configuration for the controller. ### Returns - **IController** - The initialized controller object. ``` -------------------------------- ### Firmware Information Methods Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Methods to retrieve joint firmware versions and build dates. ```APIDOC ## read_joint_firmware_version(timeout=0.5) -> NDArray[uint32] ### Description Synchronously read all joint firmware versions. ### Parameters - **timeout** (float) - Optional - Timeout in seconds, defaults to 0.5. ### Returns - **NDArray[uint32]** - Array of firmware versions. --- ## read_joint_firmware_date(timeout=0.5) -> NDArray[uint32] ### Description Synchronously read all joint firmware build dates. ### Parameters - **timeout** (float) - Optional - Timeout in seconds, defaults to 0.5. ### Returns - **NDArray[uint32]** - Array of firmware dates. ``` -------------------------------- ### Configuration and Diagnostics Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Methods for configuring device settings and retrieving diagnostic information. ```APIDOC ## Configuration and Diagnostics ### Methods - `set_sample_rate_hz(hz: int)`: Set sample rate (1–120 Hz). - `get_sample_rate_hz() -> int`: Read current sample rate. - `get_device_info() -> TactileDeviceInfo`: Read serial, hardware revision, and firmware version. - `get_diagnostics() -> TactileDiagnostics`: Read internal counters (uptime, frame count, CRC errors, etc.). - `reset_counters()`: Zero the diagnostic counters. - `reset_device()`: Perform a soft reset of the device. - `enter_bootloader(magic: int)`: Jump to bootloader mode for OTA updates. ``` -------------------------------- ### Configure Logging via Python Code Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/introduction.mdx Use the wujihandpy.logging submodule to set log parameters programmatically. This must be called before constructing the Hand object. ```python import os import wujihandpy from wujihandpy import logging as wuji_logging wuji_logging.set_log_path(os.path.expanduser("~/logs/wujihand")) wuji_logging.set_log_level(wuji_logging.Level.Debug) hand = wujihandpy.Hand() ``` -------------------------------- ### Repository Structure Overview Source: https://github.com/wuji-technology/wujihandpy/blob/main/README.md Visual representation of the project directory layout. ```text ├── src/ # Python binding source code and C++ headers │ ├── wujihandpy/ # Python package with type stubs │ │ ├── __init__.py │ │ └── _core/ │ ├── main.cpp │ └── *.hpp ├── example/ # Usage examples │ ├── joint/ # read / write / realtime / async / multithread / disconnect / glove donning / handedness-based connect │ │ ├── 1.read.py │ │ ├── 2.write.py │ │ ├── 3.realtime.py │ │ ├── 4.async.py │ │ ├── 5.multithread.py │ │ ├── 6.disconnect.py │ │ ├── 7.glove_donning.py │ │ └── 8.connect_by_side.py │ ├── tactile/ # Tactile sensing glove │ │ └── basic.py │ └── joint_with_tactile.py # Drive both subsystems together ├── wujihandcpp/ # Underlying C++ SDK implementation │ ├── include/ # C++ header files │ │ └── wujihandcpp/ │ ├── src/ # C++ source files │ └── tests/ ├── .github/ │ └── workflows/ # CI/CD automation ├── pyproject.toml ├── CMakeLists.txt └── README.md ``` -------------------------------- ### Hand Class Constructor Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Initializes the connection to the dexterous hand device. ```APIDOC ## Hand.__init__(serial_number=None, *, side=None, usb_pid=0x2000, usb_vid=0x0483, mask=None) ### Description Initialize connection to the dexterous hand. Supports specifying the device by serial number, handedness, USB ID, or device mask. ### Parameters - **serial_number** (string) - Optional - USB serial number string to select one device exactly. - **side** (string) - Optional - "left" or "right". Selects the device by handedness. Mutually exclusive with serial_number. ``` -------------------------------- ### Initialize TactileGlove Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Connect to the tactile glove using either auto-discovery or a specific serial number. ```python from wujihandpy import TactileGlove glove = TactileGlove() glove.connect() # Returns True on success ``` ```python glove = TactileGlove(serial_number="ABC123") glove.connect() ``` -------------------------------- ### Source Discovery and Executable Linking Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/example/simple_test/CMakeLists.txt Collects source files recursively and links the project against the wujihandcpp library. ```cmake file(GLOB_RECURSE PROJECT_SOURCE CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.cpp ${PROJECT_SOURCE_DIR}/src/*.c ) add_executable( ${PROJECT_NAME} ${PROJECT_SOURCE} ) include_directories(${PROJECT_SOURCE_DIR}/src) target_link_libraries(${PROJECT_NAME} PRIVATE wujihandcpp) ``` -------------------------------- ### Manage TactileGlove with Context Manager Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Use the context manager to automatically handle connection lifecycle. ```python with TactileGlove() as glove: frame = glove.read_frame() ``` -------------------------------- ### Hand.realtime_controller Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Initializes the real-time control mode for the dexterous hand and returns an IController instance. ```APIDOC ## Hand.realtime_controller(enable_upstream: bool, filter: filter.IFilter) -> IController ### Description Enters real-time control mode. This enables a 16 kHz real-time filter on the motor side to smooth control signals. ### Parameters - **enable_upstream** (bool) - Required - Whether to enable upstream control. - **filter** (filter.IFilter) - Required - The filter object (e.g., filter.LowPass) to smooth target values. ### Returns - **IController** - The controller object used for real-time operations. ``` -------------------------------- ### CMake project configuration Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/example/pdo_test/CMakeLists.txt Defines the project requirements, language standards, and build environment settings. ```cmake cmake_minimum_required(VERSION 3.15) project(wujihand_pdo_test) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Set C++ standard to C++11 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) # Set C standard to C11 set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED True) # Disable GNU extensions set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_C_EXTENSIONS OFF) # Set default build type to Release With Debug Info if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif() # Add compiler options based on compiler if(MSVC) add_compile_options(/W4 /Zc:preprocessor) add_compile_definitions(NOMINMAX _CRT_SECURE_NO_WARNINGS) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") else() # GCC/Clang add_compile_options(-Wall -Wextra -Wpedantic) endif() # Get project sources file(GLOB_RECURSE PROJECT_SOURCE CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.cpp ${PROJECT_SOURCE_DIR}/src/*.c ) add_executable( ${PROJECT_NAME} ${PROJECT_SOURCE} ) include_directories(${PROJECT_SOURCE_DIR}/src) target_link_libraries(${PROJECT_NAME} PRIVATE wujihandcpp) ``` -------------------------------- ### Run smoke test script locally Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/wujihandcpp-deb-smoke-gate.md Execute the smoke test script on an Ubuntu host with or without preinstalled libspdlog-dev. ```bash .github/scripts/smoke_wujihandcpp_deb.sh /path/to/wujihandcpp-*.deb false .github/scripts/smoke_wujihandcpp_deb.sh /path/to/wujihandcpp-*.deb true ``` -------------------------------- ### Enable Debug Logging for Troubleshooting Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/introduction.mdx Set the log level to debug to capture detailed command flow analysis. ```bash export WUJI_LOG_LEVEL=debug python3 your_script.py ``` -------------------------------- ### Configure Interprocedural Optimization Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Enables or disables LTO for the project target based on the WUJIHANDCPP_ENABLE_IPO option. ```cmake option(WUJIHANDCPP_ENABLE_IPO "Enable interprocedural optimization (LTO)" OFF) if(WUJIHANDCPP_ENABLE_IPO) set_target_properties(${PROJECT_NAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE ) endif() ``` -------------------------------- ### Configure Platform-Specific Dependencies Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Handles libusb and threading dependencies for Windows and Unix-like systems. ```cmake if(WIN32) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBUSB REQUIRED libusb-1.0) target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIRS}) target_link_directories(${PROJECT_NAME} PUBLIC "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib") target_link_libraries(${PROJECT_NAME} PUBLIC ${LIBUSB_LIBRARIES}) elseif(UNIX) target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE /usr/include/libusb-1.0) target_link_libraries(${PROJECT_NAME} PUBLIC usb-1.0) find_package(Threads REQUIRED) target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads) endif() ``` -------------------------------- ### Use Context Manager for Real-Time Control Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Using the controller as a context manager ensures that the dexterous hand correctly exits real-time control mode when the block terminates. ```python with hand.realtime_controller( enable_upstream=False, filter=wujihandpy.filter.LowPass(cutoff_freq=2.0), ) as controller: ... # Use controller to perform real-time control ``` -------------------------------- ### CMake Project Configuration Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/example/simple_test/CMakeLists.txt Defines the project requirements, language standards, and build type settings. ```cmake cmake_minimum_required(VERSION 3.15) project(wujihand_simple_test) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Set C++ standard to C++11 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) # Set C standard to C11 set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED True) # Disable GNU extensions set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_C_EXTENSIONS OFF) # Set default build type to Release With Debug Info if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif() ``` -------------------------------- ### Query product serial number Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Retrieves the product tracking number from the device firmware after connection. ```python hand = wujihandpy.Hand() product_sn = hand.get_product_sn() print(product_sn) # e.g. "WJ-DH5-R-2501001" ``` -------------------------------- ### Collect source files Source: https://github.com/wuji-technology/wujihandpy/blob/main/CMakeLists.txt Recursively finds all .cpp and .c files in the src directory. ```cmake # Get project sources file(GLOB_RECURSE PROJECT_SOURCE CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.cpp ${PROJECT_SOURCE_DIR}/src/*.c ) ``` -------------------------------- ### Configure Project Sources and Platform Exclusions Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Collects source files and removes platform-specific files if the system is not Linux. ```cmake file(GLOB_RECURSE PROJECT_SOURCE CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.cpp ${PROJECT_SOURCE_DIR}/src/*.c ) if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") # Tactile transport is Linux-only (termios + poll + /sys/class/tty). # Keep this in sync with the `#if defined(__linux__)` gates in the # public tactile headers. list(REMOVE_ITEM PROJECT_SOURCE ${PROJECT_SOURCE_DIR}/src/device/tactile_glove.cpp ${PROJECT_SOURCE_DIR}/src/device/frame_demuxer.cpp ${PROJECT_SOURCE_DIR}/src/transport/cdc_byte_stream.cpp ${PROJECT_SOURCE_DIR}/src/transport/cdc_transport.cpp ) endif() ``` -------------------------------- ### Configure compiler options Source: https://github.com/wuji-technology/wujihandpy/blob/main/CMakeLists.txt Applies compiler-specific flags for MSVC or GCC/Clang. ```cmake # Add compiler options based on compiler if(MSVC) add_compile_options(/W4 /Zc:preprocessor /EHsc) add_compile_definitions(NOMINMAX _CRT_SECURE_NO_WARNINGS) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") else() # GCC/Clang add_compile_options(-Wall -Wextra -Wpedantic) endif() ``` -------------------------------- ### C++ Bridge Compilation and Execution Source: https://github.com/wuji-technology/wujihandpy/blob/main/bridge/README.md Build instructions and command-line arguments for the C++ implementation of the bridge. ```bash cd bridge/cpp mkdir -p build && cd build cmake .. && cmake --build . -j$(nproc) # Start the bridge (--pub-rate is required) ./wujihand_zenoh_bridge --pub-rate 1000 --log-level info # Full arguments ./wujihand_zenoh_bridge --sn "DEVICE_SN" --pub-rate 1000 --log-level debug ``` -------------------------------- ### Multi-threaded Execution Output Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Sample terminal output demonstrating interleaved reader and writer thread activity. ```text Thread safety check disabled, multi-threaded access enabled [Reader] Position: [[ 0.012 0.003 -0.005 0.008] [ 0.015 0.002 0.007 0.011] [ 0.009 -0.001 0.004 0.006] [ 0.018 0.005 0.012 0.009] [ 0.011 0.004 0.008 0.013]] [Writer] Target: 0.0000 rad [Reader] Position: [[ 0.012 0.003 -0.005 0.008] [ 0.016 0.002 0.008 0.012] [ 0.010 -0.001 0.005 0.007] [ 0.019 0.005 0.013 0.010] [ 0.012 0.004 0.009 0.014]] [Writer] Target: 0.0004 rad [Writer] Target: 0.0016 rad [Reader] Position: [[ 0.012 0.003 -0.005 0.008] [ 0.018 0.002 0.010 0.014] [ 0.012 -0.001 0.007 0.009] [ 0.021 0.005 0.015 0.012] [ 0.014 0.004 0.011 0.016]] ... ``` -------------------------------- ### Run unit tests for wujihandpy Source: https://github.com/wuji-technology/wujihandpy/blob/main/bridge/README.md Executes the test suite for the bridge module using pytest. Ensure the virtual environment is activated before running. ```bash # Unit tests (no hardware required) cd wujihandpy source .venv/bin/activate PYTHONPATH=. python -m pytest tests/test_bridge.py -v ``` -------------------------------- ### Verify USB device connection Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/introduction.mdx Use the lsusb command to confirm the dexterous hand is recognized by the system. ```bash lsusb ``` -------------------------------- ### Configure Logging via Environment Variables Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/introduction.mdx Set log path and level using shell environment variables before executing the Python script. ```bash export WUJI_LOG_PATH=~/logs/wujihand export WUJI_LOG_LEVEL=debug python3 your_script.py ``` -------------------------------- ### Run smoke test in container Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/wujihandcpp-deb-smoke-gate.md Execute the smoke test script within a clean Ubuntu 24.04 container environment. ```bash docker run --rm \ -v "$PWD:$PWD" \ -w "$PWD" \ ubuntu:24.04 \ .github/scripts/smoke_wujihandcpp_deb.sh build/wujihandcpp/wujihandcpp-*.deb false ``` -------------------------------- ### IController Methods Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Methods available on the IController object for real-time interaction. ```APIDOC ## IController.set_joint_target_position(value_array: np.NDArray[np.float64]) -> None ### Description Sends target control values to the motor driver board in a non-blocking manner. ## IController.get_joint_actual_position() -> np.NDArray[np.float64] ### Description Returns the latest cached joint position readings at a 1 kHz rate. ## IController.get_joint_actual_effort() -> np.NDArray[np.float64] ### Description Returns the latest cached joint effort readings (actuation value in current space). ## IController.close() -> None ### Description Exits real-time control mode. Automatically called when using the controller as a context manager. ``` -------------------------------- ### Configure CMake Project and Standards Source: https://github.com/wuji-technology/wujihandpy/blob/main/bridge/cpp/CMakeLists.txt Sets the minimum CMake version, project name, and enforces C++20 standards. ```cmake cmake_minimum_required(VERSION 3.24) project(wujihand_zenoh_bridge LANGUAGES C CXX) # C++20 set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # Export compile_commands.json for tooling set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Default build type: RelWithDebInfo if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif() # Compiler warnings if(NOT MSVC) add_compile_options(-Wall -Wextra -Wpedantic) endif() ``` -------------------------------- ### Connection Management Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/api-reference.mdx Methods for managing the lifecycle and connection state of the tactile glove. ```APIDOC ## Connection Methods ### Methods - `connect() -> bool`: Connect to the device. Returns True on success. - `disconnect() -> None`: Disconnect and stop streaming. - `is_connected() -> bool`: Query the current connection status. - `get_handedness() -> TactileHandedness`: Return the device handedness (LEFT or RIGHT). ``` -------------------------------- ### Link spdlog Library Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Links the spdlog library to the project target, using PUBLIC or PRIVATE visibility based on the build type. ```cmake if(BUILD_STATIC_WUJIHANDCPP) target_link_libraries(${PROJECT_NAME} PUBLIC spdlog::spdlog) else() target_link_libraries(${PROJECT_NAME} PRIVATE spdlog::spdlog) endif() ``` -------------------------------- ### Configure VCPKG toolchain Source: https://github.com/wuji-technology/wujihandpy/blob/main/CMakeLists.txt Sets the CMAKE_TOOLCHAIN_FILE to use vcpkg on Windows if the VCPKG_ROOT environment variable is defined. ```cmake if(WIN32) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) if(DEFINED ENV{VCPKG_ROOT}) set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE FILEPATH "" FORCE) endif() endif() endif() ``` -------------------------------- ### Batch Reading Interface Definitions Source: https://github.com/wuji-technology/wujihandpy/blob/main/docs/external/en/tutorial.mdx Interface signatures for reading matrix-based data from Hand, Finger, and Joint objects. ```python class Hand: def read_(self) -> np.NDArray[datatype]: ... class Finger: def read_(self) -> np.NDArray[datatype]: ... class Joint: def read_(self) -> np.NDArray[datatype]: ... ``` -------------------------------- ### Configure Project Versioning via Git Source: https://github.com/wuji-technology/wujihandpy/blob/main/wujihandcpp/CMakeLists.txt Retrieves the latest Git tag to set the project version, defaulting to 0.0.0 if the tag is unavailable or invalid. ```cmake execute_process( COMMAND git describe --tags --abbrev=0 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_TAG OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE GIT_TAG_RESULT ) # Extract version number if tag format is valid if(GIT_TAG_RESULT EQUAL 0 AND GIT_TAG MATCHES "^v.+$") string(REGEX REPLACE "^v" "" PROJECT_VERSION ${GIT_TAG}) message(STATUS "Using Git tag version: ${PROJECT_VERSION}") else() set(PROJECT_VERSION 0.0.0) message(STATUS "Unable to extract git tag, using version: ${PROJECT_VERSION}") endif() ```