Skip to content

sim2bot.Robot.camera

camera(camera_id: str, fps: Optional[int] = None, width: Optional[int] = None, height: Optional[int] = None, quality: Optional[float] = None, codec: Any = None) -> CameraStream

Subscribe to a global scene camera and return a latest-frame stream.

Parameters:

  • camera_id (str) –

    Required ID returned by cameras, such as model:0, custom:<id>, or sensor:<id>.

  • fps (Optional[int], default: None ) –

    Requested frame rate from 1 to 30. None inherits GUI/default stream settings.

  • width (Optional[int], default: None ) –

    Requested width in pixels; currently clamped to 16–1920.

  • height (Optional[int], default: None ) –

    Requested height in pixels; currently clamped to 16–1080.

  • quality (Optional[float], default: None ) –

    JPEG quality from 0.1 to 1.0. Ignored for raw frames.

  • codec (Any, default: None ) –

    "jpeg" for compact frames or "raw" for uncompressed RGBA. H.264 is reserved for later.

Returns:

Raises:

  • RuntimeError

    If the control client is not connected.

  • TimeoutError

    If the video WebSocket cannot connect before its timeout.

  • ConnectionClosed

    If either WebSocket closes during subscription.

  • OSError

    If the video or control endpoint cannot be opened or written.

Notes

Cameras are global scene resources and do not take robot=. One overhead feed can observe several independently addressed robots. Unspecified stream options inherit the camera's GUI configuration.

Examples:

Read one world-mounted camera frame::

overhead = next(
    item for item in sim.cameras()
    if item["mount"]["kind"] == "world"
)
with sim.camera(overhead["id"], fps=24, width=640, height=480) as stream:
    frame = stream.read(timeout=2.0)
Source code in sim2bot/client.py
def camera(
    self,
    camera_id: str,
    fps: Optional[int] = None,
    width: Optional[int] = None,
    height: Optional[int] = None,
    quality: Optional[float] = None,
    codec: Any = None,
) -> CameraStream:
    """Subscribe to a global scene camera and return a latest-frame stream.

    Args:
        camera_id: Required ID returned by
            [`cameras`][sim2bot.client.Robot.cameras], such as
            ``model:0``, ``custom:<id>``, or ``sensor:<id>``.
        fps: Requested frame rate from 1 to 30. ``None`` inherits GUI/default
            stream settings.
        width: Requested width in pixels; currently clamped to 16–1920.
        height: Requested height in pixels; currently clamped to 16–1080.
        quality: JPEG quality from 0.1 to 1.0. Ignored for raw frames.
        codec: ``"jpeg"`` for compact frames or ``"raw"`` for uncompressed
            RGBA. H.264 is reserved for later.

    Returns:
        A [`CameraStream`][sim2bot.client.CameraStream]. Close it or use a
        context manager to stop the subscription.

    Raises:
        RuntimeError: If the control client is not connected.
        TimeoutError: If the video WebSocket cannot connect before its timeout.
        ConnectionClosed: If either WebSocket closes during subscription.
        OSError: If the video or control endpoint cannot be opened or written.

    Notes:
        Cameras are global scene resources and do not take ``robot=``. One
        overhead feed can observe several independently addressed robots.
        Unspecified stream options inherit the camera's GUI configuration.

    Examples:
        Read one world-mounted camera frame::

            overhead = next(
                item for item in sim.cameras()
                if item["mount"]["kind"] == "world"
            )
            with sim.camera(overhead["id"], fps=24, width=640, height=480) as stream:
                frame = stream.read(timeout=2.0)
    """
    self._ensure_video()
    stream = CameraStream(self, camera_id)
    # Send only the params the caller specified, so unset ones fall back to
    # the per-camera GUI default on the sim side (controller overrides GUI).
    options: dict = {"type": "camera_subscribe", "camera": camera_id}
    if fps is not None:
        options["fps"] = fps
    if width is not None:
        options["width"] = width
    if height is not None:
        options["height"] = height
    if quality is not None:
        options["quality"] = quality
    if codec is not None:
        options["codec"] = (
            _CODEC_BY_NAME.get(codec, codec) if isinstance(codec, str) else codec
        )
    with self._cameras_lock:
        self._cameras[camera_id] = (options, stream)
    self._send(options)  # initial subscribe; renewed by _renew_loop
    return stream

Practical examples

Examples use objects discovered from the current scene rather than hard-coded robot metadata.

Read one JPEG frame

camera = sim.cameras()[0]
with sim.camera(camera["id"], fps=24, width=640, height=480) as feed:
    frame = feed.read(timeout=2.0)
    if frame is not None:
        print(frame.width, frame.height, frame.age)

Use raw frames on localhost

with sim.camera(camera_id, codec="raw", width=320, height=240) as feed:
    frame = feed.read(timeout=2.0)
    image_bgr = frame.image() if frame else None

Stream an overhead view while moving two arms

overhead = next(c for c in sim.cameras() if c["mount"]["kind"] == "world")
with sim.camera(overhead["id"], fps=20) as feed:
    sim.move_to(left.home, robot=left.index)
    sim.move_to(right.home, robot=right.index)
    frame = feed.read()

Guidance

Usage tip

Use JPEG for bandwidth efficiency and raw RGBA for the lowest localhost encode/decode latency.

Common error

Camera IDs are global; camera() intentionally has no robot= argument.


See also