Files
any4lerobot/libero2lerobot/libero_h5.py
T
Qizhi Chen 7a8642edfc ♻️ Refactor LIBERO converter onto generic pipeline (#105)
* docs: update libero2lerobot readme

Co-authored-by: codex <codex@openai.com>

* refactor libero2lerobot converter

Co-authored-by: codex <codex@openai.com>

---------

Co-authored-by: codex <codex@openai.com>
2026-06-12 19:19:16 -07:00

172 lines
6.3 KiB
Python

import argparse
import re
import sys
from collections.abc import Iterable, Sequence
from pathlib import Path
import numpy as np
from h5py import File
REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from generic_converter import BaseAdapter, ConversionTask, run_converter # noqa: E402
class LiberoAdapter(BaseAdapter):
dataset_type = "libero"
fps = 20
robot_type = "franka"
features = {
"observation.images.image": {
"dtype": "video",
"shape": (256, 256, 3),
"names": ["height", "width", "rgb"],
},
"observation.images.wrist_image": {
"dtype": "video",
"shape": (256, 256, 3),
"names": ["height", "width", "rgb"],
},
"observation.state": {
"dtype": "float32",
"shape": (8,),
"names": {"motors": ["x", "y", "z", "axis_angle1", "axis_angle2", "axis_angle3", "gripper", "gripper"]},
},
"observation.states.ee_state": {
"dtype": "float32",
"shape": (6,),
"names": {"motors": ["x", "y", "z", "axis_angle1", "axis_angle2", "axis_angle3"]},
},
"observation.states.joint_state": {
"dtype": "float32",
"shape": (7,),
"names": {"motors": ["joint_0", "joint_1", "joint_2", "joint_3", "joint_4", "joint_5", "joint_6"]},
},
"observation.states.gripper_state": {
"dtype": "float32",
"shape": (2,),
"names": {"motors": ["gripper", "gripper"]},
},
"action": {
"dtype": "float32",
"shape": (7,),
"names": {"motors": ["x", "y", "z", "axis_angle1", "axis_angle2", "axis_angle3", "gripper"]},
},
}
tags = ["libero", "franka"]
def __init__(self, src_paths: list[Path], output_path: Path):
super().__init__(output_path)
self.src_paths = src_paths
def load_tasks(self) -> list[ConversionTask]:
tasks = []
for src_path in self.src_paths:
for input_h5 in src_path.glob("*.hdf5"):
pattern1 = re.compile(r"_SCENE\d+_(.*?)_demo\.hdf5")
pattern2 = re.compile(r"(.*?)_demo\.hdf5")
match = pattern1.search(input_h5.name)
if match is None:
match = pattern2.search(input_h5.name)
if match is None:
continue
else:
task_instruction = match.group(1).replace("_", " ")
tasks.append(
ConversionTask(
input_path=input_h5.resolve(),
output_path=(
self.temp_output_path
/ f"{src_path.name}"
/ input_h5.stem
).resolve(),
local_repo_id=f"{input_h5.parent.name}/{input_h5.name}",
metadata={"task": task_instruction},
)
)
return tasks
def load_subset(self, task: ConversionTask) -> Iterable[Sequence[dict]]:
input_h5 = task.input_path
task_instruction = task.metadata.get("task")
with File(input_h5, "r") as f:
for demo in f["data"].values():
demo_len = len(demo["obs/agentview_rgb"])
# (-1: open, 1: close) -> (0: close, 1: open)
action = np.array(demo["actions"])
action = np.concatenate(
[
action[:, :6],
(1 - np.clip(action[:, -1], 0, 1))[:, None],
],
axis=1,
)
state = np.concatenate(
[
np.array(demo["obs/ee_states"]),
np.array(demo["obs/gripper_states"]),
],
axis=1,
)
episode = {
"observation.images.image": np.array(demo["obs/agentview_rgb"]),
"observation.images.wrist_image": np.array(demo["obs/eye_in_hand_rgb"]),
"observation.state": np.array(state, dtype=np.float32),
"observation.states.ee_state": np.array(demo["obs/ee_states"], dtype=np.float32),
"observation.states.joint_state": np.array(demo["obs/joint_states"], dtype=np.float32),
"observation.states.gripper_state": np.array(demo["obs/gripper_states"], dtype=np.float32),
"action": np.array(action, dtype=np.float32),
}
yield [{**{k: v[i] for k, v in episode.items()}, "task": task_instruction} for i in range(demo_len)]
def main(
src_paths: list[Path],
output_path: Path,
executor: str,
cpus_per_task: int,
tasks_per_job: int,
workers: int,
resume_dir: Path | None = None,
debug: bool = False,
repo_id: str | None = None,
push_to_hub: bool = False,
):
adapter = LiberoAdapter(src_paths, output_path)
run_converter(
adapter=adapter,
executor=executor,
cpus_per_task=cpus_per_task,
tasks_per_job=tasks_per_job,
workers=workers,
resume_dir=resume_dir,
debug=debug,
local_repo_id=repo_id,
hub_repo_id=repo_id,
push_to_hub=push_to_hub,
)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--src-paths", type=Path, nargs="+", required=True)
parser.add_argument("--output-path", type=Path, required=True)
parser.add_argument("--executor", type=str, choices=["local", "ray"], default="local")
parser.add_argument("--cpus-per-task", type=int, default=1)
parser.add_argument(
"--tasks-per-job", type=int, default=1, help="number of concurrent tasks per job, only used for ray"
)
parser.add_argument("--workers", type=int, default=-1, help="number of concurrent jobs to run")
parser.add_argument("--resume-dir", type=Path, help="logs directory to resume")
parser.add_argument("--debug", action="store_true")
parser.add_argument("--repo-id", type=str, help="required when push-to-hub is True")
parser.add_argument("--push-to-hub", action="store_true", help="upload to hub")
args = parser.parse_args()
main(**vars(args))