在可视化器中显示相机流视图#

在同一个实时可视化面板中显示多个环境的真值相机图像。视图原理及受支持的可视化器见 相机流视图。

与录制共享视图#

ImageViewCfg 选择像素内容,与显示或录制目的地无关。场景传感器名称绑定已有相机,渲染器与捕获节奏仍由场景管理。WindowCfg 通过 VisualizerCfg.window 独立控制显示。VisualizerCfg.view 与 VideoRecorderCfg.source 可以使用同一选择:

from isaaclab.envs import VideoRecorderCfg
from isaaclab.visualizers import ImageViewCfg, WindowCfg
from isaaclab_visualizers.newton import NewtonGLVisualizerCfg

view = ImageViewCfg(source="front_camera", envs=(0, 2), channels=("rgb", "depth"))
env_cfg.sim.visualizer_cfgs = [NewtonGLVisualizerCfg(view=view, window=WindowCfg(size=(1280, 720)))]
env_cfg.video_recorders = [VideoRecorderCfg(source=view, output_dir="videos")]

使用 --viz newton_gl 显示窗口,RTX 示例则使用 --viz newton_rtx;仅录制时省略 --viz。场景必须声明 front_camera,并包含所请求的两种输出。视图保留环境顺序和原生平铺尺寸,将不同通道并排排列。channels 选择已有输出,depth_range 仅将深度值映射为显示颜色,二者都不改变源相机的输出、测量或裁剪面。CameraCfg.width 和 height 设置传感器分辨率,WindowCfg.size 设置初始窗口尺寸,窗口缩放合成图像用于显示。调整传感器窗口大小不会改变传感器及合成图像的存储。录制器编码原生合成图像,只有编码时才下载像素,显示直接使用设备图像。仿真重置后,录制器绑定来源时会检查无效的录制源或通道选择并报错,不会静默关闭录制。后续帧使用已绑定的捕获函数,不再重复查找图像生成器。

在同一仿真中复用同一个 ImageViewCfg,会共享同一个运行时帧,即使外层配置被复制也如此。需要独立选择时,使用 isaaclab.utils.replace(view, ...)。同一物理步内重复读取共享视图会复用已有帧和传感器采集结果。改变视图选择会重建合成状态,已捕获的合成图也必须重建;这不会调整传感器渲染产物的大小。

对于 Newton RTX,透视视图声明会在启动前展开为普通场景相机:

from isaaclab.visualizers import PerspectiveCameraCfg
from isaaclab_visualizers.newton import NewtonRTXVisualizerCfg

view = ImageViewCfg(source=PerspectiveCameraCfg(eye=(4.0, -4.0, 3.0)))
env_cfg.sim.visualizer_cfgs = [
    NewtonRTXVisualizerCfg(view=view, window=WindowCfg(size=(960, 540)))
]
env_cfg.video_recorders = [VideoRecorderCfg(source=view)]

Newton RTX 将透视渲染分辨率固定为初始 WindowCfg.size,窗口变化时缩放图像;Newton GL 的透视渲染则跟随窗口帧缓冲区尺寸。录制器读取已完成的原生渲染,不会在同一步请求第二次渲染。目前透视视图仅提供一张 RGB 图像,因此应保留 envs=(0,) 和 channels=("rgb",)。录制透视视图时,必须且只能配置一个渲染型可视化器。无界面运行时,启动器保留该图像生成器但不打开窗口;录制传感器视图无需可视化器。

Newton GL 和 RTX 支持 VisualizerCfg.view。现有的 cameras、streaming_* 配置和命令行录制来源字符串仍受支持,会在初始化时转换。请使用 view,不要将它与旧版相机选择字段混用。

快速开始#

运行 IsaacLab/scripts/tutorials/07_visualizers 中的 run_tiled_camera_visualizer.py 脚本:

uv run python scripts/tutorials/07_visualizers/run_tiled_camera_visualizer.py \
    --task Isaac-Velocity-Rough-AnymalD --num_envs 256 --viz kit
uv run python scripts/tutorials/07_visualizers/run_tiled_camera_visualizer.py ^
    --task Isaac-Velocity-Rough-AnymalD --num_envs 256 --viz kit
run_tiled_camera_visualizer.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"""
  7This script demonstrates the visualizer tiled camera panel.
  8
  9.. code-block:: bash
 10
 11    # Kit visualizer tiled camera panel
 12    uv run python scripts/tutorials/07_visualizers/run_tiled_camera_visualizer.py \
 13 --task Isaac-Velocity-Rough-AnymalD --num_envs 256 --viz kit
 14
 15    # Newton visualizer tiled camera panel
 16    uv run python scripts/tutorials/07_visualizers/run_tiled_camera_visualizer.py \
 17        --task IsaacContrib-Stack-Cube-Galbot-Left-Arm-Gripper-Visuomotor --num_envs 25 --viz newton_gl
 18
 19"""
 20
 21from __future__ import annotations
 22
 23import argparse
 24import contextlib
 25import sys
 26
 27import gymnasium as gym
 28import torch
 29from isaaclab_newton.renderers import NewtonWarpRendererCfg
 30from isaaclab_physx.renderers import IsaacRtxRendererCfg
 31from isaaclab_visualizers.kit import KitVisualizerCfg
 32from isaaclab_visualizers.newton import NewtonGLVisualizerCfg
 33
 34import isaaclab_tasks  # noqa: F401
 35
 36with contextlib.suppress(ImportError):
 37    import isaaclab_tasks_experimental  # noqa: F401
 38from isaaclab.app import add_launcher_args, launch_simulation
 39from isaaclab.sensors import CameraCfg
 40from isaaclab.sim import PinholeCameraCfg
 41from isaaclab.utils.math import create_rotation_matrix_from_view, quat_from_matrix
 42from isaaclab.visualizers import SceneCameraCfg
 43
 44from isaaclab_tasks.utils import resolve_task_config, setup_preset_cli
 45
 46KIT_DEFAULT_TASK = "Isaac-Velocity-Rough-AnymalD"
 47NEWTON_DEFAULT_TASK = "IsaacContrib-Stack-Cube-Galbot-Left-Arm-Gripper-Visuomotor"
 48
 49
 50# add argparse arguments
 51parser = argparse.ArgumentParser(description="Showcase the Kit/Newton visualizer tiled camera panel.")
 52parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
 53parser.add_argument("--task", type=str, default=None, help="Name of the task.")
 54# append simulation launcher cli args
 55add_launcher_args(parser)
 56parser.set_defaults(visualizer=["kit"])
 57args_cli, hydra_args = setup_preset_cli(parser)
 58if args_cli.task is None:
 59    visualizers = next(arg.split("=", 1)[1].split(",") for arg in hydra_args if arg.startswith("visualizer="))
 60    args_cli.task = NEWTON_DEFAULT_TASK if {"newton", "newton_gl"}.intersection(visualizers) else KIT_DEFAULT_TASK
 61sys.argv = [sys.argv[0]] + hydra_args
 62
 63
 64def main():
 65    """Run a random-action environment with a tiled camera visualizer."""
 66    # parse configuration via Hydra (supports preset selection, e.g. presets=newton_mjwarp)
 67    env_cfg, _ = resolve_task_config(args_cli.task, "")
 68    # Declare the shared scene camera before launching either viewer.
 69    visualizers = env_cfg.sim.visualizer_cfgs
 70    visualizers = visualizers if isinstance(visualizers, list) else [visualizers] if visualizers else []
 71    if not visualizers or any(not isinstance(cfg, (KitVisualizerCfg, NewtonGLVisualizerCfg)) for cfg in visualizers):
 72        raise ValueError("This tutorial supports --viz kit or --viz newton_gl.")
 73    camera_cfg = next((cfg for cfg in vars(env_cfg.scene).values() if isinstance(cfg, CameraCfg)), None)
 74    if camera_cfg is None:
 75        eye, target = torch.tensor([[3.0, 3.0, 3.0]]), torch.zeros((1, 3))
 76        rotation = quat_from_matrix(create_rotation_matrix_from_view(eye, target, device="cpu"))[0]
 77        # Attaching the sensor to the base gives it the robot's pose through the normal camera view.
 78        camera_cfg = CameraCfg(
 79            prim_path="{ENV_REGEX_NS}/Robot/base/StreamingCamera",
 80            width=320,
 81            height=240,
 82            data_types=["rgb"],
 83            spawn=PinholeCameraCfg(focal_length=24.0, clipping_range=(0.1, 1.0e5)),
 84            offset=CameraCfg.OffsetCfg(pos=(3.0, 3.0, 3.0), rot=tuple(rotation.tolist()), convention="opengl"),
 85            renderer_cfg=(
 86                IsaacRtxRendererCfg()
 87                if any(isinstance(cfg, KitVisualizerCfg) for cfg in visualizers)
 88                else NewtonWarpRendererCfg()
 89            ),
 90        )
 91        env_cfg.scene.streaming_camera = camera_cfg
 92    for cfg in visualizers:
 93        cfg.streaming_view = True
 94        cfg.streaming_envs = 36 if isinstance(cfg, KitVisualizerCfg) else 12
 95        cfg.cameras = [SceneCameraCfg(prim_path=camera_cfg.prim_path)]
 96
 97    with launch_simulation(env_cfg, args_cli):
 98        # override with CLI arguments
 99        env_cfg.scene.num_envs = args_cli.num_envs if args_cli.num_envs is not None else env_cfg.scene.num_envs
100
101        # create environment
102        env = gym.make(args_cli.task, cfg=env_cfg)
103
104        # print info (this is vectorized environment)
105        print(f"[INFO]: Gym observation space: {env.observation_space}")
106        print(f"[INFO]: Gym action space: {env.action_space}")
107        env.reset()
108
109        # keep stepping until all visualizer windows have been closed
110        while env.unwrapped.sim.is_running():
111            with torch.inference_mode():
112                actions = 2 * torch.rand(env.action_space.shape, device=env.unwrapped.device) - 1
113                env.step(actions)
114
115        env.close()
116
117
118if __name__ == "__main__":
119    main()

脚本的两种运行方式见下方 示例。定制流式显示的 VisualizerCfg 字段见 配置、显示源 和 故障排查。

选择和控制显示视图#

Kit 将流式视图打开为独立的 Streaming View 视口,可从 Viewport 标签页选择,也可与默认交互视口并排显示,便于同时监测。

Newton GL 和 Newton RTX 在侧边栏提供 Camera View 选择器。选择场景相机后,主视口显示其平铺输出,不再在背后渲染透视视图;选择透视相机后恢复交互场景渲染,Newton GL 也恢复拾取。

未显式提供 cameras 列表时,Newton GL 从透视视图启动,并列出提供所有所需 streaming_gt_types 通道的场景相机。显式设置 streaming_sensor_prim_path 时,以该场景相机作为初始选择。例如,设置 streaming_gt_types=("depth",) 可列出仅提供深度的相机。RGB 视图接受 RGB 或 RGBA 输出,显示时使用不透明 alpha。构建选择器不会捕获图像。显式配置的场景相机选项必须支持所需通道,不兼容的选项会在初始化时报错,即使该选项未启用。

Newton RTX 仅显示 view 或 cameras 声明的来源。两者均未设置时,启动准备会声明一个透视相机,不会自动将场景传感器加入选择器。

使用 cameras 声明可切换视图列表,第一项初始启用:

from isaaclab.visualizers import PerspectiveCameraCfg, SceneCameraCfg
from isaaclab_visualizers.newton import NewtonGLVisualizerCfg

viewer_cfg = NewtonGLVisualizerCfg(
    cameras=[
        SceneCameraCfg(prim_path="{ENV_REGEX_NS}/Robot/FrontCamera"),
        SceneCameraCfg(prim_path="{ENV_REGEX_NS}/Robot/BackCamera"),
        PerspectiveCameraCfg(eye=(4.0, -4.0, 3.0)),
    ],
    streaming_envs=list(range(16)),
)

cameras 字段由各可视化器共用。Kit、Rerun 和 Viser 在图像面板中使用第一个场景相机;Newton GL 和 RTX 则提供上述来源下拉框。SceneCameraCfg 选择已有传感器,PerspectiveCameraCfg 配置查看器的交互相机。对于 Newton RTX,launch_simulation(env_cfg, ...) 在构造任何场景对象前向场景添加 CameraCfg,并通过 SceneCameraCfg 选择它。Newton GL 保留原生透视相机。Newton RTX 的透视渲染分辨率固定为初始 WindowCfg.size,窗口变化时缩放图像;传感器分辨率仍由 CameraCfg 控制。调整窗口大小不会重新分配传感器或 RTX 透视图像缓冲区。Newton GL 透视图像则跟随帧缓冲区尺寸。Newton 窗口按实际时间最多以 WindowCfg.fps 显示,默认为 30;改变仿真时间步不会改变此上限。无窗口按需捕获与窗口更新频率无关,Kit 使用应用自身的更新节奏。

Newton RTX 在 Newton GL 窗口中显示普通相机输出。相等的 OVRTXRendererCfg 配置共享同一个渲染器及其场景。NewtonRTXVisualizerCfg.renderer_cfg 为生成的透视相机选择渲染器,显式选中的场景相机则保留各自的渲染器。这里不使用 Newton 的 ViewerRTX 及其借用舞台接口。isaaclab_ov.stage.OvstageBackend 仍可供需要独立原生舞台的下游使用者使用,其 populate(stage, plan) 方法导入并克隆已声明的场景。

相机负责位姿、标定、图像缓冲区、捕获和清理。窗口导航调用 Camera.set_world_poses 和 Camera.set_intrinsic_matrices,图像通过 Camera.data 读取,不存在单独的透视渲染入口。系统不添加默认灯光,未定义灯光的场景保持无光照状态。关闭窗口后,其场景相机和渲染器继续存活。

没有 InteractiveSceneCfg 的脚本使用 ReplicateSession,在生成资产和相机前声明场景,即使只有一个环境也如此:

from isaaclab.app import launch_simulation
from isaaclab.assets import AssetBaseCfg
from isaaclab.cloner import ReplicateSession
from isaaclab.sensors import CameraCfg
from isaaclab.sim import CuboidCfg, DomeLightCfg, PinholeCameraCfg, SimulationCfg, SimulationContext
from isaaclab.utils import instantiate
from isaaclab.visualizers import SceneCameraCfg
from isaaclab_ov.renderers import OVRTXRendererCfg
from isaaclab_visualizers.newton import NewtonRTXVisualizerCfg

camera_cfg = CameraCfg(
    prim_path="/World/PerspectiveCamera", width=960, height=540,
    spawn=PinholeCameraCfg(focal_length=12.0), renderer_cfg=OVRTXRendererCfg(),
    offset=CameraCfg.OffsetCfg(pos=(0.0, 0.0, 5.0), convention="opengl"),
    render_settings={"omni:rtx:quality": ("Int", 0)},
)
sim_cfg = SimulationCfg(
    visualizer_cfgs=[NewtonRTXVisualizerCfg(cameras=[SceneCameraCfg(prim_path=camera_cfg.prim_path)])]
)
target_cfg = AssetBaseCfg(prim_path="/World/Target", spawn=CuboidCfg(size=(1.0, 1.0, 1.0)))
light_cfg = AssetBaseCfg(prim_path="/World/Light", spawn=DomeLightCfg(intensity=1000.0))
with launch_simulation(sim_cfg, {"physics": "ovphysx", "visualizer": ["newton_rtx"]}):
    sim = SimulationContext(sim_cfg)
    with ReplicateSession((target_cfg, light_cfg, camera_cfg), num_clones=1, env_spacing=0.0):
        instantiate(target_cfg)
        instantiate(light_cfg)
        camera = instantiate(camera_cfg)
    sim.reset()
    while sim.is_running():
        sim.step()
        camera.update(sim.get_physics_dt())

脚本持有相机并推进其传感器计时,与其他直接生成的传感器相同。SceneCameraCfg 可通过 prim 路径发现它,无需 InteractiveScene。

当来源、选择、分辨率或颜色设置改变时,会编译显示布局与通道映射。合成写入可复用的设备 RGBA 存储。Newton GL、Newton RTX 和 Kit 直接显示 CUDA 图像,不下载像素;通过 render_tiled_rgb_array() 录制,以及 Rerun/Viser 传输,则会显式回读合成图像。GPU API render_tiled_rgba_array() 返回借用的存储,下一次合成时会复用;如需保留帧,使用者必须复制。

两个场景相机都必须预先作为 CameraCfg 条目声明在场景中。键鼠导航会对所有环境中所选传感器的副本,施加相同的相机局部平移和旋转,包括未显示的副本;不会移动其他相机选项。这会真正改变传感器位姿,因此使用该传感器的策略和其他查看器也会看到新视角。导航读取实时传感器位姿,包括父刚体运动,不受 CameraCfg.update_latest_camera_pose 影响。暂停渲染会冻结图像显示和传感器导航,仅暂停训练则不会。

当 InteractiveSceneCfg.lazy_sensor_update=True 时,查看器仅读取选中的相机。未选中的相机仍会分配资源,且在其他使用者读取或场景采用即时传感器更新时,仍可能渲染。streaming_envs 选择显示的平铺图像,不决定捕获工作量。

示例#

运行 run_tiled_camera_visualizer.py 可演示相机流视图的两种用法:

  • 场景中声明的相机附着在 AnymalD 机器人基座上,在 Kit 可视化器中显示

  • 使用机器人已有的腕部相机传输画面,在 Newton 可视化器中显示

示例 1:跟随 AnymalD 机器人#

uv run python scripts/tutorials/07_visualizers/run_tiled_camera_visualizer.py \
    --task Isaac-Velocity-Rough-AnymalD --num_envs 256 --viz kit
uv run python scripts/tutorials/07_visualizers/run_tiled_camera_visualizer.py ^
    --task Isaac-Velocity-Rough-AnymalD --num_envs 256 --viz kit

脚本在构造环境前,为每个机器人的基座下添加 CameraCfg。其偏移定义相对于基座的视图,因此相机通过正常传感器生命周期跟随机器人。在 256 个环境中抽取 36 个用于显示。

Kit 可视化器:交互式视口

Kit 可视化器:串流相机视图

示例 2:传输机器人搭载相机的画面#

uv run --extra teleop python scripts/tutorials/07_visualizers/run_tiled_camera_visualizer.py \
    --task IsaacContrib-Stack-Cube-Galbot-Left-Arm-Gripper-Visuomotor --num_envs 25 --viz newton_gl
uv run --extra teleop python scripts/tutorials/07_visualizers/run_tiled_camera_visualizer.py ^
    --task IsaacContrib-Stack-Cube-Galbot-Left-Arm-Gripper-Visuomotor --num_envs 25 --viz newton_gl

Galbot 立方体堆叠环境自带腕部相机,可以从第一视角观察夹爪、桌面和立方体。脚本中的 NewtonGLVisualizerCfg 使用 /World/envs/env_.*/Robot/head_camera_sim_view_frame/head_camera 处的已有传感器传输画面;修改 streaming_sensor_prim_path 可显示其他相机。默认显示 25 个环境中 12 个相机的画面。

Newton 可视化器:交互式视口

Newton 可视化器:串流相机视图

配置#

在场景的 CameraCfg 上声明分辨率、位姿、渲染器和输出类型,再在 VisualizerCfg 中选择显示内容:

  • cameras=[SceneCameraCfg(prim_path=...)] 选择场景相机,view=ImageViewCfg(source="name") 选择具名传感器及其图像布局。Newton RTX 使用这些显式声明。

  • 对于其他查看器,streaming_sensor_prim_path 按配置的 prim 路径选择场景 Camera,{ENV_REGEX_NS} 宏使用场景的环境模板。省略时,Newton GL 从透视模式启动,并在 Camera View 下拉框中列出兼容场景相机。Kit 和 Web 查看器选择第一个兼容场景相机,若没有则面板为空。

  • streaming_envs 控制显示的环境格数。传入 int 可随机抽取指定数量的环境,传入 list[int] 则可固定显示指定索引的环境。

  • streaming_gt_types 选择显示的真值类型,例如 ["rgb", "depth", "segmentation", "normals"] 。

  • streaming_depth_min / streaming_depth_max 设置深度色图的范围,单位为米。

选择或布局变化时,会准备图像布局和颜色表。着色与平铺在源设备上执行,并复用输出缓冲区。Kit 直接显示 CUDA 图像,录制和 Web 使用者则通过 render_tiled_rgb_array() 回读合成 RGB 图像。

显示源#

VisualizerCfg.cameras 接受 PerspectiveCameraCfg 定义交互视图,接受 SceneCameraCfg(prim_path=...) 选择场景中已有的相机传感器。Newton GL 和 RTX 提供来源下拉框,Kit、Rerun 和 Viser 则在相机面板中使用第一个场景来源。每个显式场景来源都必须提供所请求的 streaming_gt_types 通道。

故障排除#

--video viz:newton_gl:streaming_view 录制选定场景相机的平铺图像,而不是网络流。未设置 --viz 时,捕获按需执行,不打开窗口。添加 --viz newton_gl 可显示可视化器,但推进录制帧并不需要它。新的物理步会刷新图像,同一步内重复捕获则复用图像。

  • 若视图提示没有匹配相机,请在场景中声明 CameraCfg,并通过 cameras=[SceneCameraCfg(prim_path=...)] 或 view=ImageViewCfg(source="name") 选择它。

  • 每个 streaming_gt_types 条目都必须在所选相机的 data_types 中有对应输出;rgba 也满足 rgb 请求。自动列出的相机支持所有所需通道,显式选择不兼容相机会报错。

  • 如果深度面板只显示单一颜色,请调整 streaming_depth_min 和 streaming_depth_max ,使其覆盖场景中预期的深度范围。

  • 如果视图开销过大,可减小 streaming_envs 、 --num_envs 或相机分辨率。

从自动生成相机迁移#

可视化器不再创建或销毁相机传感器。请用场景中的 CameraCfg 替换 streaming_cam_target_prim_path、streaming_cam_eye 和 streaming_cam_renderer_cfg:将 prim_path 放在所需父节点下,在相机配置中设置 offset 和 renderer_cfg,再通过 cameras=[SceneCameraCfg(...)] 选择它。这样,相机就纳入克隆计划及正常的传感器初始化、更新和销毁生命周期。完整选项对照见 3.0 迁移指南。

关闭可视化器不会影响相机,也不会影响读取同一相机的其他可视化器。streaming_envs 选择显示的平铺图像,不决定相机资源分配或捕获分辨率。

另请参阅#