忍者ブログ

Fグループ電子工作講座

秋月電子SH7125ボードで始めるマイコン開発

ラズパイ5でCSIカメラを2台同時に使用する(C++)

前回はlibcameraを使ってC++からCSI接続のカメラを読み出せるようにしました。
今回は2つのカメラを同時に使用できるようにプログラムを修正します。

おさらい

前回はedward-ardu様のサンプルを利用させていただきました。
https://github.com/edward-ardu/libcamera-cpp-demo/
このサンプルはラズパイ5発売前に作成されたものなので、カメラIDは0に固定
されています。そこでIDを指定する仕様に変更しました。これでラズパイ5に
接続した2つのカメラのうちどちらか片方を指定して利用できるようになりました。

このまま2つのカメラを同時に利用してみます。

int main() {
    LibCamera cam0;
    LibCamera cam1;
    uint32_t width = 640;
    uint32_t height = 480;
    uint32_t stride0;
    uint32_t stride1;
    cv::Mat frame0;
    cv::Mat frame1;

    //カメラを初期化
    int ret0 = cam0.initCamera(0);
    int ret1 = cam1.initCamera(1);

    //以下画像取得処理等


残念ながら
Multiple ProsessManager objects are not allow
と出て実行できません。


色々と条件を変えて調べてみると
  cam1.initCamera(1)
を実行実行した時点でエラーが出ることが分かります。

さらにLibCamera.cpp内にあるinitCameraの中身を調べてみると冒頭の
    cm = std::make_unique<CameraManager>();
    ret = cm->start();
のあたりが原因だと分かります。
このCameraManagerですが、1つのプログラムから同時に2個以上できないようで、
今回作成したメイン関数では
    LibCamera cam0
    LibCamera cam1
でそれぞれ使うことになるのでエラーになってしまいます。
そこで、メイン関数でCameraManagerを管理できるようにプログラムを修正します。


修正内容

かなり行き当たりばったりな感じで、正しい方法ではない気がしますがとりあえず修正します。

LibCamera.hを確認すると
CameraManagerはLibCamera.hで
    std::unique_ptr<CameraManager> cm;
と定義されています。libcameraとしては正しい利用方法だと思います。
ただ、このままではメイン関数で設定したCameraManagerを登録できない様なので、
    std::shared_ptr<CameraManager> cm;
へ変更します。
cmはclass外から読み出せないprivate変数なのでprivateへ変更すればそれはそれで対応
できそうですが、間違ってアクセスする可能性があるので今回は関数を使うことにします。

class外からも呼び出せるようにpublicメソッド(関数)としてinitCamera関数付近
    void setCameraManager(std::shared_ptr<CameraManager> cm_ref);
を作成します。

関数の中身はこんな感じ
void LibCamera::setCameraManager(std::shared_ptr<CameraManager> cm_ref){
    cm=cm_ref;
}

続いてinitCameraも編集します。
2カメラ同時使用以外はCameraManagerを指定するのが煩わしいので、
cmが未設定の場合は既存の処理を実行することにします。

int LibCamera::initCamera(int index){
    if(cm==NULL){
        int ret;
        cm = std::make_unique<CameraManager>();
        ret = cm->start();
        if (ret){
            std::cout << "Failed to start camera manager: "
                << ret << std::endl;
            return ret;
        }
    }
    cameraId = cm->cameras()[index]->id();
    camera_ = cm->get(cameraId);
//以下そのままの処理

メイン関数ではinitCameraを実行する前にCameraManagerを作成したのち、
setCameraManagerにて登録すればいいので
int main() {
    LibCamera cam0;
    LibCamera cam1;
    uint32_t width = 640;
    uint32_t height = 480;
    uint32_t stride0;
    uint32_t stride1;
    cv::Mat frame0;
    cv::Mat frame1;

    //メイン関数でCameraManagerを作成する
    std::shared_ptr<CameraManager> cm;
    cm=std::make_unique<CameraManager>();
    cm->start();

    //camを初期化する前にCameraManagerを登録する
    cam0.setCameraManager(cm);
    cam1.setCameraManager(cm);

    int ret0 = cam0.initCamera(0);
    int ret1 = cam1.initCamera(1);

    //以下画像取得処理等

といった感じに変更します。
これで2カメラ同時にアクセスできるようになったと思います。


遅延の改善

とりあえず2カメラ同時に使えるようになりましたが、遅延が大きく同期していません。
このままステレオカメラ等に利用するのは厳しいでしょう。
原因は例によってバッファです。
v4l2ではバッファ数を指定するコマンドがありましたが、libcameraではバッファの設定方法が
よくわかりません。そこで今回はバッファがなくなるまで読み続けるという力技で対応することにします。


バッファ対応の前にCPU100%問題に対応しておきます。
前回の記事では10us程度のスリープを追加すると改善すると書きましたが、これを関数化しておきます。readFrame付近へ追加します。

int LibCamera::readFrameWait(LibcameraOutData *frameData){
    int count=0;
    int wait_us=100;
    while(!LibCamera::readFrame(frameData)){
        if(count>=1000000){    //1s以上経過したらあきらめて終了
            return -count;
        }
        usleep(wait_us);    //要 #include <signal.h>   
        count+=wait_us;
        continue;
    }
    return count;
}

この関数を使うことで画像を取得できるまでスリープ付きで繰り返します。
スリープは100us(0.1ms)とし、スリープの合計時間を戻り値としています。
1秒以上応答がなかった場合は取得を諦めてマイナス値を戻り値としています。

実際に使ってみると分かると思いますが、遅延発生中はバッファを読み出すだけなので
スリープ時間が0となります。これを利用してバッファの判定を行います。
こちらもreadFrame付近に追加します。

int LibCamera::readFrameNew(LibcameraOutData *frameData){
    int wait_time=0;
    int wait_time_sum=0;
    int count_max=10;

    for(int i=0;i<count_max;i++){
        wait_time=LibCamera::readFrameWait(frameData);
        wait_time_sum+=wait_time;
        if(wait_time<0){    //取得時間が負なら強制終了
            return wait_time;
        }
        if(wait_time>0){    //取得時間が1以上ならループ終了
            break;
        }
        if(i!=count_max-1){    //最終回以外はバッファをクリア
            LibCamera::returnFrameBuffer(*frameData);
        }
    }
    return wait_time_sum;
}

これでバッファがなくなるまで取得を繰り返すことができます。
readFrameの代わりにreadFrameNewを使うことで遅延が改善したと思います。
PR

コメント

プロフィール

HN:
ぼんどF博士
性別:
男性
自己紹介:

最新コメント