From 1c61b43b1596178adec3d2649b170787f63f1777 Mon Sep 17 00:00:00 2001 From: Steven Palma Date: Wed, 14 Jan 2026 17:14:12 +0100 Subject: [PATCH] fix(teleop): add is_connected check to get_action (#2801) --- src/lerobot/teleoperators/bi_so_leader/bi_so_leader.py | 4 ++++ src/lerobot/teleoperators/gamepad/teleop_gamepad.py | 5 +++++ src/lerobot/teleoperators/homunculus/homunculus_arm.py | 3 +++ src/lerobot/teleoperators/homunculus/homunculus_glove.py | 3 +++ src/lerobot/teleoperators/phone/teleop_phone.py | 6 ++++++ src/lerobot/teleoperators/so_leader/so_leader.py | 3 +++ 6 files changed, 24 insertions(+) diff --git a/src/lerobot/teleoperators/bi_so_leader/bi_so_leader.py b/src/lerobot/teleoperators/bi_so_leader/bi_so_leader.py index 45c46c100..0fdc32461 100644 --- a/src/lerobot/teleoperators/bi_so_leader/bi_so_leader.py +++ b/src/lerobot/teleoperators/bi_so_leader/bi_so_leader.py @@ -18,6 +18,7 @@ import logging from functools import cached_property from lerobot.teleoperators.so_leader import SOLeaderTeleopConfig +from lerobot.utils.errors import DeviceNotConnectedError from ..so_leader import SOLeader from ..teleoperator import Teleoperator @@ -92,6 +93,9 @@ class BiSOLeader(Teleoperator): self.right_arm.setup_motors() def get_action(self) -> dict[str, float]: + if not self.is_connected: + raise DeviceNotConnectedError(f"{self} is not connected.") + action_dict = {} # Add "left_" prefix diff --git a/src/lerobot/teleoperators/gamepad/teleop_gamepad.py b/src/lerobot/teleoperators/gamepad/teleop_gamepad.py index 4dbb49c1d..14d8e3cc1 100644 --- a/src/lerobot/teleoperators/gamepad/teleop_gamepad.py +++ b/src/lerobot/teleoperators/gamepad/teleop_gamepad.py @@ -21,6 +21,7 @@ from typing import Any import numpy as np from lerobot.processor import RobotAction +from lerobot.utils.errors import DeviceNotConnectedError from ..teleoperator import Teleoperator from ..utils import TeleopEvents @@ -86,6 +87,9 @@ class GamepadTeleop(Teleoperator): self.gamepad.start() def get_action(self) -> RobotAction: + if not self.is_connected: + raise DeviceNotConnectedError(f"{self} is not connected.") + # Update the controller to get fresh inputs self.gamepad.update() @@ -158,6 +162,7 @@ class GamepadTeleop(Teleoperator): self.gamepad.stop() self.gamepad = None + @property def is_connected(self) -> bool: """Check if gamepad is connected.""" return self.gamepad is not None diff --git a/src/lerobot/teleoperators/homunculus/homunculus_arm.py b/src/lerobot/teleoperators/homunculus/homunculus_arm.py index 43116f5c0..53b4dfe68 100644 --- a/src/lerobot/teleoperators/homunculus/homunculus_arm.py +++ b/src/lerobot/teleoperators/homunculus/homunculus_arm.py @@ -300,6 +300,9 @@ class HomunculusArm(Teleoperator): logger.debug(f"Error reading frame in background thread for {self}: {e}") def get_action(self) -> dict[str, float]: + if not self.is_connected: + raise DeviceNotConnectedError(f"{self} is not connected.") + joint_positions = self._read() return {f"{joint}.pos": pos for joint, pos in joint_positions.items()} diff --git a/src/lerobot/teleoperators/homunculus/homunculus_glove.py b/src/lerobot/teleoperators/homunculus/homunculus_glove.py index fefeec1e8..7feb926ef 100644 --- a/src/lerobot/teleoperators/homunculus/homunculus_glove.py +++ b/src/lerobot/teleoperators/homunculus/homunculus_glove.py @@ -326,6 +326,9 @@ class HomunculusGlove(Teleoperator): logger.debug(f"Error reading frame in background thread for {self}: {e}") def get_action(self) -> dict[str, float]: + if not self.is_connected: + raise DeviceNotConnectedError(f"{self} is not connected.") + joint_positions = self._read() return homunculus_glove_to_hope_jr_hand( {f"{joint}.pos": pos for joint, pos in joint_positions.items()} diff --git a/src/lerobot/teleoperators/phone/teleop_phone.py b/src/lerobot/teleoperators/phone/teleop_phone.py index 91e613190..402a88d43 100644 --- a/src/lerobot/teleoperators/phone/teleop_phone.py +++ b/src/lerobot/teleoperators/phone/teleop_phone.py @@ -165,6 +165,9 @@ class IOSPhone(BasePhone, Teleoperator): return True, pos, rot, pose def get_action(self) -> dict: + if not self.is_connected: + raise DeviceNotConnectedError(f"{self} is not connected.") + has_pose, raw_position, raw_rotation, fb_pose = self._read_current_pose() if not has_pose or not self.is_calibrated: return {} @@ -319,6 +322,9 @@ class AndroidPhone(BasePhone, Teleoperator): self._latest_message = message def get_action(self) -> dict: + if not self.is_connected: + raise DeviceNotConnectedError(f"{self} is not connected.") + ok, raw_pos, raw_rot, pose = self._read_current_pose() if not ok or not self.is_calibrated: return {} diff --git a/src/lerobot/teleoperators/so_leader/so_leader.py b/src/lerobot/teleoperators/so_leader/so_leader.py index 760ef2eb1..d09262c74 100644 --- a/src/lerobot/teleoperators/so_leader/so_leader.py +++ b/src/lerobot/teleoperators/so_leader/so_leader.py @@ -140,6 +140,9 @@ class SOLeader(Teleoperator): print(f"'{motor}' motor id set to {self.bus.motors[motor].id}") def get_action(self) -> dict[str, float]: + if not self.is_connected: + raise DeviceNotConnectedError(f"{self} is not connected.") + start = time.perf_counter() action = self.bus.sync_read("Present_Position") action = {f"{motor}.pos": val for motor, val in action.items()}