This repository provides a toolkit for converting ManiSkill replay HDF5 trajectory files into the RLDS format, usable by downstream robot learning pipelines, including VLA-style pipelines.
ManiSkill provides high-quality simulated robot manipulation trajectories, and these trajectories are commonly stored as HDF5 files after recording or replaying demonstrations. However, many robot learning data pipelines use episode-based dataset formats such as TFDS/RLDS, where each trajectory is represented as an episode and each timestep is represented as a step.
This toolkit bridges the gap between ManiSkill replay HDF5 files and the RLDS ecosystem. It converts ManiSkill trajectories into a structured TFDS/RLDS dataset with observations, actions, rewards, robot proprio, language instructions, and episode metadata.
This toolkit is useful if you want to:
- Convert ManiSkill replay HDF5 trajectories into TFDS/RLDS format.
- Preserve RGB, optional wrist RGB, depth, and robot state observations.
- Store each ManiSkill
traj_xas one RLDS episode. - Validate converted datasets before using them in downstream learning pipelines.
- Build a reusable dataset conversion workflow for different ManiSkill tasks.
The converter takes ManiSkill replay HDF5 files as input and produces a TFDS/RLDS dataset as output. Each ManiSkill trajectory group, such as traj_0, traj_1, or traj_2, is converted into one RLDS episode. Inside each episode, the trajectory timesteps are stored as RLDS steps.
The overall workflow is:
ManiSkill demo or generated trajectory
|
| replay_trajectory
v
ManiSkill HDF5 file with RGB/RGBD observations
|
| inspect_h5.py
v
Check H5 paths, shapes, and dtypes
|
| tfds build (maniskill_convert_dataset.py)
v
TFDS/RLDS dataset
|
| check_tfds_dataset.py
v
Validate episode-level and step-level fields
|
| check_dlimp_dataset.py
v
Validate trajectory-level RLDS reading
Create a clean Python environment.
conda create -n maniskill-rlds python=3.10 -y
conda activate maniskill-rldsClone this repository and enter the project directory:
git clone https://github.com/exileJordan/Maniskill_RLDS_Converter.git
cd Maniskill_RLDS_ConverterInstall dependencies from requirements.txt:
pip install -r requirements.txtIn the quick start section, use "PickCube-v1" as example.
(1) Download Maniskill "PickCube-v1" Trajectory and Replay
python -m mani_skill.utils.download_demo "PickCube-v1"The downloaded demo directory should look like:
# result
demos/
└── PickCube-v1/motionplanning
├── trajectory.h5
└── trajectory.json(2) Replay PickCube trajectory and convert to target dataset. We recommend starting with a small number of trajectories.
python -m mani_skill.trajectory.replay_trajectory \
--traj-path ${DEMO_PATH}PickCube-v1/motionplanning/trajectory.h5 \
--use-first-env-state -c pd_ee_delta_pos -o rgb \
--save-traj --num-envs 10 -b physx_cpu \
--count 4(3) Create a new directory and copy your replayed H5 files into that directory:
mkdir -p /path/to/maniskill_h5
cp /path/to/trajectory*.h5 /path/to/maniskill_h5/The directory should look like:
/path/to/maniskill_h5/
trajectory_*.h5
Before building the dataset, inspect your H5 file structure:
python maniskill_rlds_tool/inspect_h5.py \
/path/to/maniskill_h5/trajectory_0.h5Expected output for example:
traj_98/
traj_98/actions: shape=(81, 4), dtype=float32
traj_98/env_states/
traj_98/env_states/actors/
traj_98/env_states/actors/cube: shape=(82, 13), dtype=float32
traj_98/env_states/actors/goal_site: shape=(82, 13), dtype=float32
traj_98/env_states/actors/table-workspace: shape=(82, 13), dtype=float32
traj_98/env_states/articulations/
traj_98/env_states/articulations/panda: shape=(82, 31), dtype=float32
traj_98/obs/
traj_98/obs/agent/
traj_98/obs/agent/qpos: shape=(82, 9), dtype=float32
traj_98/obs/agent/qvel: shape=(82, 9), dtype=float32
traj_98/obs/extra/
traj_98/obs/extra/goal_pos: shape=(82, 3), dtype=float32
traj_98/obs/extra/is_grasped: shape=(82,), dtype=bool
traj_98/obs/extra/tcp_pose: shape=(82, 7), dtype=float32
traj_98/obs/sensor_data/
traj_98/obs/sensor_data/base_camera/
traj_98/obs/sensor_data/base_camera/rgb: shape=(82, 128, 128, 3), dtype=uint8
traj_98/obs/sensor_param/
traj_98/obs/sensor_param/base_camera/
traj_98/obs/sensor_param/base_camera/cam2world_gl: shape=(82, 4, 4), dtype=float32
traj_98/obs/sensor_param/base_camera/extrinsic_cv: shape=(82, 3, 4), dtype=float32
traj_98/obs/sensor_param/base_camera/intrinsic_cv: shape=(82, 3, 3), dtype=float32
traj_98/success: shape=(81,), dtype=bool
traj_98/terminated: shape=(81,), dtype=bool
traj_98/truncated: shape=(81,), dtype=bool
Record the relevant H5 paths and shapes. They must match the constants and feature schema in maniskill_convert_dataset.py.
The converter uses actions, rgb, qpos, qvel, goal_pos, tcp_pose, is_grasped, terminated, and truncated. If rewards are missing, zero rewards are generated.
If your H5 file uses different camera names, dimensions, or observation paths, update the constants in:
maniskill_rlds_tool/maniskill_convert_dataset/maniskill_convert_dataset.py
Common constants to check:
RGB_PATH
WRIST_RGB_PATHS
DEPTH_PATHS
WRIST_DEPTH_PATHS
QPOS_PATH
QVEL_PATH
TCP_POSE_PATH
IS_GRASPED_PATH
GOAL_POS_PATHRun tfds build from the repository root:
tfds build maniskill_rlds_tool/maniskill_convert_dataset \
--manual_dir=/path/to/maniskill_h5 \
--data_dir=/path/to/tfds_output--manual_dir: directory containing your input H5 files.--data_dir: directory where TFDS/RLDS output will be written.
Expected output directory:
/path/to/tfds_output/
maniskill_convert_dataset/
1.0.0/
dataset_info.json
features.json
maniskill_convert_dataset-train.tfrecord-00000-of-00001
maniskill_convert_dataset-val.tfrecord-00000-of-00001
Use the TFDS checker to read one RLDS episode and inspect one step:
python maniskill_rlds_tool/check_tfds_dataset.py \
--name=maniskill_convert_dataset \
--data_dir=/path/to/tfds_output \
--split=trainValidate the validation split as well:
python maniskill_rlds_tool/check_tfds_dataset.py \
--name=maniskill_convert_dataset \
--data_dir=/path/to/tfds_output \
--split=valExpected output includes:
Episode keys: episode_metadata, steps
Observation keys: image, wrist_image, has_wrist_image, depth, has_depth, wrist_depth, has_wrist_depth, qpos, qvel, tcp_pose, is_grasped, goal_pos
Image shape: (128, 128, 3)
Qpos shape: (9,)
Qvel shape: (9,)
TCP pose shape: (7,)
Goal pos shape: (3,)
Action shape: (4,)
Use dlimp to flatten RLDS episodes into trajectory-level samples:
python maniskill_rlds_tool/check_dlimp_dataset.py \
--name=maniskill_convert_dataset \
--data_dir=/path/to/tfds_output \
--split=trainValidate the validation split:
python maniskill_rlds_tool/check_dlimp_dataset.py \
--name=maniskill_convert_dataset \
--data_dir=/path/to/tfds_output \
--split=valExpected output examples:
Trajectory keys: ...
Observation keys: ...
Image shape: (T,)
Image dtype: string
Qpos shape: (T, 9)
Qvel shape: (T, 9)
TCP pose shape: (T, 7)
Goal pos shape: (T, 3)
Action shape: (T, 4)
Language shape: (T,)
T is the length of one trajectory.
If both TFDS and dlimp validation pass, the RLDS dataset was generated successfully.
For the complete input HDF5 and output RLDS schema, see dataset_schema.md.
This is supported.
If wrist RGB, depth, or wrist depth is missing, the converter fills zero placeholders and records validity flags:
has_wrist_image
has_depth
has_wrist_depth
Use these flags to determine whether the modality was actually present in the original H5 file.
During the TFDS builder process, the dataset is split into train and val.
train: 90%
val: 10%
To check the split sizes, inspect the builder.info output printed by check_tfds_dataset.py.
You should see something like:
splits={
'train': <SplitInfo num_examples=90>,
'val': <SplitInfo num_examples=10>,
}
You can change the proportion in _split_generators() in maniskill_convert_dataset.py.
Error example:
No .h5 files found in manual_dir
Cause:
--manual_dir does not contain H5 files, or the path is incorrect.
Fix:
ls /path/to/maniskill_h5Make sure the directory contains files ending in .h5.
Error example:
KeyError: Unable to open object
Cause:
A path in the builder does not exist in your H5 file.
Fix:
1. Run inspect_h5.py.
2. Find the real path in your H5 file.
3. Update the corresponding path constant in maniskill_convert_dataset.py.
Cause:
The schema in `_info()` does not match the actual shape of your H5 data.
Common examples:
action shape is not (4,)
qpos/qvel shape is not (9,)
image resolution is not 128 x 128
depth shape is missing the channel dimension
Fix:
Update the feature shapes in `_info()` and rebuild the dataset.
This project builds on ManiSkill, TensorFlow Datasets, RLDS, and dlimp.
This project is released under the MIT License.