录制视频#

本指南介绍如何在运行过程中,从可视化器或场景传感器录制 mp4 视频片段。

Shadow Hand 立方体转向任务的录像,录制 Rerun 可视化器的串流视图,包含四个串流环境和四种真值数据

每个 VideoRecorderCfg 条目独立运行:不同数据源按照各自的录制周期写入不同文件,同时运行的录制器数量没有限制。

快速开始#

在 env_cfg.video_recorders 中添加 VideoRecorderCfg :

from isaaclab.envs.utils.video_recorder_cfg import VideoRecorderCfg

env_cfg.video_recorders = [
    VideoRecorderCfg(source="viz:kit", output_dir="videos/")
]

source 也接受 ImageViewCfg:使用 VideoRecorderCfg(source=view),可与窗口共享图像选择和帧。传感器与透视视图示例见 在可视化器中显示相机流视图。

也可在命令行传入 --video [SOURCE],无需修改环境配置即可录制。--video 添加一个录制器;若要同时录制多个来源,在 env_cfg.video_recorders 中为每个来源列出一个 VideoRecorderCfg。此列表优先于 --video 的来源设置,但 --video_length 和 --video_interval 仍应用于所有录制器:

uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --video

--video 从指定来源录制,--viz 仍决定哪些可视化器打开窗口。所有命令行形式见 --video 来源,VideoRecorderCfg 选项见下方 来源 和 片段控制。从不同于交互视图的角度录制,见 从独立相机视角录制。

示例#

这三个示例均使用 Shadow Hand 立方体重定向任务 Isaac-Reorient-Cube-Shadow-Camera-Direct ,该任务内置平铺相机传感器。示例 1 和 2 分别演示一种录制来源,示例 3 则组合全部四种来源。

run_video_recording.py 的代码
  1# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
  2# All rights reserved.
  3#
  4# SPDX-License-Identifier: BSD-3-Clause
  5
  6"""Tutorial: recording video from visualizers and scene sensors.
  7
  8This script demonstrates three progressively richer recording configurations,
  9all using the Shadow Hand cube-reorientation task
 10(``Isaac-Reorient-Cube-Shadow-Camera-Direct``).
 11
 12Example 1 — Kit viewport (simplest)
 13    One clip from the Kit interactive viewport, showing 4 parallel environments.
 14
 15    .. code-block:: bash
 16
 17        uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
 18            --example 1 --num_envs 4 --viz kit
 19
 20Example 2 — scene sensor only, headless
 21    One clip captured directly from the scene's tiled-camera sensor.
 22    No visualizer window opens; the sensor renders offline.
 23
 24    .. code-block:: bash
 25
 26        uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
 27            --example 2 --num_envs 16
 28
 29Example 3 — Kit viewport + Kit tiled grid + Newton viewport + scene sensor
 30    Four independent clip streams recorded simultaneously.
 31
 32    .. code-block:: bash
 33
 34        uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
 35            --example 3 --num_envs 4 --viz kit,newton_gl
 36
 37Clips are written to ``videos/recording_tutorial/example_<N>/`` in the working directory.
 38Examples 1 and 2 each demonstrate one recording source; Example 3 combines all of them.
 39``--viz`` selects the visualizers to record from; the configs below only set their camera and panels.
 40"""
 41
 42from __future__ import annotations
 43
 44import argparse
 45import contextlib
 46import os
 47import sys
 48
 49import gymnasium as gym
 50import torch
 51
 52from isaaclab.visualizers import WindowCfg
 53
 54import isaaclab_tasks  # noqa: F401
 55
 56with contextlib.suppress(ImportError):
 57    import isaaclab_tasks_experimental  # noqa: F401
 58
 59from isaaclab.app import add_launcher_args, launch_simulation
 60from isaaclab.envs.utils.video_recorder_cfg import VideoRecorderCfg
 61
 62from isaaclab_tasks.utils import resolve_task_config, setup_preset_cli
 63
 64# ---------------------------------------------------------------------------
 65# Constants
 66# ---------------------------------------------------------------------------
 67
 68_VIDEO_LENGTH = 100  # env steps per clip
 69_NUM_STEPS = 115  # slightly more than _VIDEO_LENGTH so the clip flushes cleanly
 70
 71_TASK_SHADOW = "Isaac-Reorient-Cube-Shadow-Camera-Direct"
 72
 73# Kit viewport camera: positioned to show a 2×2 grid of Shadow Hand environments.
 74# env_spacing=1.0 with 4 envs → envs centered at ±0.5 in x and y.
 75# Cube spawns at ~(0, -0.39, 0.6) per env; wrist cylinder is the landmark at the top.
 76_SHADOW_EYE = (0.0, -2.2, 1.8)
 77_SHADOW_LOOKAT = (0.0, -0.1, 0.4)
 78_SHADOW_ENV_SPACING = 1.0
 79
 80# Skip the first few steps so the RTX renderer has warmed up before recording starts.
 81_KIT_STEP_OFFSET = 5
 82
 83
 84def _output_dir(example: int) -> str:
 85    return os.path.join("videos", "recording_tutorial", f"example_{example}")
 86
 87
 88def _shadow_env_cfg(num_envs: int, env_spacing: float = _SHADOW_ENV_SPACING):
 89    """Build a base Shadow Hand camera env cfg shared by all examples."""
 90    env_cfg, _ = resolve_task_config(_TASK_SHADOW, "", overrides=(*sys.argv[1:], "env.scene.tiled_camera=rgb"))
 91    env_cfg.scene.tiled_camera.height = 256
 92    env_cfg.scene.tiled_camera.width = 256
 93    env_cfg.scene.num_envs = num_envs
 94    env_cfg.scene.env_spacing = env_spacing
 95    return env_cfg
 96
 97
 98# ---------------------------------------------------------------------------
 99# Per-example environment config builders
100# ---------------------------------------------------------------------------
101
102
103def _build_env_cfg_example_1(num_envs: int):
104    """Shadow Hand + Kit viewport: one clip from the interactive viewport."""
105    from isaaclab_visualizers.kit import KitVisualizerCfg
106
107    env_cfg = _shadow_env_cfg(num_envs)
108    env_cfg.sim.visualizer_cfgs = [KitVisualizerCfg(eye=_SHADOW_EYE, lookat=_SHADOW_LOOKAT)]
109
110    out = _output_dir(1)
111    env_cfg.video_recorders = [
112        VideoRecorderCfg(
113            source="viz:kit",
114            output_dir=out,
115            output_filename_prefix="kit_viewport",
116            video_length=_VIDEO_LENGTH,
117            fps=30,
118            step_offset=_KIT_STEP_OFFSET,
119        ),
120    ]
121    return env_cfg, _TASK_SHADOW
122
123
124def _build_env_cfg_example_2(num_envs: int):
125    """Shadow Hand + headless: scene tiled-camera sensor clip only."""
126    env_cfg = _shadow_env_cfg(num_envs, env_spacing=2.0)
127
128    out = _output_dir(2)
129    env_cfg.video_recorders = [
130        VideoRecorderCfg(
131            source="sensor:tiled_camera",
132            output_dir=out,
133            output_filename_prefix="sensor",
134            video_length=_VIDEO_LENGTH,
135            fps=30,
136        ),
137    ]
138    return env_cfg, _TASK_SHADOW
139
140
141def _build_env_cfg_example_3(num_envs: int):
142    """Shadow Hand + Kit viewport + Kit tiled grid + Newton viewport + sensor: four simultaneous streams.
143
144    Note: ``source='viz:newton_gl'`` captures the full Newton GL window. When
145    ``streaming_view=True`` is set on :class:`~isaaclab_visualizers.newton.NewtonGLVisualizerCfg`,
146    the GL window displays the per-environment camera panel, so this effectively records
147    a Newton streaming view without a separate ``render_tiled_rgb_array()`` call.
148    """
149    from isaaclab_visualizers.kit import KitVisualizerCfg
150    from isaaclab_visualizers.newton import NewtonGLVisualizerCfg
151
152    env_cfg = _shadow_env_cfg(num_envs)
153    kit_cfg = KitVisualizerCfg(
154        eye=_SHADOW_EYE,
155        lookat=_SHADOW_LOOKAT,
156        streaming_view=True,
157        streaming_envs=min(num_envs, 16),
158        # No streaming_sensor_prim_path: selects the existing
159        # tiled_camera sensor automatically, so the streaming panel shows the same
160        # RTX-rendered views as source="sensor:tiled_camera".
161    )
162    newton_cfg = NewtonGLVisualizerCfg(
163        eye=_SHADOW_EYE,
164        lookat=_SHADOW_LOOKAT,
165        window=WindowCfg(size=(1280, 720)),
166        focal_length=25.0,
167    )
168    env_cfg.sim.visualizer_cfgs = [kit_cfg, newton_cfg]
169
170    out = _output_dir(3)
171    env_cfg.video_recorders = [
172        VideoRecorderCfg(
173            source="viz:kit",
174            output_dir=out,
175            output_filename_prefix="kit_viewport",
176            video_length=_VIDEO_LENGTH,
177            fps=30,
178            step_offset=_KIT_STEP_OFFSET,
179        ),
180        VideoRecorderCfg(
181            source="viz:kit:streaming_view",
182            output_dir=out,
183            output_filename_prefix="tiled_kit_viewport",
184            video_length=_VIDEO_LENGTH,
185            fps=30,
186            step_offset=_KIT_STEP_OFFSET,
187        ),
188        VideoRecorderCfg(
189            source="viz:newton_gl",
190            output_dir=out,
191            output_filename_prefix="newton_viewport",
192            video_length=_VIDEO_LENGTH,
193            fps=30,
194        ),
195        VideoRecorderCfg(
196            source="sensor:tiled_camera",
197            output_dir=out,
198            output_filename_prefix="sensor",
199            video_length=_VIDEO_LENGTH,
200            fps=30,
201        ),
202    ]
203    return env_cfg, _TASK_SHADOW
204
205
206_BUILDERS = {
207    1: _build_env_cfg_example_1,
208    2: _build_env_cfg_example_2,
209    3: _build_env_cfg_example_3,
210}
211
212# ---------------------------------------------------------------------------
213# Argument parsing
214# ---------------------------------------------------------------------------
215parser = argparse.ArgumentParser(description="Video recording tutorial for Isaac Lab environments.")
216parser.add_argument(
217    "--example", type=int, default=1, choices=[1, 2, 3], help="Which recording example to run (1, 2, or 3)."
218)
219parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
220add_launcher_args(parser)
221args_cli, hydra_args = setup_preset_cli(parser)
222sys.argv = [sys.argv[0]] + hydra_args
223
224
225def main():
226    """Run the selected video recording example."""
227    defaults = {1: 4, 2: 16, 3: 4}
228    num_envs = args_cli.num_envs if args_cli.num_envs is not None else defaults[args_cli.example]
229    env_cfg, task = _BUILDERS[args_cli.example](num_envs)
230    env_cfg.sim.device = args_cli.device if args_cli.device is not None else env_cfg.sim.device
231
232    # Examples 1 and 3 record from the Kit viewport via omni.replicator, which requires
233    # camera rendering support.  Force it here for visualizer-only recording.
234    if args_cli.example in (1, 3):
235        args_cli.enable_cameras = True
236
237    with launch_simulation(env_cfg, args_cli):
238        env = gym.make(task, cfg=env_cfg)
239
240        out = _output_dir(args_cli.example)
241        print(f"[INFO]: Running Example {args_cli.example} — clips → {out}/")
242        print(f"[INFO]: Gym observation space: {env.observation_space}")
243        print(f"[INFO]: Gym action space: {env.action_space}")
244
245        print("[INFO]: Setup complete.")
246        env.reset()
247        for _ in range(_NUM_STEPS):
248            with torch.inference_mode():
249                actions = 2 * torch.rand(env.action_space.shape, device=env.unwrapped.device) - 1
250                env.step(actions)
251
252        env.close()
253        print(f"[INFO]: Done. Clips written to {out}/")
254
255
256if __name__ == "__main__":
257    main()

示例 1:Kit 视口#

uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
    --example 1 --num_envs 4 --viz kit
  • 录制 Kit 交互视口(RTX 渲染器)

  • 显示 4 个并行环境

  • 将一个视频片段写入 videos/recording_tutorial/example_1/kit_viewport_0000.mp4

Kit 可视化器

示例 2:场景传感器,无界面运行#

uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
    --example 2 --num_envs 16

场景传感器

  • 不打开可视化器窗口;直接从 tiled_camera 传感器读取帧

  • 将一个视频片段写入 videos/recording_tutorial/example_2/sensor_0000.mp4

  • source="sensor:tiled_camera" 中的 tiled_camera 是该相机在 env.scene.sensors 中注册时使用的键

  • 传感器的 data_types 必须包含待录制通道;默认通道为 rgb,也支持 depth、segmentation 和 normals,见 数据源

示例 3:同时录制所有来源#

uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
    --example 3 --num_envs 4 --viz kit,newton_gl

将四个独立的视频片段写入 videos/recording_tutorial/example_3/ :

  • kit_viewport_0000.mp4 :Kit 交互视口(RTX 渲染器)

  • tiled_kit_viewport_0000.mp4 :Kit 平铺相机网格(各环境的视图)

  • newton_viewport_0000.mp4 :Newton GL 查看器帧缓冲

  • sensor_0000.mp4 :场景平铺相机传感器(离线渲染)

Newton GL 可视化器

Kit 可视化器平铺串流

常用配置#

以下命令行和配置示例不属于教程脚本,可按实际需要使用。

在强化学习训练中录制#

训练时不打开窗口,按固定间隔录制短片段:

uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --video \
    --video_length 200 --video_interval 2000

未设置 --viz 时,--video 从无窗口 Newton GL 可视化器录制。以下命令每 2,000 步录制一段 200 步的视频。除非录制器设置了 output_dir,train 和 play 入口将视频写入本次运行日志目录下的 videos/;自行构建录制器的脚本写入 output_dir,默认是 videos。添加 --viz kit,可在窗口中观察运行并录制相同视图。

从场景传感器录制深度或分割结果#

在传感器数据源后追加通道名称,即可录制 RGB 以外的数据。每个录制器写入独立文件,因此一次运行可以同时录制同一相机的多个通道:

env_cfg.video_recorders = [
    VideoRecorderCfg(source="sensor:tiled_camera:rgb", output_filename_prefix="rgb"),
    VideoRecorderCfg(source="sensor:tiled_camera:depth", output_filename_prefix="depth"),
]

深度使用 turbo 色图,映射范围为 depth_colormap_min 到 depth_colormap_max。segmentation 和 normals 同样可以为每个录制器设置独立文件名前缀。

使用 Viser 或 Rerun 查看视频流时录制#

Viser 和 Rerun 向浏览器或查看器发送视频流,但不提供帧捕获,无法直接录制。可以同时运行一个支持帧捕获的无界面可视化器:

uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --viz viser --video viz:newton_gl

也可以设置 source="sensor:<name>",直接从场景相机录制,无需可视化器。

从独立相机视角录制#

录制角度应在可视化器上配置,不在录制器上配置。如需在交互式 Kit 查看器之外,从不同角度的无窗口 Newton 可视化器录制,请使用 --viz kit 并配置两个可视化器;录制器会以无窗口方式添加 Newton 可视化器:

from isaaclab_visualizers.kit import KitVisualizerCfg
from isaaclab_visualizers.newton import NewtonGLVisualizerCfg

env_cfg.sim.visualizer_cfgs = [
    KitVisualizerCfg(eye=(4.0, 4.0, 2.0)),
    NewtonGLVisualizerCfg(eye=(12.0, 0.0, 6.0)),
]
env_cfg.video_recorders = [
    VideoRecorderCfg(source="viz:newton_gl", output_dir="videos/"),
]

也可以在场景中使用 CameraCfg 传感器,并通过 source="sensor:<name>" 录制。这样可以完全控制录制视角,无需第二个交互式可视化器。

数据源#

source 字符串用于选择采集对象:

来源字符串

采集对象

"viz" (默认值)

--viz 选中的第一个支持捕获的可视化器,否则使用无窗口 Newton GL 可视化器

"viz:kit"

Kit 可视化器视口

"viz:kit:streaming_view"

Kit 相机流面板,需要设置 streaming_view=True

"viz:newton_gl"

Newton GL 可视化器视口

"viz:newton_rtx"

Newton OVRTX 路径追踪视口

"viz:newton_gl:streaming_view"

Newton GL 相机流面板,需要设置 streaming_view=True

"sensor:<name>"

env.scene.sensors[name] 的 RGB 数据(默认)

"sensor:<name>:rgb"

RGB 通道

"sensor:<name>:depth"

深度,采用 turbo 色图,范围为 depth_colormap_min … depth_colormap_max

"sensor:<name>:segmentation"

分割,使用颜色区分

"sensor:<name>:normals"

表面法线,使用颜色表示

相机角度、分辨率及其他可视化设置应在对应的可视化器配置中设置,不在录制器中设置。--viz 未选中的 viz:<type> 可视化器仅为录制而无窗口运行,使用 sim.visualizer_cfgs 中对应配置的设置。

visualizer 可作为 viz 前缀的完整形式,例如 "visualizer:kit" 等同于 "viz:kit"。已弃用的 newton 类型("viz:newton")仍可使用,但会发出警告;请改用 newton_gl。这里也接受不带前缀的可视化器类型,例如 VideoRecorderCfg(source="newton_gl") 等同于 VideoRecorderCfg(source="viz:newton_gl")。

备注

可通过 "viz:newton_rtx" 录制 Newton RTX 查看器的帧缓冲区,但不支持录制其流式视图。

片段控制#

字段

默认值

含义

video_length

200

每个片段的环境步数

video_interval

0

0 = 从第 1 步开始录制一个片段; N > 0 = 每 N 步开始录制一个新片段

fps

None

输出帧率;设为 None 时,从 env.metadata["render_fps"] 或 1 / step_dt 确定

output_dir

"videos"

输出文件目录(按需创建)

output_filename_prefix

"clip"

文件名前缀;输出文件名为 <prefix>_NNNN.mp4

keep_last_n_clips

None

删除较早的片段;设为 None 时保留全部片段

运行开始时录制一个片段:

VideoRecorderCfg(source="viz:kit", video_length=500, video_interval=0)

每隔 1,000 个环境步录制一个片段:

VideoRecorderCfg(source="viz:kit", video_length=200, video_interval=1000)

磁盘上仅保留最新片段:

VideoRecorderCfg(source="viz:kit", video_length=200, video_interval=1000,
                 keep_last_n_clips=1)

限制#

  • source="viz:newton_gl:streaming_view" 和 source="viz:kit:streaming_view" 要求在对应可视化器配置中设置 streaming_view=True;否则构造录制器时会抛出 RuntimeError。

  • 使用 source="sensor:<name>" 时,场景配置中必须存在该名称的传感器,其 data_types 必须包含待录制通道,默认通道为 "rgb"。

可视化器

--video

说明

kit

✓

启动 Isaac Sim,并在启动时为录制启用相机渲染。

newton_gl

✓

无窗口时使用 pyglet 的 EGL 后端;这是 --video 在未选择支持捕获的 --viz 时的默认来源。

newton_rtx

✓

启动 OVRTX 运行时;捕获帧时,将路径追踪帧缓冲区从 GPU 回读到 CPU。

rerun

✗

远程流式工具,无本地帧捕获 API。viz:rerun 会报错;--video --viz rerun 则从无窗口 newton_gl 录制。

viser

✗

浏览器流式工具,无本地帧捕获 API。viz:viser 会报错;--video --viz viser 则从无窗口 newton_gl 录制。

使用 Rerun 或 Viser 查看视频流时的录制方法,见 使用 Viser 或 Rerun 查看视频流时录制。

另请参阅#