0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberry Pi 5のDocker環境でRaspberry Pi AI Camera(imx500)を使う

0
Posted at

今回もGeminiに助けてもらいながら構築していきました。

Geminiは Debian 13 (Trixie) では、OSのアップデートに伴いHailoやIMX500のサポートがOSの標準パッケージとして統合され、環境構築自体は以前よりもシンプルになっています。 と言っていましたがけっこう試行錯誤しました。

今回も、OSのインストール直後から、Docker環境を構築し、Python (Picamera2) を使ってテキストで認識結果を出力するまでの全手順をまとめました。


ホストOS (Raspberry Pi 5) の環境構築

まずはOSインストール直後の状態から、システムを最新化し、AI Cameraを認識させるために必要なファームウェアとモデル群をインストールします。

1. パッケージのアップデートとAI Cameraツールのインストール

ターミナルを開き、以下のコマンドを実行します。Trixieでは imx500-all を入れることで必要なものが一括で揃います。

パッケージのアップデート
sudo apt update && sudo apt full-upgrade -y
AI Cameraツールのインストール
sudo apt install -y imx500-all rpicam-apps

注意: Trixie環境では、AIモデルファイル(.rpk)は標準で /usr/share/imx500-models/ に配置されます。

$ ls /usr/share/imx500-models/
imx500_network_deeplabv3plus.rpk          imx500_network_efficientnetv2_b2.rpk  imx500_network_mobilevit_xxs.rpk            imx500_network_regnety_004.rpk
imx500_network_efficientdet_lite0_pp.rpk  imx500_network_higherhrnet_coco.rpk   imx500_network_nanodet_plus_416x416_pp.rpk  imx500_network_resnet18.rpk
imx500_network_efficientnet_bo.rpk        imx500_network_inputtensoronly.rpk    imx500_network_nanodet_plus_416x416.rpk     imx500_network_shufflenet_v2_x1_5.rpk
imx500_network_efficientnet_lite0.rpk     imx500_network_mnasnet1.0.rpk         imx500_network_posenet.rpk                  imx500_network_squeezenet1.0.rpk
imx500_network_efficientnetv2_b0.rpk      imx500_network_mobilenet_v2.rpk       imx500_network_regnetx_002.rpk              imx500_network_ssd_mobilenetv2_fpnlite_320x320_pp.rpk
imx500_network_efficientnetv2_b1.rpk      imx500_network_mobilevit_xs.rpk       imx500_network_regnety_002.rpk

2. 再起動とカメラの認識確認

一度再起動してファームウェアを読み込ませます。

sudo reboot

再起動後、以下のコマンドでカメラが認識されているか確認します。

rpicam-hello --list-cameras

IMX500 という名前が表示されれば、ハードウェア側の準備は完了です。

Available cameras
-----------------
0 : imx500 [4056x3040 10-bit RGGB] (/base/axi/pcie@1000120000/rp1/i2c@88000/imx500@1a)
    Modes: 'SRGGB10_CSI2P' : 2028x1520 [30.02 fps - (0, 0)/4056x3040 crop]
                             4056x3040 [10.00 fps - (0, 0)/4056x3040 crop]

3. Dockerのインストール

公式のインストールスクリプトを使用してDockerをインストールし、現在のユーザー(例: pi)をdockerグループに追加します。

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

設定を反映させるため、一度ログアウトして再度ログインするか、newgrp docker を実行してください。


Dockerコンテナ環境の作成

AI CameraをDockerから叩くには、デバイスファイルへのアクセス権(特権モード)と、メモリ共有(IPCホストモード)、そして推論モデルファイルのマウントが必要です。

作業用のディレクトリを作成し、移動します。

mkdir ~/ai_camera_docker
cd ~/ai_camera_docker

以下の3つのファイルを作成します。

1. Dockerfile

コンテナのOSもDebian 13 (Trixie) に合わせ、カメラ制御に必要な python3-picamera2 などをインストールします。

vi Dockerfile
Dockerfile
FROM debian:trixie

# 1. Raspberry Pi公式リポジトリを [trusted=yes] 付きで追加
# これにより、SHA1の署名エラーを無視してパッケージ取得を許可します
RUN echo "deb [trusted=yes] http://archive.raspberrypi.com/debian trixie main" > /etc/apt/sources.list.d/raspi.list

# 2. リポジトリを更新し、目的のパッケージをインストール
RUN apt-get update && apt-get install -y \
    python3 \
    python3-picamera2 \
    python3-opencv \
    python3-numpy \
    imx500-all \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY ai_detect_text.py /app/

CMD ["python3", "-u", "ai_detect_text.py"]

2. docker-compose.yml

コンテナからRaspberry Piのハードウェアリソースへアクセスするための設定です。

vi docker-compose.yml
docker-compose.yml
services:
  ai_camera:
    build: .
    privileged: true
    ipc: host
    volumes:
      - /dev:/dev
      - /run/udev:/run/udev:ro
      - /usr/share/imx500-models:/usr/share/imx500-models:ro
      - /sys/kernel/debug:/sys/kernel/debug
    restart: unless-stopped

ポイント: --ipc=host/dev ディレクトリ全体のマウント(特に /dev/dma_heap/dev/video*)は、IMX500のテンソルデータ(推論結果)や画像バッファをやり取りするために必須です。

3. ai_detect_text.py

GUI画面を出さず、推論結果のメタデータをパースしてコンソールにテキストで出力するPythonプログラムです。今回は標準で入っている MobileNet SSD v2 を使用します。

vi ai_detect_text.py
ai_detect_text.py
import time
import json
from picamera2 import Picamera2
from picamera2.devices.imx500 import IMX500

def main():
    print("Initializing AI Camera...")
    picam2 = Picamera2()

    model_path = "/usr/share/imx500-models/imx500_network_ssd_mobilenetv2_fpnlite_320x320_pp.rpk"
    imx500 = IMX500(model_path)

    # IMX500 SSD MobileNet V2 用の標準ラベルリスト
    labels = [
        "background", "person", "bicycle", "car", "motorcycle", "airplane", "bus",
        "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign",
        "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
        "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag",
        "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite",
        "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket",
        "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana",
        "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza",
        "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table",
        "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
        "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock",
        "vase", "scissors", "teddy bear", "hair drier", "toothbrush"
    ]

    config = picam2.create_preview_configuration(main={"size": (640, 480), "format": "RGB888"})
    picam2.configure(config)
    picam2.start()

    print("AI Inference Started (Manual Label Mode)!")

    try:
        while True:
            request = picam2.capture_request()
            metadata = request.get_metadata()

            np_outputs = imx500.get_outputs(metadata)
            if np_outputs is not None:
                # 取得するデータの階層を調整します
                # np_outputs[0] が boxes, [1] が scores, [2] が classes です
                boxes = np_outputs[0]
                scores = np_outputs[1]
                classes = np_outputs[2]

                # もしデータが多次元配列(1, N, 4)などになっている場合、
                # [0] を使って最初のバッチを取り出します
                if boxes.ndim == 3:
                    boxes = boxes[0]
                    scores = scores[0]
                    classes = classes[0]

                for i in range(len(scores)):
                    score = scores[i]
                    if score > 0.55:  # 👈 ここから下が「検知に成功した時」の処理
                        category = int(classes[i])
                        box = boxes[i]

                        # ラベル名の決定
                        label_name = labels[category] if category < len(labels) else f"ID {category}"

                        # 座標の計算
                        coords = [round(float(c), 3) for c in box]

                        # ログ出力
                        print(f"✅ 検出: {label_name:<10} (信頼度: {score:.2f}) | 座標: {coords}")

                        # --- ここで「人」を判定する! ---
                        if label_name == "person" and score > 0.6:
                            print("⚠️  警告: 人を検知しました!")

            request.release()
            time.sleep(0.1)

    except KeyboardInterrupt:
        picam2.stop()

if __name__ == "__main__":
    main()

今回はラベルリストを手動で作っています。


プログラムの実行方法

ビルドと起動

ファイルがすべて準備できたら、同じディレクトリ(~/ai_camera_docker)で以下のコマンドを実行し、コンテナをビルドして起動します。

docker compose up --build

期待される動作

コンテナが立ち上がり、モデルのロード(数秒〜数十秒かかります)が完了すると、カメラの前に何かかざすと情報が検出されます。
ただし、今回はモデルなのかモデルラベルの問題か検出したものをうまく判別できてなさそうだったので、そこの改善がこんごの課題となります。

Attaching to ai_camera-1
ai_camera-1  | Initializing AI Camera...
ai_camera-1  | [1:23:43.437043025] [1]  INFO Camera camera_manager.cpp:340 libcamera v0.7.0+rpt20260205
ai_camera-1  | [1:23:43.446385910] [10]  INFO RPI pisp.cpp:720 libpisp version 1.3.0
ai_camera-1  | [1:23:43.455629295] [10]  INFO IPAProxy ipa_proxy.cpp:180 Using tuning file /usr/share/libcamera/ipa/rpi/pisp/imx500.json
ai_camera-1  | [1:23:43.462598495] [10]  INFO Camera camera_manager.cpp:223 Adding camera '/base/axi/pcie@1000120000/rp1/i2c@88000/imx500@1a' for pipeline handler rpi/pisp
ai_camera-1  | [1:23:43.462677291] [10]  INFO RPI pisp.cpp:1181 Registered camera /base/axi/pcie@1000120000/rp1/i2c@88000/imx500@1a to CFE device /dev/media2 and ISP device /dev/media0 using PiSP variant BCM2712_D0
ai_camera-1  | [1:23:43.481627890] [1]  INFO Camera camera.cpp:1215 configuring streams: (0) 640x480-RGB888/sRGB (1) 2028x1520-RGGB_PISP_COMP1/RAW
ai_camera-1  | [1:23:43.481764852] [10]  INFO RPI pisp.cpp:1485 Sensor: /base/axi/pcie@1000120000/rp1/i2c@88000/imx500@1a - Selected sensor format: 2028x1520-SRGGB10_1X10/RAW - Selected CFE format: 2028x1520-PC1R/RAW
ai_camera-1  | AI Inference Started (Manual Label Mode)!
ai_camera-1  | ✅ 検出: background (信頼度: 0.56) | 座標: [0.086, 0.002, 0.994, 0.518]
ai_camera-1  | ✅ 検出: background (信頼度: 0.62) | 座標: [0.002, 0.019, 0.998, 0.868]
ai_camera-1  | ✅ 検出: background (信頼度: 0.62) | 座標: [0.0, 0.0, 1.0, 0.882]
ai_camera-1  | ✅ 検出: background (信頼度: 0.68) | 座標: [0.002, 0.0, 0.998, 0.865]
ai_camera-1  | ✅ 検出: background (信頼度: 0.68) | 座標: [0.02, 0.007, 1.0, 0.871]
ai_camera-1  | ✅ 検出: background (信頼度: 0.73) | 座標: [0.067, 0.017, 0.99, 0.569]
ai_camera-1  | ✅ 検出: background (信頼度: 0.62) | 座標: [0.087, 0.014, 0.995, 0.667]
ai_camera-1  | ✅ 検出: background (信頼度: 0.68) | 座標: [0.063, 0.009, 1.0, 0.809]
ai_camera-1  | ✅ 検出: background (信頼度: 0.68) | 座標: [0.087, 0.008, 0.995, 0.829]
ai_camera-1  | ✅ 検出: background (信頼度: 0.62) | 座標: [0.097, 0.0, 1.0, 0.885]
ai_camera-1  | ✅ 検出: background (信頼度: 0.68) | 座標: [0.139, 0.016, 1.0, 0.902]

終了する場合は Ctrl + C を押してください。


💡 Debian 13 (Trixie) 環境での注意点まとめ

  • モデルファイルの場所: 以前のOSバージョンやサードパーティ製の環境ではパスがバラバラでしたが、Trixieでは /usr/share/imx500-models/ に統一されています。コンテナ内で推論を実行する場合、このディレクトリをVolumeマウントして共有するのが最も確実です。
  • Picamera2の利用: IMX500は「生画像のキャプチャ」と「推論結果のテンソルデータの受け渡し」を同時に行います。コンテナ内でこれを取り回すため、docker-compose.yml での --ipc=hostprivileged: true は外さないようにしてください。

さいごに

ということで、Docker上でAI Cameraは動作しました。今後は物体検出をして、その検出結果をMQTT経由で以前作成したTimescaleDBに格納するところを作っていきたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?