Tendon-driven dexterous hand physics/control abstraction layer
Project description
Tendon-Driven Dexterous Hand Library
A Python library for simulating, controlling, and training tendon-driven dexterous hands.
This library does NOT include any robot model (URDF/STL). You must provide your own URDF file.
Design Philosophy
- Motor-centric API: The library exposes motor commands, not joint targets.
- Physics abstraction first: Tendon routing, tension, and compliance are modeled in the core library.
- Simulator-agnostic core: PyBullet is an adapter, not a dependency of the physics core.
- Composable architecture: Finger, tendon, joint, actuator, hand, and controller are layered.
- Deterministic by default: Same input reproduces same output.
Architecture
User / Policy / RL
↓
Controller Layer
↓
Hand Model API
↓
Actuation Model (motor → tendon displacement / tension)
↓
Tendon Physics Layer (routing, friction, elasticity)
↓
Joint Response Layer (torque, stiffness, limits, coupling)
↓
Simulation Adapter (PyBullet / dummy backend / real hardware)
Installation
From source (development)
git clone https://github.com/YOUR_USERNAME/tendon-hand.git
cd tendon-hand
pip install -e ".[all]"
From PyPI
pip install tendon-hand # core only
pip install "tendon-hand[sim]" # core + PyBullet simulation
pip install "tendon-hand[rl]" # core + RL dependencies
pip install "tendon-hand[all]" # everything
Quick Start
1. Pure control (no simulator)
from tendon_hand.control.hand_controller import HandController
ctrl = HandController()
# Open / close poses
ctrl.open_pose()
ctrl.close_pose()
# Per-finger control
ctrl.set_index(m1=4.0, m2=3.0, m3=-0.3)
# Read joint targets
targets = ctrl.get_joint_targets()
print(targets["finger_2_link3_joint"]) # rad
2. PyBullet simulation (provide your own URDF)
from tendon_hand.control.hand_controller import HandController
from tendon_hand.sim.pybullet_adapter import PyBulletHandAdapter
ctrl = HandController()
# You must provide your own URDF file
sim = PyBulletHandAdapter(
hand=ctrl.hand,
urdf_path="/path/to/your/hand.urdf",
gui=True,
)
sim.reset()
# Close hand
ctrl.close_pose()
sim.apply_joint_targets(ctrl.get_joint_targets())
sim.step(120)
# Read actual joint angles
states = sim.get_joint_states()
pos, vel = states["finger_2_link3_joint"]
3. Transmission model (motor → joint mapping)
from tendon_hand.core.models.transmission import CascadeTransmissionModel
import numpy as np
model = CascadeTransmissionModel()
# 17-D normalized motor action → 22-D joint targets
action = np.zeros(17)
action[3] = 1.0 # index_m1 full flex
action[4] = 0.8 # index_m2 partial flex
action[5] = 0.5 # index_m3 knuckle outward
targets = model.map(action)
4. Gymnasium RL environment
import gymnasium as gym
import tendon_hand.env.gym_env # registers the env
# You must provide your own URDF file
env = gym.make(
"TendonHand-v1",
render_mode="human",
urdf_path="/path/to/your/hand.urdf",
)
obs, info = env.reset()
for _ in range(100):
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
if terminated or truncated:
obs, info = env.reset()
Demos
All demos require you to provide your own URDF via --urdf:
# Precise angle control (shows cable-driven cascade mechanics)
python examples/demo_precise_angle.py --urdf /path/to/your/hand.urdf index --no-gui
# Full hand demo
python examples/demo_hand.py --urdf /path/to/your/hand.urdf sim
# Finger-by-finger sign verification
python examples/demo_finger_test.py --urdf /path/to/your/hand.urdf all --no-gui
Cable-Driven Cascade Mechanics
This is an underactuated tendon-driven hand. You cannot independently set each joint angle.
Motor1 (Tendon-1): distal → middle → proximal → palm
Motor2 (Tendon-2): middle (distal follows) → proximal → palm
Final angle : max(motor1_effect, motor2_effect) per joint
Key constraint: To get proximal movement, distal and middle must already be at their limits.
See examples/demo_precise_angle.py for a step-by-step demonstration.
Project Structure
tendon-hand/
├── src/tendon_hand/
│ ├── core/ # Physics-agnostic hand model
│ ├── control/ # High-level controller API
│ ├── sim/ # PyBullet adapter
│ ├── env/ # Gymnasium RL environment
│ ├── io/ # Config loading
│ └── utils/ # Math utilities + asset resolver
├── examples/ # Demo scripts (require your own URDF)
├── tests/ # pytest suite
└── pyproject.toml
No robot models are included. The library is designed to work with any tendon-driven hand URDF that follows the expected joint naming convention.
Development
# Run tests (core tests do not require a URDF)
pytest tests/ -v
# Run simulation tests (provide your own URDF)
pytest tests/test_gym_env.py -v # requires URDF in your local assets/
# Format code
black src/ tests/ examples/
ruff check src/ tests/ examples/
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tendon_hand-0.1.1.tar.gz.
File metadata
- Download URL: tendon_hand-0.1.1.tar.gz
- Upload date:
- Size: 27.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2e97bcb80ec828558e7d4ac11fb128b194c0c1d402ef64c933a6af965336870
|
|
| MD5 |
6d9818a5638d72331b677b1fbe1a68c4
|
|
| BLAKE2b-256 |
ae5c22a2b61ac4c693297bdf16d65a68cfab9222b4d0f13dc71ae3d2fe918047
|
File details
Details for the file tendon_hand-0.1.1-py3-none-any.whl.
File metadata
- Download URL: tendon_hand-0.1.1-py3-none-any.whl
- Upload date:
- Size: 30.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94de0d6bf04211a7f35783945becf7c083e035d3d57acc28ffd1d228e05a88d4
|
|
| MD5 |
ff17588b1ee54dae048395bd4cfc3fb3
|
|
| BLAKE2b-256 |
843ed4933b8329e627b012b1fbb25a17b95f02205ecbcdbbc436efe173851695
|