录制仿真动画#

Isaac Lab支持两种记录物理仿真动画的方法: 场景录制器OVD录制器 。两者都会生成可以在Omniverse中回放的USD输出,但它们在工作方式和使用场景上略有不同。

场景录制器 扩展监听在仿真过程中场景上所有运动和 USD 属性更改,并将其记录为 时间采样数据 。 最终结果是一个 USD 文件,只捕捉动画更改—**而不是**完整的场景—并且在记录时与原始场景的层次结构相匹配。这使得将其作为子图层进行播放或渲染变得更容易。

这种方法通过 BaseEnvWindow 内置在 Isaac Lab 的UI中。然而,为了记录仿真的动画,您需要禁用 Fabric ,以允许读取和写入所有更改(比如运动和USD属性)到USD场景。

OVD Recorder 旨在为更具可扩展性或自动化的工作流程而设计。它使用 OmniPVD 来捕获从播放场景中仿真的物理效果,然后直接将其“烘焙”到一个动画 USD 文件中。它支持 Fabric 功能,并可以通过 CLI 参数运行。通过在时间轴窗口中拖动来快速重播和查看动画 USD,无需进行昂贵的物理仿真操作。

备注

Omniverse 仅支持在 USD prim 上进行 物理仿真动画播放 ,不可同时进行。对希望进行动画的 prim 禁用物理效果。

场景录制器#

在 Isaac Lab 中,场景记录器集成到 BaseEnvWindow 类中。这是捕捉物理仿真的最简单方式,并直接通过 UI 运行。

要记录,必须禁用 Fabric——这样才能让记录器跟踪 USD 的更改并将其写出。

场景录制器设置#

Isaac Lab 在 base_env_window.py 中使用明智的默认设置设置场景记录器。如果需要,您可以通过在 Omniverse Create 中直接使用场景记录器扩展来覆盖或检查这些设置。

在 base_env_window.py 中使用的设置
 1    def _toggle_recording_animation_fn(self, value: bool):
 2        """Toggles the animation recording."""
 3        if value:
 4            # log directory to save the recording
 5            if not hasattr(self, "animation_log_dir"):
 6                # create a new log directory
 7                log_dir = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
 8                self.animation_log_dir = os.path.join(os.getcwd(), "recordings", log_dir)
 9            # start the recording
10            _ = omni.kit.commands.execute(
11                "StartRecording",
12                target_paths=[("/World", True)],
13                live_mode=True,
14                use_frame_range=False,
15                start_frame=0,
16                end_frame=0,
17                use_preroll=False,
18                preroll_frame=0,
19                record_to="FILE",
20                fps=0,
21                apply_root_anim=False,
22                increment_name=True,
23                record_folder=self.animation_log_dir,
24                take_name="TimeSample",
25            )
26        else:
27            # stop the recording
28            _ = omni.kit.commands.execute("StopRecording")
29            # save the current stage
30            source_layer = self.stage.GetRootLayer()
31            # output the stage to a file
32            stage_usd_path = os.path.join(self.animation_log_dir, "Stage.usd")
33            source_prim_path = "/"
34            # creates empty anon layer
35            temp_layer = Sdf.Find(stage_usd_path)
36            if temp_layer is None:
37                temp_layer = Sdf.Layer.CreateNew(stage_usd_path)
38            temp_stage = Usd.Stage.Open(temp_layer)
39            # update stage data
40            UsdGeom.SetStageUpAxis(temp_stage, UsdGeom.GetStageUpAxis(self.stage))
41            UsdGeom.SetStageMetersPerUnit(temp_stage, UsdGeom.GetStageMetersPerUnit(self.stage))
42            # copy the prim
43            Sdf.CreatePrimInLayer(temp_layer, source_prim_path)
44            Sdf.CopySpec(source_layer, source_prim_path, temp_layer, source_prim_path)
45            # set the default prim
46            temp_layer.defaultPrim = Sdf.Path(source_prim_path).name
47            # remove all physics from the stage
48            for prim in temp_stage.TraverseAll():
49                # skip if the prim is an instance
50                if prim.IsInstanceable():
51                    continue
52                # if prim has articulation then disable it
53                if prim.HasAPI(UsdPhysics.ArticulationRootAPI):
54                    prim.RemoveAPI(UsdPhysics.ArticulationRootAPI)
55                    prim.RemoveAPI(PhysxSchema.PhysxArticulationAPI)
56                # if prim has rigid body then disable it
57                if prim.HasAPI(UsdPhysics.RigidBodyAPI):
58                    prim.RemoveAPI(UsdPhysics.RigidBodyAPI)
59                    prim.RemoveAPI(PhysxSchema.PhysxRigidBodyAPI)
60                # if prim is a joint type then disable it
61                if prim.IsA(UsdPhysics.Joint):
62                    prim.GetAttribute("physics:jointEnabled").Set(False)
63            # resolve all paths relative to layer path
64            omni.usd.resolve_paths(source_layer.identifier, temp_layer.identifier)
65            # save the stage
66            temp_layer.Save()
67            # print the path to the saved stage
68            print("Recording completed.")
69            print(f"\tSaved recorded stage to    : {stage_usd_path}")
70            print(f"\tSaved recorded animation to: {os.path.join(self.animation_log_dir, 'TimeSample_tk001.usd')}")
71            print("\nTo play the animation, check the instructions in the following link:")
72            print(
73                "\thttps://docs.omniverse.nvidia.com/extensions/latest/ext_animation_stage-recorder.html#using-the-captured-timesamples"
74            )
75            print("\n")
76            # reset the log directory
77            self.animation_log_dir = None

示例用法#

在独立的 Isaac Lab 环境中,传递 --disable_fabric 标志:

./isaaclab.sh -p scripts/environments/state_machine/lift_cube_sm.py --num_envs 8 --device cpu --disable_fabric

启动后,Isaac Lab UI 窗口将显示一个 “Record Animation” 按钮。点击开始录制。再次点击以停止。

以下文件被保存到 recordings/ 文件夹中:

  • Stage.usd — 禁用物理的原始场景

  • TimeSample_tk001.usd — 动画(时间采样)图层

要回放:

./isaaclab.sh -s  # Opens Isaac Sim

在“Layers”面板中,插入 Stage.usdTimeSample_tk001.usd 作为子图层。现在,当您点击播放按钮时,动画将开始播放。

查看 Omniverse 中的 图层教程 ,了解更多关于图层工作的信息。

OVD Recorder#

OVD 录制器 使用 OmniPVD 来记录仿真数据,并将其直接烘焙到新的 USD 场景中。这种方法更具可扩展性,更适合大规模训练场景(例如,多环境 RL)。

这不是通过用户界面控制的——整个过程是通过命令行标志启用的,并自动运行。

工作流程总结#

  1. 用户通过CLI运行 Isaac Lab ,启用动画录制。

  2. Isaac Lab 开始仿真

  3. OVD 数据在仿真运行时记录。

  4. 在指定的停止时间,仿真被烘烤成一个输出的USD文件,并关闭IsaacLab。

  5. 最终结果是一个完全成型的、独立的 USD 动画

示例用法#

要记录动画:

./isaaclab.sh -p scripts/tutorials/03_envs/run_cartpole_rl_env.py \
  --anim_recording_enabled \
  --anim_recording_start_time 1 \
  --anim_recording_stop_time 3

备注

提供的 --anim_recording_stop_time 应大于仿真时间。

警告

当前,最终录制步骤可以从 [omni.usd] 中输出许多警告日志。这是一个已知问题,可以忽略这些警告消息。

停止时间达到后,文件将保存到:

anim_recordings/<timestamp>/baked_animation_recording.usda