# カメラ

LeRobotでは、動画キャプチャに複数のオプションが提供されています：

| クラス             | サポートされているカメラ                   |
| ----------------- | ----------------------------------- |
| `OpenCVCamera`    | スマートフォン、内蔵ラップトップカメラ、USBウェブカメラ |
| `ZMQCamera`       | ネットワーク接続カメラ           |
| `RealSenseCamera` | Intel RealSense（深度付き）        |
| `Reachy2Camera`   | Reachy 2 ロボットのカメラ              |

> [!TIP]
> `OpenCVCamera`の互換性に関する詳細については、[OpenCVによるビデオI/Oの概要](https://docs.opencv.org/4.x/d0/da7/videoio_overview.html)を参照してください。

### カメラを見つける

各カメラをインスタンス化するには一意の識別子が必要であり、これにより接続された複数のデバイスを区別できます。

`OpenCVCamera` と `RealSenseCamera` は自動検出に対応しています。以下のコマンドを実行すると、利用可能なデバイスとその識別子が一覧表示されます。OS によっては、コンピューター再起動やカメラの再接続後に識別子が変更される場合があります。

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

2つのカメラが接続されている場合、出力は次のようになります。

```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]
> `macOS` で Intel RealSense カメラを使用すると、[エラー](https://github.com/realsenseai/librealsense/issues/12307) `Error finding RealSense cameras: failed to set power state` が発生する場合があります。この問題は、`sudo` 権限で同じコマンドを実行することで解決できます。なお、`macOS` での RealSense カメラの使用は不安定です。

`ZMQCamera` と `Reachy2Camera` は自動検出に対応していません。ネットワークアドレスとポート、またはロボット SDK の設定を手動で指定して構成する必要があります。

## カメラの配置と照明

カメラの画像はポリシーが学習する対象となるため、高解像度よりも、適切なカメラの設置の方が重要です。

- **シーンを均一に照明する。** 拡散した一貫性のある光を使用し、直射日光、強い反射、移動する影を避けてください。録画時とデプロイ時の照明が異なる場合、ポリシーが失敗する可能性があります。
- **カメラを固定します。** 剛性に取り付け、録画セッションの間や、録画とデプロイの間で移動しないようにしてください。
- **タスクを定義する。**対象物、グリップ、ターゲットは少なくとも1つのカメラで視認可能である必要があります。良いテスト方法: カメラの映像だけを見て、あなた自身でそのタスクを実行できますか?
- **解像度は控えめに設定してください。** 640×480、30 fps ならほとんどのタスクで十分であり、録画とトレーニングを高速に保つことができます。

## カメラを使用する

### フレームアクセスモード

すべてのカメラクラスは、フレームのキャプチャに3つのアクセスモードを実装しています:

| メソッド                    | 動作                                                                                                                                                   | ブロック？        | 最適な用途                                 |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------- |
| `read()`                  | カメラハードウェアがフレームを返すまで待機します。カメラやSDKによって、長時間ブロックされる場合があります。                                                | はい            | シンプルなスクリプト、シーケンシャルキャプチャ       |
| `async_read(timeout_ms)`  | バックグラウンドスレッドから最新かつ未消費のフレームを返します。バッファが空の場合のみ、`timeout_ms` までブロックします。フレームが届かない場合は `TimeoutError` を送出します。 | タイムアウト付き | カメラのFPSに同期された制御ループ |
| `read_latest(max_age_ms)` | バッファ内の最新のフレームを参照します（古い可能性があります）。フレームが`max_age_ms`より古い場合、`TimeoutError`を発生させます。                                        | いいえ             | UIの可視化、ログ記録、モニタリング    |

### 使用例

以下の例では、カメラ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()` を呼び出すと、**カラー**フレームが `(H, W, 3)` の `uint8` として返されます。`read_depth()` を呼び出すと、**深度マップ**が `(H, W, 1)` の `uint16` として返され、各ピクセルの値はセンサーからの距離を**ミリメートル**単位で表します。ピクセル値が `0` であることは、通常「測定値が利用できない」（範囲外、遮蔽、低信頼度）ことを意味します。

録画の間、制御ループは`read_latest()`（カラー）および`read_latest_depth()`（深度）を介して、最新バッファリングされたフレームを非ブロッキングで参照し、深度マップを兄弟機能として追加します（例：`front`の隣に`front_depth`）。

データセットの録画時に深度ストリームがどのように保存・エンコードされるかについては、動画エンコーディングガイドの[Depth streams](./video_encoding_parameters#depth-streams)セクションを参照してください。

## スマートフォンのカメラを使用する

macOSでiPhoneをカメラとして使用するには、Continuity Camera機能を有効にしてください：

- MacがmacOS 13以降、iPhoneがiOS 16以降のOSで動作していることを確認してください。
- 同じApple IDで両方のデバイスにサインインしてください。
- デバイスをUSBケーブルで接続するか、ワイヤレス接続のためにWi-FiとBluetoothをオンにしてください。

詳細については、[Apple サポート](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` に移動します。`Base(Canvas) Resolution` と `Output(Scaled) Resolution` を手動で入力して `640x480` に変更します。
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**: ネイティブのCameraアプリは仮想カメラに対応していません。ビデオ会議アプリ（Zoom、Teams）を使用するか、`lerobot-find-cameras opencv`を直接実行して確認してください。

トラブルシューティング

> 仮想カメラの解像度が正しくありません。

仮想カメラソースを削除して再作成してください。解像度は作成後に変更できません。

> OpenCVCamera(X) のバックグラウンドスレッドでフレームの読み取り中にエラーが発生しました: OpenCVCamera(X) のフレーム幅=640 または高さ=480 が、設定された幅=1920 または高さ=1080 と一致しません。

このエラーは、リサイズ後もOBS Virtual Cameraが`1920x1080`の解像度を報告していることが原因です。現時点での唯一の対処法は、`_postprocess_image()`内の幅と高さのチェックをコメントアウトすることです。

すべてが正しく設定されている場合、スマートフォンは標準的な OpenCV カメラとして認識され、`OpenCVCamera` で使用できます。

