API Reference
1. Field List
This section introduces all available data fields in wujihandpy.
1.1 Hand Layer
This layer contains device-unique general fields, such as handedness, firmware version, etc.
handedness
This field indicates the handedness of the dexterous hand (left/right hand, 0 for right, 1 for left).
- Type:
uint8 - Read/Write: Read-only
firmware_version
This field indicates the version number of the dexterous hand firmware.
- Type:
uint32 - Read/Write: Read-only
firmware_date
This field indicates the compilation date of the dexterous hand firmware.
- Type:
uint32 - Read/Write: Read-only
system_time
This field indicates the system running time of the dexterous hand, in milliseconds.
- Type:
uint32 - Read/Write: Read-only
temperature
This field indicates the current temperature near the wrist of the dexterous hand, in degrees Celsius.
- Type:
float32 - Read/Write: Read-only
input_voltage
This field indicates the measured input voltage of the dexterous hand, in volts.
- Type:
float32 - Read/Write: Read-only
1.2 Joint Layer
This layer contains fields where each joint holds an independent copy, such as enabled status, target position, etc.
joint_firmware_version
This field records the version number of the joint driver board firmware.
- Type:
uint32 - Read/Write: Read-only
joint_firmware_date
This field records the compilation date of the joint driver board firmware.
- Type:
uint32 - Read/Write: Read-only
joint_control_mode
This field is used to set the control mode of the joint.
- Type:
uint16 - Read/Write: Write-only
Note: The mode switches automatically, and this field should not be modified under normal circumstances.
joint_sin_level
This field is used to set the sine wave excitation level of the joint drive.
- Type:
uint16 - Read/Write: Write-only
Note: This field is used for internal debugging and should not be modified under normal circumstances.
joint_effort_limit
This field is used to set the upper effort limit allowed by the joint.
- Type:
float64 - Unit: Ampere (A)
- Default:
1.5 - Valid Range:
0.0 ~ 3.5(values exceeding 3.5 will be clamped to 3.5) - Read/Write: Read-Write
What is Effort?
Effort is the actuation value in current space, filtered before output. It is not the actual measured current—think of it as relative actuation strength, useful for load monitoring and collision detection. See joint_effort for details.
Thermal Protection: Effort Limit is dynamically adjusted based on temperature
To protect the device, the firmware automatically reduces the effective effort limit based on joint temperature:
| Temperature Status | Trigger Condition | Effective Effort Limit |
|---|---|---|
| Normal | Below 85°C | User-set value (default 1.5A) |
| Temperature Warning (Tier 1) | Greater than or equal to 85°C | User-set value × 70% |
| Temperature Alert (Tier 2) | Greater than or equal to 93°C | User-set value × 40% |
Recovery uses a 4°C hysteresis threshold:
- Tier 2 returns to Tier 1 when temperature drops below 89°C
- Tier 1 returns to Normal when temperature drops below 81°C
When the temperature returns to a safe range, the effort limit automatically recovers.
Warning: Modifying Effort Limit may cause device damage
Modifying this parameter changes the maximum output capability of the joint. Users are responsible for any damage caused by adjusting the effort limit.
- Default value of 1.5 is suitable for most applications
- Maximum value is 3.5, values exceeding this will be automatically clamped
- Increasing this value increases joint load capacity but also increases the risk of damage
- Affects joint lifespan and accelerates component wear with unpredictable impact
- May cause irreversible damage to joints under certain operating conditions
- May accelerate thermal protection triggering, reducing continuous operation time
- May cause other serious consequences beyond those listed above
joint_bus_voltage
This field records the current measured bus voltage of the joint.
- Type:
float32 - Read/Write: Read-only
joint_temperature
This field records the current temperature of the joint driver board, in degrees Celsius.
- Type:
float32 - Read/Write: Read-only
joint_reset_error
Writing 1 to this field can clear the error status reported by the joint.
- Type:
uint16 - Read/Write: Write-only
This field can only clear error codes and cannot resolve issues with the dexterous hand. Do not abuse this field, especially do not use it in a loop, as it may cause damage to the dexterous hand.
joint_error_code
This field indicates the current error code of each joint, used for diagnosing operational status.
A value of 0 indicates no abnormality in the joint.
- Type:
uint32 - Read/Write: Read-only
joint_effort
This field provides real-time effort feedback of the joint.
- Type:
float32 - Unit: Ampere (A)
- Read/Write: Read-only
Understanding Effort
Effort is the actuation effort in current space, filtered before output.
- It is not the actual measured current
- It is not the direct output of the current loop
- Should be interpreted as relative actuation strength, suitable for load monitoring and collision detection
Typical use cases:
- Collision detection: sudden increase in effort indicates joint obstruction
- Output monitoring: calculate output percentage via
effort / effort_limit
This field is only accessible through IController.get_joint_actual_effort() in real-time control mode.
joint_enabled
This field controls the enabling and disabling of the joint.
- Type:
bool - Default:
False - Valid Range:
True / False - Read/Write: Write-only
joint_actual_position
This field provides the actual position of the joint.
- Type:
float64 - Read/Write: Read-only
joint_target_position
Writing to this field can issue the target control angle of the joint. If the value exceeds the valid range, it will be automatically clamped to the allowed interval.
- Type:
float64 - Valid Range:
joint_lower_limit ~ joint_upper_limit - Read/Write: Write-only
joint_upper_limit
This field records the upper position limit of the joint.
- Type:
float64 - Read/Write: Read-only
Difference from URDF Joint Range
There are slight precision errors during joint processing, causing the actual physical limits of each joint to vary slightly.
The dexterous hand calibrates the actual limits of each joint at the factory, and the results are written to the joint_upper_limit and joint_lower_limit fields.
Therefore, these two fields record the calibrated actual physical limit values, which are usually slightly larger than the range defined in the URDF file.
The joint range in the URDF file is a theoretical design value, conservatively scaled to account for processing errors, and can be used for simulation or neural network training.
While joint_upper_limit and joint_lower_limit represent the actual measured range of motion.
joint_lower_limit
This field records the lower position limit of the joint.
- Type:
float64 - Read/Write: Read-only
2. API List
2.1 Hand Class
2.1.1 Constructor
-
__init__(serial_number=None, *, side=None, usb_pid=0x2000, usb_vid=0x0483, mask=None) -> NoneInitialize connection to the dexterous hand. Supports specifying the device by serial number, handedness, USB ID, or device mask.serial_number: USB serial number string to select one device exactlyside:"left"or"right". Selects the device by handedness. Mutually exclusive withserial_number(see Tutorial §1.3)
2.1.2 Finger Access
finger(index) -> FingerGet the finger object at the specified index, index range 0-4
2.1.3 Thread Safety Control
disable_thread_safe_check() -> NoneDisable thread safety check to allow concurrent multi-threaded access to the Hand object
Note: After disabling the thread safety check, you must use synchronization mechanisms such as threading.Lock to ensure thread-safe operations.
2.1.4 System Information
Firmware Information
-
read_firmware_version(timeout=0.5) -> uint32Synchronously read firmware version -
read_firmware_version_async(timeout=0.5) -> Awaitable[uint32]Asynchronously read firmware version -
read_firmware_version_unchecked(timeout=0.5) -> NoneNon-blocking read firmware version -
get_firmware_version() -> uint32Get cached firmware version -
read_firmware_date(timeout=0.5) -> uint32Synchronously read firmware compilation date -
read_firmware_date_async(timeout=0.5) -> Awaitable[uint32]Asynchronously read firmware compilation date -
read_firmware_date_unchecked(timeout=0.5) -> NoneNon-blocking read firmware compilation date -
get_firmware_date() -> uint32Get cached firmware compilation date -
read_handedness(timeout=0.5) -> uint8Synchronously read left/right hand identifier (0=right, 1=left) -
read_handedness_async(timeout=0.5) -> Awaitable[uint8]Asynchronously read left/right hand identifier -
read_handedness_unchecked(timeout=0.5) -> NoneNon-blocking read left/right hand identifier -
get_handedness() -> uint8Get cached left/right hand identifier
System Status
-
read_system_time(timeout=0.5) -> uint32Synchronously read system running time (milliseconds) -
read_system_time_async(timeout=0.5) -> Awaitable[uint32]Asynchronously read system running time -
read_system_time_unchecked(timeout=0.5) -> NoneNon-blocking read system running time -
get_system_time() -> uint32Get cached system running time -
read_temperature(timeout=0.5) -> float32Synchronously read palm temperature (Celsius) -
read_temperature_async(timeout=0.5) -> Awaitable[float32]Asynchronously read palm temperature -
read_temperature_unchecked(timeout=0.5) -> NoneNon-blocking read palm temperature -
get_temperature() -> float32Get cached palm temperature -
read_input_voltage(timeout=0.5) -> float32Synchronously read input voltage (volts) -
read_input_voltage_async(timeout=0.5) -> Awaitable[float32]Asynchronously read input voltage -
read_input_voltage_unchecked(timeout=0.5) -> NoneNon-blocking read input voltage -
get_input_voltage() -> float32Get cached input voltage
2.1.5 Joint Data (Batch Read, Returns 4 Array)
Position Data
-
read_joint_actual_position(timeout=0.5) -> NDArray[float64]Synchronously read all joint actual positions (radians) -
read_joint_actual_position_async(timeout=0.5) -> Awaitable[NDArray[float64]]Asynchronously read all joint actual positions -
read_joint_actual_position_unchecked(timeout=0.5) -> NoneNon-blocking read all joint actual positions -
get_joint_actual_position() -> NDArray[float64]Get cached all joint actual positions -
read_joint_upper_limit(timeout=0.5) -> NDArray[float64]Synchronously read all joint upper limit positions (radians) -
read_joint_upper_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]]Asynchronously read all joint upper limit positions -
read_joint_upper_limit_unchecked(timeout=0.5) -> NoneNon-blocking read all joint upper limit positions -
get_joint_upper_limit() -> NDArray[float64]Get cached all joint upper limit positions -
read_joint_lower_limit(timeout=0.5) -> NDArray[float64]Synchronously read all joint lower limit positions (radians) -
read_joint_lower_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]]Asynchronously read all joint lower limit positions -
read_joint_lower_limit_unchecked(timeout=0.5) -> NoneNon-blocking read all joint lower limit positions -
get_joint_lower_limit() -> NDArray[float64]Get cached all joint lower limit positions
Status Monitoring
-
read_joint_bus_voltage(timeout=0.5) -> NDArray[float32]Synchronously read all joint bus voltages (volts) -
read_joint_bus_voltage_async(timeout=0.5) -> Awaitable[NDArray[float32]]Asynchronously read all joint bus voltages -
read_joint_bus_voltage_unchecked(timeout=0.5) -> NoneNon-blocking read all joint bus voltages -
get_joint_bus_voltage() -> NDArray[float32]Get cached all joint bus voltages -
read_joint_temperature(timeout=0.5) -> NDArray[float32]Synchronously read all joint temperatures (Celsius) -
read_joint_temperature_async(timeout=0.5) -> Awaitable[NDArray[float32]]Asynchronously read all joint temperatures -
read_joint_temperature_unchecked(timeout=0.5) -> NoneNon-blocking read all joint temperatures -
get_joint_temperature() -> NDArray[float32]Get cached all joint temperatures -
read_joint_error_code(timeout=0.5) -> NDArray[uint32]Synchronously read all joint error codes -
read_joint_error_code_async(timeout=0.5) -> Awaitable[NDArray[uint32]]Asynchronously read all joint error codes -
read_joint_error_code_unchecked(timeout=0.5) -> NoneNon-blocking read all joint error codes -
get_joint_error_code() -> NDArray[uint32]Get cached all joint error codes
Effort Limit Data
read_joint_effort_limit(timeout=0.5) -> NDArray[float64]Synchronously read all joint effort limits (Ampere)read_joint_effort_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]]Asynchronously read all joint effort limitsread_joint_effort_limit_unchecked(timeout=0.5) -> NoneNon-blocking read all joint effort limitsget_joint_effort_limit() -> NDArray[float64]Get cached all joint effort limits
Firmware Information
-
read_joint_firmware_version(timeout=0.5) -> NDArray[uint32]Synchronously read all joint firmware versions -
read_joint_firmware_version_async(timeout=0.5) -> Awaitable[NDArray[uint32]]Asynchronously read all joint firmware versions -
read_joint_firmware_version_unchecked(timeout=0.5) -> NoneNon-blocking read all joint firmware versions -
get_joint_firmware_version() -> NDArray[uint32]Get cached all joint firmware versions -
read_joint_firmware_date(timeout=0.5) -> NDArray[uint32]Synchronously read all joint firmware build dates -
read_joint_firmware_date_async(timeout=0.5) -> Awaitable[NDArray[uint32]]Asynchronously read all joint firmware build dates -
read_joint_firmware_date_unchecked(timeout=0.5) -> NoneNon-blocking read all joint firmware build dates -
get_joint_firmware_date() -> NDArray[uint32]Get cached all joint firmware build dates
2.1.6 Joint Control (Supports Single Value or 4 Array)
Enable Control
write_joint_enabled(value, timeout=0.5) -> NoneSynchronously set all joint enable stateswrite_joint_enabled(value_array, timeout=0.5) -> NoneSynchronously batch set each joint enable statewrite_joint_enabled_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint enable stateswrite_joint_enabled_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint enable statewrite_joint_enabled_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint enable stateswrite_joint_enabled_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint enable state
Position Control
write_joint_target_position(value, timeout=0.5) -> NoneSynchronously set all joint target positions (radians)write_joint_target_position(value_array, timeout=0.5) -> NoneSynchronously batch set each joint target positionwrite_joint_target_position_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint target positionswrite_joint_target_position_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint target positionwrite_joint_target_position_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint target positionswrite_joint_target_position_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint target position
Mode Control
write_joint_control_mode(value, timeout=0.5) -> NoneSynchronously set all joint control modeswrite_joint_control_mode(value_array, timeout=0.5) -> NoneSynchronously batch set each joint control modewrite_joint_control_mode_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint control modeswrite_joint_control_mode_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint control modewrite_joint_control_mode_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint control modeswrite_joint_control_mode_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint control mode
Effort Limit Configuration
Warning: Modifying effort limit may cause device damage, see field description for details.
write_joint_effort_limit(value, timeout=0.5) -> NoneSynchronously set all joint effort limits (Ampere)write_joint_effort_limit(value_array, timeout=0.5) -> NoneSynchronously batch set each joint effort limitwrite_joint_effort_limit_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint effort limitswrite_joint_effort_limit_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint effort limitwrite_joint_effort_limit_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint effort limitswrite_joint_effort_limit_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint effort limit
Other Parameter Configuration
-
write_joint_sin_level(value, timeout=0.5) -> NoneSynchronously set all joint sine wave excitation levels -
write_joint_sin_level(value_array, timeout=0.5) -> NoneSynchronously batch set each joint sine wave excitation level -
write_joint_sin_level_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint sine wave excitation levels -
write_joint_sin_level_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint sine wave excitation level -
write_joint_sin_level_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint sine wave excitation levels -
write_joint_sin_level_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint sine wave excitation level -
write_joint_reset_error(value, timeout=0.5) -> NoneSynchronously reset all joint errors -
write_joint_reset_error(value_array, timeout=0.5) -> NoneSynchronously batch reset each joint error -
write_joint_reset_error_async(value, timeout=0.5) -> Awaitable[None]Asynchronously reset all joint errors -
write_joint_reset_error_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch reset each joint error -
write_joint_reset_error_unchecked(value, timeout=0.5) -> NoneNon-blocking reset all joint errors -
write_joint_reset_error_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch reset each joint error
2.1.7 Real-time Controller
realtime_controller(enable_upstream, filter) -> IControllerCreate a real-time controller for high-frequency control scenarios
2.2 Finger Class
2.2.1 Joint Access
joint(index) -> JointGet the joint object at the specified index, index range 0-3
2.2.2 Joint Data (Returns 4-Element Array)
Position Data
-
read_joint_actual_position(timeout=0.5) -> NDArray[float64]Synchronously read all joint actual positions of the finger (radians) -
read_joint_actual_position_async(timeout=0.5) -> Awaitable[NDArray[float64]]Asynchronously read all joint actual positions of the finger -
read_joint_actual_position_unchecked(timeout=0.5) -> NoneNon-blocking read all joint actual positions of the finger -
get_joint_actual_position() -> NDArray[float64]Get cached all joint actual positions of the finger -
read_joint_upper_limit(timeout=0.5) -> NDArray[float64]Synchronously read all joint upper limit positions of the finger (radians) -
read_joint_upper_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]]Asynchronously read all joint upper limit positions of the finger -
read_joint_upper_limit_unchecked(timeout=0.5) -> NoneNon-blocking read all joint upper limit positions of the finger -
get_joint_upper_limit() -> NDArray[float64]Get cached all joint upper limit positions of the finger -
read_joint_lower_limit(timeout=0.5) -> NDArray[float64]Synchronously read all joint lower limit positions of the finger (radians) -
read_joint_lower_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]]Asynchronously read all joint lower limit positions of the finger -
read_joint_lower_limit_unchecked(timeout=0.5) -> NoneNon-blocking read all joint lower limit positions of the finger -
get_joint_lower_limit() -> NDArray[float64]Get cached all joint lower limit positions of the finger
Status Monitoring
-
read_joint_bus_voltage(timeout=0.5) -> NDArray[float32]Synchronously read all joint bus voltages of the finger (volts) -
read_joint_bus_voltage_async(timeout=0.5) -> Awaitable[NDArray[float32]]Asynchronously read all joint bus voltages of the finger -
read_joint_bus_voltage_unchecked(timeout=0.5) -> NoneNon-blocking read all joint bus voltages of the finger -
get_joint_bus_voltage() -> NDArray[float32]Get cached all joint bus voltages of the finger -
read_joint_temperature(timeout=0.5) -> NDArray[float32]Synchronously read all joint temperatures of the finger (Celsius) -
read_joint_temperature_async(timeout=0.5) -> Awaitable[NDArray[float32]]Asynchronously read all joint temperatures of the finger -
read_joint_temperature_unchecked(timeout=0.5) -> NoneNon-blocking read all joint temperatures of the finger -
get_joint_temperature() -> NDArray[float32]Get cached all joint temperatures of the finger -
read_joint_error_code(timeout=0.5) -> NDArray[uint32]Synchronously read all joint error codes of the finger -
read_joint_error_code_async(timeout=0.5) -> Awaitable[NDArray[uint32]]Asynchronously read all joint error codes of the finger -
read_joint_error_code_unchecked(timeout=0.5) -> NoneNon-blocking read all joint error codes of the finger -
get_joint_error_code() -> NDArray[uint32]Get cached all joint error codes of the finger
Effort Limit Data
read_joint_effort_limit(timeout=0.5) -> NDArray[float64]Synchronously read all joint effort limits of the finger (Ampere)read_joint_effort_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]]Asynchronously read all joint effort limits of the fingerread_joint_effort_limit_unchecked(timeout=0.5) -> NoneNon-blocking read all joint effort limits of the fingerget_joint_effort_limit() -> NDArray[float64]Get cached all joint effort limits of the finger
Firmware Information
-
read_joint_firmware_version(timeout=0.5) -> NDArray[uint32]Synchronously read all joint firmware versions of the finger -
read_joint_firmware_version_async(timeout=0.5) -> Awaitable[NDArray[uint32]]Asynchronously read all joint firmware versions of the finger -
read_joint_firmware_version_unchecked(timeout=0.5) -> NoneNon-blocking read all joint firmware versions of the finger -
get_joint_firmware_version() -> NDArray[uint32]Get cached all joint firmware versions of the finger -
read_joint_firmware_date(timeout=0.5) -> NDArray[uint32]Synchronously read all joint firmware build dates of the finger -
read_joint_firmware_date_async(timeout=0.5) -> Awaitable[NDArray[uint32]]Asynchronously read all joint firmware build dates of the finger -
read_joint_firmware_date_unchecked(timeout=0.5) -> NoneNon-blocking read all joint firmware build dates of the finger -
get_joint_firmware_date() -> NDArray[uint32]Get cached all joint firmware build dates of the finger
2.2.3 Joint Control (Supports Single Value or 4-Element Array)
Enable Control
write_joint_enabled(value, timeout=0.5) -> NoneSynchronously set all joint enable states of the fingerwrite_joint_enabled(value_array, timeout=0.5) -> NoneSynchronously batch set each joint enable state of the fingerwrite_joint_enabled_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint enable states of the fingerwrite_joint_enabled_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint enable state of the fingerwrite_joint_enabled_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint enable states of the fingerwrite_joint_enabled_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint enable state of the finger
Position Control
write_joint_target_position(value, timeout=0.5) -> NoneSynchronously set all joint target positions of the finger (radians)write_joint_target_position(value_array, timeout=0.5) -> NoneSynchronously batch set each joint target position of the fingerwrite_joint_target_position_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint target positions of the fingerwrite_joint_target_position_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint target position of the fingerwrite_joint_target_position_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint target positions of the fingerwrite_joint_target_position_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint target position of the finger
Mode Control
write_joint_control_mode(value, timeout=0.5) -> NoneSynchronously set all joint control modes of the fingerwrite_joint_control_mode(value_array, timeout=0.5) -> NoneSynchronously batch set each joint control mode of the fingerwrite_joint_control_mode_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint control modes of the fingerwrite_joint_control_mode_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint control mode of the fingerwrite_joint_control_mode_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint control modes of the fingerwrite_joint_control_mode_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint control mode of the finger
Effort Limit Configuration
Warning: Modifying effort limit may cause device damage, see field description for details.
write_joint_effort_limit(value, timeout=0.5) -> NoneSynchronously set all joint effort limits of the finger (Ampere)write_joint_effort_limit(value_array, timeout=0.5) -> NoneSynchronously batch set each joint effort limit of the fingerwrite_joint_effort_limit_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint effort limits of the fingerwrite_joint_effort_limit_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint effort limit of the fingerwrite_joint_effort_limit_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint effort limits of the fingerwrite_joint_effort_limit_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint effort limit of the finger
Other Parameter Configuration
-
write_joint_sin_level(value, timeout=0.5) -> NoneSynchronously set all joint sine wave excitation levels of the finger -
write_joint_sin_level(value_array, timeout=0.5) -> NoneSynchronously batch set each joint sine wave excitation level of the finger -
write_joint_sin_level_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set all joint sine wave excitation levels of the finger -
write_joint_sin_level_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch set each joint sine wave excitation level of the finger -
write_joint_sin_level_unchecked(value, timeout=0.5) -> NoneNon-blocking set all joint sine wave excitation levels of the finger -
write_joint_sin_level_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch set each joint sine wave excitation level of the finger -
write_joint_reset_error(value, timeout=0.5) -> NoneSynchronously reset all joint errors of the finger -
write_joint_reset_error(value_array, timeout=0.5) -> NoneSynchronously batch reset each joint error of the finger -
write_joint_reset_error_async(value, timeout=0.5) -> Awaitable[None]Asynchronously reset all joint errors of the finger -
write_joint_reset_error_async(value_array, timeout=0.5) -> Awaitable[None]Asynchronously batch reset each joint error of the finger -
write_joint_reset_error_unchecked(value, timeout=0.5) -> NoneNon-blocking reset all joint errors of the finger -
write_joint_reset_error_unchecked(value_array, timeout=0.5) -> NoneNon-blocking batch reset each joint error of the finger
2.3 Joint Class
2.3.1 Joint Data (Returns Single Value)
Position Data
-
read_joint_actual_position(timeout=0.5) -> float64Synchronously read joint actual position (radians) -
read_joint_actual_position_async(timeout=0.5) -> Awaitable[float64]Asynchronously read joint actual position -
read_joint_actual_position_unchecked(timeout=0.5) -> NoneNon-blocking read joint actual position -
get_joint_actual_position() -> float64Get cached joint actual position -
read_joint_upper_limit(timeout=0.5) -> float64Synchronously read joint upper limit position (radians) -
read_joint_upper_limit_async(timeout=0.5) -> Awaitable[float64]Asynchronously read joint upper limit position -
read_joint_upper_limit_unchecked(timeout=0.5) -> NoneNon-blocking read joint upper limit position -
get_joint_upper_limit() -> float64Get cached joint upper limit position -
read_joint_lower_limit(timeout=0.5) -> float64Synchronously read joint lower limit position (radians) -
read_joint_lower_limit_async(timeout=0.5) -> Awaitable[float64]Asynchronously read joint lower limit position -
read_joint_lower_limit_unchecked(timeout=0.5) -> NoneNon-blocking read joint lower limit position -
get_joint_lower_limit() -> float64Get cached joint lower limit position
Status Monitoring
-
read_joint_bus_voltage(timeout=0.5) -> float32Synchronously read joint bus voltage (volts) -
read_joint_bus_voltage_async(timeout=0.5) -> Awaitable[float32]Asynchronously read joint bus voltage -
read_joint_bus_voltage_unchecked(timeout=0.5) -> NoneNon-blocking read joint bus voltage -
get_joint_bus_voltage() -> float32Get cached joint bus voltage -
read_joint_temperature(timeout=0.5) -> float32Synchronously read joint temperature (Celsius) -
read_joint_temperature_async(timeout=0.5) -> Awaitable[float32]Asynchronously read joint temperature -
read_joint_temperature_unchecked(timeout=0.5) -> NoneNon-blocking read joint temperature -
get_joint_temperature() -> float32Get cached joint temperature -
read_joint_error_code(timeout=0.5) -> uint32Synchronously read joint error code -
read_joint_error_code_async(timeout=0.5) -> Awaitable[uint32]Asynchronously read joint error code -
read_joint_error_code_unchecked(timeout=0.5) -> NoneNon-blocking read joint error code -
get_joint_error_code() -> uint32Get cached joint error code
Effort Limit Data
read_joint_effort_limit(timeout=0.5) -> float64Synchronously read joint effort limit (Ampere)read_joint_effort_limit_async(timeout=0.5) -> Awaitable[float64]Asynchronously read joint effort limitread_joint_effort_limit_unchecked(timeout=0.5) -> NoneNon-blocking read joint effort limitget_joint_effort_limit() -> float64Get cached joint effort limit
Firmware Information
-
read_joint_firmware_version(timeout=0.5) -> uint32Synchronously read joint firmware version -
read_joint_firmware_version_async(timeout=0.5) -> Awaitable[uint32]Asynchronously read joint firmware version -
read_joint_firmware_version_unchecked(timeout=0.5) -> NoneNon-blocking read joint firmware version -
get_joint_firmware_version() -> uint32Get cached joint firmware version -
read_joint_firmware_date(timeout=0.5) -> uint32Synchronously read joint firmware build date -
read_joint_firmware_date_async(timeout=0.5) -> Awaitable[uint32]Asynchronously read joint firmware build date -
read_joint_firmware_date_unchecked(timeout=0.5) -> NoneNon-blocking read joint firmware build date -
get_joint_firmware_date() -> uint32Get cached joint firmware build date
2.3.2 Joint Control (Single Value)
Enable Control
write_joint_enabled(value, timeout=0.5) -> NoneSynchronously set joint enable statewrite_joint_enabled_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set joint enable statewrite_joint_enabled_unchecked(value, timeout=0.5) -> NoneNon-blocking set joint enable state
Position Control
write_joint_target_position(value, timeout=0.5) -> NoneSynchronously set joint target position (radians)write_joint_target_position_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set joint target positionwrite_joint_target_position_unchecked(value, timeout=0.5) -> NoneNon-blocking set joint target position
Mode Control
write_joint_control_mode(value, timeout=0.5) -> NoneSynchronously set joint control modewrite_joint_control_mode_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set joint control modewrite_joint_control_mode_unchecked(value, timeout=0.5) -> NoneNon-blocking set joint control mode
Effort Limit Configuration
Warning: Modifying effort limit may cause device damage, see field description for details.
write_joint_effort_limit(value, timeout=0.5) -> NoneSynchronously set joint effort limit (Ampere)write_joint_effort_limit_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set joint effort limitwrite_joint_effort_limit_unchecked(value, timeout=0.5) -> NoneNon-blocking set joint effort limit
Other Parameter Configuration
-
write_joint_sin_level(value, timeout=0.5) -> NoneSynchronously set joint sine wave excitation level -
write_joint_sin_level_async(value, timeout=0.5) -> Awaitable[None]Asynchronously set joint sine wave excitation level -
write_joint_sin_level_unchecked(value, timeout=0.5) -> NoneNon-blocking set joint sine wave excitation level -
write_joint_reset_error(value, timeout=0.5) -> NoneSynchronously reset joint error -
write_joint_reset_error_async(value, timeout=0.5) -> Awaitable[None]Asynchronously reset joint error -
write_joint_reset_error_unchecked(value, timeout=0.5) -> NoneNon-blocking reset joint error
2.4 IController Interface
2.4.1 Context Management
__enter__() -> IControllerContext manager entry supporting with statement__exit__(arg0, arg1, arg2) -> NoneContext manager exit supporting with statementclose() -> NoneManually close the real-time controller
2.4.2 Real-time Data Retrieval
get_joint_actual_position() -> NDArray[float64]Real-time get all joint actual positions (high-frequency read, returns 4 array)get_joint_actual_effort() -> NDArray[float64]Real-time get all joint efforts (high-frequency read, returns 4 array, unit: Ampere)
Effort Real-time Feedback
get_joint_actual_effort() returns the actuation effort in current space, filtered before output.
- Only available in real-time controller mode
- Suitable for collision detection and load monitoring
- Calculate output percentage via
effort / effort_limit
2.4.3 Real-time Data Setting
set_joint_target_position(value_array) -> NoneReal-time set all joint target positions (high-frequency write, parameter is 4 array)
2.5 Array Shape Description
- Hand Class Batch Operations: Returns/receives arrays shaped
{5, 4}(5 fingers × 4 joints) - Finger Class Batch Operations: Returns/receives arrays shaped
{4}(4 joints) - Joint Class Operations: Returns/receives scalar values (single joint)
- IController Operations: Handles arrays shaped
{5, 4}for high-frequency real-time control
2.6 Operation Mode Description
- Synchronous Operations (
read_*,write_*): Blocking operations, ensure read/write success - Asynchronous Operations (
*_async): Returns Awaitable objects, supports await syntax - Non-blocking Operations (
*_unchecked): Returns immediately, does not guarantee operation success, suitable for high-frequency scenarios - Cached Retrieval (
get_*): Gets the most recently read data, does not block
3. Tactile Sensing Glove
The Tactile Sensing Glove is an optional tactile perception accessory for Wuji Hand. The SDK provides device connection and data reading through the TactileGlove class.
3.1 Core Classes
TactileHandedness
Handedness enum:
TactileHandedness.LEFT(0): Left handTactileHandedness.RIGHT(1): Right hand
TactileFrame
Single-frame tactile data with the following properties:
| Property | Type | Description |
|---|---|---|
hand | TactileHandedness | Left or right hand |
sequence | int (uint16) | Frame counter, wraps at 65535 |
timestamp_ms | int (uint32) | Milliseconds since device boot |
pressure | numpy.ndarray (24, 32) float32 | Pressure matrix |
Pressure value semantics:
- Range:
[0.0, 1.0]—0.0= no contact,1.0= maximum contact NaNmarks an invalid cell (not part of any finger or palm region). Skip such cells withnumpy.isnan()
CRC validation happens inside the SDK before delivery, so callers never see a bad-CRC frame.
TactileGlove
Connection and data reading for the Tactile Sensing Glove.
Constructor:
TactileGlove(serial_number: str | None = None)serial_number is the USB device serial number. Pass None to auto-discover the only attached device. Raises if more than one is present.
3.2 Device Connection
Auto-discovery
from wujihandpy import TactileGlove
glove = TactileGlove()
glove.connect() # Returns True on successSpecific device
glove = TactileGlove(serial_number="ABC123")
glove.connect()Context Manager
with TactileGlove() as glove:
frame = glove.read_frame()Connection management methods
connect() -> bool: Connect to device. ReturnsTrueon successdisconnect() -> None: Disconnect and stop streamingis_connected() -> bool: Query connection statusget_handedness() -> TactileHandedness: Return device handedness
3.3 Data Reading
Blocking read
import numpy as np
frame = glove.read_frame(timeout_ms=100)
print(f"Sequence: {frame.sequence}")
print(f"Pressure shape: {frame.pressure.shape}") # (24, 32)
print(f"Max pressure: {np.nanmax(frame.pressure)}")- Performs frame synchronization and CRC validation internally
- Raises
RuntimeErroron timeout - Raises
RuntimeErroron disconnect
Streaming callback
import numpy as np
def on_frame(frame):
print(f"Seq={frame.sequence}, Max={np.nanmax(frame.pressure)}")
glove.start_streaming(on_frame)
# ... application logic ...
glove.stop_streaming()- Callback invoked on the internal reader thread. Must not block
- Register a disconnect callback with
set_disconnect_callback(fn)to react to USB drop. Streaming stops automatically afterward start_streamingandread_frameare mutually exclusive- All blocking operations release the Python GIL
3.4 Configuration and Diagnostics
| Method | Description |
|---|---|
set_sample_rate_hz(hz) | Set sample rate, 1–120 Hz |
get_sample_rate_hz() -> int | Read current sample rate |
set_streaming(enable) | Toggle the device data-frame stream |
get_streaming_enabled() -> bool | Read the streaming-enabled flag |
set_disconnect_callback(callback) | Register a Callable[[], None] invoked once when the USB device drops off the bus |
get_device_info() -> TactileDeviceInfo | Read serial, hw_revision, fw_version |
get_fw_build() -> TactileFwBuild | Read firmware build (git_short_sha) |
get_diagnostics() -> TactileDiagnostics | Read counters: uptime_ms, frame_count, crc_err_count, dropout_count, usb_reset_count |
reset_counters() | Zero the diagnostic counters |
get_device_time() -> TactileDeviceTime | Read device monotonic clock (device_monotonic_ns) |
sync_host_epoch(host_unix_ns) -> TactileSyncResult | Exchange host UTC nanoseconds for the device clock at the same moment |
reset_device() | Soft reset. The device drops off the bus and re-enumerates—call connect() again afterward |
enter_bootloader(magic) | Jump to bootloader for OTA. Pass TACTILE_BOOTLOADER_MAGIC |
3.5 Exception Handling
| Exception | Trigger |
|---|---|
RuntimeError | read_frame timeout, USB device disconnected during read, read_frame called when not connected, or start_streaming called while already streaming |
TactileError | Device-reported protocol error from a command (bad length, bad CRC, unknown command, bad payload) |
3.6 Complete Example
Covers the full TactileGlove workflow: auto-discovery and device info queries, host/device clock synchronization, sample-rate configuration, blocking reads and streaming callbacks, data saving, and diagnostic counters. reset_device and enter_bootloader close the current connection (device reset or jump to bootloader, respectively) and appear only as trailing comments.
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)Example Output
[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/<timestamp>_<pid>.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.npy3.7 Use with Wuji Hand
Hand and TactileGlove use independent USB transports and threads, so the two APIs compose in the same process with no extra coordination. The frame callback fires on the glove's streaming consumer thread, while joint commands run on the thread that calls them. This pattern enables closed-loop scenarios where tactile feedback drives motion—for example, scaling joint amplitude by the current pressure peak.
Reference example: example/joint_with_tactile.py.
Runtime behavior:
- Prints
Squeeze the glove to dampen the motion. Ctrl-C to exit.on startup - After joints are enabled, fingers F2–F5 perform periodic flexion–extension in a 100 Hz realtime loop. F1 (thumb) stays still
- Higher tactile pressure peaks shrink the motion amplitude: a relaxed glove yields the full sweep (about 0.4 rad), while a firm squeeze drives amplitude toward 0
- Releasing the glove restores the full amplitude
- Ctrl-C stops streaming, disables joints, and disconnects the glove cleanly
3.8 Protocol Reference
This section is for advanced developers who need to parse tactile data frames directly. The TactileGlove class handles all protocol details automatically.
Data frame format
The Tactile Sensing Glove streams fixed 3088-byte frames at up to 120 Hz over USB CDC (virtual serial port). The effective rate is set via set_sample_rate_hz (1–120 Hz):
| Offset | Length | Field | Type | Description |
|---|---|---|---|---|
| 0–1 | 2 | Header | u8[2] | Fixed 0xAA 0x55 |
| 2–3 | 2 | Frame length | u16 LE | Fixed 3088 |
| 4 | 1 | Hand | u8 | 0x00 = left, 0x01 = right |
| 5–7 | 3 | Padding | u8[3] | Aligns tactile data to a 4-byte boundary |
| 8–3079 | 3072 | Tactile data | f32[24][32] LE | Pressure matrix, row-major, [0.0, 1.0]. NaN = invalid cell |
| 3080–3081 | 2 | Sequence | u16 LE | Frame counter, wraps at 65535 |
| 3082–3085 | 4 | Timestamp | u32 LE | Milliseconds since device boot |
| 3086–3087 | 2 | CRC16 | u16 LE | CRC16-CCITT checksum |
CRC16-CCITT
- Polynomial:
0x1021, initial value:0xFFFF - Coverage: bytes
[2, 3086)(frame length through timestamp, 3084 bytes) - Excludes header and CRC field
Frame synchronization algorithm
- Scan byte-by-byte for header
0xAA 0x55 - Read bytes 2–3, verify frame length == 3088
- Read remaining bytes to complete the frame
- Compute CRC16 and compare
- CRC failure: discard, resume scanning from header_position + 1
- CRC success: parse and deliver frame
0xAA 0x55 may appear within pressure data (false headers). CRC validation is the only reliable way to distinguish real headers from false ones.
Reference Implementation
The Python snippet below parses a single frame straight from a raw byte stream (for example, a CDC serial port opened with pyserial) without depending on the SDK. Use it as a starting point for a custom parser.
import struct
import numpy as np
FRAME_LEN = 3088
HEADER = b"\xAA\x55"
def crc16_ccitt(data: bytes, init: int = 0xFFFF) -> int:
crc = init
for b in data:
crc ^= b << 8
for _ in range(8):
if crc & 0x8000:
crc = ((crc << 1) ^ 0x1021) & 0xFFFF
else:
crc = (crc << 1) & 0xFFFF
return crc
def parse_frame(buf: bytes) -> dict:
"""Validate a 3088-byte candidate and decode its fields."""
if len(buf) != FRAME_LEN or buf[:2] != HEADER:
raise ValueError("bad header or length")
if struct.unpack_from("<H", buf, 2)[0] != FRAME_LEN:
raise ValueError("length field mismatch")
if crc16_ccitt(buf[2:3086]) != struct.unpack_from("<H", buf, 3086)[0]:
raise ValueError("CRC mismatch")
return {
"hand": "L" if buf[4] == 0 else "R",
"pressure": np.frombuffer(buf, dtype="<f4", count=24 * 32, offset=8).reshape(24, 32),
"sequence": struct.unpack_from("<H", buf, 3080)[0],
"timestamp_ms": struct.unpack_from("<I", buf, 3082)[0],
}
def read_frame(port) -> dict:
"""Resync on a raw byte stream and return the next valid frame."""
buf = bytearray()
while True:
chunk = port.read(FRAME_LEN)
if not chunk:
raise TimeoutError("stream exhausted")
buf.extend(chunk)
i = buf.find(HEADER)
while 0 <= i and i + FRAME_LEN <= len(buf):
try:
frame = parse_frame(bytes(buf[i:i + FRAME_LEN]))
del buf[: i + FRAME_LEN]
return frame
except ValueError:
i = buf.find(HEADER, i + 1)
# Keep tail so a header spanning chunk boundaries survives.
if len(buf) > FRAME_LEN - 1:
del buf[: len(buf) - (FRAME_LEN - 1)]Usage:
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}")4. logging Module
The wujihandpy.logging submodule configures SDK logging at runtime. For default behavior, environment variables, and troubleshooting tips, see Getting Started - Get Logs.
4.1 Level Enum
| Member | Value | Meaning |
|---|---|---|
Level.Trace | 0 | Most verbose, includes raw TX/RX bytes |
Level.Debug | 1 | Debug details |
Level.Info | 2 | Key events (default) |
Level.Warn | 3 | Warnings |
Level.Error | 4 | Errors |
Level.Critical | 5 | Critical errors |
Level.Off | 6 | Disable logging |
4.2 Functions
set_log_path
Set the log file directory. Must be called before the logger initializes (in the common Hand flow that means before creating the first Hand. flush() or any other logging API also triggers logger initialization), otherwise it raises RuntimeError.
def set_log_path(value: str) -> Noneset_log_level
Set the log level. Safe to call at any time and takes effect immediately.
def set_log_level(value: Level) -> Noneset_log_to_file
Toggle file output. The log file is opened only if WUJI_LOG_TO_FILE is true at logger initialization, and after init this function can temporarily disable or restore file output. If file output was disabled at init, calling set_log_to_file(True) later won't re-enable it.
def set_log_to_file(value: bool) -> Noneset_log_to_console
Toggle terminal output (stdout / stderr). Safe to call at any time and takes effect immediately.
def set_log_to_console(value: bool) -> Noneflush
Force pending log entries to file and terminal. The SDK calls this automatically on process exit, so manual calls are rarely needed.
def flush() -> None