momomomomomomomomomo
@momomomomomomomomomo

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

libcamera(RaspberryPi bookworm)のエラー

解決したいこと

libcameraのエラーについて

ラズパイを起動して1回目のプログラム実行は正常に動くが、
同じプログラムを再度実行したり「rpicam-hello」をするとエラーになりカメラが起動されない。
以前から使用していたコードなのに今週からエラー続出・・・
エラーメッセージを調べたところカメラが正常終了しておらず、
2回目以降に実行した際、「別でカメラが使われてるから起動できないよ」みたいな感じだそうです。

使用環境など
Raspberri Pi4 Model B
OS : Bookworm
camera : Camera Module V3

発生している問題・エラー

①プログラム実行2回目以降のエラー

RuntimeError: Failed to configure camera: Invalid argument

②「rpicam-hello」時のエラー

rp4@raspberrypi:~ $ libcamera-vid -o test.h264 -t 500 -n
[0:47:37.137430560] [2434]  INFO Camera camera_manager.cpp:325 libcamera v0.3.2+99-1230f78d
[0:47:37.154949139] [2440] ERROR V4L2 v4l2_device.cpp:351 'imx708': Unable to set controls: Device or resource busy
[0:47:37.184857854] [2440]  WARN RPiSdn sdn.cpp:40 Using legacy SDN tuning - please consider moving SDN inside rpi.denoise
[0:47:37.187160662] [2440]  INFO RPI vc4.cpp:447 Registered camera /base/soc/i2c0mux/i2c@1/imx708@1a to Unicam device /dev/media0 and ISP device /dev/media1
[0:47:37.187272646] [2440]  INFO RPI pipeline_base.cpp:1120 Using configuration file '/usr/share/libcamera/pipeline/rpi/vc4/rpi_apps.yaml'
Mode selection for 640:480:12:P
    SBGGR10_CSI2P,1536x864/0 - Score: 1486.67
    SBGGR10_CSI2P,2304x1296/0 - Score: 1786.67
    SBGGR10_CSI2P,4608x2592/0 - Score: 2686.67
Stream configuration adjusted
[0:47:37.189070740] [2434]  INFO Camera camera.cpp:1197 configuring streams: (0) 640x480-YUV420 (1) 1536x864-SRGGB10_CSI2P
[0:47:37.189167483] [2440] ERROR V4L2 v4l2_device.cpp:351 'imx708': Unable to set controls: Device or resource busy

該当するソースコード

(※仕事で使うものなのでコード全文の公開が出来ません。カメラに関する部分を抽出しました。)

# カメラの設定
picam2 = Picamera2()
picam2.configure(picam2.create_preview_configuration(main={"format": 'XRGB8888', "size": (frameWidth, frameHeight)}))
picam2.start()
# カメラを連続オートフォーカスモードにする
picam2.set_controls({"AfMode": controls.AfModeEnum.Continuous})

while True:
    match s:
        case Status.A:
            frame = picam2.capture_array()		# 撮影画面の表示
            cv2.waitKey(1)

            # ESCを押すと終了
            if key == 27:
                s = "D"

            if key == ord("m"):
                s = "B"
                
            # 他のkey省略
            
    
        case Status.B:
            cv2.imwrite('ColorFrame.png', frame)
            frameGray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            cv2.imwrite('GrayFrame.png', frameGray)		# グレースケールで保存
        # ~他の処理は省略~
        
        case Status.C:
        # 省略
        
        case Status.D:
            print("click : ESC")
            break

    # match終了、静止画の連続表示で動画のように見せるため0.03秒待つ
    time.sleep(0.03)

# ESCが押されたらカメラ停止とカメラ映像を閉じる
picam2.stop()
cv2.destroyAllWindows()
    

自分で試したこと

・config.txtに dtoverlay=imx708の追加
・再起動(再起動後1回目は正常に動くが2回目以降はエラーになる)
・アップデートとアップグレード

試した方がいい事や、コードで間違っているところがあればご指摘願います。

0

1Answer

こんにちは。解決できるかわからないのですが、カメラを確実にクローズしてアプリを終了するという狙いで、AIを使ってコードに手を加えてみました

キーボード入力以外に、Ctrl+Zなどの終了イベントやエラーなどあってもカメラ停止処理は呼ばれるような処理を目指しています。期待した修正になっているか確認できるように適宜標準出力も追加しています

色々やっているうちにフレームレート制御が入ったり想定以上に元のコードから変わってしまったのですが、参考として貼り付けさせています

修正イメージ.py
import cv2
import time
from picamera2 import Picamera2, controls
import signal
import sys
import os
from enum import Enum

# 定数
FRAME_WIDTH = 640
FRAME_HEIGHT = 480
ESC_KEY = 27
M_KEY = ord("m")
TARGET_FPS = 30  # 目標FPS

# カメラの設定
picam2 = Picamera2()
picam2.configure(picam2.create_preview_configuration(main={"format": 'XRGB8888', "size": (FRAME_WIDTH, FRAME_HEIGHT)}))

# 状態を定義するenum
class Status(Enum):
    A = "A"  # プレビュー表示
    B = "B"  # 画像保存
    C = "C"  # (今回は使用しないが、将来の拡張のために残しておく)
    D = "D"  # 終了

s = Status.A
exit_reason = None
signal_received = False  # シグナル受信フラグ

def signal_handler(sig, frame):
    """シグナルをキャッチするハンドラ"""
    global signal_received, exit_reason
    signal_received = True  # フラグを設定
    if sig == signal.SIGINT:
        print("Ctrl+C を検知しました。終了処理を開始します。")
        exit_reason = "ctrl_c"
    elif sig == signal.SIGTERM:
        print("SIGTERM を検知しました。終了処理を開始します。")
        exit_reason = "sigterm"
    elif sig == signal.SIGTSTP:
        print("Ctrl+Z を検知しました。終了処理を開始します。")
        exit_reason = "ctrl_z"
    elif sig == signal.SIGQUIT:
        print("Ctrl+\ を検知しました。終了処理を開始します。")
        exit_reason = "ctrl_backslash"

# シグナルをキャッチ
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGTSTP, signal_handler)
signal.signal(signal.SIGQUIT, signal_handler)

try:
    picam2.start()
    picam2.set_controls({"AfMode": controls.AfModeEnum.Continuous})
    prev_time = time.perf_counter()  # 時間計測開始
    while True:
        current_time = time.perf_counter()
        elapsed_time = current_time - prev_time

        key = cv2.waitKey(1) & 0xFF

        if signal_received:  # フラグをチェック
            s = Status.D
            break

        match s:
            case Status.A:
                frame = picam2.capture_array()
                cv2.imshow('Camera Preview', frame)

                if key == ESC_KEY:
                    s = Status.D
                    exit_reason = "esc"
                    break

                if key == M_KEY:
                    s = Status.B

            case Status.B:
                try:
                    cv2.imwrite('ColorFrame.png', frame)
                    try:
                        frameGray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                        cv2.imwrite('GrayFrame.png', frameGray)
                    except Exception as e:
                        print(f"グレースケール変換中にエラーが発生しました: {e}")
                        exit_reason = "error"
                        s = Status.D
                        break
                except Exception as e:
                    print(f"画像保存中にエラーが発生しました: {e}")
                    exit_reason = "error"
                    s = Status.D
                    break

                s = Status.C # BからCへ遷移
                print("Status BからStatus Cへ遷移しました")

            case Status.C:
                # 何らかの処理(今回は何もしない)
                print("Status Cの処理を実行しました")
                s = Status.A # CからAへ遷移

            case Status.D:
                break

        # フレームレート制御
        wait_time = max(0, (1/TARGET_FPS) - elapsed_time)
        time.sleep(wait_time)
        prev_time = current_time

except Exception as e:
    print(f"エラーが発生しました: {e}")
    exit_reason = "error"

finally:
    if picam2.started:
        picam2.stop()
    cv2.destroyAllWindows()

    if exit_reason == "esc":
        print("終了:ESCキーが押されました。")
    elif exit_reason == "ctrl_c":
        print("終了:Ctrl+Cが押されました。")
    elif exit_reason == "ctrl_z":
        print("終了:Ctrl+Zが押されました。")
    elif exit_reason == "ctrl_backslash":
        print("終了:Ctrl+\ が押されました。")
    elif exit_reason == "sigterm":
        print("終了:SIGTERMシグナルを受信しました。")
    elif exit_reason == "error":
        print("終了:エラーが発生しました。")
    else:
        print("終了:正常終了しました。")
0Like

Your answer might help someone💌