| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| from typing import Any |
|
|
| from lerobot.utils.decorators import check_if_already_connected, check_if_not_connected |
|
|
|
|
| class BimanualMixin: |
| """Lifecycle delegation for bimanual robots and teleoperators. |
| |
| Concrete subclasses must populate ``self.left_arm`` and ``self.right_arm`` in |
| their own ``__init__``. They retain ownership of feature dicts and the |
| data-routing methods (``get_action`` / ``send_action`` / ``get_observation`` / |
| ``send_feedback``), which vary per-embodiment. |
| |
| Inherit before the ``Robot`` / ``Teleoperator`` base so the mixin's methods |
| take precedence in the MRO:: |
| |
| class BiFooFollower(BimanualMixin, Robot): ... |
| """ |
|
|
| left_arm: Any |
| right_arm: Any |
|
|
| @property |
| def is_connected(self) -> bool: |
| return self.left_arm.is_connected and self.right_arm.is_connected |
|
|
| @property |
| def is_calibrated(self) -> bool: |
| return self.left_arm.is_calibrated and self.right_arm.is_calibrated |
|
|
| @check_if_already_connected |
| def connect(self, calibrate: bool = True) -> None: |
| self.left_arm.connect(calibrate) |
| self.right_arm.connect(calibrate) |
|
|
| def calibrate(self) -> None: |
| self.left_arm.calibrate() |
| self.right_arm.calibrate() |
|
|
| def configure(self) -> None: |
| self.left_arm.configure() |
| self.right_arm.configure() |
|
|
| @check_if_not_connected |
| def disconnect(self) -> None: |
| self.left_arm.disconnect() |
| self.right_arm.disconnect() |
|
|