Device Connection and Selection
This section describes how to connect to dexterous hand devices in wujihandpy and how to filter devices when multiple devices are present.Connection and Filtering Interface Overview
Connecting to a Device
Callwujihandpy.Hand() to create a Hand object and connect to the dexterous hand:
Filtering Devices
When multiple dexterous hands are connected to the system, using the no-argument constructor will result in an error. For example:Select by serial number
Pass theserial_number parameter to identify the device explicitly:
Select by handedness
When the system has exactly one left hand and one right hand connected, theside parameter selects the device directly, without needing to remember any serial number:
side and serial_number are mutually exclusivePass only one of them. Passing both raises ValueError. side accepts only the string "left" or "right". Any other value raises ValueError.Common failure cases:- No hand of the requested side is connected → raises
ConnectionErrorwithNo <side> hand found - Two hands of the same side are connected → raises
ConnectionErrorsuggestingserial_numberinstead
Querying Serial Numbers
The dexterous hand has two types of serial numbers:- USB serial number: An identifier in the USB hardware descriptor, used to distinguish multiple devices during connection. The constructor’s
serial_numberparameter uses this number. - Product serial number: A product tracking number stored in firmware, used for after-sales service and device management.
Querying the USB serial number
Execute the following command in the terminal to view all connected dexterous hands:serial_number parameter.
Querying the product serial number
After connecting to a device, callget_product_sn() to retrieve the product serial number:
Reading Data
This section describes how to read data in synchronous mode.Reading Interface Overview
Callingread_* series functions sends a synchronous read request to the dexterous hand and blocks the current thread until the device returns a result. Function return indicates that the read has completed successfully.
Single Value Reading
When the read level matches the object level, the function returns a single scalar value.hand:
joint:
Batch Reading
When the read level is lower than the object level, the function returns a matrix containing multiple data points.hand (Joint level):
np.NDArray[np.float64], where each row corresponds to a finger and each column corresponds to a joint:
finger:
More Reading Examples
Writing Data
This section describes how to write data in synchronous mode.Writing Interface Overview
Callingwrite_* series functions sends a synchronous write request to the dexterous hand and blocks the current thread until the device returns a result. Function return indicates that the write has completed successfully.
Single Value Writing
When the write level matches the object level, the function uses a single scalar value.Batch Writing
When the write level is lower than the object level, the function can accept either a single scalar value or a matrix containing multiple data points.hand:
Invalid Value Writing
Typically, invalid value writes are handled automatically, such as:- Angles exceeding the valid range are automatically clamped to upper and lower bounds.
- Setting the effort limit of a joint to an invalid value (too large or too small)
More Writing Examples
Real-Time Control
This section describes how to control the dexterous hand in real-time mode in wujihandpy.Real-Time Control Interface Overview
Hand.realtime_controller() puts the dexterous hand into real-time control mode and returns a real-time controller object IController.
After entering real-time control mode, a 16 kHz real-time filter starts on the motor side to smooth control signals into a stable 16 kHz stream.
Filters
The real-time controller requires a filter object (filter.IFilter) to smoothly adjust target values.
The initial value of the filter is automatically set to the actual position of the dexterous hand, so there is no need to worry about initial transition issues.
filter.LowPass(cutoff_freq: float): First-order low-pass filter with adjustable cutoff frequency. When set to a cutoff frequency >= 1 kHz, the filter is disabled.
Context Manager
IController can be used as a context manager:
with block ends, controller.close() is automatically called, ensuring the dexterous hand correctly exits real-time control mode.
Automatic Destruction
If a context manager is not used,IController will automatically call close() during destruction. Due to Python’s garbage collection delay, the dexterous hand may not immediately exit real-time mode. If precise control of exit timing is not needed, you can directly rely on destruction behavior.
Real-Time Writing
set_joint_target_position() function sends target control values to the motor driver board as quickly as possible without blocking.
Real-Time Reading
The motor automatically reports current position and effort at a 1 kHz rate. The following functions are non-blocking and immediately return the latest cached readings.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.Typical use cases:
- Collision detection: a sudden increase in effort indicates joint obstruction
- Load monitoring: calculate output percentage via
effort / effort_limit
Advanced Real-Time Control Examples
Async Read/Write
This section describes how to read/write data in asynchronous mode in wujihandpy.Async Interface Overview
Both read and write interfaces provide asynchronous versions with function names suffixed with_async for performing non-blocking I/O operations in coroutine environments.
await. The event loop is not blocked during the wait. Function return indicates that the operation has completed successfully.
Example
Advanced Async Examples
Multi-Threaded Operations
This section describes how to perform multi-threaded operations in wujihandpy.Multi-Threading Interface Overview
By default, wujihandpy performs thread safety checks on theHand object to prevent data race conditions from concurrent multi-threaded access. If you need to use the same Hand object in a multi-threaded environment, you must first call Hand.disable_thread_safe_check() to disable this check.
Multi-Threading Safety Considerations
- You must call
disable_thread_safe_check()before performing multi-threaded operations on theHandobject - You must use synchronization mechanisms such as
threading.Lockto protect concurrent access to theHandobject - It is recommended to wrap all operations on the
Handobject and its derived objects (such asIController) withwith lock:blocks
Expected Output
After running the example, the terminal will display output similar to the following:- The first line indicates that the thread safety check has been disabled
[Reader]prints the current joint position matrix (5×4, corresponding to 4 joints for each of 5 fingers)[Writer]prints the current target position (cosine wave, starting from 0 and gradually increasing)- The two threads output alternately, and the order may be interleaved (this is normal behavior for multi-threading)
- F1 (thumb) remains stationary, while F2 (index), F3 (middle), F4 (ring), and F5 (pinky) perform periodic flexion-extension movements
Advanced Multi-Threading Examples
USB Disconnect Handling
During operation, the USB connection between the dexterous hand and the computer can drop—for example, when the data cable is unplugged. This section describes how wujihandpy behaves on disconnect and how to catch disconnect exceptions.Disconnect Behavior
On disconnect, wujihandpy raises an exception. Catch it to recover without crashing the process. An operation started after a disconnect raises Python’s built-inConnectionError immediately, without waiting for the 500 ms timeout. Affected interfaces include:
- Synchronous read/write (
read_*/write_*) - Asynchronous read/write (
read_*_async/write_*_async)
An async request already in flight at the moment of disconnect is an exception: it ends with
TimeoutError rather than ConnectionError. If your code runs concurrent async operations, catch their common base class OSError to cover both disconnect and timeout.Catching Disconnect Exceptions
Wrap device operations in atry block and catch ConnectionError to handle disconnects:
ConnectionError, and the program exits normally without crashing.
Disconnect Detection in Real-Time Control Mode
Real-time control loops rely on high-speed PDO data. On disconnect, these interfaces simply stop updating the cache instead of raising an exception. To detect a disconnect in real-time scenarios, periodically call a synchronous read (such asread_input_voltage()) as a probe in a low-speed bypass loop, and catch ConnectionError.
When the real-time controller is used as a context manager, the end of the with block automatically calls close() to clean up. close() also returns safely on disconnect, so no extra handling is needed.