Welcome to Tilburg Hand’s documentation!

This is the documentation for the basic software and Python interface for the Tilburg Hand. The Tilburg Hand is a new low-cost, Open Source robot hand designed for research on dexterous manipulation with Deep Reinforcement Learning.

Contents
Installation and basic usage

The tilburg-hand library contains the basic Python interface to communicate with the Tilburg Hand via USB, together with examples and a handy motor control GUI application (tilburg_hand_motorgui).
Installation
The library can be installed via PyPI
$ pip install tilburg-hand
or directly from source (GitHub), running the following from inside the main directory:
$ pip install -e .
The most critical dependency of the library is the dynamixel-sdk Python wrapper, available from PyPi or from Dynamixel . On Linux, the following enables access to USB devices without need of root privileges:
$ sudo usermod -a -G dialout $USER
Usage
Using the library is fairly simple. You need to instantiate a Tilburg HandMotorInterface() object, and connect() to the motors.
Before using the Tilburg Hand, you should generate a configuration file. A default configuration file (including the range of each joints and their zero position) is generated automatically using the included motor control GUI (see below).
The first time the GUI is opened, you will be asked to configure either a Left or Right Tilburg Hand. The default configuration file will be saved in your user folder. For example, on Linux it will be saved as $HOME/tilburg_hand/calibration.json . This is the directory that the Tilburg Hand library will look for the configuration file in, by default.
The motor GUI, like the Tilburg Hand library, rely on a second config.json configuration file (included by default within the installed Python library, in the subfolder tilburg_hand/motorgui/config.json). The config.json file includes default names for the USB port to use and/or the VID/PID of the U2D2 interface board (for automatic detection of the USB port).
from tilburg_hand import TilburgHandMotorInterface, Unit
motors = TilburgHandMotorInterface()
ret = motors.connect()
pos_normalized = [0.9, 0.7, 0.2, 0.5, 0.0, 0, 0, 0.9, 0.0, 0, 0, 0.1, 0.9, 0.85, 0.85, 0, 0, 0]
motors.set_pos_vector(pos_normalized, unit=Unit.NORMALIZED)
sleep(3)
motors.goto_zero_position()
sleep(1)
motors.disconnect()
Example code is included with the Python package under the examples subfolder, and at the accompanying repository tilburg-hand-contrib .
Motor GUI
A simple motor-control GUI is installed together with the tilburg_hand library, and can be run as:
$ tilburg_hand_motorgui

Customizing Motors Settings
Motors settings like the individual PID gains can be set using the Dynamixel Wizard 2 application . Set the baudrate to 4000000 and the protocol version to 2.0.
USB Latency
In order to reduce delays between the computer and the Tilburg Hand, you should enable low-latency settings for USB on your computer. This is done automatically by the TilburgHandMotorInterface() object on Linux. For Windows, please follow the instructions at usb_latency_setting .
Simulation and URDF/Mujoco
Model files for the Tilburg Hand are available in both urdf and Mujoco format, to make it easier to use them in a wide range of simulation software. The files are included in the src/tilburg_hand_urdf_mujoco subfolder, as robot.urdf and robot.xml. The stl files are included in the same directory.
Examples
Included examples
demo_all_joints.py: activates all joints of the Tilburg Hand in sequence to show the full range of movements. Two modes can be selected in code, slow and fast.
hand_pose_demo.py: sets the Tilburg Hand into a fixed pose.
Tilburg Hand demonstrations repository
Additional code, demos (including a webcam-based hand tracking controller), and materials are available in the tilburg-hand-contrib repository.
TilburgHandMotorInterface
test
- class tilburg_hand.motor_interface.Finger(value)
Bases:
IntEnum
Constants to identify the position of each joint in the motor vectors, for example to identify individual joints from the vector returned by get_encoder_vector(), or to select individual joints in the vector passed to set_position_vector().
- THUMB_IP = 0
- THUMB_MCP = 1
- THUMB_ABD = 2
- THUMB_CMC = 3
- INDEX_DIP = 4
- INDEX_PIP = 5
- INDEX_MCP = 6
- INDEX_ABD = 7
- MIDDLE_DIP = 8
- MIDDLE_PIP = 9
- MIDDLE_MCP = 10
- MIDDLE_ABD = 11
- RING_DIP = 12
- RING_PIP = 13
- RING_MCP = 14
- RING_ABD = 15
- class tilburg_hand.motor_interface.Wrist(value)
Bases:
IntEnum
An enumeration.
- PITCH = 16
- YAW = 17
- class tilburg_hand.motor_interface.OperatingMode(value)
Bases:
IntEnum
Constants to identify the 3 control modes for the motor. The default mode is POSITION, which acts as servo control with a pre-defined PID controlled. More details at https://emanual.robotis.com/docs/en/dxl/x/xl330-m288/#operating-mode .
- POSITION = 3
- CURRENT = 0
- CURRENT_BASED_POSITION = 5
- class tilburg_hand.motor_interface.Unit(value)
Bases:
IntEnum
Constants to identify the units used in each function.
- RAW = 1
raw values directly read/written in the Dynamixel motors.
- Type:
RAW
- NORMALIZED = 2
values clipped in [0, 1], automatically mapped to the full range of the joints. 0 corresponds to ‘joint open/extended’, while 1 corresponds to ‘joint closed’. For abduction-adduction joints (lateral finger movement)), 0.5 is the middle position, 0 is movement towards the thumb (regardless of hand left/right), and 1 is movement away from the thumb.
- Type:
NORMALIZED
- DEGREES = 3
values are expressed in degrees, within the valid range for each joint. Low values extend the joint while high value flex/close the joint.
- Type:
DEGREES
- DEG_PER_SECOND = 4
angular velocity reading in degrees/s.
- Type:
DEG_PER_SECOND
- REV_PER_MINUTE = 5
angular velocity reading in revolutions/min.
- Type:
REV_PER_MINUTE
- class tilburg_hand.motor_interface.TilburgHandMotorInterface(config_file=None, calibration_file=None, hand_orientation='right', default_unit=Unit.NORMALIZED, verbose=False)
Bases:
object
Main Python interface for the Tilburg Hand.
Example:
from tilburg_hand import TilburgHandMotorInterface, Unit motors = TilburgHandMotorInterface() ret = motors.connect() pos_normalized = [0.9, 0.7, 0.2, 0.5, 0.0, 0, 0, 0.9, 0.0, 0, 0, 0.1, 0.9, 0.85, 0.85, 0, 0, 0] motors.set_pos_vector(pos_normalized, unit=Unit.NORMALIZED) sleep(3) motors.goto_zero_position() sleep(1) motors.disconnect()
- connect()
Start a connection to the Tilburg Hand motors.
If connection is successful and the calibration file does not contain initial default motor positions (motor_calib_zero_pos_ticks), the default initial position is filled in with the position of the motors at startup.
The method also sets useful fields such as self.connected (True/False), and `self.motor_enabled’ (list of booleans for each motor, depending on whether each motor was detected or not).
- Returns:
1 on successful connection, -1 on error.
- Return type:
int
- disconnect()
Closes the connection to the Tilburg Hand motors. Please note that this is required, or else the motors will remain in Torque-enabled mode.
- load_calibration(calibration_file)
- save_calibration(calibration_file)
- check_enabled_motors()
Detect which motors are connected.
- Returns:
list of booleans for each motor, identifying which motors were detected.
- Return type:
list
- set_operating_mode(mode=OperatingMode.POSITION)
Set the desired operating mode. You must call this function with motor torque off, i.e., temporarily setting set_torques(False).
- Parameters:
mode (OperatingMode, optional) –
one of OperatingMode modes. Allowed modes are:
OperatingMode.POSITION: control using set_pos_vector and set_pos_single OperatingMode.CURRENT: control using set_current_vector OperatingMode.CURRENT_BASED_POSITION: control using both position and current
- set_pos_vector(positions, unit=None, margin_pct=0.05)
Set the position of all motors at the same time (the vector must have 16 or 18 components).
- Parameters:
positions (list) – vector with a position value for each motor
unit (Unit, optional) – unit of the values in the position vector (Unit.RAW, Unit.NORMALIZED, Unit.DEGREES). If None, the default unit is used, as selected in the constructor.
margin_pct – if normalized positions are chosen, then values from [0,1] are renormalized to [margin_pct, 1-margin_pct] to decrease the likelihood of self collisions.
0 corresponds to open, 1 to closed (e.g., in flex joints)
- set_pos_single(motor_id, position, unit=None, margin_pct=0.05)
Set the position of a single motors.
- Parameters:
motor_id (int) – id of the motor to control (Finger.x)
position (int) – position value for each motor
unit (Unit, optional) – unit of the values in the position vector (Unit.RAW, Unit.NORMALIZED, Unit.DEGREES). If None, the default unit is used, as selected in the constructor.
margin_pct – if normalized positions are chosen, then values from [0,1] are renormalized to [margin_pct, 1-margin_pct] to decrease the likelihood of self collisions.
0 corresponds to open, 1 to closed (e.g., in flex joints)
- get_encoder_vector(unit=None)
Get the position of all motors at the same time.
- Parameters:
unit (Unit, optional) – unit of the values in the position vector (Unit.RAW, Unit.NORMALIZED, Unit.DEGREES). If None, the default unit is used, as selected in the constructor.
- Returns:
the encoder values for all motors, converted to the desired unit.
- Return type:
int or float
- get_encoder_single(motor_id, unit=None)
Get the position of a single motor.
- Parameters:
motor_id (int) – id of the motor (Finger.x)
unit (Unit, optional) – unit of the value for the position (Unit.RAW, Unit.NORMALIZED, Unit.DEGREES). If None, the default unit is used, as selected in the constructor.
- Returns:
the encoder value for the motor with motor_id, converted to the desired unit.
- Return type:
int or float
- set_current_vector(currents)
Set the target current/force for all motors at the same time (the vector must have 16 or 18 components).
- Parameters:
currents (list) – vector with a current value for each motor (raw in [-1700, 1700], roughly corresponding to 1mA per tick).
- set_current_single(motor_id, current)
Set the target current/force for a single motor.
- Parameters:
motor_id (int) – id of the motor (Finger.x)
current (int) – vector with a current value for the selected motor (raw in [-1700, 1700], roughly corresponding to 1mA per tick).
- get_current_vector()
Get the current measurement (roughly indicative of torque) for all motors at the same time. This can be used to detect torque/forces applied to each motor.
- Returns:
the value of current measurements for all motors (roughly corresponding to 1mA per tick).
- Return type:
int
- get_velocity_vector(unit=None)
Get the velocity of all motors at the same time.
- Parameters:
unit (Unit, optional) – unit of the values in the position vector (Unit.RAW, Unit.DEG_PER_SECOND, Unit.REV_PER_MINUTE). If None, RAW is selected. Raw values are ~ 0.229 rev/min (1.374 deg/s) per tick.
- Returns:
the velocity values for all motors, converted to the desired unit.
- Return type:
int or float
- get_temperature_single(motor_id)
Get the temperature reading for a single motor, expressed in degrees Celsius.
- Parameters:
motor_id (int) – id of the motor (Finger.x)
- set_torques(value=True)
Turn on/off the torque for all motors. Note that this is called automatically in connect()/disconnect(). Motors will ignore movement commands if torque is not enabled.
- Parameters:
value (boolean or int) – True/False to enable/disable
- set_leds(value=True)
Turn on/off the LEDs for all motors. Note that this is called automatically in connect()/disconnect().
- Parameters:
value (boolean or int) – True/False to enable/disable
- goto_zero_position()
Utility method to move all motors to their default zero position.