项目文件夹

文件
2026-07-13 13:05:14 +08:00

172 行
5.1 KiB
Python

"""Demonstrate converting Rerun recording to LeRobot dataset."""
from __future__ import annotations
import atexit
import pathlib
import shutil
import tempfile
TMP_DIR = pathlib.Path(tempfile.mkdtemp())
atexit.register(lambda: shutil.rmtree(TMP_DIR) if TMP_DIR.exists() else None)
# region: setup
from pathlib import Path
from lerobot.datasets.lerobot_dataset import (
LeRobotDataset, # type: ignore[import-untyped,import-not-found]
)
from rerun_lerobot.converter import convert_dataframe_to_episode
from rerun_lerobot.feature_inference import infer_features
from rerun_lerobot.types import LeRobotConversionConfig, VideoSpec
import rerun as rr
# Start a server with RRD recordings
# In practice, you would point this to your directory of RRD files
sample_5_path = (
Path(__file__).parents[4] / "tests" / "assets" / "rrd" / "sample_5"
)
server = rr.server.Server(datasets={"robot_dataset": sample_5_path})
client = server.client()
dataset = client.get_dataset(name="robot_dataset")
# endregion: setup
# region: filter_data
# Select a single recording (episode) to export
single_recording = dataset.segment_ids()[0]
# Filter the dataset to include only the data we need for training:
# - Action commands sent to the robot
# - Observed joint positions (robot state)
# - Camera feeds from multiple viewpoints
# - Task descriptions (e.g., language instructions)
training_data = (
dataset
.filter_segments(single_recording)
.filter_contents([
"/action/joint_positions",
"/observation/joint_positions",
"/camera/**",
"/language_instruction",
])
.reader(index="real_time")
)
# endregion: filter_data
# region: configure_export
# Define how to extract task instructions from the recording
# This could be from a TextDocument, static metadata, etc.
# For this example, we assume a static instruction
instructions = "/language_instruction:TextDocument:text"
# Specify video streams to include in the dataset.
# Each stream needs a key (camera identifier) and entity path where the
# VideoStream is logged
videos = [
VideoSpec(key="ext1", path="/camera/ext1", video_format="h264"),
VideoSpec(key="ext2", path="/camera/ext2", video_format="h264"),
VideoSpec(key="wrist", path="/camera/wrist", video_format="h264"),
]
# Configure the conversion parameters
# This maps Rerun's flexible data model to LeRobot's standardized format
config = LeRobotConversionConfig(
fps=15, # Target frame rate for the dataset
index_column="real_time", # Timeline to use for alignment
# Fully qualified action column
action="/action/joint_positions:Scalars:scalars",
# Fully qualified state column
state="/observation/joint_positions:Scalars:scalars",
task=instructions, # Task description column
videos=videos, # Video streams to include
)
# endregion: configure_export
# region: infer_features
# Infer the LeRobot feature schema from the data
# This automatically detects data types, shapes, and creates the appropriate
# LeRobot feature definitions
features = infer_features(
table=training_data.to_arrow_table(),
config=config,
)
# endregion: infer_features
# region: create_dataset
# Create the LeRobot dataset structure on disk
lerobot_dataset = LeRobotDataset.create(
repo_id="rerun/droid_lerobot", # Dataset identifier
fps=config.fps,
features=features,
root=TMP_DIR / "lerobot_dataset",
use_videos=config.use_videos,
)
# endregion: create_dataset
# region: export_episode
# Convert the recording to a LeRobot episode
# This aligns all time series to the target frame rate, extracts video frames,
# and writes the episode data in LeRobot's Parquet format
print("Creating episode")
convert_dataframe_to_episode(
df=training_data,
config=config,
lerobot_dataset=lerobot_dataset,
segment_id=single_recording,
features=features,
)
# Finalize the dataset (write metadata, close files, etc.)
lerobot_dataset.finalize()
# endregion: export_episode
# region: multi_episode_export
# Create a new LeRobot dataset for multiple episodes
lerobot_dataset = LeRobotDataset.create(
repo_id="rerun/droid_lerobot_full",
fps=config.fps,
features=features,
root=TMP_DIR / "lerobot_dataset_full",
use_videos=config.use_videos,
)
# To export multiple recordings, repeat the filtering and conversion steps
for segment_id in dataset.segment_ids():
print(f"Exporting segment: {segment_id}")
segment_data = (
dataset
.filter_segments(segment_id)
.filter_contents([
"/action/joint_positions",
"/observation/joint_positions",
"/camera/**",
"/language_instruction",
])
.reader(index="real_time")
)
convert_dataframe_to_episode(
df=segment_data,
config=config,
lerobot_dataset=lerobot_dataset,
segment_id=segment_id,
features=features,
)
lerobot_dataset.finalize()
# endregion: multi_episode_export
# region: use_dataset
# Access episode data
print(f"Total episodes: {lerobot_dataset.num_episodes}")
print(f"Total frames: {lerobot_dataset.num_frames}")
# endregion: use_dataset