From 8997c00eb78f744a2c5d3adc028657aff0fb39ea Mon Sep 17 00:00:00 2001 From: Renan Date: Sun, 15 Feb 2026 20:32:50 -0600 Subject: [PATCH 1/2] solution to decouple lerobot --- .../datasets/lerobot_dataset_handler.py | 75 +++++++++++++++---- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/source/leisaac/leisaac/enhance/datasets/lerobot_dataset_handler.py b/source/leisaac/leisaac/enhance/datasets/lerobot_dataset_handler.py index 5bdc2161..cb4bc617 100644 --- a/source/leisaac/leisaac/enhance/datasets/lerobot_dataset_handler.py +++ b/source/leisaac/leisaac/enhance/datasets/lerobot_dataset_handler.py @@ -3,7 +3,12 @@ from isaaclab.utils import configclass from isaaclab.utils.datasets.dataset_file_handler_base import DatasetFileHandlerBase from isaaclab.utils.datasets.episode_data import EpisodeData -from lerobot.datasets.lerobot_dataset import LeRobotDataset +try: + from lerobot.datasets.lerobot_dataset import LeRobotDataset + HAS_LEROBOT = True +except ImportError: + HAS_LEROBOT = False + LeRobotDataset = None # Placeholder @configclass @@ -30,23 +35,39 @@ def __init__(self, cfg: LeRobotDatasetCfg): self._env_args = {} def create(self, file_path: str, env_name: str = None, resume: bool = False): - if resume: + if HAS_LEROBOT: + # Original LeRobot logic + if resume: + self._lerobot_dataset = LeRobotDataset(repo_id=self._cfg.repo_id) + else: + self._lerobot_dataset = LeRobotDataset.create( + repo_id=self._cfg.repo_id, + fps=self._cfg.fps, + robot_type=self._cfg.robot_type, + features=self._cfg.features, + ) + else: + # Decoupled logic: Use a simple dictionary logger + print("LeRobot not found. Using GenericDataRecorder.") + self._lerobot_dataset = GenericDataRecorder(self._cfg.repo_id, self._cfg.fps, self._cfg.features) + + def open(self, file_path: str, mode: str = "r"): + """Opens an existing dataset for reading or appending.""" + if HAS_LEROBOT: + # Standard LeRobot initialization self._lerobot_dataset = LeRobotDataset( repo_id=self._cfg.repo_id, ) else: - self._lerobot_dataset = LeRobotDataset.create( - repo_id=self._cfg.repo_id, - fps=self._cfg.fps, - robot_type=self._cfg.robot_type, - features=self._cfg.features, + # Decoupled logic: Initialize your custom handler + # You can use file_path here to load existing metadata + self._lerobot_dataset = GenericDataRecorder( + repo_id=self._cfg.repo_id, + fps=self._cfg.fps, + features=self._cfg.features ) - self._env_args["env_name"] = env_name - - def open(self, file_path: str, mode: str = "r"): - self._lerobot_dataset = LeRobotDataset( - repo_id=self._cfg.repo_id, - ) + if mode == "r": + self._lerobot_dataset.load_from_disk(file_path) def get_env_name(self) -> str | None: return self._env_args["env_name"] @@ -77,3 +98,31 @@ def load_episode(self, episode_name: str) -> EpisodeData | None: def get_num_episodes(self) -> int: raise NotImplementedError("get_num_episodes is not supported for LeRobotDatasetHandler") + +# Create a simple fallback recorder if LeRobot is missing +class GenericDataRecorder: + def __init__(self, repo_id, fps, features): + self.repo_id = repo_id + self.fps = fps + self.features = features + self.buffer = [] + + def add_frame(self, frame): + # Just store in memory or append to a list + self.buffer.append(frame) + + def save_episode(self, **kwargs): + # Save to a standard format like pickle or numpy + import pickle + with open(f"{self.repo_id}_episode.pkl", "wb") as f: + pickle.dump(self.buffer, f) + self.buffer = [] + + def finalize(self): + print("Dataset finalized.") + + def load_from_disk(self, path): + # Implementation to load your custom .npz or .json files + # This ensures that even without LeRobot, your 'open' method + # populates the metadata correctly. + pass \ No newline at end of file From 9389e9717f8d3701efda74c9763cb7c4a9c15576 Mon Sep 17 00:00:00 2001 From: EverNorif <1320346985@qq.com> Date: Wed, 4 Mar 2026 13:19:17 +0800 Subject: [PATCH 2/2] format. --- .../datasets/lerobot_dataset_handler.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/source/leisaac/leisaac/enhance/datasets/lerobot_dataset_handler.py b/source/leisaac/leisaac/enhance/datasets/lerobot_dataset_handler.py index cb4bc617..ffb640fb 100644 --- a/source/leisaac/leisaac/enhance/datasets/lerobot_dataset_handler.py +++ b/source/leisaac/leisaac/enhance/datasets/lerobot_dataset_handler.py @@ -3,12 +3,14 @@ from isaaclab.utils import configclass from isaaclab.utils.datasets.dataset_file_handler_base import DatasetFileHandlerBase from isaaclab.utils.datasets.episode_data import EpisodeData + try: from lerobot.datasets.lerobot_dataset import LeRobotDataset - HAS_LEROBOT = True + + _HAS_LEROBOT = True except ImportError: - HAS_LEROBOT = False - LeRobotDataset = None # Placeholder + _HAS_LEROBOT = False + LeRobotDataset = None # Placeholder @configclass @@ -35,7 +37,7 @@ def __init__(self, cfg: LeRobotDatasetCfg): self._env_args = {} def create(self, file_path: str, env_name: str = None, resume: bool = False): - if HAS_LEROBOT: + if _HAS_LEROBOT: # Original LeRobot logic if resume: self._lerobot_dataset = LeRobotDataset(repo_id=self._cfg.repo_id) @@ -53,7 +55,7 @@ def create(self, file_path: str, env_name: str = None, resume: bool = False): def open(self, file_path: str, mode: str = "r"): """Opens an existing dataset for reading or appending.""" - if HAS_LEROBOT: + if _HAS_LEROBOT: # Standard LeRobot initialization self._lerobot_dataset = LeRobotDataset( repo_id=self._cfg.repo_id, @@ -62,9 +64,7 @@ def open(self, file_path: str, mode: str = "r"): # Decoupled logic: Initialize your custom handler # You can use file_path here to load existing metadata self._lerobot_dataset = GenericDataRecorder( - repo_id=self._cfg.repo_id, - fps=self._cfg.fps, - features=self._cfg.features + repo_id=self._cfg.repo_id, fps=self._cfg.fps, features=self._cfg.features ) if mode == "r": self._lerobot_dataset.load_from_disk(file_path) @@ -99,6 +99,7 @@ def load_episode(self, episode_name: str) -> EpisodeData | None: def get_num_episodes(self) -> int: raise NotImplementedError("get_num_episodes is not supported for LeRobotDatasetHandler") + # Create a simple fallback recorder if LeRobot is missing class GenericDataRecorder: def __init__(self, repo_id, fps, features): @@ -114,6 +115,7 @@ def add_frame(self, frame): def save_episode(self, **kwargs): # Save to a standard format like pickle or numpy import pickle + with open(f"{self.repo_id}_episode.pkl", "wb") as f: pickle.dump(self.buffer, f) self.buffer = [] @@ -123,6 +125,6 @@ def finalize(self): def load_from_disk(self, path): # Implementation to load your custom .npz or .json files - # This ensures that even without LeRobot, your 'open' method + # This ensures that even without LeRobot, your 'open' method # populates the metadata correctly. - pass \ No newline at end of file + pass