-
Notifications
You must be signed in to change notification settings - Fork 926
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds recorder manager in manager-based environments #1336
base: main
Are you sure you want to change the base?
Conversation
37b6d9c
to
62087f0
Compare
0e16d99
to
18f8f71
Compare
@@ -303,8 +377,13 @@ def step(self, action: torch.Tensor) -> tuple[VecEnvObs, dict]: | |||
if "interval" in self.event_manager.available_modes: | |||
self.event_manager.apply(mode="interval", dt=self.step_dt) | |||
|
|||
# -- compute observations | |||
self.prev_obs_buf = self.obs_buf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would obs_buf need to be cloned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does prev_obs_buf
get used? I only see it set.
# -- update command | ||
self.command_manager.compute(dt=self.step_dt) | ||
# -- step interval events | ||
if "interval" in self.event_manager.available_modes: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not 100% sure if it's ok to do randomization before reset. could there be cases where the events could trigger resets?
if "interval" in self.event_manager.available_modes: | ||
self.event_manager.apply(mode="interval", dt=self.step_dt) | ||
|
||
self.prev_obs_buf = self.obs_buf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could introduce additional observation computation if recording is not needed
* Pre-reset recording: This callback is invoked at the beginning of `env.reset()` before the reset is effective. | ||
* Post-reset recording: This callback is invoked at the end of `env.reset()`. | ||
* Pre-step recording: This callback is invoked at the beginning of `env.step()`, after the step action is processed | ||
and before the action is applied the action manager. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
applied "to" the action manager?
for env_id in range(env.num_envs): | ||
self._episodes[env_id] = EpisodeData() | ||
|
||
if hasattr(env.cfg, "env_name"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can use getattr
with default None
env_ids = slice(None) | ||
# -- assets | ||
for asset_name, articulation in self._articulations.items(): | ||
joint_position = torch.Tensor(state["articulation"][asset_name]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this need to be wrapped in torch.Tensor? and if so, we would also need to specify the device?
|
||
# set up the environment | ||
self.num_envs = 20 | ||
self.device = "cpu" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can device only be cpu? should we test for GPU case as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, ideally with subTest, but this will need to be set in each test_ method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second this, ideally use subtest within each test_ method below.
episode = EpisodeData() | ||
self.assertTrue(episode.is_empty()) | ||
|
||
episode.add("key", torch.Tensor([1, 2, 3], device="cpu")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar comment about device
|
||
# set the state | ||
self.scene.reset_to(state, env_ids, is_relative=is_relative) | ||
self.scene.write_data_to_sim() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reset_to seems to already call write_data_to_sim, is it necessary to call this again?
return torch.concat([delta_pose, gripper_vel], dim=1) | ||
|
||
|
||
def main(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems to be similar to the collect_demonstrations.py script, are there key differences for us to keep both scripts?
test_episode_data.py test seems to be failing |
@@ -303,8 +377,13 @@ def step(self, action: torch.Tensor) -> tuple[VecEnvObs, dict]: | |||
if "interval" in self.event_manager.available_modes: | |||
self.event_manager.apply(mode="interval", dt=self.step_dt) | |||
|
|||
# -- compute observations | |||
self.prev_obs_buf = self.obs_buf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does prev_obs_buf
get used? I only see it set.
state = dict() | ||
# -- assets | ||
state["articulation"] = dict() | ||
for asset_name, articulation in self._articulations.items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also velocity, and applied torque
|
||
# set up the environment | ||
self.num_envs = 20 | ||
self.device = "cpu" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, ideally with subTest, but this will need to be set in each test_ method
def __init__(self, cfg: RecorderTermCfg, env: ManagerBasedEnv) -> None: | ||
super().__init__(cfg, env) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: This is unnecessary if you are not adding to the init of the parent class. Can be removed.
def record_pre_reset(self, env_ids: Sequence[int] | None) -> tuple[str | None, torch.Tensor | None]: | ||
"""Record data at the beginning of env.reset() before reset is effective.""" | ||
return None, None | ||
|
||
def record_post_reset(self, env_ids: Sequence[int] | None) -> tuple[str | None, torch.Tensor | None]: | ||
"""Record data at the end of env.reset().""" | ||
return None, None | ||
|
||
def record_pre_step(self) -> tuple[str | None, torch.Tensor | None]: | ||
"""Record data in the beginning of env.step() after action is cached/processed in the ActionManager.""" | ||
return None, None | ||
|
||
def record_post_step(self) -> tuple[str | None, torch.Tensor | None]: | ||
"""Record data at the end of env.step() when all the managers are processed.""" | ||
return None, None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a description of the returns in the docstrings of these functions.
def get_state(self, is_relative: bool = False) -> dict[str, dict[str, torch.Tensor]]: | ||
"""Returns the state of the scene entities. | ||
Returns: | ||
A dictionary of the state of the scene entities. | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you describe the is_relative in the docstring? It could be interpreted as relative joint position and it looks like its just relative to environment origins.
articulation.write_joint_state_to_sim(joint_position, torch.zeros_like(joint_position), env_ids=env_ids) | ||
articulation.set_joint_position_target(joint_position, env_ids=env_ids) | ||
articulation.set_joint_velocity_target(torch.zeros_like(joint_position), env_ids=env_ids) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as with the comments in scene.get_state we will need to set velocity and effort here too.,
state = dict() | ||
# -- assets | ||
state["articulation"] = dict() | ||
for asset_name, articulation in self._articulations.items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be good to get velocity and applied_torque too for more general use cases.
|
||
# set up the environment | ||
self.num_envs = 20 | ||
self.device = "cpu" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second this, ideally use subtest within each test_ method below.
self.export_episodes(env_ids) | ||
|
||
def record_post_reset(self, env_ids: Sequence[int] | None) -> None: | ||
"""Trigger recorder terms for post-reset functions.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add env_ids to docstring
acf1518
to
d004d77
Compare
Description
This PR adds a recorder manager (RecorderManager) and relevant utility classes for recording data produced in various reset and step stages in manager-based environments.
Wither the built-in recorder manager, users can create custom recorder terms in their environment configurations with callback functions returning tensors to be recorded as environments advance. It is particularly useful for implementing an app that collects human-operated demos and for those who want to record robot actions for post-validation/replay in Isaac Lab environments.
The recorder manager works in both single- and multi-environment use cases. An episode for an environment instance is exported to a dataset file, via a dataset file handler, upon completion (a termination term is signaled a reset to the environment instance is triggered).
By default, the recorder manager is inactive (by assigning no recorder terms in the default configuration), which should have minimal performance impact for existing apps that do not require data recording.
Type of change
Checklist
pre-commit
checks with./isaaclab.sh --format
config/extension.toml
fileCONTRIBUTORS.md
or my name already exists there