# Камеры

LeRobot предлагает несколько вариантов записи видео:

| Класс             | Поддерживаемые камеры                   |
| ----------------- | ----------------------------------- |
| `OpenCVCamera`    | Телефон, встроенная в ноутбук камера, USB-веб-камеры |
| `ZMQCamera`       | Сетевые камеры           |
| `RealSenseCamera` | Intel RealSense (с глубиной)        |
| `Reachy2Camera`   | Камеры робота Reachy 2              |

> [!TIP]
> Сведения о совместимости с `OpenCVCamera` приведены в разделе [Обзор ввода-вывода видео с OpenCV](https://docs.opencv.org/4.x/d0/da7/videoio_overview.html).

### Найдите свою камеру

Каждая камера требует уникального идентификатора для инициализации, что позволяет различать несколько подключенных устройств.

`OpenCVCamera` и `RealSenseCamera` поддерживают автоматическое обнаружение устройств. Выполните команду ниже, чтобы получить список доступных устройств и их идентификаторов. Обратите внимание, что эти идентификаторы могут измениться после перезагрузки компьютера или повторного подключения камеры, в зависимости от вашей операционной системы.

```bash
lerobot-find-cameras opencv # or realsense for Intel Realsense cameras
```

Если подключено две камеры, вывод будет примерно таким:

```bash
--- Detected Cameras ---
Camera #0:
  Name: OpenCV Camera @ 0
  Type: OpenCV
  Id: 0
  Backend api: AVFOUNDATION
  Default stream profile:
    Format: 16.0
    Width: 1920
    Height: 1080
    Fps: 15.0
--------------------
(more cameras ...)
```

> [!WARNING]
> При использовании камер Intel RealSense в `macOS` может возникать [ошибка](https://github.com/realsenseai/librealsense/issues/12307): `Error finding RealSense cameras: failed to set power state`. Чтобы её устранить, выполните ту же команду с правами `sudo`. Обратите внимание, что использование камер RealSense в `macOS` нестабильно.

`ZMQCamera` и `Reachy2Camera` не поддерживают автоматическое обнаружение. Их необходимо настроить вручную, указав сетевой адрес и порт или параметры SDK робота.

## Расположение камеры и освещение

Изображения с камеры — это то, на основе чего обучается ваша политика, поэтому правильная настройка камеры важнее высокого разрешения:

- **Освещайте сцену равномерно.** Используйте рассеянный, стабильный свет и избегайте прямого солнечного света, сильных бликов и движущихся теней. Различия в освещении между записью и эксплуатацией могут привести к сбою политики.
- **Закрепите камеры.** Установите их жёстко и не перемещайте между сессиями записи, а также между записью и развёртыванием.
- **Определите задачу.** Объект, захват и цель должны быть видны как минимум в одной камере. Хороший тест: смогли бы вы выполнить задачу, глядя только на изображения с камер?
- **Держите разрешение умеренным.** 640×480 при 30 кадрах в секунду достаточно для большинства задач и позволяет ускорить запись и обучение.

## Использование камер

### Режимы доступа к кадрам

Все классы камер реализуют три режима доступа для захвата кадров:

| Метод                    | Поведение                                                                                                                                                   | Блоки?        | Лучше всего подходит для                                 |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------- |
| `read()`                  | Ожидает возврата кадра от аппаратного обеспечения камеры. Может блокироваться на длительное время в зависимости от камеры и SDK.                                                | Да            | Простые скрипты, последовательный захват       |
| `async_read(timeout_ms)`  | Возвращает последнюю непрочитанную рамку из фонового потока. Блокируется только при пустом буфере, максимум на `timeout_ms`. Вызывает `TimeoutError`, если рамка не поступает. | С таймаутом | Циклы управления, синхронизированные с частотой кадров камеры |
| `read_latest(max_age_ms)` | Просматривает последний кадр в буфере (может быть устаревшим). Вызывает `TimeoutError`, если возраст кадра превышает `max_age_ms`.                                        | Нет             | Визуализация в интерфейсе, ведение журналов, мониторинг    |

### Примеры использования

Приведённые ниже примеры показывают, как использовать API камеры для настройки и захвата кадров с различных типов камер.

- **Блокирующая и неблокирующая захват кадров** с камерой на базе OpenCV
- **Захват цвета и глубины** с помощью камеры Intel RealSense

> [!WARNING]
> Некорректное отключение камер может привести к утечкам ресурсов. Используйте протокол менеджера контекста для обеспечения автоматической очистки:
>
> ```python
> with OpenCVCamera(config) as camera:
>     ...
> ```
>
> Вы также можете вызывать `connect()` и `disconnect()` вручную, но для последнего всегда используйте блок `finally`.

```python
from lerobot.cameras.opencv import OpenCVCamera, OpenCVCameraConfig
from lerobot.cameras import ColorMode, Cv2Rotation

# Construct an `OpenCVCameraConfig` with your desired FPS, resolution, color mode, and rotation.
config = OpenCVCameraConfig(
    index_or_path=0,
    fps=15,
    width=1920,
    height=1080,
    color_mode=ColorMode.RGB,
    rotation=Cv2Rotation.NO_ROTATION
)

# Instantiate and connect an `OpenCVCamera`, performing a warm-up read (default).
with OpenCVCamera(config) as camera:

    # Read a frame synchronously — blocks until hardware delivers a new frame
    frame = camera.read()
    print(f"read() call returned frame with shape:", frame.shape)

    # Read a frame asynchronously with a timeout — returns the latest unconsumed frame or waits up to timeout_ms for a new one
    try:
        for i in range(10):
            frame = camera.async_read(timeout_ms=200)
            print(f"async_read call returned frame {i} with shape:", frame.shape)
    except TimeoutError as e:
        print(f"No frame received within timeout: {e}")

    # Instantly return a frame - returns the most recent frame captured by the camera
    try:
        initial_frame = camera.read_latest(max_age_ms=1000)
        for i in range(10):
            frame = camera.read_latest(max_age_ms=1000)
            print(f"read_latest call returned frame {i} with shape:", frame.shape)
            print(f"Was a new frame received by the camera? {not (initial_frame == frame).any()}")
    except TimeoutError as e:
        print(f"Frame too old: {e}")

```

```python
from lerobot.cameras.realsense import RealSenseCamera, RealSenseCameraConfig
from lerobot.cameras import ColorMode, Cv2Rotation

# Create a `RealSenseCameraConfig` specifying your camera’s serial number and enabling depth.
config = RealSenseCameraConfig(
    serial_number_or_name="233522074606",
    fps=15,
    width=640,
    height=480,
    color_mode=ColorMode.RGB,
    use_depth=True,
    # Optional fixed color controls. Omit them to leave the current sensor settings unchanged.
    exposure=120,
    gain=64,
    white_balance=4600,
    rotation=Cv2Rotation.NO_ROTATION
)

# Instantiate and connect a `RealSenseCamera` with warm-up read (default).
camera = RealSenseCamera(config)
camera.connect()

# Capture a color frame via `read()` and a depth map via `read_depth()`.
try:
    color_frame = camera.read()
    depth_map = camera.read_depth()
    print("Color frame shape:", color_frame.shape)
    print("Depth map shape:", depth_map.shape)
finally:
    camera.disconnect()
```

Ручное управление цветом отключает соответствующий автоматический режим экспозиции или баланса белого. Поддерживаемые диапазоны значений зависят от модели камеры; некорректное значение вызывает ошибку при подключении, в которой указывается диапазон, сообщённый датчиком. Запрос неподдерживаемого параметра также вызывает ошибку. Если параметр не указан, существующая автоматическая или ручная настройка датчика остаётся без изменений. Эти опции требуют `use_rgb=True`.

Ручное управление цветом требует наличия отдельного модуля RGB. Камеры, не оснащённые таким модулем, например RealSense D405, не поддерживают эти функции и выдают ошибку при подключении.

### Работа с глубиной

Камеры Intel RealSense и Reachy 2 могут захватывать цветные и глубинные данные синхронно. Вызов `read()` возвращает кадр **color** в формате `(H, W, 3)` `uint8`. Вызов `read_depth()` возвращает **карту глубины** в формате `(H, W, 1)` `uint16`, где значение каждого пикселя представляет расстояние от датчика, выраженное в **миллиметрах**. Значение пикселя `0` обычно означает «измерение недоступно» (вне диапазона, перекрыто или низкая уверенность).

Во время записи контрольный цикл неблокирующе считывает самые свежие кадры из буфера с помощью `read_latest()` (цвет) и `read_latest_depth()` (глубина), добавляя карту глубины как соседний признак (например, `front_depth` рядом с `front`).

Сведения о том, как потоки глубины хранятся и кодируются при записи набора данных, приведены в разделе [Depth streams](./video_encoding_parameters#depth-streams) руководства по кодированию видео.

## Используйте камеру вашего телефона

Чтобы использовать iPhone в качестве камеры на macOS, включите функцию Continuity Camera:

- Убедитесь, что на вашем Mac установлена macOS 13 или более поздняя версия, а на вашем iPhone — iOS 16 или более поздняя версия.
- Войдите на обоих устройствах с одним и тем же Apple ID.
- Подключите устройства с помощью USB-кабеля или включите Wi-Fi и Bluetooth для беспроводного соединения.

Для получения подробной информации посетите [Apple support](https://support.apple.com/en-gb/guide/mac-help/mchl77879b8a/mac).

Если вы хотите использовать свой телефон в качестве камеры в OBS, выполните следующие шаги для настройки виртуальной камеры.

1. _(только Linux) Установите `v4l2loopback-dkms` и `v4l-utils`_. Эти пакеты создают виртуальные устройства камеры и проверяют их настройки. Установите с помощью:

```bash
sudo apt install v4l2loopback-dkms v4l-utils
```

2. _Установите приложение [DroidCam](https://droidcam.app) на свой телефон_. Приложение доступно для iOS и Android.
3. _Скачайте и установите [OBS Studio](https://obsproject.com)_.
4. _Скачайте и установите [плагин DroidCam для OBS](https://droidcam.app/obs)_.
5. _Запустите OBS Studio_.

6. _Добавьте телефон в качестве источника_. Следуйте инструкциям [здесь](https://droidcam.app/obs/usage). Убедитесь, что разрешение установлено на `640x480`, чтобы избежать водяных знаков.
7. _Настройка разрешения_. В OBS Studio перейдите в `File > Settings > Video` или `OBS > Preferences... > Video`. Вручную введите `640x480` в поля `Base(Canvas) Resolution` и `Output(Scaled) Resolution`.
8. _Запустите виртуальную камеру_. В OBS Studio следуйте инструкциям [здесь](https://obsproject.com/kb/virtual-camera-guide).
9. _Проверьте настройку виртуальной камеры и разрешение_.
   - **Linux**: используйте `v4l2-ctl`, чтобы получить список устройств и проверить разрешение:
     ```bash
     v4l2-ctl --list-devices  # find VirtualCam and note its /dev/videoX path
     v4l2-ctl -d /dev/videoX --get-fmt-video  # replace with your VirtualCam path
     ```
     Вы должны увидеть `VirtualCam` в списке и разрешение `640x480`.
   - **macOS**: откройте Photo Booth или FaceTime и выберите «OBS Virtual Camera» в качестве источника.
   - **Windows**: встроенное приложение «Камера» не поддерживает виртуальные камеры. Используйте приложение для видеоконференций (Zoom, Teams) или выполните `lerobot-find-cameras opencv` напрямую для проверки.

Устранение неполадок

> Неверное разрешение виртуальной камеры.

Удалите источник виртуальной камеры и создайте его заново. Разрешение нельзя изменить после создания.

> Ошибка чтения кадра в фоновом потоке для OpenCVCamera(X): ширина кадра OpenCVCamera(X) = 640 или высота = 480 не соответствуют настроенным значениям ширины = 1920 или высоты = 1080.

Эта ошибка возникает из-за того, что OBS Virtual Camera сообщает разрешение `1920x1080`, несмотря на изменение масштаба. Единственное решение на данный момент — закомментировать проверку ширины и высоты в `_postprocess_image()`.

Если всё настроено правильно, ваш телефон будет отображаться как стандартная камера OpenCV и сможет использоваться с `OpenCVCamera`.

