8000 Add keyboard teleoperation support for SO100/SO101 robot by GGmorello · Pull Request #1338 · huggingface/lerobot · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add keyboard teleoperation support for SO100/SO101 robot #1338

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
35 changes: 34 additions & 1 deletion lerobot/common/teleoperators/keyboard/configuration_keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from dataclasses import dataclass
from dataclasses import dataclass, field

from ..config import TeleoperatorConfig

Expand All @@ -25,6 +25,39 @@ class KeyboardTeleopConfig(TeleoperatorConfig):
# TODO(Steven): Consider setting in here the keys that we want to capture/listen
mock: bool = False

# Motor mappings for SO100 follower
motor_mappings: dict[str, dict[str, str]] = field(
default_factory=lambda: {
"shoulder_pan": {
"left": "a",
"right": "d",
},
"shoulder_lift": {
"up": "w",
"down": "s",
},
"elbow_flex": {
"up": "q",
"down": "e",
},
"wrist_flex": {
"up": "r",
"down": "f",
},
"wrist_roll": {
"left": "z",
"right": "x",
},
"gripper": {
"open": "g",
"close": "h",
},
}
)

# Step size for each motor movement
step_size: float = 5.0


@TeleoperatorConfig.register_subclass("keyboard_ee")
@dataclass
Expand Down
43 changes: 29 additions & 14 deletions lerobot/common/teleoperators/keyboard/teleop_keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import logging
import os
import sys
import time
from queue import Queue
from typing import Any

Expand Down Expand Up @@ -60,13 +59,22 @@ def __init__(self, config: KeyboardTeleopConfig):
self.listener = None
self.logs = {}

# Initialize current positions
self.current_positions = {f"{motor}.pos": 0.0 for motor in self.config.motor_mappings.keys()}

# Set limits for each motor
self.motor_limits = {
"shoulder_pan": (-100, 100),
"shoulder_lift": (-100, 100),
"elbow_flex": (-100, 100),
"wrist_flex": (-100, 100),
"wrist_roll": (-100, 100),
"gripper": (0, 100),
}

@property
def action_features(self) -> dict:
return {
"dtype": "float32",
"shape": (len(self.arm),),
"names": {"motors": list(self.arm.motors)},
}
return {f"{motor}.pos": float for motor in self.config.motor_mappings.keys()}

@property
def feedback_features(self) -> dict:
Expand All @@ -78,7 +86,7 @@ def is_connected(self) -> bool:

@property
def is_calibrated(self) -> bool:
pass
return True

def connect(self) -> None:
if self.is_connected:
Expand Down Expand Up @@ -119,21 +127,28 @@ def _drain_pressed_keys(self):
def configure(self):
pass

def get_action(self) -> dict[str, Any]:
before_read_t = time.perf_counter()

def get_action(self) -> dict[str, float]:
if not self.is_connected:
raise DeviceNotConnectedError(
"KeyboardTeleop is not connected. You need to run `connect()` before `get_action()`."
)

self._drain_pressed_keys()

# Generate action based on current key states
action = {key for key, val in self.current_pressed.items() if val}
self.logs["read_pos_dt_s"] = time.perf_counter() - before_read_t
for motor, mappings in self.config.motor_mappings.items():
for direction, key in mappings.items():
if key in self.current_pressed and self.current_pressed[key]:
min_val, max_val = self.motor_limits[motor]
current_val = self.current_positions[f"{motor}.pos"]

if direction in ["up", "right", "open"]:
new_val = min(current_val + self.config.step_size, max_val)
else:
new_val = max(current_val - self.config.step_size, min_val)

self.current_positions[f"{motor}.pos"] = new_val

return dict.fromkeys(action, None)
return self.current_positions

def send_feedback(self, feedback: dict[str, Any]) -> None:
pass
Expand Down
9 changes: 6 additions & 3 deletions lerobot/teleoperate.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,20 @@
so100_follower,
so101_follower,
)
from lerobot.common.teleoperators import (
from lerobot.common.teleoperators import ( # noqa: F401
Teleoperator,
TeleoperatorConfig,
gamepad,
keyboard,
koch_leader,
make_teleoperator_from_config,
so100_leader,
so101_leader,
)
from lerobot.common.utils.robot_utils import busy_wait
from lerobot.common.utils.utils import init_logging, move_cursor_up
from lerobot.common.utils.visualization_utils import _init_rerun

from .common.teleoperators import gamepad, koch_leader, so100_leader, so101_leader # noqa: F401


@dataclass
class TeleoperateConfig:
Expand Down
0