Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ _build
/datasets/

# Tests
tests/
# NOTE: this repository commits tests/ (architecture + pipeline tests are part of
# the deliverable). Only ignore compiled/test caches, never the test sources.
**/__pycache__/
**/.pytest_cache/

# Docker history
.isaac-lab-docker-history
Expand Down
17 changes: 17 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
SO-101 Pi0.5 Research Platform
Copyright 2026 Chepuri Natraj

This product includes software developed by the LightwheelAI team as part of
the LeIsaac project (https://github.com/LightwheelAI/leisaac), licensed under
the Apache License, Version 2.0.

This repository is an independent research derivative of LeIsaac. It retains
LeIsaac's IsaacLab-based simulation, SO-101 robot assets, teleoperation,
data-collection, and dataset-conversion infrastructure, and re-targets the
primary learned policy to pi0.5 (Pi0.5) from Physical Intelligence (OpenPI).

Derived from LeIsaac upstream commit:
24d3bcd3f1e4585740fc79921782c41617237812 (v0.4.0)

Portions of this software incorporate or interface with third-party projects.
See THIRD_PARTY.md for the full attribution list and their licenses.
515 changes: 460 additions & 55 deletions README.md

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions THIRD_PARTY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Third-Party Attribution

All entries below were verified against the actual files vendored in or depended
on by this repository. Versions/commits are recorded as observed, not assumed.

## Primary upstream: LeIsaac

- **Project:** LeIsaac (LeRobot & IsaacLab Integration)
- **Source:** https://github.com/LightwheelAI/leisaac
- **License:** Apache License 2.0 (see `LICENSE`, retained unmodified)
- **Base commit:** `24d3bcd3f1e4585740fc79921782c41617237812` (package version `0.4.0`)
- **Author (upstream `pyproject.toml`):** Yinghao Shuai <yinghao.shuai@lightwheel.ai>
- **What is retained:** IsaacLab task/environment infrastructure
(`source/leisaac/leisaac/tasks`), SO-101 robot + scene assets
(`.../assets`), teleoperation devices (`.../devices`: keyboard, gamepad,
lerobot/so101 leader, lekiwi), dataset conversion
(`scripts/convert/isaaclab2lerobot*.py`), state-machine datagen, and the
policy client layer (`.../policy`).

Apache-2.0 obligations honoured: `LICENSE` retained verbatim, `NOTICE` added
stating the derivation and upstream copyright, modifications documented in
`docs/upstream.md`. Existing per-file copyright headers are left intact.

## Vendored / interfaced third-party code

| Project | Where | License | Verified detail |
|---|---|---|---|
| IsaacLab | `dependencies/IsaacLab` (git submodule) | BSD-3-Clause | Submodule pinned to `3c6e67bb5c7ada942a6d1884ab69338f57596f77`; local checkout at `~/IsaacLab` reports `VERSION` = `2.3.0` at the same commit. |
| Isaac Sim | bundled with IsaacLab | NVIDIA Omniverse License | Local `_isaac_sim/VERSION` = `5.1.0-rc.19+release.26219.9c81211b.gl`. LeIsaac `pyproject.toml` classifier declares `Isaac Sim :: 5.1.0`. |
| OpenPI (Physical Intelligence) | interfaced via `.../policy/openpi/` | Apache-2.0 | `msgpack_numpy.py` + `image_tools.py` are adapted from OpenPI. Upstream client targets OpenPI commit `5bff19b0c0c447c7a7eaaaccf03f36d50998ec9d`. |
| msgpack-numpy | `.../policy/openpi/msgpack_numpy.py` | BSD-3-Clause | File docstring credits https://github.com/lebedov/msgpack-numpy as the adaptation source. |
| LeRobot (HuggingFace) | `.../policy/lerobot/`, dataset conversion | Apache-2.0 | Upstream client targets LeRobot `v0.3.3`; `pyproject.toml` optional extra pins `lerobot==0.4.2`. |
| Isaac-GR00T (NVIDIA) | `.../policy/gr00t/` (demoted to optional) | Apache-2.0 | N1.5 client targets commit `4af2b622892f7dcb5aae5a3fb70bcb02dc217b96`; N1.6 client targets `e8e625f4f21898c506a1d8f7d20a289c97a52acf`. |

## Note on GR00T

GR00T is **retained** as an optional policy backend, not deleted. Its code is
Apache-2.0 and its attribution above remains binding for as long as the files
are present. The re-targeting work moves GR00T off the *primary* policy path;
it does not strip its licensing.
70 changes: 70 additions & 0 deletions data_collection/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Data collection architecture.

Control source -> Canonical Action -> Recorder
-------------------------------------------------
Teleoperator (keyboard / gamepad / SO-101 leader / lekiwi)
ScriptedPolicy (state-machine expert, e.g. pick_orange)
Policy (Pi05Policy / Gr00tPolicy for replay demos)

The recorder does NOT care where the action came from. It consumes a canonical
RobotAction each step and appends it to a raw episode.

Existing reusable LeIsaac infrastructure (KEEP):
- leisaac.enhance.managers.lerobot_recorder_manager.LeRobotRecorderManager
(IsaacLab RecorderManager subclass; writes LeRobot frames per step)
- leisaac.enhance.datasets.hdf5_dataset_file_handler.StreamingHDF5DatasetFileHandler
(raw HDF5 episode stream)
- leisaac.devices.* (keyboard, gamepad, lerobot SO-101 leader, lekiwi)
- scripts/datagen/state_machine/{generate,replay}.py (scripted expert)

This module provides the canonical interface so new control sources plug in
without touching the recorder.

Runtime status:
- Interface + recorder seam : IMPLEMENTED (pure Python)
- LeIsaac recorder/manager : VERIFIED in source (needs Isaac Sim to run)
- Teleoperation devices : VERIFIED in source (SO-101 leader keyboard/gamepad)
- Live demo recording : BLOCKED by Isaac Sim runtime (omni.isaac.ml_archive)
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Iterator

from environments.action import RobotAction
from environments.observation import Observation


class ControlSource(ABC):
"""Produces canonical RobotActions given the current observation.

Implemented by teleoperators and scripted/scripted-expert policies.
"""

@abstractmethod
def get_action(self, observation: Observation) -> RobotAction:
...

def reset(self) -> None:
return None


class EpisodeRecorder(ABC):
"""Consumes canonical actions + observations and stores a raw episode."""

@abstractmethod
def reset(self) -> None:
...

@abstractmethod
def record_step(self, observation: Observation, action: RobotAction) -> None:
...

@abstractmethod
def finalize(self) -> dict:
"""Return episode metadata (id, length, instruction, paths)."""

def __iter__(self) -> Iterator[tuple[Observation, RobotAction]]:
# Optional: default recorders may not support replay.
raise NotImplementedError
67 changes: 67 additions & 0 deletions dataset/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Dataset pipeline architecture.

Raw Simulation Episodes (HDF5 / IsaacLab EpisodeData)
↓
Raw Dataset Schema
↓
Converter (IsaacLab HDF5 -> LeRobot Dataset v3)
↓
LeRobot Dataset
↓
Dataset Validator

Existing reusable LeIsaac infrastructure (KEEP, do not rewrite):
- scripts/convert/isaaclab2lerobotv3.py : HDF5 -> LeRobot v3 (lerobot==0.4.2)
- scripts/convert/isaaclab2lerobot.py : earlier HDF5 -> LeRobot converter
- scripts/convert/lerobot2isaaclab.py : reverse direction
- leisaac.enhance.datasets.lerobot_dataset_handler.LeRobotDatasetHandler
(builds the LeRobot feature schema from an IsaacLab env)
- leisaac.enhance.datasets.hdf5_dataset_file_handler
(streaming HDF5 raw-episode writer)

This module defines the schema/validation seam so the canonical representation
(raw episode) is distinct from the LeRobot representation.

Mapping documented for SO-101 (verified against LeIsaac source):
camera "front" -> observation.images.front (uint8 HWC, 640x480)
camera "wrist" -> observation.images.wrist (uint8 HWC, 640x480)
robot state -> observation.state (6,) radians
action -> action (6,) radians
instruction -> task_description string
timestamps -> per-frame sim time
episodes -> HDF5 group per demo; LeRobot episode index

Runtime status:
- Schema/validation seam : IMPLEMENTED (pure Python)
- LeIsaac HDF5 converters : VERIFIED in source (need Isaac Sim + lerobot==0.4.2)
- Live conversion : BLOCKED by Isaac Sim runtime
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any


@dataclass
class RawEpisodeSchema:
"""Canonical raw-episode description (before LeRobot conversion)."""

camera_keys: tuple[str, ...] = ("front", "wrist")
image_shape: tuple[int, int, int] = (480, 640, 3) # (h, w, c)
state_dim: int = 6
action_dim: int = 6
fps: int = 30
has_instruction: bool = True
extra: dict[str, Any] = field(default_factory=dict)


@dataclass
class LerobotDatasetRef:
"""Pointer to a produced LeRobot dataset."""

repo_id: str
root: str
fps: int = 30
episode_count: int = 0
features: dict[str, Any] = field(default_factory=dict)
Loading