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?

Basler pylon SDKでソフトウェアトリガー撮影を行う(C# / .NET対応)

Posted at

Basler pylon SDKでソフトウェアトリガー撮影を行う(C# / .NET対応)

Baslerなどの産業用カメラを使うとき、常に撮り続ける「フリーラン撮影」ではなく、
任意のタイミングで撮影したい 場面があります。

例えば──

  • 実験装置のボタンを押した瞬間
  • 温度や電圧がある値に達した瞬間
  • 外部イベントに同期したタイミング

このような場合に便利なのが ソフトウェアトリガー撮影 です。
本記事では、pylon SDKを使ってC#からソフトウェアトリガー撮影を行う方法を紹介し、
フリーラン撮影との違いをストップウォッチで比較してみます。


✅ 環境

項目 内容
カメラ Basler acA2500-14gm
SDK pylon Camera Software Suite
言語 C# / .NET 8.0 (Windows)

今回も前回記事(Baslerカメラで撮影画像に設定値を記録する方法(C# / .NET対応))で紹介したBaslerCameraSampleクラスに機能を追加し、実行例を紹介していきます。


🔧 ソフトウェアトリガーの設定

pylon SDKでは、以下のようにトリガーモードを設定します。

public bool SetSoftwareTrigger(bool enable)
{
    if(Camera == null)
        return false;
    if (enable)
    {
        
        // トリガーをソフトウェアトリガーに設定する。
        var isTriggerSourceSet = SetPLCameraParameter(PLCamera.TriggerSource, PLCamera.TriggerSource.Software);
        // トリガーソースが設定されているか確認
        if (!isTriggerSourceSet)
        {
            Console.WriteLine("Failed to set software trigger source.");
            return false;
        }
        // トリガーを有効化する
        return SetPLCameraParameter(PLCamera.TriggerMode, PLCamera.TriggerMode.On);
        
    }
    else
    {
        // トリガーを無効にする
        return SetPLCameraParameter(PLCamera.TriggerMode, PLCamera.TriggerMode.Off);
    }
}

撮影時は ExecuteSoftwareTrigger() を呼び出すことで、
その瞬間のフレームを取得できます。

// トリガーを発行して1枚撮影
public void ExecuteSoftwareTrigger(int timeoutMs = 5000)
{
    if (Camera?.WaitForFrameTriggerReady(timeoutMs, TimeoutHandling.ThrowException) == true)
    {
        Camera?.ExecuteSoftwareTrigger();
    }
}

ExecuteSoftwareTriggerを呼ぶ前には、カメラのストリーミングを開始している必要があるため、Camera.StreamGrabber.Start()をあらかじめ呼ぶ必要があります。

実行例

ソフトウェアトリガーの実行例です。やり方は本当にいろいろありますが、これまでの実装を流用しつつ、以下の流れで実行します。

  1. トリガーモードを設定
  2. カメラのストリーミングを開始
  3. ソフトウェアトリガー作動
  4. フレーム取得を検知し、ストリーミング停止

ソフトウェアトリガーを作動をさせた後、ImageGrabbedイベントが発生するまで時間かかるため、確認してからストリーミングを停止します。

[TestMethod()]
public async Task ExecuteSoftwareTriggerTest()
{
    if (!_baslerCameraSample.IsConnected)
        _baslerCameraSample.Connect();

    // トリガーモードを設定
    var isEnabled = _baslerCameraSample.SetSoftwareTrigger(true);
    Assert.IsTrue(isEnabled);

    // ストリーミングを開始
    _baslerCameraSample.StartGrabbing();

    // 1000ms後に何か起きたと仮定する。
    await Task.Delay(1000);
    // イベント発生からの経過時間を調べる。
    var startTime = DateTime.Now;

    // ソフトウェアトリガー作動
    _baslerCameraSample.ExecuteSoftwareTrigger();
    var elapsedTrigger = (DateTime.Now - startTime).TotalMilliseconds;
    // 例:Elapsed time(Trigger): 5.9921 ms
    Console.WriteLine($"Elapsed time(Trigger): {elapsedTrigger} ms");
    Assert.IsTrue(elapsedTrigger <= 10, "Elapsed time should be less than 10 ms");

    // フレームを取得してOnImageGrabedが走るまでに時間がかかるので待つ。
    // CancellationTokenを使用して待機をキャンセルできるようにする。
    try
    {
        var cts = new CancellationTokenSource();
        var waitTask = Task.Delay(1000, cts.Token);
        while (!waitTask.IsCompleted)
        {
            await Task.Delay(1);
            // フレーム取得を検知
            if (_baslerCameraSample.BufferedImageQueue.Count > 0)
                cts.Cancel();
        }
        await waitTask;
        // キャンセルされなければ、フレームがきていないので失敗とする。
        Assert.Fail("OnImageGrabed was not called within the expected time.");
    }
    catch (TaskCanceledException)
    {
        // 期待通り、OnImageGrabedでキャンセルされた。
    }
    // フレーム取得後にストリーミングを停止する。
    _baslerCameraSample.StopGrabbing();

    // 取得した画像を取り出し、OnImageGrabedが呼ばれた時間を計測したり、画像を保存したりする。
    Assert.AreEqual(1, _baslerCameraSample.BufferedImageQueue.Count);
    _baslerCameraSample.BufferedImageQueue.TryDequeue(out var item);
    var elapsedOnImageGrabed = (item.Item1-startTime).TotalMilliseconds;
    // 例:Elapsed time(OnImageGrabed): 112.0971 ms
    Console.WriteLine($"Elapsed time(OnImageGrabed): {elapsedOnImageGrabed} ms");
    var bitmap = BaslerCameraSample.ConvertGrabResultToBitmap(item.Item2);
    var fileName = $"TriggeredCapture_{DateTime.Now:yyyyMMdd_HHmmss}.bmp";
    BaslerCameraSample.SaveBitmap(bitmap, fileName);
    Assert.IsTrue(File.Exists(fileName), $"File {fileName} should be created.");
}

⏱ 実験:ソフトウェアトリガー vs 単に1枚撮影(GrabOne())

「イベント発生してからGrabOne()」などで画像取得するのと何が違うのか?
と疑問に思いませんか?

そこで、ExecuteSoftwareTriggerTestに比較用のコードを足して、時間を比較します。

[TestMethod()]
public async Task ExecuteSoftwareTriggerTest()
{
    // ~~前略~~
    
    // トリガーを無効にする
    var isDisabled = _baslerCameraSample.SetSoftwareTrigger(false);
    Assert.IsTrue(isDisabled);
    // GrabOneで1枚取得するのにかかる時間を計測して比較する。
    var grabSW = new Stopwatch();
    grabSW.Start();
    var grabbedImage = _baslerCameraSample.Snap();
    grabSW.Stop();
    var elapesedGrabOne = grabSW.ElapsedMilliseconds;
    // 例:Elapsed time(GrabOne): 193 ms
    Console.WriteLine($"Elapsed time(GrabOne): {elapesedGrabOne} ms");

    // トリガー待ちしていた方が、普通に1枚画像取得するよりも早く画像が取得できる。
    // また、GrabOneではいつトリガーが入ったのか分からないが、ソフトウェアトリガーでは確実にトリガー直後に画像が取得できる。
    Assert.IsTrue(elapsedOnImageGrabed < elapesedGrabOne);
}

📊 実験結果

「筆者の環境(acA2500-14gm, GigE, AMD Ryzen 9 Windows環境)では、ソフトウェアトリガーの方が早く画像を取得できました。ストリーミングをすでに開始しているため、その分早く画像を取得できたのだと思います。

まとめると以下のとおりです。

モード 取得時間 特徴
ソフトトリガー 112ms 画像取得が早い。いつトリガーが入ったのか分かる。
GrabOne 193ms 実装は簡単。トリガーがいつ入ったのか厳密にはわからない。

📝 まとめ

  • ソフトウェアトリガーは「意味のある瞬間を狙って撮影する」のに便利
    • 特に研究や検査では 実験データと同期できる自由度の高さ が大きなメリットになる

次回はWPFでカメラ画像を表示させる方法をご紹介します。現在開発中のカメラ設定管理ツールにも採用している設計です。


筆者:@MilleVision


🛠 サンプルコード完全版のご案内

Qiita記事で紹介しているサンプルコードをまとめた C#プロジェクト(単体テスト付き)
BOOTHにて配布しています。

  • 単体テストプロジェクト同梱で動作確認や学習がスムーズ
  • 記事更新にあわせてアップデート予定

👉 商品ページはこちら

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?