跳至內容

應用說明:為可程式化幀擷取卡設計訊號處理小程式#

所有可程式化幀擷取卡的設計,皆可讓您透過圖形化 FPGA 程式設計環境VisualApplets ,自行編寫專屬的影像處理解決方案(小程式)。

GPI/GPO 操作員在VisualApplets#

透過VisualApplets ,您可以設計專屬於您應用的觸發系統。例如,您可以指定由哪個個別的實體輸入/輸出引腳傳送或接收哪個訊號。

在VisualApplets 中,您有兩種運算子可用於將個別引腳與您的 Applet 設計進行連接:

運算子 GPI(用於觸發 IN 訊號)

運算子 GPO(用於觸發器 OUT 訊號)

VisualApplets 使用 GPI 與 GPO 運算子進行設計

上圖展示了一個實作訊號處理的範例。您要實作的訊號處理功能取決於您的具體需求。若要為您的應用尋找合適的運算子,請參閱「訊號函式庫」。同時,請根據您的需求選擇前端 GPIO。

上述範例展示了針對兩個輸入訊號的降採樣功能。 兩組訊號分別從 GPI1 和 Front GPI0 輸入至可程式化幀擷取器。系統會先對訊號進行去抖動處理,並選取上升沿或下降沿,接著將訊號分別以 8 倍或 3 倍的比例進行降採樣。設定兩組訊號的長度後,訊號便會轉發至 FrontGPO0、GPO1、GPO2 以及攝影機,以控制影像擷取。

使用 GPI/GPO 運算子#

您若要使用前端 GPIO 或觸發訊號擴充板上任一觸發訊號輸入(IN)/觸發訊號輸出(OUT)接腳,都必須將其與一個 GPI/GPO 運算子實例相互連接。

資訊

由於運算子 GPI 與運算子 GPO 具有相同的功能,以下將一併說明這兩項運算子。

  1. 在設計中雙擊運算子圖示,即可開啟「模組屬性」視窗。

VisualApplets GPI 操作員

此時會開啟「模組屬性」視窗:

VisualApplets 「模組屬性」視窗

如您所見,此運算子有兩個參數:

  • Pin_ID: 透過 Parameter Pin_ID,您可以指定要對應至所選連接器上的哪個特定接腳。
  • ConnectorType: 透過 ConnectorType 參數,您可以指定要選用的連接器(前端 GPIO 或透過 GPIO 連接的觸發器擴充板)。

資訊

對於 CXP-12 影像擷取卡而言,該 ConnectorType 該參數包含在 Pin_ID 參數中選擇性地指定 HTML 記錄檔 (*.html) 的路徑與檔名。

參數連接器類型#

此參數可讓您指定要對應哪一組觸發引腳,即 15 針 D-Sub 插座中提供的那些引腳(Front GPIO),或是觸發器擴充板上提供的那些(GPIO). 此參數的取值範圍為 {前端 GPIO, GPIO}。

Value Front GPIO#

若您選擇該值 Front GPIO, 請連接可程式化幀擷取卡插槽支架上的以下連接器(觸發針腳組):

幀擷取卡的前置 GPIO

Value GPIO#

若您選擇該值 GPIO, 您需設定可程式化影像擷取卡上的以下這組觸發腳位:

幀擷取卡的 GPIO

GPIO 用於連接觸發擴充板:

透過 GPIO 連接觸發板

參數 Pin_ID#

此參數可讓您指定要存取所選連接器(觸發針腳群組)中的哪一個特定針腳。此參數的數值範圍為 Pin_ID is {0;7} or {0;3} for connector type GPIO 或 Front GPIO.

資訊

參數的數值範圍 Pin_ID for marathon frame grabbers and operator GPO is {0;1}.

使用 VisualApplet 運算子參考手冊設定前端 GPIO 的針腳配置#

前面板 GPIO 接腳

使用 VisualApplet 運算子參考手冊設定前端 GPIO 的針腳配置

在差分模式下,第 9 腳可透過 GPI 操作器(Pin_ID=3)接收一個單端訊號,詳見「差分」模式(預設)。

注意

幀擷取器的觸發系統需要透過 Voltage IN 接腳獲得供電電壓。若要連接沒有 PWR 接腳的裝置,您需要從外部電源為 Voltage IN 接腳提供電源。

在前端 GPIO 上觸發 INPUT#

如果你

  • 正在使用 GPI 運算子(用於定義觸發輸入訊號),
  • 已設定參數 Connector Type 設為 Front GPIO,

參數的值 Pin_ID 處理以下引腳:

前面板 GPIO 及其引腳編號

前置 GPIO 的輸入 (GPI) 接腳可進行設定。您可以使用這六個輸入接腳來接收以下任一訊號:

  1. 3 個差分訊號,或
  2. 2 個差分訊號和 1 個單端訊號,或
  3. 4 個單端訊號。

若要將前端 GPIO 上的輸入腳位設定為特定訊號類型(差分或單端),您可以選擇以下兩種模式:

  • Mode Differential (提供選項 a 和 b)
  • Mode Single-ended (提供選項 c)

前端 GPIO 的預設模式為 mode Differential. 如何切換模式,請參閱相關主題 輸入設定 在幀擷取卡的使用手冊中。

注意

在預設設定下,前置 GPIO 的 GPIO 接腳提供:

  • 3 個差分輸入訊號,或 2 個差分輸入訊號與 1 個單端輸入訊號(處於上拉模式)
  • 兩個 TTL 輸出訊號

如果您需要另一種輸入訊號的配置,例如:

  • 下拉式模式,
  • 4 個單端訊號(且無差分訊號),

您可以根據需求輕鬆設定 GPI。設定時,請使用隨「Framegrabber SDK 」一併安裝在系統上的命令列工具。

Mode Differential (預設)#

在模式中 Differential (預設),前端 GPIO 的所有六個輸入腳位均設定為接收差分訊號:

  • 第 8/9 腳位 → 1 個差分訊號
  • 第 11/12 針腳 -> 1 個差分訊號
  • 第 13/14 腳位 → 1 個差分訊號

此外,您也可以使用第 8/9 號針腳對(在模式 Differential) 用於接收一個單端訊號:

  • 第 9 腳位 → 1 個單端訊號
  • 第 11/12 針腳 -> 1 個差分訊號
  • 第 13/14 腳位 → 1 個差分訊號

「模式」中的個別圖釘 Differential:

「差分」模式下的個別引腳

第 11 和 12 腳:

  • 接在第 11 和 12 腳位上的輸入訊號會被解讀為一個差分訊號(第 11 腳位為正值,第 12 腳位為負值)。
  • 若要在VisualApplets 中接收此差分訊號,請使用 Pin_ID = 0 的 GPI 運算子實例。

第 13 和 14 腳位:

  • 第 13 和 14 腳位的輸入訊號被解讀為一個差分訊號(第 13 腳位為正值,第 14 腳位為負值)。
  • 若要在VisualApplets 中接收此差分訊號,請使用 Pin_ID = 1 的 GPI 運算子實例。

第 8 及 9 腳:

  • 第 8 及 9 腳位的輸入訊號被解讀為一個差分訊號(第 8 腳位為正值,第 9 腳位為負值)。
  • 若要在VisualApplets 中接收此差分訊號,請使用 Pin_ID = 2 的 GPI 運算子實例。

此外,您也可以使用第 8/9 號引腳對來接收一個單端訊號(儘管在該模式下 Differential): 在這種情況下,

  • 請將單端輸入訊號連接至第 9 號物理接腳。
  • 若要在VisualApplets 中接收此單端訊號,請使用 Pin_ID = 3 的 GPI 運算子實例。

Mode Single-Ended#

在模式中 Single-ended (可配置),前端 GPIO 的六個輸入引腳被配置為接收 4 個單端訊號:

  • 第 8 腳位 → 1 個單端訊號
  • 第 9 腳位 → 1 個單端訊號
  • 第 11 腳位 → 1 個單端訊號
  • 第 13 腳位 → 1 個單端訊號

「模式」中的個別圖釘 Single-ended:

「單端」模式下的個別引腳

第 11 及 12 腳位的訊號:

  • 第 11 腳位的輸入訊號被解讀為單端訊號。
  • 若要在VisualApplets 中接收此單端訊號,請使用 Pin_ID = 0 的 GPI 運算子實例。
  • 在此模式下,第 12 腳位未使用 Single-ended.

第 13 及 14 腳位的訊號:

  • 第 13 腳位的輸入訊號被解讀為單端訊號。
  • 若要在VisualApplets 中接收此單端訊號,請使用 Pin_ID = 1 的 GPI 運算子實例。
  • 在此模式下,第 14 腳位未被使用 Single-ended.

第 8 及 9 腳:

  • 第 8 及 9 腳的輸入訊號被視為兩個單端訊號。
  • 要在VisualApplets 中接收這些單端訊號,請使用

    • 一個 GPI 運算子實例,其 Pin_ID = 2 對應於第 8 個接腳,以及
    • 一個 GPI 運算子實例,其 Pin_ID = 3,對應於第 9 號接腳。

觸發輸出 TTL 在前端 GPIO 上#

如果你

  • 使用 GPO 運算子來定義觸發輸出訊號,
  • 您已將「連接器類型」參數設定為「前置 GPIO」,

參數 Pin_ID 的值對應於以下接腳:

前面板 GPIO TTL 輸出腳位編號

這兩個接腳正在傳輸 TTL 訊號(5 V)。

前端 GPIO 上的傳出 TTL 訊號(5 V):

前端 GPIO 端口的傳出 TTL 訊號(5 V)

觸發訊號:透過 GPIO 控制觸發擴充板#

GPIO 連接器是可程式化幀擷取卡的實體觸發介面,專為連接觸發擴充板而設計:

幀擷取器與觸發板的 GPIO

GPIO 連接器(即每塊觸發器擴充板)提供

  • 8 個數位輸入端子 (IN 0 - 7)(8 個單端訊號或 4 個差分訊號)
  • 8 個數位輸出端子(OUT 0 - 7)

這八個輸入的索引為 0 到 7。這八個輸出的索引同樣為 0 到 7。

有關針腳佈局詳情,請參閱SUB-D15 針腳佈局 -TTL Trigger 電路板,或PIN 佈局Opto-Coupled Trigger 5 -Opto-Coupled Trigger 5 擴充電路板。

Pin_ID 關於TTL Trigger 擴充板的作業#

A 號碼頭#

接腳編號 Pin_ID 在 Visual Applets 中
1 0
2 1
3 2
4 3
5
6
7
8
9
10
11 0
12 1
13 2
14 3
15 0

B 港#

接腳編號 Pin_ID 在 Visual Applets 中
(單端訊號)
1 4
2 5
3 6
4 7
5
6
7
8
9
10
11 4
12 5
13 6
14 7
15

Pin_ID 關於 OPTO Trigger 5 擴充板的作業#

A 號碼頭#

接腳編號 Pin_ID 在 Visual Applets 中
(單端訊號)
Pin_ID 在 Visual Applets 中
(差分訊號)
1 0
2 1
3 2
4 3
5
6
7
8
9
10
11 0 0
12 1 0
13 2 2
14 3 2
15

B 港#

接腳編號 Pin_ID 在 Visual Applets 中
(單端訊號)
Pin_ID 在 Visual Applets 中
(差分訊號)
1 4
2 5
3 6
4 7
5
6
7
8
9
10
11 4 4
12 5 4
13 6 6
14 7 6
15

設定觸發系統#

您可以透過以下兩種方式存取觸發器設定:

  • microDisplay X: 在此處,您可以透過圖形化使用者介面 (GUI) 輸入觸發參數值,以檢視特定設定的結果。
  • SDK:用於將幀擷取卡程式設計介面整合至特定的影像處理軟體應用程式中。

對扳機系統的要求因應用情境而異:

  • 對於區域掃描,僅需一種觸發類型(幀觸發)。
  • 對於行掃描,需要兩種觸發類型:行觸發(每行發送一個觸發脈衝)和幀觸發。在行掃描應用中,幀觸發控制著每張影像中會追加多少行。

您可於此處查閱有關如何觸發區域式與線掃式攝影機的進一步資訊。

資訊

有關幀擷取器觸發系統的更多資訊,請參閱「GPIO 連接器」一節。

資訊

有關可用的參數(以及特定設定可能產生的效果)的資訊,請參閱您所使用的 Applet 的說明文件。

在硬體上執行您的 Applet#

先決條件:

  • 您的可程式化幀擷取卡已依照「連接幀擷取卡」一節所述的方式安裝至您的系統中。
  • Basler 的Framegrabber SDK 已依照《安裝Framegrabber SDK 》中的說明安裝至您的系統上。

在可程式化影像擷取卡上執行您的 Applet 之前,您必須先將您的 *.hap 將檔案(Applet)複製到主機電腦上的 BaslerFramegrabber SDK 安裝目錄中,並將其燒錄至可程式化影像擷取卡中。

資訊

僅marathon 系列的幀擷取卡才需要進行韌體更新。

有關如何將VisualApplets 設計轉譯為硬體小程式的詳細資訊,請參閱〈硬體小程式:從構想到應用〉一節。

  1. 複製該 *.hap 將您在VisualApplets 中建置的檔案存入該目錄中 [Framegrabber SDK installation directory]/Hardware Applets/[frame grabber model]. 如果沒有以可程式化影像擷取卡型號命名的子目錄,請建立該目錄。如果您已使用VisualApplets 中的預設設定,您會發現您的 *.hap 位於您的VisualApplets 安裝目錄下的子目錄中的檔案 Designs.

  2. 請依照《Flashing》中的說明,將您的 Applet 安裝至可程式化影像擷取卡上。

完成此步驟後,您可以:

  • 請使用 Basler 工具microDisplay X 來測試您的 Applet,並直接查看其影像處理結果;該工具是Framegrabber SDK 套件的一部分。請參閱「設定 Applet」一節(microDisplay X )。

範例:當您將訊號處理範例設計的硬體小程式載入至microDisplay X 時,可如下圖所示,在參數樹中設定動態參數,例如降採樣係數和訊號寬度。

訊號處理設計範例

在您自己的軟體中啟動 Applet#

若要在您自己的軟體中開始使用此小程式:

  1. 在 Basler 應用程式介面上,請使用以下呼叫: Fg_Init 以啟動該小程式。請指定該檔案所在位置的路徑: *.hap 檔案路徑與位置,該檔案是您使用 VisualApplets 建立的。

有關所有可用函式呼叫的詳細說明與參考資料,請參閱《Framegrabber API 》手冊及《Framegrabber API 》函式參考。

作為 C++ SDK 的基礎,您可以使用VisualApplets 中的內部 SDK 程式碼產生器,該工具可於圖示選單中找到,或參閱VisualApplets 中的「建置與 產生 SDK 範例」。此功能會自動產生對應的 CMake 檔案與 Visual Studio 專案檔案(另請參閱《硬體小程式:從構想到應用程式》)。

自動 SDK 程式碼包含以下功能模組,這些模組對於影像擷取與參數設定而言是不可或缺的:

  • 幀擷取卡初始化
  • 為緩衝區分配記憶體
  • 「」的定義 Get 進行明確的生命週期管理即呼叫 Set 參數
  • 建立顯示畫面
  • 針對每個可用連接埠,開始從小程式和相機擷取影像
  • 擷取影像
  • 中止收購
  • 關閉顯示畫面
  • 釋放緩衝區的記憶體
  • 幀擷取卡未初始化

以下是訊號處理範例的 C++Framegrabber API 程式碼範例。

  • 幀擷取卡的初始化及緩衝區的記憶體分配:
int main ()
{

    // ---------------------------------------------------------------------------
    // Frame Grabber: Initialization
    // ---------------------------------------------------------------------------


    int nrOfCycles = 5000; // Acquire 5000 frames, TODO: insert the number according to your needs
    unsigned int boardIndex = 0; // TODO: Use the correct board index at multi frame grabber systems
    int rc = 0; // return code

    Fg_Struct* fg = Fg_Init("SignalProcessingDesign.hap", boardIndex);
    if(fg == NULL){
        fprintf(stderr, "Error in Fg_Init(): %s\n", Fg_getLastErrorDescription(NULL));
        fprintf(stderr, "Press <Return> to continue...\n");
        getchar();
        return -1;
    }

    int nrOfParameter = Fg_getNrOfParameter(fg);
    fprintf(stderr, "No. of Parameter: %d\n", nrOfParameter);

    int i;
    for (i = 0; i < nrOfParameter; i++) {
        const char *name = Fg_getParameterName(fg,i);
        fprintf(stderr," Param %d: %s,%x\n",i,name,Fg_getParameterId(fg,i));
    }


    / ---------------------------------------------------------------------------
    // Frame Grabber: Allocate memory for buffer(s)
    // ---------------------------------------------------------------------------


    // Feature Cnt 0,0
    int width0 = 1024;
    int height0 = 1024;
    int byteWidth0 = 1;
    size_t bufSize0 = width0 * height0 * (size_t)byteWidth0;
    const unsigned int mem0_bufs = 4;
    dma_mem *pmem0 = Fg_AllocMemEx(fg, bufSize0 * mem0_bufs, mem0_bufs);
    if(pmem0 == NULL){
        fprintf(stderr, "Error in Fg_AllocMemEx(): %s\n", Fg_getLastErrorDescription(fg));
        Fg_FreeGrabber(fg);
        fg = NULL;
        fprintf(stderr, "Press <Return> to continue...\n");
        getchar();
        return -1;
    }
  • 摘錄自 Get 進行明確的生命週期管理即呼叫 Set 參數區段。在此您可以了解如何設定動態參數。以下程式碼示範了如何設定縮放因子,以及如何將特定脈衝選為運算子的參數 Device1_Process0_ByFactor8:
// ---------------------------------------------------------------------------
// Frame Grabber: Get* and Set* parameters for Process0
// ---------------------------------------------------------------------------


/*============ Downscale : ByFactor8 ============== */


int Device1_Process0_ByFactor8_Downscale_Id = Fg_getParameterIdByName(fg, "Device1_Process0_ByFactor8_Downscale");
if(Device1_Process0_ByFactor8_Downscale_Id <= 0) {
    fprintf(stderr, "\n");
    fprintf(stderr, "Error in Fg_getParameterIdByName(Device1_Process0_ByFactor8_Downscale): %s (%d)\n", Fg_getLastErrorDescription(fg), Device1_Process0_ByFactor8_Downscale_Id);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}

unsigned int Device1_Process0_ByFactor8_Downscale;
Device1_Process0_ByFactor8_Downscale = 8;
rc = Fg_setParameterWithType(fg, Device1_Process0_ByFactor8_Downscale_Id, Device1_Process0_ByFactor8_Downscale, 0);
if(rc < 0) {
    fprintf(stderr, "\n");
    fprintf(stderr, "Error in Fg_setParameterWithType(Device1_Process0_ByFactor8_Downscale_Id): %s (%d)\n", Fg_getLastErrorDescription(fg), rc);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}

int Device1_Process0_ByFactor8_SelectedPulse_Id = Fg_getParameterIdByName(fg, "Device1_Process0_ByFactor8_SelectedPulse");
if(Device1_Process0_ByFactor8_SelectedPulse_Id <= 0) {
    fprintf(stderr, "\n");
    fprintf(stderr, "Error in Fg_getParameterIdByName(Device1_Process0_ByFactor8_SelectedPulse): %s (%d)\n", Fg_getLastErrorDescription(fg), Device1_Process0_ByFactor8_SelectedPulse_Id);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}

unsigned int Device1_Process0_ByFactor8_SelectedPulse;
Device1_Process0_ByFactor8_SelectedPulse = 1;
rc = Fg_setParameterWithType(fg, Device1_Process0_ByFactor8_SelectedPulse_Id, Device1_Process0_ByFactor8_SelectedPulse, 0);
if(rc < 0) {
    fprintf(stderr, "\n");
    fprintf(stderr, "Error in Fg_setParameterWithType(Device1_Process0_ByFactor8_SelectedPulse_Id): %s (%d)\n", Fg_getLastErrorDescription(fg), rc);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}

資訊

您可以從自動生成的 C++Framegrabber API 程式碼中刪除所有與擷取過程中的參數設定無關的參數,僅保留擷取過程中需要配置的參數。

  • 建立顯示器、開始擷取,並擷取影像:
// ---------------------------------------------------------------------------
// Frame Grabber: Create the display(s)
// ---------------------------------------------------------------------------

int id0 = CreateDisplay(8 * byteWidth0, width0, height0);
if(id0 < 0) {
    fprintf(stderr, "Error in CreateDisplay(): %s (%d)\n", Fg_getLastErrorDescription(fg), id0);
    Fg_FreeMemEx(fg, pmem0);
    pmem0 = NULL;
    Fg_FreeGrabber(fg);
    fg = NULL;
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
    return id0;
}
SetBufferWidth(id0, width0, height0);


// ---------------------------------------------------------------------------
// Frame Grabber: Start acquisition at applet and camera for each present port
// ---------------------------------------------------------------------------

rc = Fg_AcquireEx(fg, 0, nrOfCycles, ACQ_STANDARD, pmem0);
if(rc != 0) {
    fprintf(stderr, "Error in Fg_AcquireEx() for channel 0: %s\n", Fg_getErrorDescription(fg, rc));
    CloseDisplay(id0);
    Fg_FreeMemEx(fg, pmem0);
    pmem0 = NULL;
    Fg_FreeGrabber(fg);
    fg = NULL;
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
    return rc;
}

// ---------------------------------------------------------------------------
// Frame Grabber: Grab images
// ---------------------------------------------------------------------------

frameindex_t frameNo0 = 0;
for(i = 1; i <= nrOfCycles; ++i){
frameNo0 = Fg_getLastPicNumberBlockingEx(fg, frameNo0 + 1, 0, 5, pmem0);
    if(frameNo0 < 0) {
        fprintf(stderr, "Error in Fg_getLastPicNumberBlockingEx(): %s (%d)\n", Fg_getLastErrorDescription(fg), frameNo0);
        fprintf(stderr, "Press <Return> to continue...\n");
        getchar();
        break;
    } else {
        fprintf(stderr, "grabbed image %d\n", frameNo0 );
    }

    void *iPtr0 = Fg_getImagePtrEx(fg, frameNo0, 0, pmem0);
    if(iPtr0 == NULL) {
        fprintf(stderr, "Error in Fg_getImagePtrEx(): %s\n", Fg_getLastErrorDescription(fg));
        Fg_stopAcquireEx(fg, 0, pmem0, 0);
        CloseDisplay(id0);
        Fg_FreeMemEx(fg, pmem0);
        pmem0 = NULL;
        Fg_FreeGrabber(fg);
        fg = NULL;
        fprintf(stderr, "Press <Return> to continue...\n");
        getchar();
        return -1;
    }
    DrawBuffer(id0, iPtr0, frameNo0, "");
}
  • 停止擷取、關閉顯示、釋放緩衝區的記憶體:
// ---------------------------------------------------------------------------
// Frame Grabber: Stop acquisition
// ---------------------------------------------------------------------------

rc = Fg_stopAcquireEx(fg, 0, pmem0, 0);
if(rc < 0) {
    fprintf(stderr, "Error in Fg_stopAcquireEx(): %s (%d)\n", Fg_getLastErrorDescription(fg), rc);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}


// ---------------------------------------------------------------------------
// Frame Grabber: Close the display
// ---------------------------------------------------------------------------

CloseDisplay(id0);


// ---------------------------------------------------------------------------
// Frame Grabber: Release the memory for buffer(s)
// ---------------------------------------------------------------------------

rc = Fg_FreeMemEx(fg, pmem0);
if(rc < 0) {
    fprintf(stderr, "Error in Fg_FreeMemEx(): %s (%d)\n", Fg_getLastErrorDescription(fg), rc);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}
pmem0 = NULL;


// ---------------------------------------------------------------------------
// Frame Grabber: Uninitialization
// ---------------------------------------------------------------------------

rc = Fg_FreeGrabber(fg);
if(rc < 0) {
    fprintf(stderr, "Error in Fg_FreeGrabber(): %s\n", Fg_getLastErrorDescription(NULL));
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}
fg = NULL;


fprintf(stderr, "Press <Return> to exit...\n");
getchar();

return (0);
}

您可以將此範例程式碼整合至較大規模的 SDK 環境中。