📷 相机(Camera)#

系统相机#

当您使用渲染器功能时,MotrixSim 会自动创建一个系统相机。 系统相机可以接受用户的鼠标操作,实现移动、缩放等等。

系统相机操作效果

操作说明:

  • 鼠标左键按下并拖动:绕着焦点旋转摄像头

  • 鼠标右键按下并拖动:移动焦点(此时显示红圈为焦点)

  • 鼠标滚轮:缩放(到焦点位置不可再放大)

系统相机支持mjcf/visual/global中的部分配置,例如:

  • orthographic: 是否采用正交投影

  • fovy: 相机视野

  • azimuth: 初始化下,系统相机绕 z 轴的方位角

  • elevation: 初始化下,系统相机的俯仰角

场景相机#

除了系统相机之外,您可以在 MJCF 文件中配置额外的 Camera 标签。 我们把这种相机称为场景相机。场景相机可以为可视化提供额外的视角

自定义额外相机#

go1.xml为例,我们在 mjcf 中定义通过如下方式定义了相机:

<body name="trunk" pos="0 0 0.445" childclass="go1">
    <camera name="track" pos="0.846 -1.3 0.316" xyaxes="0.866 0.500 0.000 -0.171 0.296 0.940" mode="track"/>
    <camera name="top" pos="-1 0 1" xyaxes="0 -1 0 0.7 0 0.7" mode="track"/>
    <camera name="side" pos="0 -2 1" xyaxes="1 0 0 0 1 2" mode="track"/>
    <camera name="back" pos="-2.4 0 0.8" target="trunk" mode="targetbody"/>
</body>

这意味着我们在 go1 的 trunk 上定义了 4 个额外的相机。 相机的位置和朝向由 pos 和 xyaxes 指定,mode 指定了相机的运动模式。

切换场景主相机#

默认情况下,MotrixSim 使用系统相机作为主相机。 如果您想使用场景相机作为主相机,可以在 Python API 中通过如下方式切换:

cameras = model.cameras # 获取所有相机(注意这里不包括系统相机)
preview_cameras = [None,*cameras] # None表示系统相机

通过键盘事件切换相机:

if render.input.is_key_just_pressed("right"):
    # change to next camera
    preview_camera_idx = (preview_camera_idx + 1) % len(preview_cameras)
    render.set_main_camera(preview_cameras[preview_camera_idx])

if render.input.is_key_just_pressed("left"):
    # change to previous camera
    preview_camera_idx = (preview_camera_idx + len(preview_cameras) - 1) % len(preview_cameras)
    render.set_main_camera(preview_cameras[preview_camera_idx])


通过左右按键切换场景主相机

RGBD 传感器#

MotrixSim 支持将场景相机转变为 RGBD 视觉传感器,从而允许您获取相机的 RGB 图像和深度图像。 您可以通过以下接口来让一个场景相机渲染到一个离线图片上:

cameras = model.cameras
cameras[0].set_render_target("image", 320, 240)  # let this camera render to a image with 320x240 resolution

默认情况下,相机采用 RGB 渲染模式。 如果您想让相机只渲染深度图像,可以设置相机的 depth_only 属性为 True:

cameras[1].set_render_target("image", 640, 480)
cameras[1].depth_only = True  # This is a depth only camera
cameras[1].set_near_far(0.1, 1)  # Set the near and far plane of the camera

备注

深度图里保存的数值是NDC(normalized device coordinates)空间的。 如果您想将其转换为实际3D空间中与摄像机的距离,可以使用如下公式:

view_z = camera.near_plane / depth

RGBD 相机的实时效果会在左侧的 Camera 面板中显示:

左侧面板展示RGB相机和深度相机实时效果

读取相机图像数据#

要读取相机图像数据,您需要从 RenderApp 上获取 RenderCamera 对象,然后执行 capture 操作。

# press space to capture the rcamera.
if render.input.is_key_just_pressed("space"):
    rcam = render.get_camera(0)  # get render camera from index
    capture_tasks.append((capture_index, rcam.capture()))
    capture_index += 1

render.sync([data])

while len(capture_tasks) > 0:
    task: CaptureTask
    idx, task = capture_tasks[0]
    if task.state != "pending":
        capture_tasks.popleft()
        img = task.take_image()
        if img is not None:
            import os

            os.makedirs("shot", exist_ok=True)
            img.save_to_disk(f"shot/capture_{idx}.png")
    else:
        break

注意到,我们的 capture 操作是异步的,因此RenderCamera.capture方法会返回一个 CaptureTask 对象,您需要保存这个 CaptureTask 对象,并持续去检查它的状态,直到它完成。完成后,您可以通过 task.take_image()方法获取图像数据。

您可以看 Api 文档来获取更多细节: