Skip to content

Commit 6813682

Browse files
Adds APIs to Isaac Lab Mimic for supporting loco-manipulation datagen (#3992)
# Description <!-- Thank you for your interest in sending a pull request. Please make sure to check the contribution guidelines. Link: https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html 💡 Please try to keep PRs small and focused. Large PRs are harder to review and merge. --> This PR adds new APIs to Isaac Lab Mimic to expand support for loco-manipulation data generation. It adds: - Processing for body end effector to treat a robot's base as an eef during Mimic data generation. This enables the use of the same Mimic annotation and subtask interface to enable lower body movement. - An optional way to cleanly add custom recorders for Mimic data generation (useful for when users want to record beyond the action/state data provided by Isaac Lab's default recorder). - Interface for enabling a navigation p-controller during Mimic data generation for robot's with mobile bases. ## Type of change <!-- As you go through the list, delete the ones that are not applicable. --> - New feature (non-breaking change which adds functionality) ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there <!-- As you go through the checklist above, you can mark something as done by putting an x character in it For example, - [x] I have done this task - [ ] I have not done this task --> --------- Signed-off-by: peterd-NV <[email protected]> Co-authored-by: Kelly Guo <[email protected]>
1 parent 18c7c58 commit 6813682

File tree

8 files changed

+136
-11
lines changed

8 files changed

+136
-11
lines changed

source/isaaclab/config/extension.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
# Note: Semantic Versioning is used: https://semver.org/
4-
version = "0.48.8"
4+
version = "0.48.9"
55

66
# Description
77
title = "Isaac Lab framework for Robot Learning"

source/isaaclab/docs/CHANGELOG.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
Changelog
22
---------
33

4+
5+
0.48.9 (2025-11-21)
6+
~~~~~~~~~~~~~~~~~~~
7+
8+
Added
9+
^^^^^
10+
11+
* Add navigation state API to IsaacLabManagerBasedRLMimicEnv
12+
* Add optional custom recorder config to MimicEnvCfg
13+
14+
415
0.48.8 (2025-10-15)
516
~~~~~~~~~~~~~~~~~~~
617

@@ -18,6 +29,7 @@ Changed
1829

1930
* Changed import from ``isaaclab.sim.utils`` to ``isaaclab.sim.utils.stage`` to properly propagate the Isaac Sim stage context.
2031

32+
2133
0.48.6 (2025-11-18)
2234
~~~~~~~~~~~~~~~~~~~
2335

source/isaaclab/isaaclab/envs/manager_based_rl_mimic_env.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
from isaaclab.envs import ManagerBasedRLEnv
1212

1313

14+
def optional_method(func):
15+
"""Decorator to mark a method as optional."""
16+
func.__is_optional__ = True
17+
return func
18+
19+
1420
class ManagerBasedRLMimicEnv(ManagerBasedRLEnv):
1521
"""The superclass for the Isaac Lab Mimic environments.
1622
@@ -156,3 +162,21 @@ def serialize(self):
156162
and used in utils/env_utils.py.
157163
"""
158164
return dict(env_name=self.spec.id, type=2, env_kwargs=dict())
165+
166+
@optional_method
167+
def get_navigation_state(self, env_ids: Sequence[int] | None = None) -> dict[str, torch.Tensor]:
168+
"""
169+
Optional method. Only required when using navigation controller locomanipulation data generation.
170+
171+
Gets the navigation state of the robot. Required when use of the navigation controller is
172+
enabled. The navigation state includes a boolean flag "is_navigating" to indicate when the
173+
robot is under control by the navigation controller, and a boolean flag "navigation_goal_reached"
174+
to indicate when the navigation goal has been reached.
175+
176+
Args:
177+
env_ids: The environment index to get the navigation state for. If None, all envs are considered.
178+
179+
Returns:
180+
A dictionary that of navigation state flags (False or True).
181+
"""
182+
raise NotImplementedError

source/isaaclab/isaaclab/envs/mimic_env_cfg.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"""
1212
import enum
1313

14+
from isaaclab.managers.recorder_manager import RecorderManagerBaseCfg
1415
from isaaclab.utils import configclass
1516

1617

@@ -76,6 +77,9 @@ class DataGenConfig:
7677
use_skillgen: bool = False
7778
"""Whether to use skillgen to generate motion trajectories."""
7879

80+
use_navigation_controller: bool = False
81+
"""Whether to use a navigation controller to generate loco-manipulation trajectories."""
82+
7983

8084
@configclass
8185
class SubTaskConfig:
@@ -308,3 +312,6 @@ class MimicEnvCfg:
308312

309313
# List of configurations for subtask constraints
310314
task_constraint_configs: list[SubTaskConstraintConfig] = []
315+
316+
# Optional recorder configuration
317+
mimic_recorder_config: RecorderManagerBaseCfg | None = None

source/isaaclab_mimic/config/extension.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
# Semantic Versioning is used: https://semver.org/
4-
version = "1.0.15"
4+
version = "1.0.16"
55

66
# Description
77
category = "isaaclab"

source/isaaclab_mimic/docs/CHANGELOG.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
Changelog
22
---------
33

4+
5+
1.0.16 (2025-11-10)
6+
~~~~~~~~~~~~~~~~~~~
7+
8+
Added
9+
^^^^^
10+
11+
* Add body end effector to Mimic data generation to enable loco-manipulation data generation when a navigation p-controller is provided.
12+
13+
414
1.0.15 (2025-09-25)
515

616
Fixed

source/isaaclab_mimic/isaaclab_mimic/datagen/data_generator.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@
77
Base class for data generator.
88
"""
99
import asyncio
10+
import copy
11+
import logging
1012
import numpy as np
1113
import torch
1214
from typing import Any
1315

1416
import isaaclab.utils.math as PoseUtils
17+
18+
logger = logging.getLogger(__name__)
19+
1520
from isaaclab.envs import (
1621
ManagerBasedRLMimicEnv,
1722
MimicEnvCfg,
@@ -688,6 +693,10 @@ async def generate( # noqa: C901
688693
eef_subtasks_done[eef_name] = False
689694

690695
prev_src_demo_datagen_info_pool_size = 0
696+
697+
if self.env_cfg.datagen_config.use_navigation_controller:
698+
was_navigating = False
699+
691700
# While loop that runs per time step
692701
while True:
693702
async with self.src_demo_datagen_info_pool.asyncio_lock:
@@ -880,8 +889,54 @@ async def generate( # noqa: C901
880889
generated_actions.extend(exec_results["actions"])
881890
generated_success = generated_success or exec_results["success"]
882891

892+
# Get the navigation state
893+
if self.env_cfg.datagen_config.use_navigation_controller:
894+
processed_nav_subtask = False
895+
navigation_state = self.env.get_navigation_state(env_id)
896+
assert navigation_state is not None, "Navigation state cannot be None when using navigation controller"
897+
is_navigating = navigation_state["is_navigating"]
898+
navigation_goal_reached = navigation_state["navigation_goal_reached"]
899+
883900
for eef_name in self.env_cfg.subtask_configs.keys():
884901
current_eef_subtask_step_indices[eef_name] += 1
902+
903+
# Execute locomanip navigation controller if it is enabled via the use_navigation_controller flag
904+
if self.env_cfg.datagen_config.use_navigation_controller:
905+
if "body" not in self.env_cfg.subtask_configs.keys():
906+
error_msg = (
907+
'End effector with name "body" not found in subtask configs. "body" must be a valid end'
908+
" effector to use the navigation controller.\n"
909+
)
910+
logger.error(error_msg)
911+
raise RuntimeError(error_msg)
912+
913+
# Repeat the last nav subtask action if the robot is navigating and hasn't reached the waypoint goal
914+
if (
915+
current_eef_subtask_step_indices["body"] == len(current_eef_subtask_trajectories["body"]) - 1
916+
and not processed_nav_subtask
917+
):
918+
if is_navigating and not navigation_goal_reached:
919+
for name in self.env_cfg.subtask_configs.keys():
920+
current_eef_subtask_step_indices[name] -= 1
921+
processed_nav_subtask = True
922+
923+
# Else skip to the end of the nav subtask if the robot has reached the waypoint goal before the end
924+
# of the human recorded trajectory
925+
elif was_navigating and not is_navigating and not processed_nav_subtask:
926+
number_of_steps_to_skip = len(current_eef_subtask_trajectories["body"]) - (
927+
current_eef_subtask_step_indices["body"] + 1
928+
)
929+
for name in self.env_cfg.subtask_configs.keys():
930+
if current_eef_subtask_step_indices[name] + number_of_steps_to_skip < len(
931+
current_eef_subtask_trajectories[name]
932+
):
933+
current_eef_subtask_step_indices[name] = (
934+
current_eef_subtask_step_indices[name] + number_of_steps_to_skip
935+
)
936+
else:
937+
current_eef_subtask_step_indices[name] = len(current_eef_subtask_trajectories[name]) - 1
938+
processed_nav_subtask = True
939+
885940
subtask_ind = current_eef_subtask_indices[eef_name]
886941
if current_eef_subtask_step_indices[eef_name] == len(
887942
current_eef_subtask_trajectories[eef_name]
@@ -923,6 +978,10 @@ async def generate( # noqa: C901
923978
else:
924979
current_eef_subtask_step_indices[eef_name] = None
925980
current_eef_subtask_indices[eef_name] += 1
981+
982+
if self.env_cfg.datagen_config.use_navigation_controller:
983+
was_navigating = copy.deepcopy(is_navigating)
984+
926985
# Check if all eef_subtasks_done values are True
927986
if all(eef_subtasks_done.values()):
928987
break

source/isaaclab_mimic/isaaclab_mimic/datagen/generation.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55

66
import asyncio
77
import contextlib
8+
import sys
89
import torch
10+
import traceback
911
from typing import Any
1012

1113
from isaaclab.envs import ManagerBasedRLMimicEnv
1214
from isaaclab.envs.mdp.recorders.recorders_cfg import ActionStateRecorderManagerCfg
1315
from isaaclab.managers import DatasetExportMode, TerminationTermCfg
16+
from isaaclab.managers.recorder_manager import RecorderManagerBaseCfg
1417

1518
from isaaclab_mimic.datagen.data_generator import DataGenerator
1619
from isaaclab_mimic.datagen.datagen_info_pool import DataGenInfoPool
@@ -47,14 +50,20 @@ async def run_data_generator(
4750
"""
4851
global num_success, num_failures, num_attempts
4952
while True:
50-
results = await data_generator.generate(
51-
env_id=env_id,
52-
success_term=success_term,
53-
env_reset_queue=env_reset_queue,
54-
env_action_queue=env_action_queue,
55-
pause_subtask=pause_subtask,
56-
motion_planner=motion_planner,
57-
)
53+
try:
54+
results = await data_generator.generate(
55+
env_id=env_id,
56+
success_term=success_term,
57+
env_reset_queue=env_reset_queue,
58+
env_action_queue=env_action_queue,
59+
pause_subtask=pause_subtask,
60+
motion_planner=motion_planner,
61+
)
62+
except Exception as e:
63+
sys.stderr.write(traceback.format_exc())
64+
sys.stderr.flush()
65+
raise e
66+
5867
if bool(results["success"]):
5968
num_success += 1
6069
else:
@@ -141,6 +150,7 @@ def setup_env_config(
141150
num_envs: int,
142151
device: str,
143152
generation_num_trials: int | None = None,
153+
recorder_cfg: RecorderManagerBaseCfg | None = None,
144154
) -> tuple[Any, Any]:
145155
"""Configure the environment for data generation.
146156
@@ -180,7 +190,10 @@ def setup_env_config(
180190
env_cfg.observations.policy.concatenate_terms = False
181191

182192
# Setup recorders
183-
env_cfg.recorders = ActionStateRecorderManagerCfg()
193+
if recorder_cfg is None:
194+
env_cfg.recorders = ActionStateRecorderManagerCfg()
195+
else:
196+
env_cfg.recorders = recorder_cfg
184197
env_cfg.recorders.dataset_export_dir_path = output_dir
185198
env_cfg.recorders.dataset_filename = output_file_name
186199

0 commit comments

Comments
 (0)