Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
344 changes: 344 additions & 0 deletions README_piper_teleoperation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
# πŸ€– GuΓ­a RΓ‘pida: TeleoperaciΓ³n Bi-Manual Piper con SO100

GuΓ­a prΓ‘ctica para teleoperar 2 robots Piper usando 2 brazos SO100 modificados (7 DOF) como lΓ­deres.

---

## πŸ“‹ Hardware

- **2 Robots Piper** (followers) - USB-to-CAN
- **2 Brazos SO100 modificados** (leaders) - 7 motores/brazo (IDs 1-7)
- **2 Adaptadores USB-to-CAN** (Geschwister Schneider)
- **CΓ‘maras** (opcional)

**Mapeo de Juntas:**
```
Motor SO100 β†’ Piper Joint
Motor 1 (ID 1) β†’ joint_0 (shoulder_pan)
Motor 2 (ID 2) β†’ joint_1 (shoulder_lift)
Motor 3 (ID 3) β†’ joint_2 (elbow_flex)
Motor 4 (ID 4) β†’ joint_3 (forearm_roll) ⭐ Nuevo motor
Motor 5 (ID 5) β†’ joint_4 (wrist_flex)
Motor 6 (ID 6) β†’ joint_5 (wrist_roll)
Motor 7 (ID 7) β†’ joint_6 (gripper)
```

---

## πŸš€ GUÍA PASO A PASO

### **PASO 1: Activar Entorno**

```bash
conda activate lerobot_piper
cd /home/mbrq/NONHUMAN/TELEOPERATION/lerobot
```

---

### **PASO 2: Verificar Hardware SO100 (Puertos Seriales)**

```bash
# Ver puertos seriales de los SO100
ls -l /dev/ttyACM* /dev/ttyUSB* 2>/dev/null
```

**Salida esperada:**
```
crw-rw---- 1 root dialout 166, 0 Nov 3 20:20 /dev/ttyACM0
crw-rw---- 1 root dialout 166, 1 Nov 3 20:20 /dev/ttyACM1
```

βœ… 2 SO100 detectados

**Si no tienes permisos:**
```bash
sudo usermod -a -G dialout $USER
# Cerrar sesiΓ³n y volver a entrar
```

---

### **PASO 3: Verificar Hardware Piper (Adaptadores CAN)**

```bash
# Ver adaptadores USB-to-CAN
lsusb | grep -i "CAN\|gs_usb\|Schneider\|OpenMoko"
```

**Salida esperada:**
```
Bus 001 Device 080: ID 1d50:606f OpenMoko, Inc. Geschwister Schneider CAN adapter
Bus 001 Device 086: ID 1d50:606f OpenMoko, Inc. Geschwister Schneider CAN adapter
```

βœ… 2 Pipers detectados

---

### **PASO 4: Ver Interfaces CAN**

```bash
# Ver interfaces CAN creadas
ip link show type can
```

**Salida esperada:**
```
22: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT
23: can1: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT
```

- `state DOWN` β†’ Necesita activarse ❌
- `state UP` β†’ Listo para usar βœ…

---

### **PASO 5: Identificar Direcciones USB de las Interfaces CAN**

```bash
for iface in $(ip -br link show type can | awk '{print $1}'); do
BUS_INFO=$(sudo ethtool -i "$iface" 2>/dev/null | grep "bus-info" | awk '{print $2}')
echo "Interfaz: $iface -> Puerto USB: $BUS_INFO"
done
```

**Salida esperada:**
```
Interfaz: can0 -> Puerto USB: 1-3.3:1.0
Interfaz: can1 -> Puerto USB: 1-1:1.0
```

βœ… **Guarda estas direcciones USB** (necesarias para el siguiente paso)

---

### **PASO 6: Activar Interfaces CAN (ponerlas UP)**

**⚠️ Usa las direcciones USB del paso anterior**

```bash
# Activar can0 (ajusta la direcciΓ³n USB segΓΊn tu sistema)
sudo bash ~/miniconda3/envs/lerobot_piper/lib/python3.11/site-packages/piper_sdk/can_activate.sh can0 1000000 1-3.3:1.0

# Activar can1 (ajusta la direcciΓ³n USB segΓΊn tu sistema)
sudo bash ~/miniconda3/envs/lerobot_piper/lib/python3.11/site-packages/piper_sdk/can_activate.sh can1 1000000 1-1:1.0
```

**Verificar que estΓ©n UP:**
```bash
ip link show can0 | grep "state"
ip link show can1 | grep "state"
```

**Debe mostrar:**
```
state UP
state UP
```

βœ… Interfaces CAN activadas

---

### **PASO 7: Calibrar Brazos LΓ­deres (solo primera vez)**

**⚠️ Este paso solo se hace UNA VEZ. La calibración se guarda permanentemente.**

```bash
python -m lerobot.calibrate \
--teleop.type=bi_so100_piper_leader \
--teleop.left_arm_port=/dev/ttyACM0 \
--teleop.right_arm_port=/dev/ttyACM1 \
--teleop.id=my_bi_piper_leader
```

**Proceso interactivo:**

#### **Para BRAZO IZQUIERDO:**

1. **Mensaje:** "Mueve el brazo a la mitad de su rango y presiona ENTER..."
- βœ‹ Mover brazo izquierdo a posiciΓ³n intermedia β†’ **ENTER**

2. **Mensaje:** "Mueve todas las juntas (menos 'wrist_roll') a travΓ©s de su rango completo. ENTER para parar..."
- βœ‹ Mover cada junta desde su mΓ­nimo hasta su mΓ‘ximo:
- shoulder_pan
- shoulder_lift
- elbow_flex
- forearm_roll
- wrist_flex
- gripper
- **ENTER** cuando termines

3. VerΓ‘s una tabla con los rangos registrados

#### **Para BRAZO DERECHO:**

Repite el mismo proceso (el sistema lo pedirΓ‘ automΓ‘ticamente)

**ConfirmaciΓ³n:**
```
CalibraciΓ³n guardada en ~/.cache/huggingface/lerobot/calibration/teleoperators/so100_piper_leader/my_bi_piper_leader_left.json
CalibraciΓ³n guardada en ~/.cache/huggingface/lerobot/calibration/teleoperators/so100_piper_leader/my_bi_piper_leader_right.json
```

βœ… CalibraciΓ³n completada

---

### **PASO 8: Teleoperar (Β‘El momento de la verdad!)**

#### **OpciΓ³n A: Sin cΓ‘maras (mΓ‘s simple)**

```bash
python -m lerobot.teleoperate \
--robot.type=bi_piper_follower \
--robot.left_port=can0 \
--robot.right_port=can1 \
--robot.id=my_bi_piper \
--teleop.type=bi_so100_piper_leader \
--teleop.left_arm_port=/dev/ttyACM0 \
--teleop.right_arm_port=/dev/ttyACM1 \
--teleop.id=my_bi_piper_leader \
--fps=60
```

#### **OpciΓ³n B: Con cΓ‘maras y visualizaciΓ³n**

```bash
python -m lerobot.teleoperate \
--robot.type=bi_piper_follower \
--robot.left_port=can0 \
--robot.right_port=can1 \
--robot.id=my_bi_piper \
--robot.cameras='{
left: {"type": "opencv", "index_or_path": 0, "width": 640, "height": 480, "fps": 30},
top: {"type": "opencv", "index_or_path": 1, "width": 640, "height": 480, "fps": 30},
right: {"type": "opencv", "index_or_path": 2, "width": 640, "height": 480, "fps": 30}
}' \
--teleop.type=bi_so100_piper_leader \
--teleop.left_arm_port=/dev/ttyACM0 \
--teleop.right_arm_port=/dev/ttyACM1 \
--teleop.id=my_bi_piper_leader \
--display_data=true \
--fps=60
```

**Para detener:** `Ctrl+C`

---

## βœ… CHECKLIST RÁPIDO

```bash
# 1. Entorno activado
conda activate lerobot_piper

# 2. Ver SO100 (2 puertos esperados)
ls -l /dev/ttyACM*

# 3. Ver Pipers (2 adaptadores esperados)
lsusb | grep -i CAN

# 4. Ver interfaces CAN (2 interfaces esperadas)
ip link show type can

# 5. Identificar USB
for iface in $(ip -br link show type can | awk '{print $1}'); do BUS_INFO=$(sudo ethtool -i "$iface" 2>/dev/null | grep "bus-info" | awk '{print $2}'); echo "$iface -> $BUS_INFO"; done

# 6. Activar CAN (usar direcciones del paso 5)
sudo bash ~/miniconda3/envs/lerobot_piper/lib/python3.11/site-packages/piper_sdk/can_activate.sh can0 1000000 1-X.X:1.0
sudo bash ~/miniconda3/envs/lerobot_piper/lib/python3.11/site-packages/piper_sdk/can_activate.sh can1 1000000 1-Y.Y:1.0

# 7. Verificar estado UP
ip link show can0 | grep "state"
ip link show can1 | grep "state"

# 8. Calibrar (solo primera vez)
python -m lerobot.calibrate --teleop.type=bi_so100_piper_leader --teleop.left_arm_port=/dev/ttyACM0 --teleop.right_arm_port=/dev/ttyACM1 --teleop.id=my_bi_piper_leader

# 9. Teleoperar
python -m lerobot.teleoperate --robot.type=bi_piper_follower --robot.left_port=can0 --robot.right_port=can1 --robot.id=my_bi_piper --teleop.type=bi_so100_piper_leader --teleop.left_arm_port=/dev/ttyACM0 --teleop.right_arm_port=/dev/ttyACM1 --teleop.id=my_bi_piper_leader --fps=60
```

---

## πŸ› SOLUCIΓ“N DE PROBLEMAS

### ❌ Problema: Interface CAN en estado DOWN

**SoluciΓ³n:**
```bash
# Activar con direcciΓ³n USB (obtener con ethtool -i canX)
sudo bash ~/miniconda3/envs/lerobot_piper/lib/python3.11/site-packages/piper_sdk/can_activate.sh can0 1000000 1-3.3:1.0
```

### ❌ Problema: Permission denied en /dev/ttyACM*

**SoluciΓ³n:**
```bash
sudo usermod -a -G dialout $USER
# Cerrar sesiΓ³n y volver a entrar
```

### ❌ Problema: Solo 1 Piper detectado

**Verificar:**
```bash
lsusb | grep -i CAN
```
Si solo aparece 1 lΓ­nea β†’ revisar conexiΓ³n fΓ­sica del segundo Piper

### ❌ Problema: "Failed to initialize Piper SDK"

**Verificar que interfaces estΓ©n UP:**
```bash
ip link show can0 can1 | grep state
```
Ambos deben mostrar `state UP`

### ❌ Problema: Motor ID no detectado en SO100

**Verificar IDs de motores:**
```bash
python -m lerobot.find_port
# Revisar que los 7 motores (IDs 1-7) estΓ©n presentes
```

### ❌ Problema: Puertos USB cambiaron después de desconectar

**Re-verificar puertos:**
```bash
ls -l /dev/ttyACM* # SO100
ip link show type can # Piper
```

---

## πŸ“ NOTAS IMPORTANTES

⚠️ **IMPORTANTE:**
1. **Las interfaces CAN se desactivan al reiniciar** β†’ Activa `can0` y `can1` cada sesiΓ³n
2. **La calibraciΓ³n se guarda permanentemente** β†’ Solo calibrar una vez (a menos que muevas los motores o cambies el setup)
3. **Los puertos USB pueden cambiar** β†’ Siempre verificar `/dev/ttyACM*` antes de teleoperar
4. **El bitrate debe ser 1000000** para comunicaciΓ³n Piper
5. **Motor ID 6 (wrist_roll)** β†’ No se calibra manualmente, rango automΓ‘tico 0-4095

---

## πŸŽ“ REFERENCIAS

- [LeRobot Documentation](https://huggingface.co/docs/lerobot)
- [Piper SDK](https://github.com/agilexrobotics/piper_sdk)
- [SO100 Arm](https://github.com/TheRobotStudio/SO-ARM100)

---

**βœ… TELEOPERACIΓ“N EXITOSA**

Este documento fue validado con una teleoperaciΓ³n bi-manual exitosa el 4 de noviembre de 2025.

**Creado:** Noviembre 2025
**Última actualización:** Noviembre 2025
**Proyecto:** TELEOPERATION - NONHUMAN Lab

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,12 @@ feetech = ["feetech-servo-sdk>=1.0.0"]
dynamixel = ["dynamixel-sdk>=3.7.31"]

# Robots
piper = ["piper-sdk>=0.3.2"]
gamepad = ["lerobot[pygame-dep]", "hidapi>=0.14.0"]
hopejr = ["lerobot[feetech]", "lerobot[pygame-dep]"]
lekiwi = ["lerobot[feetech]", "pyzmq>=26.2.1"]
kinematics = ["lerobot[placo-dep]"]

intelrealsense = [
"pyrealsense2>=2.55.1.6486 ; sys_platform != 'darwin'",
"pyrealsense2-macosx>=2.54 ; sys_platform == 'darwin'",
Expand Down
2 changes: 2 additions & 0 deletions src/lerobot/calibrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from lerobot.robots import ( # noqa: F401
Robot,
RobotConfig,
bi_piper_follower,
hope_jr,
koch_follower,
lekiwi,
Expand All @@ -46,6 +47,7 @@
from lerobot.teleoperators import ( # noqa: F401
Teleoperator,
TeleoperatorConfig,
bi_so100_piper_leader,
homunculus,
koch_leader,
make_teleoperator_from_config,
Expand Down
2 changes: 2 additions & 0 deletions src/lerobot/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
from lerobot.robots import ( # noqa: F401
Robot,
RobotConfig,
bi_piper_follower,
bi_so100_follower,
hope_jr,
koch_follower,
Expand All @@ -90,6 +91,7 @@
Teleoperator,
TeleoperatorConfig,
bi_so100_leader,
bi_so100_piper_leader,
homunculus,
koch_leader,
make_teleoperator_from_config,
Expand Down
5 changes: 5 additions & 0 deletions src/lerobot/robots/bi_piper_follower/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# src/lerobot/robots/bi_piper_follower/__init__.py
from .bi_piper_follower import BiPiperFollower
from .config_bi_piper_follower import BiPiperFollowerConfig

__all__ = ["BiPiperFollower", "BiPiperFollowerConfig"]
Loading