I/O Utilities¶
Reading and writing episode data, converting recordings to LeRobot datasets, image conversion, and reading body-model
pickles without chumpy. The datasets and recording guide explains the
recording workflow.
File handlers¶
The recorder manager writes episodes through a file handler, chosen by name from FILE_HANDLER_REGISTRY (HDF5 or
NPZ). EpisodeData is the in-memory container of one recorded trajectory.
Base class and registry for dataset file handlers.
Attributes¶
Classes¶
FileHandlerBase
¶
FileHandlerBase(options: T)
Bases: ConfigurableMixin[FileHandlerOptions]
Abstract class for handling dataset files.
Source code in eden/utils/common.py
Methods:¶
resolve_path
abstractmethod
¶
open
abstractmethod
¶
Open a file. If env_cfg is provided, implementations may warn on mismatch.
create
abstractmethod
¶
write_episode
abstractmethod
¶
write_episode(episode: EpisodeData)
flush
abstractmethod
¶
close
abstractmethod
¶
load_episode
abstractmethod
¶
load_episode(episode_name: str, device: str = 'cpu') -> EpisodeData | None
EpisodeData container for recorded trajectories.
Classes¶
EpisodeData
¶
Class to store episode data.
Source code in eden/utils/file_handler/episode_data.py
Attributes¶
Methods:¶
is_empty
¶
add
¶
Add a key-value pair to the dataset.
The key can be nested by using the "/" character. For example: "obs/joint_pos".
| PARAMETER | DESCRIPTION |
|---|---|
key
|
The key name.
TYPE:
|
value
|
The corresponding value, either a tensor or a (possibly nested) dict.
TYPE:
|
Source code in eden/utils/file_handler/episode_data.py
get
¶
General getter for any key in the episode data.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
Hierarchical key, e.g.
TYPE:
|
index
|
Specific timestep to retrieve.
TYPE:
|
sequential
|
If True, use and increment a per-key pointer.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor or dict or None
|
The tensor (or dict of tensors) at the requested timestep, or None if the key is missing or the index is out of range. |
Examples:
>>> # Random access
>>> state5 = episode.get("states/obs/joint_pos", index=5)
>>> action3 = episode.get("actions", index=3)
>>> # Sequential access
>>> next_state = episode.get("states/obs", sequential=True)
>>> next_action = episode.get("actions", sequential=True)
Source code in eden/utils/file_handler/episode_data.py
size
¶
size(key: str)
Return the size of the data at the given key.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
Hierarchical key, e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
int or dict or None
|
Size of the stored value, a dict of sizes for nested keys, or None if the key is missing. |
Source code in eden/utils/file_handler/episode_data.py
pre_export
¶
pre_export_copy
¶
Return a new EpisodeData with lists stacked into tensors.
Source code in eden/utils/file_handler/episode_data.py
HDF5 dataset file handler.
Attributes¶
Classes¶
HDF5FileHandler
¶
Bases: FileHandlerBase
HDF5 dataset file handler for storing and loading episode data.
The format of the stored structure dataset_file.hdf5 └── data/ # _hdf5_data_group ├── demo_0/ # Episode group │ ├── actions # Dataset (Tensor saved as NumPy array) │ ├── states # Nested dict -> datasets │ │ └── obs # Dataset │ └── ... # Any other keys │ ├── attrs: │ │ seed: int │ │ success: bool │ │ env_id: int ├── demo_1/ │ └── ... └── ... # Additional episodes attrs of data group: env_cfg: str (JSON) # Serialized environment configuration total: int # Number of episodes written
Initialize the HDF5 dataset file handler.
Source code in eden/utils/file_handler/hdf5_file_handler.py
Attributes¶
Methods:¶
resolve_path
¶
open
¶
Open an existing dataset file.
Source code in eden/utils/file_handler/hdf5_file_handler.py
create
¶
Create a new dataset file.
Source code in eden/utils/file_handler/hdf5_file_handler.py
get_episode_names
¶
load_episode
¶
load_episode(episode_name: str, device: str = 'cpu') -> EpisodeData | None
Load episode data from the file.
Source code in eden/utils/file_handler/hdf5_file_handler.py
write_episode
¶
write_episode(episode: EpisodeData)
Add an episode to the dataset.
Source code in eden/utils/file_handler/hdf5_file_handler.py
flush
¶
Functions:¶
NPZ dataset file handler.
Attributes¶
Classes¶
NPZFileHandler
¶
Bases: FileHandlerBase
NPZ dataset file handler for storing and loading episode data.
Since NPZ files are flat key-value stores of numpy arrays, we use a naming convention to represent the hierarchical episode structure:
dataset_file.npz
├── __meta__env_cfg # JSON string stored as bytes array
├── __meta__total # scalar: number of episodes
├── demo_0/actions # episode data (slash-separated hierarchy)
├── demo_0/states/obs # nested episode data
├── demo_0/__attrs__ # episode attrs as a JSON bytes array
├── demo_1/actions
└── ...
Source code in eden/utils/file_handler/npz_file_handler.py
Attributes¶
Methods:¶
resolve_path
¶
open
¶
Source code in eden/utils/file_handler/npz_file_handler.py
create
¶
Source code in eden/utils/file_handler/npz_file_handler.py
get_episode_names
¶
load_episode
¶
load_episode(episode_name: str, device: str = 'cpu') -> EpisodeData | None
Source code in eden/utils/file_handler/npz_file_handler.py
write_episode
¶
write_episode(episode: EpisodeData)
Source code in eden/utils/file_handler/npz_file_handler.py
flush
¶
Source code in eden/utils/file_handler/npz_file_handler.py
close
¶
Source code in eden/utils/file_handler/npz_file_handler.py
Functions:¶
Configuration options for dataset file handlers.
Classes¶
FileHandlerOptions
¶
Bases: DynamicConfigurableOptions
Source code in eden/options/options.py
LeRobot conversion¶
Convert an Eden HDF5 recording into a LeRobot v3 dataset (eden lerobot convert).
Convert an Eden teleoperation HDF5 recording into a LeRobot v3 dataset.
Used as a library:
from eden.utils.lerobot import convert_hdf5_to_lerobot
convert_hdf5_to_lerobot([...]) # argparse-style argv
or via CLI: eden lerobot convert --hdf5 ... --output-dir ....
Attributes¶
Classes¶
Functions:¶
convert
¶
convert(hdf5_path: Path, output_dir: Path, repo_id: str, robot_type: str, task: str, force: bool = False, image_writer_threads: int = 4) -> None
Convert an HDF5 recording into a LeRobot v3 dataset on disk.
Source code in eden/utils/lerobot/convert.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | |
convert_hdf5_to_lerobot
¶
CLI entry point: eden lerobot convert ....
Source code in eden/utils/lerobot/convert.py
Images¶
Image conversion and resizing helpers.
Eden camera terms (:func:eden.managers.terms.observations.camera.rgb_image) return
(num_envs, H, W, C) uint8 tensors. Consumers (policy servers, recorders, ...)
differ only in layout (HWC vs CHW) and in how they want the image resized, so those are
the two axes exposed here:
- :func:
to_uint8_hwc/ :func:to_chw_uint8— layout, no resampling. - :func:
resize_uint8— plain resize (cv2 bilinear; Isaac-GR00T's SimplerEnv eval). - :func:
resize_with_pad— aspect-preserving letterbox resize, a bit-exact port ofopenpi_client.image_tools.resize_with_pad(pinned bytests/utils/test_image.py) so OpenPI-family clients do not needopenpi_clientinstalled.
Functions:¶
to_uint8_hwc
¶
(1, H, W, C) or (H, W, C) -> (H, W, 3) uint8 numpy.
A 4-channel (RGBA, as the madrona/Nyx renderers return) input has its alpha channel dropped. Any other channel count, dtype, or a batch axis larger than one raises: a float image or a batched render reaching a server is a wiring error, not something to rescale or slice quietly.
Source code in eden/utils/image.py
to_chw_uint8
¶
(1, H, W, C) or (H, W, C) -> contiguous (3, H, W) uint8 numpy.
resize_uint8
¶
Resize an (H, W, 3) uint8 image to image_size=(h, w) with cv2 bilinear filtering.
No-op when the size already matches. Matches gr00t/eval/sim/SimplerEnv/simpler_env.py.
Source code in eden/utils/image.py
resize_with_pad
¶
Letterbox-resize [..., H, W, C] images to (height, width) without distortion.
Port of openpi_client.image_tools.resize_with_pad (itself replicating
tf.image.resize_with_pad): scale by the larger of the two ratios with PIL bilinear
filtering, then paste centred on a zero canvas. Returns the input unchanged when the
size already matches.
Source code in eden/utils/image.py
convert_to_uint8
¶
[0, 1] float image -> uint8; uint8 input is returned unchanged.
Port of openpi_client.image_tools.convert_to_uint8. Kept only for the OpenPI
pipeline, where it is documented as the last preprocessing step; new code should
hand :func:to_uint8_hwc a uint8 render instead.
Source code in eden/utils/image.py
prepare_image_padded
¶
Eden render -> (h, w, 3) uint8 via :func:resize_with_pad (the OpenPI client pipeline).
Source code in eden/utils/image.py
prepare_image_resized
¶
Eden render -> (h, w, 3) uint8 via :func:resize_uint8 (the GR00T client pipeline).
Source code in eden/utils/image.py
Body-model pickles¶
MANO, SMPL and SMPL-H .pkl files pickle chumpy arrays; these helpers read them without installing chumpy.
Read the body-model pickles that hold chumpy arrays (MANO, SMPL, SMPL-H) without chumpy.
Those releases pickle shapedirs as a chumpy.ch.Ch (SMPL) or a chumpy.reordering.Select over one (MANO,
SMPL-H). chumpy itself is unmaintained, its PyPI release does not build on a current numpy, and a published wheel cannot
depend on the git commit that does, so Eden reads the two classes itself: :class:ChumpyFreeUnpickler for Eden's own
loader (:func:eden.datasets.utils.body_model.body_model_arrays), and :func:install_chumpy_pickle_modules for a
third-party loader that calls a plain pickle.load (smplx, in the SOMA retargeter's worker process).
numpy-only on purpose: the SOMA worker imports it and must stay free of Genesis.
Classes¶
ChumpyFreeUnpickler
¶
Bases: Unpickler
Unpickles a body-model .pkl with :data:_CHUMPY_CLASSES standing in for chumpy's, so the file reads
without chumpy installed (its PyPI release does not build on a current numpy). Every other class resolves as
pickle.load would resolve it.
Methods:¶
find_class
¶
Source code in eden/utils/chumpy_pickle.py
Functions:¶
install_chumpy_pickle_modules
¶
Make chumpy.ch.Ch / chumpy.reordering.Select resolve to Eden's stand-ins, process-wide.
For a third-party loader that reads a model .pkl with a plain pickle.load (smplx, under the SOMA
retargeter), which a custom unpickler cannot reach. It registers chumpy, chumpy.ch and
chumpy.reordering in sys.modules, shadowing an installed chumpy for the rest of the process, so only a
process that exists to run such a loader calls it (the SOMA worker). The stand-ins read every MANO, SMPL and
SMPL-H release bit-identically to chumpy. A chumpy the process has already imported is left in place; a
sys.modules["chumpy"] = None (an import blocked on purpose) is not an import, and is replaced.
Source code in eden/utils/chumpy_pickle.py
Buffers¶
The buffers in eden/utils/buffers/ (CircularBuffer for observation history, ConcatBatchTensor, and the
ResetStateBuffer that the ResetFromStateBuffer event restores from) are not rendered here: the directory has no
__init__.py. Read their docstrings in the source.