From 0b8b35857dfbdd2a61f28889f18c2301f14fad2a Mon Sep 17 00:00:00 2001 From: abhistjain Date: Sun, 21 Jun 2026 13:52:41 +0530 Subject: [PATCH] MacOS compatibitliy Fixes! --- MACOS_SETUP.md | 266 +++++++++++++++++++++++++++++++++++++++++++++++ python/main.py | 28 +++-- python/readme.md | 86 +++++++++++---- readme.md | 1 + 4 files changed, 353 insertions(+), 28 deletions(-) create mode 100644 MACOS_SETUP.md diff --git a/MACOS_SETUP.md b/MACOS_SETUP.md new file mode 100644 index 0000000..f343f97 --- /dev/null +++ b/MACOS_SETUP.md @@ -0,0 +1,266 @@ +# LEAP Hand — macOS Setup Guide + +Complete end-to-end guide for first-time users on any Mac. No prior experience assumed. + +--- + +## Prerequisites + +Before starting, confirm you have the following: + +| Requirement | How to check | +|---|---| +| macOS 12 or later | Apple menu → About This Mac | +| Python 3 | Open Terminal, run `python3 --version` — if missing, download from [python.org](https://www.python.org/downloads/) | +| Git | Run `git --version` in Terminal — if missing, macOS will offer to install it automatically | +| Data USB cable | Must support data transfer, not charge-only | +| 5V power supply | Required — USB alone does not power the motors | + +--- + +## Step 1 — Open Terminal + +Press `Cmd + Space`, type **Terminal**, press Enter. + +All commands in this guide are typed into Terminal and run by pressing Enter. + +--- + +## Step 2 — Clone the Repository + +Download the LEAP Hand code to your Mac: + +```bash +git clone https://github.com/leap-hand/LEAP_Hand_API.git +``` + +Navigate into the project: + +```bash +cd LEAP_Hand_API/python +``` + +You are now inside `LEAP_Hand_API/python/` — run everything from here. + +--- + +## Step 3 — Create a Virtual Environment + +A virtual environment keeps dependencies isolated from the rest of your Mac. Do this once. + +```bash +python3 -m venv test_env +``` + +Activate it: + +```bash +source test_env/bin/activate +``` + +Your Terminal prompt will now start with `(test_env)` — this means it is active. + +> Every time you open a new Terminal window, re-run `source test_env/bin/activate` before using the hand. + +--- + +## Step 4 — Install Dependencies + +With the environment active: + +```bash +pip install dynamixel_sdk numpy +``` + +Takes about 30 seconds. Do this once only. + +--- + +## Step 5 — Connect the Hardware + +1. Plug in the **5V power supply** first — motors should briefly twitch or LEDs light up +2. Plug the **Micro-USB cable** from the hand into your Mac + - Use a direct port — avoid USB hubs or long extension cables + - Charge-only cables will not work + +--- + +## Step 6 — Find Your Device Name + +macOS automatically assigns each USB device a unique name. Run: + +```bash +ls /dev/tty.usb* +``` + +Expected output: + +``` +/dev/tty.usbserial-FT94CRX2 +``` + +**What each part means:** + +| Part | Meaning | +|---|---| +| `/dev/` | Folder where macOS stores all hardware devices | +| `tty.` | This is a serial communication device | +| `usbserial-` | Connected over USB | +| `FT94CRX2` | Unique ID of your hand's USB chip — **yours will be different** | + +**If nothing shows up:** + +| Possible cause | Fix | +|---|---| +| Hand not powered | Plug in 5V power first, then USB | +| Charge-only cable | Replace with a data-capable cable | +| Loose connection | Unplug, wait 5 seconds, replug | +| Wrong USB port | Try a different port on your Mac | + +--- + +## Step 7 — Grant Serial Port Permission + +macOS blocks serial port access by default. Unlock it: + +```bash +sudo chmod 777 /dev/tty.usbserial-* +``` + +You will be asked for your Mac login password. +The password **will not appear as you type** — no dots, no characters. This is normal. Type it and press Enter. + +> Run this command every time you unplug and replug the hand. Permissions reset on each connection. + +--- + +## Step 8 — Run + +Confirm `(test_env)` is visible in your prompt, then: + +```bash +python main.py +``` + +--- + +## Step 9 — What You Should See + +``` +Position: [3.1385 3.1308 3.1400 3.1400 3.1415 3.1400 3.1385 3.1446 3.1339 3.1339 3.1415 3.1400 3.1385 3.1400 3.1277 3.1369] +Position: [3.1385 3.1308 3.1400 3.1400 3.1415 3.1400 3.1385 3.1446 ...] +``` + +**What those numbers mean:** + +| Value | Meaning | +|---|---| +| 16 numbers | One per motor joint (4 per finger × 4 fingers) | +| Near `3.14` | Finger is in flat, fully open home position | +| Updates at ~20 Hz | Script reads and prints positions continuously | + +Press `Ctrl + C` to stop. + +--- + +## Quick Reference — Every Session + +```bash +# 1. Navigate to the code folder +cd ~/LEAP_Hand_API/python + +# 2. Activate the virtual environment +source test_env/bin/activate + +# 3. Grant USB permission (re-run after every replug) +sudo chmod 777 /dev/tty.usbserial-* + +# 4. Run +python main.py +``` + +--- + +## Troubleshooting + +| Error | Cause | Fix | +|---|---|---| +| `ls: /dev/tty.usb*: No such file or directory` | Mac cannot see the hand | Check power, cable, port. Replug. | +| `RuntimeError: Could not connect to LEAP Hand` | Permission not granted or device replugged | Run `sudo chmod 777 /dev/tty.usbserial-*` | +| `command not found: python` | Wrong command or env not active | Use `python3`, or activate env first | +| `(test_env)` not in prompt | Virtual environment not active | Run `source test_env/bin/activate` | +| `Incorrect status packet!` repeating | macOS USB latency — not a hardware fault | Hand still works. See latency fix below. | +| Motors not moving | No 5V power | Check power supply connection | +| Motors flash red | Overload | Power cycle. Lower `curr_lim` in `main.py`. | +| Motor off by 90° or 180° | Horn mounted wrong | Remount the horn (hardware fix) | + +--- + +## Optional — Fix USB Latency Errors (Advanced) + +The `Incorrect status packet!` warnings are caused by the FTDI USB chip's latency timer defaulting to 16 ms. Reducing it to 1 ms eliminates most errors. + +**Steps:** + +1. Install the [FTDI VCP driver for macOS](https://ftdichip.com/drivers/vcp-drivers/) +2. Find your device ID from Step 6 (the part after `usbserial-`, e.g. `FT94CRX2`) +3. Run: + +```bash +sudo /usr/local/bin/ftdi_latency_timer /dev/tty.usbserial-FT94CRX2 1 +``` + +Replace `FT94CRX2` with your own device ID. + +--- + +## Project Structure + +``` +LEAP_Hand_API/ +├── python/ +│ ├── main.py ← entry point — edit this for your application +│ ├── leap_hand_utils/ +│ │ ├── dynamixel_client.py ← USB motor communication +│ │ └── leap_hand_utils.py ← joint angle conversion helpers +│ └── test_env/ ← virtual environment (do not delete) +├── ros_module/ ← ROS 1 integration +├── ros2_module/ ← ROS 2 integration +├── cpp/ ← C++ API +├── useful_tools/ ← MANO mapping and other utilities +├── MACOS_SETUP.md ← this file +└── readme.md ← general overview +``` + +--- + +## API Reference + +All control happens through the `LeapNode` class in `main.py`. + +### Command methods + +| Method | Convention | Open position | +|---|---|---| +| `set_leap(pose)` | LEAP native | `3.14` rad | +| `set_allegro(pose)` | Allegro compat | `0.0` rad | +| `set_ones(pose)` | Normalized sim | `-1` to `1` | + +### Read methods + +| Method | Returns | +|---|---| +| `read_pos()` | 16 joint positions (radians) | +| `read_vel()` | 16 joint velocities | +| `read_cur()` | 16 motor currents (milliamps) | +| `pos_vel()` | Positions + velocities (faster combined read) | +| `pos_vel_eff_srv()` | Positions + velocities + currents (fastest combined read) | + +### Joint numbering + +``` +Index → 0 (MCP Side) 1 (MCP Fwd) 2 (PIP) 3 (DIP) +Middle → 4 5 6 7 +Ring → 8 9 10 11 +Thumb → 12 13 14 15 +``` diff --git a/python/main.py b/python/main.py index cb4af4b..1f27907 100644 --- a/python/main.py +++ b/python/main.py @@ -1,3 +1,5 @@ +import glob +import platform import numpy as np from leap_hand_utils.dynamixel_client import * @@ -35,16 +37,24 @@ def __init__(self): # For example ls /dev/serial/by-id/* to find your LEAP Hand. Then use the result. # For example: /dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter_FT7W91VW-if00-port0 self.motors = motors = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] - try: - self.dxl_client = DynamixelClient(motors, '/dev/ttyUSB0', 4000000) - self.dxl_client.connect() - except Exception: + # Build candidate port list based on OS to avoid slow timeouts on non-existent ports + if platform.system() == 'Darwin': + candidate_ports = sorted(glob.glob('/dev/tty.usbserial-*') + glob.glob('/dev/tty.usbmodem*')) + elif platform.system() == 'Windows': + candidate_ports = ['COM' + str(i) for i in range(1, 20)] + else: + candidate_ports = ['/dev/ttyUSB' + str(i) for i in range(4)] + self.dxl_client = None + for port in candidate_ports: try: - self.dxl_client = DynamixelClient(motors, '/dev/ttyUSB1', 4000000) - self.dxl_client.connect() + client = DynamixelClient(motors, port, 4000000) + client.connect() + self.dxl_client = client + break except Exception: - self.dxl_client = DynamixelClient(motors, 'COM13', 4000000) - self.dxl_client.connect() + continue + if self.dxl_client is None: + raise RuntimeError('Could not connect to LEAP Hand on any port: ' + str(candidate_ports)) #Enables position-current control mode and the default parameters, it commands a position and then caps the current so the motors don't overload self.dxl_client.sync_write(motors, np.ones(len(motors))*5, 11, 1) self.dxl_client.set_torque_enabled(motors, True) @@ -96,7 +106,7 @@ def main(**kwargs): #Set to an open pose and read the joint angles 33hz leap_hand.set_allegro(np.zeros(16)) print("Position: " + str(leap_hand.read_pos())) - time.sleep(0.03) + time.sleep(0.05) if __name__ == "__main__": main() diff --git a/python/readme.md b/python/readme.md index f8534ac..a6ce9f9 100644 --- a/python/readme.md +++ b/python/readme.md @@ -1,19 +1,67 @@ -## Welcome to the LEAP Hand Python SDK - -#### Install On Ubuntu -- `python -m venv test_env` -- `source test_env/bin/activate` -- `pip install dynamixel_sdk numpy` -- `sudo chmod 777 /dev/serial/by-id/*` -- `python main.py` - -#### Install On Windows -- Use Windows powershell -- We recommend to create a virtual environment and pip install the code: -- `Set-ExecutionPolicy Unrestricted -Scope Process` -- `python -m venv test_env` -- `.\test_env\Scripts\activate.ps1` -- `pip install dynamixel_sdk numpy` -- `python main.py` - -Please see main.py for further details. It should be easy to read. :) +# LEAP Hand Python SDK + +--- + +## Installation + +### Ubuntu + +```bash +python -m venv test_env +source test_env/bin/activate +pip install dynamixel_sdk numpy +sudo chmod 777 /dev/serial/by-id/* +python main.py +``` + +--- + +### Windows + +```powershell +Set-ExecutionPolicy Unrestricted -Scope Process +python -m venv test_env +.\test_env\Scripts\activate.ps1 +pip install dynamixel_sdk numpy +python main.py +``` + +--- + +### macOS + +```bash +python3 -m venv test_env +source test_env/bin/activate +pip install dynamixel_sdk numpy +sudo chmod 777 /dev/tty.usbserial-* +python main.py +``` + +> For a full step-by-step macOS walkthrough — finding your device, permissions, troubleshooting — see [MACOS_SETUP.md](../MACOS_SETUP.md). + +--- + +## Usage + +See `main.py` — it is well commented and easy to follow. + +### Key methods on `LeapNode` + +| Method | Description | +|---|---| +| `set_allegro(pose)` | Command joints in Allegro convention — `0` = fully open | +| `set_leap(pose)` | Command joints in LEAP convention — `3.14` = fully open | +| `set_ones(pose)` | Command joints using normalized `[-1, 1]` range for sim policies | +| `read_pos()` | Read current joint positions (16 values, radians) | +| `read_vel()` | Read current joint velocities | +| `read_cur()` | Read current draw per motor (milliamps) | + +### Joint numbering + +| Finger | Joints (MCP Side, MCP Forward, PIP, DIP) | +|---|---| +| Index | 0, 1, 2, 3 | +| Middle | 4, 5, 6, 7 | +| Ring | 8, 9, 10, 11 | +| Thumb | 12, 13, 14, 15 | diff --git a/readme.md b/readme.md index 34753e0..441cd46 100644 --- a/readme.md +++ b/readme.md @@ -11,6 +11,7 @@ - [ROS API](https://github.com/leap-hand/LEAP_Hand_API/tree/main/ros_module) - [ROS2 API](https://github.com/leap-hand/LEAP_Hand_API/tree/main/ros2_module) - [Useful Tools](https://github.com/leap-hand/LEAP_Hand_API/tree/main/useful_tools) +- **macOS users:** See [MACOS_SETUP.md](MACOS_SETUP.md) for a complete end-to-end setup guide. ---