mirror of
https://github.com/huggingface/lerobot.git
synced 2026-05-15 16:49:55 +00:00
542 lines
14 KiB
Plaintext
542 lines
14 KiB
Plaintext
# OpenArms Robot
|
|
|
|
OpenArms is a 7 DOF robotic arm with a gripper, designed by [Enactic, Inc.](https://www.enactic.com/) It uses Damiao motors controlled via CAN bus communication and MIT control mode for smooth, precise motion.
|
|
|
|
## Hardware Overview
|
|
|
|
- **7 DOF per arm** (14 DOF total for dual arm setup)
|
|
- **1 gripper per arm** (2 grippers total)
|
|
- **Damiao motors** with 4 different types:
|
|
- **DM8009** (DM-J8009P-2EC) for shoulders (J1, J2) - high torque
|
|
- **DM4340** for shoulder rotation and elbow (J3, J4)
|
|
- **DM4310** (DM-J4310-2EC V1.1) for wrist (J5, J6, J7) and gripper (J8)
|
|
- **24V power supply** required
|
|
- **CAN interface device**:
|
|
- **Linux**: Any SocketCAN-compatible adapter
|
|
- **macOS**: CANable, PEAK PCAN-USB, or Kvaser USBcan
|
|
- Proper CAN wiring (CANH, CANL, 120Ω termination)
|
|
|
|
|
|
## Motor Configuration
|
|
|
|
Each arm has the following motor configuration based on the [OpenArm setup guide](https://docs.openarm.dev/software/setup/):
|
|
|
|
| Joint | Motor | Motor Type | Sender CAN ID | Receiver ID | Description |
|
|
|-------|-------|------------|---------------|-------------|-------------|
|
|
| J1 | joint_1 | DM8009 | 0x01 | 0x11 | Shoulder pan |
|
|
| J2 | joint_2 | DM8009 | 0x02 | 0x12 | Shoulder lift |
|
|
| J3 | joint_3 | DM4340 | 0x03 | 0x13 | Shoulder rotation |
|
|
| J4 | joint_4 | DM4340 | 0x04 | 0x14 | Elbow flex |
|
|
| J5 | joint_5 | DM4310 | 0x05 | 0x15 | Wrist roll |
|
|
| J6 | joint_6 | DM4310 | 0x06 | 0x16 | Wrist pitch |
|
|
| J7 | joint_7 | DM4310 | 0x07 | 0x17 | Wrist rotation |
|
|
| J8 | gripper | DM4310 | 0x08 | 0x18 | Gripper |
|
|
|
|
For dual arm setups, the left arm uses IDs 0x09-0x10 for joints 1-8 with the same motor types.
|
|
|
|
## Quick Start (macOS)
|
|
|
|
If you're on macOS, here's the fastest way to get started:
|
|
|
|
```bash
|
|
# 1. Install LeRobot with OpenArms dependencies
|
|
pip install -e ".[openarms]"
|
|
|
|
# 2. Find your USB-CAN adapter
|
|
ls /dev/cu.usbmodem*
|
|
|
|
# 3. Test communication
|
|
python3 -c "
|
|
import can
|
|
bus = can.interface.Bus(channel='/dev/cu.usbmodem00000000050C1', interface='slcan', bitrate=1000000)
|
|
print('✓ CAN interface connected')
|
|
bus.shutdown()
|
|
"
|
|
```
|
|
|
|
Then use this configuration:
|
|
|
|
```python
|
|
from lerobot.robots.openarms import OpenArmsFollower
|
|
from lerobot.robots.openarms.config_openarms_follower import OpenArmsFollowerConfig
|
|
|
|
config = OpenArmsFollowerConfig(
|
|
port="/dev/cu.usbmodem00000000050C1", # Your adapter
|
|
can_interface="auto", # Auto-detects slcan for /dev/* ports
|
|
is_dual_arm=True,
|
|
)
|
|
|
|
robot = OpenArmsFollower(config)
|
|
robot.connect()
|
|
```
|
|
|
|
## Prerequisites
|
|
|
|
### Software Requirements
|
|
|
|
**Linux:**
|
|
- Ubuntu 22.04/24.04 (or any Linux with SocketCAN)
|
|
- Python 3.8+
|
|
- `can-utils` and `iproute2` packages
|
|
- LeRobot with OpenArms dependencies
|
|
|
|
```bash
|
|
# Install system dependencies
|
|
sudo apt install can-utils iproute2
|
|
|
|
# Install LeRobot with OpenArms support
|
|
pip install -e ".[openarms]"
|
|
```
|
|
|
|
**macOS:**
|
|
- macOS 12+ (Monterey or later)
|
|
- Python 3.8+
|
|
- LeRobot with OpenArms dependencies
|
|
|
|
```bash
|
|
# Install LeRobot with OpenArms support (includes python-can)
|
|
pip install -e ".[openarms]"
|
|
```
|
|
|
|
The `openarms` extra installs:
|
|
- `python-can>=4.2.0` - CAN bus communication library (supports SocketCAN on Linux and SLCAN on macOS)
|
|
|
|
:::tip
|
|
If you've already installed LeRobot and want to add OpenArms support, run:
|
|
```bash
|
|
pip install -e ".[openarms]"
|
|
```
|
|
:::
|
|
|
|
## Setup Guide
|
|
|
|
### Step 1: Motor ID Configuration
|
|
|
|
**IMPORTANT**: Before using the robot, motors must be configured with the correct CAN IDs.
|
|
|
|
Refer to the [OpenArm Motor ID Configuration Guide](https://docs.openarm.dev/software/setup/motor-id) for detailed instructions using the Damiao Debugging Tools on Windows.
|
|
|
|
Key points:
|
|
- Each motor needs a unique **Sender CAN ID** (0x01-0x08 for first arm)
|
|
- Each motor needs a unique **Receiver/Master ID** (0x11-0x18 for first arm)
|
|
- Use the Damiao Debugging Tools to set these IDs
|
|
- For dual arm setups, use 0x09-0x10 for the second arm
|
|
|
|
### Step 2: Setup CAN Interface
|
|
|
|
Configure your CAN interface as described in the [OpenArm CAN Setup Guide](https://docs.openarm.dev/software/setup/can-setup):
|
|
|
|
#### Linux (SocketCAN)
|
|
|
|
```bash
|
|
# Find your CAN interface
|
|
ip link show
|
|
|
|
# Setup CAN 2.0 at 1 Mbps (standard)
|
|
sudo ip link set can0 down
|
|
sudo ip link set can0 type can bitrate 1000000
|
|
sudo ip link set can0 up
|
|
|
|
# Verify configuration
|
|
ip link show can0
|
|
```
|
|
|
|
#### macOS
|
|
|
|
macOS doesn't have native SocketCAN support.
|
|
|
|
**Use SLCAN (Serial Line CAN)**
|
|
|
|
For USB-CAN adapters that support SLCAN protocol (like CANable):
|
|
|
|
```bash
|
|
# Install python-can if not already installed
|
|
pip install python-can
|
|
|
|
# The adapter will appear as a serial device
|
|
ls /dev/cu.usbmodem*
|
|
|
|
# Use with python-can slcan interface
|
|
# Example: /dev/cu.usbmodem14201
|
|
```
|
|
|
|
In your code, specify the slcan interface:
|
|
|
|
```python
|
|
from lerobot.robots.openarms.config_openarms_follower import OpenArmsFollowerConfig
|
|
|
|
config = OpenArmsFollowerConfig(
|
|
port="/dev/cu.usbmodem14201", # Your USB-CAN adapter
|
|
can_interface="slcan", # Will auto-detect if set to "auto"
|
|
)
|
|
```
|
|
|
|
### Step 3: Test Motor Communication
|
|
|
|
**On Linux:**
|
|
|
|
Test basic communication as described in the [OpenArm Motor Test Guide](https://docs.openarm.dev/software/setup/configure-test):
|
|
|
|
```bash
|
|
# Terminal 1: Monitor CAN traffic
|
|
candump can0
|
|
|
|
# Terminal 2: Enable motor 1
|
|
cansend can0 001#FFFFFFFFFFFFFFFC
|
|
|
|
# Expected response on Terminal 1:
|
|
# can0 011 [8] XX XX XX XX XX XX XX XX
|
|
|
|
# Disable motor 1
|
|
cansend can0 001#FFFFFFFFFFFFFFFD
|
|
```
|
|
|
|
**On macOS:**
|
|
|
|
Testing is done differently since you'll use serial-based adapters:
|
|
|
|
```bash
|
|
# Find your USB-CAN adapter
|
|
ls /dev/cu.usbmodem*
|
|
|
|
# Example output: /dev/cu.usbmodem00000000050C1
|
|
|
|
# Test with Python directly (can-utils don't work on macOS)
|
|
python3 -c "
|
|
import can
|
|
bus = can.interface.Bus(channel='/dev/cu.usbmodem00000000050C1', interface='slcan', bitrate=1000000)
|
|
msg = can.Message(arbitration_id=0x01, data=[0xFF]*7+[0xFC])
|
|
bus.send(msg)
|
|
response = bus.recv(timeout=1.0)
|
|
if response:
|
|
print(f'✓ Motor responded: ID 0x{response.arbitration_id:02X}')
|
|
else:
|
|
print('✗ No response')
|
|
bus.shutdown()
|
|
"
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Basic Setup
|
|
|
|
**On Linux:**
|
|
|
|
```python
|
|
from lerobot.robots.openarms import OpenArmsFollower
|
|
from lerobot.robots.openarms.config_openarms_follower import OpenArmsFollowerConfig
|
|
|
|
# Configure for dual arm setup
|
|
config = OpenArmsFollowerConfig(
|
|
port="can0",
|
|
can_interface="socketcan", # Or "auto" for auto-detection
|
|
id="openarms_dual",
|
|
is_dual_arm=True,
|
|
)
|
|
|
|
robot = OpenArmsFollower(config)
|
|
robot.connect()
|
|
```
|
|
|
|
**On macOS:**
|
|
|
|
```python
|
|
from lerobot.robots.openarms import OpenArmsFollower
|
|
from lerobot.robots.openarms.config_openarms_follower import OpenArmsFollowerConfig
|
|
|
|
# Find your USB-CAN adapter first
|
|
# ls /dev/cu.usbmodem*
|
|
|
|
config = OpenArmsFollowerConfig(
|
|
port="/dev/cu.usbmodem14201", # Your adapter's serial port
|
|
can_interface="slcan", # Or "auto" for auto-detection
|
|
id="openarms_dual",
|
|
is_dual_arm=True,
|
|
)
|
|
|
|
robot = OpenArmsFollower(config)
|
|
robot.connect()
|
|
```
|
|
|
|
### Calibration
|
|
|
|
On first use, you'll need to calibrate the robot:
|
|
|
|
```python
|
|
robot.calibrate()
|
|
```
|
|
|
|
The calibration process will:
|
|
1. Disable torque on all motors
|
|
2. Ask you to position arms in **hanging position with grippers closed**
|
|
3. Set this as the zero position
|
|
4. Ask you to move each joint through its full range
|
|
5. Record min/max positions for each joint
|
|
6. Save calibration to file
|
|
|
|
### Reading Observations
|
|
|
|
The robot provides comprehensive state information:
|
|
|
|
```python
|
|
observation = robot.get_observation()
|
|
|
|
# Observation includes for each motor:
|
|
# - {motor_name}.pos: Position in degrees
|
|
# - {motor_name}.vel: Velocity in degrees/second
|
|
# - {motor_name}.torque: Motor torque
|
|
# - {camera_name}: Camera images (if configured)
|
|
|
|
print(f"Right arm joint 1 position: {observation['right_joint_1.pos']:.1f}°")
|
|
print(f"Right arm joint 1 velocity: {observation['right_joint_1.vel']:.1f}°/s")
|
|
print(f"Right arm joint 1 torque: {observation['right_joint_1.torque']:.3f} N·m")
|
|
```
|
|
|
|
### Sending Actions
|
|
|
|
```python
|
|
# Send target positions (in degrees)
|
|
action = {
|
|
"right_joint_1.pos": 45.0,
|
|
"right_joint_2.pos": -30.0,
|
|
# ... all joints
|
|
"right_gripper.pos": 45.0, # Half-closed
|
|
}
|
|
|
|
actual_action = robot.send_action(action)
|
|
```
|
|
|
|
### Gripper Control
|
|
|
|
```python
|
|
# Open gripper
|
|
robot.open_gripper(arm="right")
|
|
|
|
# Close gripper
|
|
robot.close_gripper(arm="right")
|
|
```
|
|
|
|
## Safety Features
|
|
|
|
### 1. Maximum Relative Target
|
|
|
|
Limits how far a joint can move in a single command to prevent sudden movements:
|
|
|
|
```python
|
|
config = OpenArmsFollowerConfig(
|
|
port="can0",
|
|
# Limit all joints to 10 degrees per command
|
|
max_relative_target=10.0,
|
|
|
|
# Or set per-motor limits
|
|
max_relative_target={
|
|
"right_joint_1": 15.0, # Slower moving joint
|
|
"right_joint_2": 10.0,
|
|
"right_gripper": 5.0, # Very slow gripper
|
|
}
|
|
)
|
|
```
|
|
|
|
**How it works**: If current position is 50° and you command 80°, with `max_relative_target=10.0`, the robot will only move to 60° in that step.
|
|
|
|
### 2. Torque Limits
|
|
|
|
Control maximum torque output, especially important for grippers and teleoperation:
|
|
|
|
```python
|
|
config = OpenArmsFollowerConfig(
|
|
port="can0",
|
|
# Gripper torque limit (fraction of motor's max torque)
|
|
gripper_torque_limit=0.5, # 50% of max torque
|
|
)
|
|
```
|
|
|
|
Lower torque limits prevent damage when gripping delicate objects.
|
|
|
|
### 3. MIT Control Gains
|
|
|
|
Control responsiveness and stability via PID-like gains:
|
|
|
|
```python
|
|
config = OpenArmsFollowerConfig(
|
|
port="can0",
|
|
position_kp=10.0, # Position gain (higher = more responsive)
|
|
position_kd=0.5, # Velocity damping (higher = more damped)
|
|
)
|
|
```
|
|
|
|
**Guidelines**:
|
|
- **For following (robot)**: Higher gains for responsiveness
|
|
- `position_kp=10.0`, `position_kd=0.5`
|
|
- **For teleoperation (leader)**: Lower gains or disable torque for manual movement
|
|
- `manual_control=True` (torque disabled)
|
|
|
|
### 4. Velocity Limits
|
|
|
|
Velocity limits are enforced by the Damiao motors based on motor type. For DM4310:
|
|
- Max velocity: 30 rad/s ≈ 1718°/s
|
|
|
|
The motors will automatically limit velocity to safe values.
|
|
|
|
## Teleoperation
|
|
|
|
### Leader Arm Setup
|
|
|
|
The leader arm is moved manually (torque disabled) to generate commands:
|
|
|
|
```python
|
|
from lerobot.teleoperators.openarms import OpenArmsLeader
|
|
from lerobot.teleoperators.openarms.config_openarms_leader import OpenArmsLeaderConfig
|
|
|
|
config = OpenArmsLeaderConfig(
|
|
port="can1", # Separate CAN interface for leader
|
|
id="openarms_leader",
|
|
manual_control=True, # Torque disabled for manual movement
|
|
is_dual_arm=True,
|
|
)
|
|
|
|
leader = OpenArmsLeader(config)
|
|
leader.connect()
|
|
|
|
# Read current position as action
|
|
action = leader.get_action()
|
|
# action contains positions for all joints in degrees
|
|
```
|
|
|
|
### Safety Considerations for Teleoperation
|
|
|
|
1. **Use separate CAN interfaces** for leader and follower to avoid conflicts
|
|
2. **Enable max_relative_target** on follower to smooth abrupt movements
|
|
3. **Lower torque limits** on follower to prevent damage from tracking errors
|
|
4. **Test with one arm** before enabling dual arm teleoperation
|
|
5. **Have emergency stop** ready (power switch or CAN disable)
|
|
|
|
```python
|
|
# Recommended follower config for teleoperation
|
|
follower_config = OpenArmsFollowerConfig(
|
|
port="can0",
|
|
max_relative_target=5.0, # Small steps for smooth following
|
|
gripper_torque_limit=0.3, # Low torque for safety
|
|
position_kp=5.0, # Lower gains for gentler following
|
|
position_kd=0.3,
|
|
)
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Motors Not Responding
|
|
|
|
**Linux:**
|
|
1. **Check power supply**: 24V with sufficient current
|
|
2. **Verify CAN interface**: `ip link show can0` should show "UP"
|
|
3. **Test with cansend**: Follow [motor test guide](https://docs.openarm.dev/software/setup/configure-test)
|
|
4. **Check motor IDs**: Use Damiao Debugging Tools to verify IDs
|
|
5. **Check termination**: 120Ω resistor should be enabled on CAN interface
|
|
|
|
**macOS:**
|
|
1. **Check power supply**: 24V with sufficient current
|
|
2. **Find adapter**: `ls /dev/cu.usbmodem*` should show your device
|
|
3. **Test connection**: Use Python script above to test communication
|
|
4. **Check motor IDs**: Use Damiao Debugging Tools on Windows
|
|
5. **Verify drivers**: Ensure USB-CAN adapter drivers are installed
|
|
6. **Try different baudrate**: Some adapters default to different rates
|
|
|
|
### macOS-Specific Issues
|
|
|
|
**"No such interface" error:**
|
|
```python
|
|
# Try auto-detection
|
|
config.can_interface = "auto"
|
|
|
|
# Or explicitly list available interfaces
|
|
import can
|
|
print(can.detect_available_configs())
|
|
```
|
|
|
|
**Permission denied on `/dev/cu.*`:**
|
|
```bash
|
|
# Add user to dialout group (if applicable)
|
|
sudo dscl . -append /Groups/_dialout GroupMembership $USER
|
|
|
|
# Or run with sudo (not recommended)
|
|
sudo python your_script.py
|
|
```
|
|
|
|
**Adapter not showing up:**
|
|
```bash
|
|
# Check USB devices
|
|
system_profiler SPUSBDataType
|
|
|
|
# Reinstall python-can
|
|
pip install --upgrade --force-reinstall python-can
|
|
```
|
|
|
|
### Motor Shaking/Unstable
|
|
|
|
- **Lower control gains**: Reduce `position_kp` and `position_kd`
|
|
- **Check calibration**: Re-run calibration procedure
|
|
- **Verify power**: Insufficient current can cause instability
|
|
- **Check mechanical**: Loose connections, binding, or damaged components
|
|
|
|
### CAN Bus Errors
|
|
|
|
**Linux:**
|
|
```bash
|
|
# Check for errors
|
|
ip -s link show can0
|
|
|
|
# Reset CAN interface
|
|
sudo ip link set can0 down
|
|
sudo ip link set can0 up
|
|
```
|
|
|
|
**macOS:**
|
|
```bash
|
|
# Reconnect USB adapter
|
|
# Unplug and replug the USB cable
|
|
|
|
# Restart Python script
|
|
# The slcan interface auto-reconnects
|
|
```
|
|
|
|
### Position Drift
|
|
|
|
- **Re-calibrate**: Run calibration procedure
|
|
- **Set zero position**: Use `robot.bus.set_zero_position()` with arm in known position
|
|
- **Check temperature**: Motors may drift when hot
|
|
|
|
## Technical Details
|
|
|
|
### Position Units
|
|
|
|
All positions are in **degrees**:
|
|
- Motor internal representation: radians
|
|
- User API: degrees
|
|
- Automatic conversion handled by `DamiaoMotorsBus`
|
|
|
|
### Control Mode
|
|
|
|
OpenArms uses **MIT control mode** which allows simultaneous control of:
|
|
- Position (degrees)
|
|
- Velocity (degrees/second)
|
|
- Torque (N·m)
|
|
- Position gain (Kp)
|
|
- Velocity damping (Kd)
|
|
|
|
### Communication
|
|
|
|
- **Protocol**: CAN 2.0 at 1 Mbps (or CAN-FD at 5 Mbps)
|
|
- **Frame format**: Standard 11-bit IDs
|
|
- **Update rate**: Typically 50-100 Hz depending on motor count
|
|
- **Latency**: ~10-20ms per motor command
|
|
|
|
## References
|
|
|
|
- [OpenArm Official Documentation](https://docs.openarm.dev/)
|
|
- [OpenArm Setup Guide](https://docs.openarm.dev/software/setup/)
|
|
- [Motor ID Configuration](https://docs.openarm.dev/software/setup/motor-id)
|
|
- [CAN Interface Setup](https://docs.openarm.dev/software/setup/can-setup)
|
|
- [Motor Communication Test](https://docs.openarm.dev/software/setup/configure-test)
|
|
- [Damiao Motor Documentation](https://wiki.seeedstudio.com/damiao_series/)
|
|
- [Enactic GitHub](https://github.com/enactic/openarm_can)
|