1111import omni .isaac .core .utils .torch as torch_utils
1212import omni .log
1313
14- from omni .isaac .lab .managers import ActionManager , EventManager , ObservationManager
14+ from omni .isaac .lab .managers import ActionManager , EventManager , ObservationManager , RecorderManager
1515from omni .isaac .lab .scene import InteractiveScene
1616from omni .isaac .lab .sim import SimulationContext
1717from omni .isaac .lab .utils .timer import Timer
@@ -45,6 +45,9 @@ class ManagerBasedEnv:
4545 This includes resetting the scene to a default state, applying random pushes to the robot at different intervals
4646 of time, or randomizing properties such as mass and friction coefficients. This is useful for training
4747 and evaluating the robot in a variety of scenarios.
48+ * **Recorder Manager**: The recorder manager that handles recording data produced during different steps
49+ in the simulation. This includes recording in the beginning and end of a reset and a step. The recorded data
50+ is distinguished per episode, per environment and can be exported through a dataset file handler to a file.
4851
4952 The environment provides a unified interface for interacting with the simulation. However, it does not
5053 include task-specific quantities such as the reward function, or the termination conditions. These
@@ -153,6 +156,9 @@ def __init__(self, cfg: ManagerBasedEnvCfg):
153156 # allocate dictionary to store metrics
154157 self .extras = {}
155158
159+ # initialize observation buffers
160+ self .obs_buf = {}
161+
156162 def __del__ (self ):
157163 """Cleanup for the environment."""
158164 self .close ()
@@ -208,6 +214,9 @@ def load_managers(self):
208214
209215 """
210216 # prepare the managers
217+ # -- recorder manager
218+ self .recorder_manager = RecorderManager (self .cfg .recorders , self )
219+ print ("[INFO] Recorder Manager: " , self .recorder_manager )
211220 # -- action manager
212221 self .action_manager = ActionManager (self .cfg .actions , self )
213222 print ("[INFO] Action Manager: " , self .action_manager )
@@ -228,15 +237,18 @@ def load_managers(self):
228237 Operations - MDP.
229238 """
230239
231- def reset (self , seed : int | None = None , options : dict [str , Any ] | None = None ) -> tuple [VecEnvObs , dict ]:
232- """Resets all the environments and returns observations.
240+ def reset (
241+ self , seed : int | None = None , env_ids : Sequence [int ] | None = None , options : dict [str , Any ] | None = None
242+ ) -> tuple [VecEnvObs , dict ]:
243+ """Resets the specified environments and returns observations.
233244
234- This function calls the :meth:`_reset_idx` function to reset all the environments.
245+ This function calls the :meth:`_reset_idx` function to reset the specified environments.
235246 However, certain operations, such as procedural terrain generation, that happened during initialization
236247 are not repeated.
237248
238249 Args:
239250 seed: The seed to use for randomization. Defaults to None, in which case the seed is not set.
251+ env_ids: The environment ids to reset. Defaults to None, in which case all environments are reset.
240252 options: Additional information to specify how the environment is reset. Defaults to None.
241253
242254 Note:
@@ -245,20 +257,80 @@ def reset(self, seed: int | None = None, options: dict[str, Any] | None = None)
245257 Returns:
246258 A tuple containing the observations and extras.
247259 """
260+ if env_ids is None :
261+ env_ids = torch .arange (self .num_envs , dtype = torch .int64 , device = self .device )
262+
263+ # trigger recorder terms for pre-reset calls
264+ self .recorder_manager .record_pre_reset (env_ids )
265+
248266 # set the seed
249267 if seed is not None :
250268 self .seed (seed )
251269
252270 # reset state of scene
253- indices = torch .arange (self .num_envs , dtype = torch .int64 , device = self .device )
254- self ._reset_idx (indices )
271+ self ._reset_idx (env_ids )
272+
273+ self .scene .write_data_to_sim ()
274+
275+ # trigger recorder terms for post-reset calls
276+ self .recorder_manager .record_post_reset (env_ids )
277+
278+ # if sensors are added to the scene, make sure we render to reflect changes in reset
279+ if self .sim .has_rtx_sensors () and self .cfg .rerender_on_reset :
280+ self .sim .render ()
281+
282+ # compute observations
283+ self .obs_buf = self .observation_manager .compute ()
284+
285+ # return observations
286+ return self .obs_buf , self .extras
287+
288+ def reset_to (
289+ self ,
290+ state : dict [str , dict [str , torch .Tensor ]],
291+ env_ids : Sequence [int ] | None ,
292+ seed : int | None = None ,
293+ is_relative : bool = False ,
294+ ) -> None :
295+ """Resets specified environments to known states.
296+
297+ Note that this is different from reset() function as it resets the environments to specific states
298+
299+ Args:
300+ state: The state to reset the specified environments to.
301+ env_ids: The environment ids to reset. Defaults to None, in which case all environments are reset.
302+ seed: The seed to use for randomization. Defaults to None, in which case the seed is not set.
303+ is_relative: If set to True, the state is considered relative to the environment origins. Defaults to False.
304+ """
305+ # reset all envs in the scene if env_ids is None
306+ if env_ids is None :
307+ env_ids = torch .arange (self .num_envs , dtype = torch .int64 , device = self .device )
308+
309+ # trigger recorder terms for pre-reset calls
310+ self .recorder_manager .record_pre_reset (env_ids )
311+
312+ # set the seed
313+ if seed is not None :
314+ self .seed (seed )
315+
316+ self ._reset_idx (env_ids )
317+
318+ # set the state
319+ self .scene .reset_to (state , env_ids , is_relative = is_relative )
320+ self .scene .write_data_to_sim ()
321+
322+ # trigger recorder terms for post-reset calls
323+ self .recorder_manager .record_post_reset (env_ids )
255324
256325 # if sensors are added to the scene, make sure we render to reflect changes in reset
257326 if self .sim .has_rtx_sensors () and self .cfg .rerender_on_reset :
258327 self .sim .render ()
259328
329+ # compute observations
330+ self .obs_buf = self .observation_manager .compute ()
331+
260332 # return observations
261- return self .observation_manager . compute () , self .extras
333+ return self .obs_buf , self .extras
262334
263335 def step (self , action : torch .Tensor ) -> tuple [VecEnvObs , dict ]:
264336 """Execute one time-step of the environment's dynamics.
@@ -278,6 +350,8 @@ def step(self, action: torch.Tensor) -> tuple[VecEnvObs, dict]:
278350 # process actions
279351 self .action_manager .process_action (action .to (self .device ))
280352
353+ self .recorder_manager .record_pre_step ()
354+
281355 # check if we need to do rendering within the physics loop
282356 # note: checked here once to avoid multiple checks within the loop
283357 is_rendering = self .sim .has_gui () or self .sim .has_rtx_sensors ()
@@ -303,8 +377,13 @@ def step(self, action: torch.Tensor) -> tuple[VecEnvObs, dict]:
303377 if "interval" in self .event_manager .available_modes :
304378 self .event_manager .apply (mode = "interval" , dt = self .step_dt )
305379
380+ # -- compute observations
381+ self .prev_obs_buf = self .obs_buf
382+ self .obs_buf = self .observation_manager .compute ()
383+ self .recorder_manager .record_post_step ()
384+
306385 # return observations and extras
307- return self .observation_manager . compute () , self .extras
386+ return self .obs_buf , self .extras
308387
309388 @staticmethod
310389 def seed (seed : int = - 1 ) -> int :
@@ -334,6 +413,7 @@ def close(self):
334413 del self .action_manager
335414 del self .observation_manager
336415 del self .event_manager
416+ del self .recorder_manager
337417 del self .scene
338418 # clear callbacks and instance
339419 self .sim .clear_all_callbacks ()
@@ -375,3 +455,6 @@ def _reset_idx(self, env_ids: Sequence[int]):
375455 # -- event manager
376456 info = self .event_manager .reset (env_ids )
377457 self .extras ["log" ].update (info )
458+ # -- recroder manager
459+ info = self .recorder_manager .reset (env_ids )
460+ self .extras ["log" ].update (info )
0 commit comments