Generalize LeRobot dataset converters to N-dof robots#165
Open
asierarranz wants to merge 1 commit into
Open
Conversation
The isaaclab2lerobot(v3) conversion path hardcoded the 6 SO-101 joints: convert_leisaac_action_to_lerobot() always used the SO-101 limit tables and silently zeroed any extra action dimensions, and the frame builders sliced actions with a literal 6. - robot_utils: convert_leisaac_action_to_lerobot() and convert_lerobot_action_to_leisaac() accept per-robot joint_limits/motor_limits (defaulting to the SO-101 follower) and validate that every converted dimension has a configured limit instead of silently dropping values. - build_feature_from_env() validates that default_feature_joint_names is non-empty and unique; feature shapes already derive from it. - Task templates expose usd_joint_limits/motor_limits cfg fields and derive arm slicing from the configured joint count. - isaaclab2lerobot(v3).py fail loudly instead of writing an empty LeRobot dataset when every episode is skipped. - Docs: note that the converters take joint names, joint count, and value ranges from the task's environment configuration. Existing SO-101, bi-SO101, and LeKiwi tasks see no behavior change: all defaults remain the SO-101 follower limits. Validated by onboarding an 8-dof arm (6 revolute + 2 prismatic joints) end to end. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
EverNorif
reviewed
Jul 9, 2026
EverNorif
left a comment
Collaborator
There was a problem hiding this comment.
Thank you for your PR. I’ve left a few comments.
| if now_episode_index == 0: | ||
| raise RuntimeError( | ||
| "No episodes were converted: every episode was skipped because it was unsuccessful or" | ||
| " shorter than 10 frames. Refusing to write an empty LeRobot dataset." |
Collaborator
There was a problem hiding this comment.
It seems this should be “shorter than 5 frames.” Alternatively, we could provide a global SKIP_FRAMES parameter to control this.
| if now_episode_index == 0: | ||
| raise RuntimeError( | ||
| "No episodes were converted: every episode was skipped because it was unsuccessful or" | ||
| " shorter than 10 frames. Refusing to write an empty LeRobot dataset." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Right now the dataset converters only really work for the SO-101: point them at any other arm and the extra joints are silently written as zeros. No error, no warning, just a corrupted dataset. That is exactly what #142 and #136 ran into, and what we hit while onboarding an 8-dof arm (reBot DevArm, 6 revolute + 2 prismatic joints). This PR makes the converters work for any arm.
What changed:
convert_leisaac_action_to_lerobot()andconvert_lerobot_action_to_leisaac()take optionaljoint_limits/motor_limitsand validate that every converted dimension has a configured limit, instead of silently zero-filling.usd_joint_limits/motor_limitscfg fields and derive arm slicing from the configured joint count. A new robot only overrides its env cfg, same mechanism asdefault_feature_joint_names.build_feature_from_env()checks that joint names are non-empty and unique.isaaclab2lerobot.pyandisaaclab2lerobotv3.pynow raise if zero episodes were converted instead of writing an empty dataset. That silent empty output cost us an hour of debugging.policy_support.mdon how the converters pick up joint names, count and ranges from the task cfg.Zero behavior change for existing SO-101, bi-SO101 and LeKiwi tasks: all defaults are the same SO-101 limit tables as before, and the converted values stay bit-identical (verified).
Test plan: converted 40 XR teleop demos of the 8-dof arm with
isaaclab2lerobotv3.pyand fine-tuned GR00T N1.7 on the result, end to end. The repo'sblack/isort/flake8checks are clean on the touched files.This could also give #156 a common base, instead of adding per-robot copies of the conversion functions.