跳到主要內容

AI 驅動文件

您想了解什麼?

連線到 Tulip

Tulip 是一個無程式碼 MES / 互聯一線運營平臺。OV20i 相機透過執行在相機上的 Node-RED 橋接到 Tulip — 這與您用於 PLC、MQTT 和其他 I/O 整合的流程編輯器相同。Tulip 可以:

  • 透過操作員按鈕或應用事件觸發檢測。
  • 接收每個 ROI 和全域性的透過/失敗結果以及捕獲後設資料。
  • 當操作員掃描零件條形碼或選擇不同產品時,切換活動程式
  • 在 Tulip 應用程式內顯示實時影象(帶邊界框疊加),讓操作員看到 AI 所看到的內容。

本指南將端到端地介紹這四個整合點中的每一個。

架構

┌────────────────────────────┐               ┌──────────────────────────────┐
│ TULIP │ │ OV20i │
│ │ │ │
│ ┌──────────────────┐ │ HTTP/MQTT │ ┌──────────────────────┐ │
│ │ Operator Button │──────┼───trigger─────┼──▶│ Node-RED HTTP IN │──▶│
│ │ in Tulip App │ │ │ │ /trigger │ │
│ └──────────────────┘ │ │ └──────────────────────┘ │
│ │ │ │ │
│ ┌──────────────────┐ │ │ ▼ │
│ │ Tulip Connector │◀─────┼─── results ───┼─── All Block Outputs │
│ │ Function (HTTP) │ │ │ (image_url, pass/fail, │
│ └──────────────────┘ │ │ per-ROI classes) │
│ │ │ │ │
│ ┌──────────────────┐ │ │ ▼ │
│ │ Image Widget │◀─────┼── live-feed ──┼─── http://CAMERA_IP/live-feed│
│ │ (iframe URL) │ │ (HMI w/ ROI │ (shows bounding boxes │
│ └──────────────────┘ │ overlays) │ on the captured image) │
│ │ │ │
│ ┌──────────────────┐ │ │ ┌──────────────────────┐ │
│ │ Recipe Selector │──────┼─── HTTP ──────┼──▶│ /pipeline/activate │ │
│ │ (barcode / btn) │ │ │ │ {id: <recipe_id>} │ │
│ └──────────────────┘ │ │ └──────────────────────┘ │
└────────────────────────────┘ └──────────────────────────────┘

中間的橋樑始終是執行在相機 IO模組 內部的 Node-RED — 這是您構建所有四個整合的地方。

相機 IO模組內的 Node-RED 畫布 — IO 邏輯階段的高階模式

為 Tulip 端點使用環境變數

將 Tulip Connector URL 硬編碼到 Node-RED 流程中會將流程繫結到一個特定部署。在系統設定 → 環境變數中定義一個 TULIP_ENDPOINT 環境變數,然後透過 ${TULIP_ENDPOINT} 在 HTTP Request 節點中讀取它。這樣您就可以匯出相同的流程並將其重新匯入到生產線上的每臺相機。完整模式請參閱環境變數

選擇傳輸方式:HTTP 或 MQTT

選項何時使用說明
HTTP(推薦用於大多數 Tulip 部署)Tulip Connector Functions 原生呼叫 HTTP 端點。從 Tulip 控制元件按鈕觸發的最簡單方式。無需任何額外基礎設施即可工作。Tulip → 相機和相機 → Tulip 都使用普通的 POST
MQTT您的工廠已執行 MQTT broker(Sparkplug B、IT/OT broker 等),並且 Tulip Edge Connector 已配置為與其通訊。高頻訊號傳遞延遲更低。需要 broker。
OPC UA您正在執行帶有 OPC UA Connector 的 Tulip Edge MC 例項。OV20i 出廠時已預裝 OPC UA Node-RED 模型。

本指南其餘部分的示例使用 HTTP —— 這是大多數 Tulip 部署的預設方式。相同的模式也適用於 MQTT(將 HTTP IN / HTTP Request 節點替換為 MQTT in / MQTT out 節點)。

1. 從 Tulip 應用觸發相機

Tulip 操作員按下按鈕 → Tulip 的 Connector Function 呼叫相機的 Node-RED 正在監聽的 HTTP 端點 → Node-RED 觸發捕獲。

Node-RED 側:接收觸發

在相機的 IO模組(Advanced Mode → Node-RED)中,構建:

[ HTTP IN: POST /trigger ]──▶[ Trigger node ]──▶[ HTTP Response: 200 ok ]
  1. network 類別中拖入一個 http in 節點。
    • Method: POST
    • URL: /trigger
  2. 將其連線到 OV 專用的 Trigger 節點(位於 OV 調色盤的 block logic 下)。Trigger 節點會啟動相機的捕獲流水線。
  3. 新增一個 http response 節點,以便 Tulip 的 Connector 立即收到 200 OK 響應(不要讓 Tulip 等待 AI 推理 —— 讓結果非同步返回,參見第 2 節)。
  4. 點選 部署

該端點現已在 http://<CAMERA_IP>/node-red/trigger 上執行(Node-RED 的 HTTP 根路徑掛載在 /node-red/ 下)。

Tulip 側:透過按鈕呼叫觸發

在您的 Tulip 賬戶中:

  1. 前往 Apps → Connectors,如果還沒有,請建立一個新的 HTTP Connector
  2. Host 設定為相機的 IP,Port 設定為 80(如果啟用了 HTTPS,則為 443)。
  3. 建立一個新的 Connector Function,名為 Trigger Camera
    • Method: POST
    • Endpoint: /node-red/trigger
    • 基本觸發不需要 body 或輸入引數 —— 相機使用當前活動的程式。
  4. 儲存並測試該 Connector Function。您應當看到 200 響應,並且相機應當捕獲一張影象。
  5. 在您的 Tulip 應用中,新增一個 Button 控制元件。在其操作中,選擇 Run Connector Function → Trigger Camera

當操作員點選按鈕時,相機即被觸發。

使用觸發傳遞額外的上下文

如果您希望在每次觸發時傳送序列號或零件 ID,請更改 Tulip Connector Function 以接受輸入(例如 serial_number),並將其放入請求 body 中。在相機側,您的 HTTP IN 節點將其作為 msg.payload 接收,您可以將其儲存在 flow 變數中,以便結果傳送 flow(第 2 節)可以將其附加到檢測報告中。

2. 將結果傳送回 Tulip

相機捕獲後,All Block Outputs 節點會觸發,並攜帶完整的檢測結果。模式如下:

[ All Block Outputs ]──▶[ Function: format for Tulip ]──▶[ HTTP Request: POST to Tulip ]

構建流程

  1. All Block Outputs 節點的下游連線一個 function 節點。
  2. 該函式將各 ROI 的結果合併為一個全域性的透過/失敗結果,並將載荷精簡為 Tulip 實際需要的內容:
// Read the camera's full inspection payload
const p = msg.payload || {};

// Consolidate per-ROI classifier predictions into a global pass/fail
const preds = p.classification?.predictions || [];
const allPass = preds.length > 0 && preds.every(r => r.predicted_class === 'pass');

// Optional: count segmentation defects (e.g., for thresholded fail)
const defectPixels = (p.segmentation?.blobs || [])
.reduce((s, b) => s + (b.pixel_count || 0), 0);

// Build a flat object for Tulip (Tulip Connector Functions like flat fields)
msg.payload = {
result: allPass ? "pass" : "fail",
inspection_id: p.inspection_id || null,
timestamp: new Date().toISOString(),
image_url: p.image_url || null, // see Section 4 for displaying this in Tulip
defect_pixel_count: defectPixels,
per_roi: preds.map(r => ({
roi: r.roi_name,
class: r.predicted_class,
confidence: r.confidence,
})),
// If you stashed a serial number from the trigger (see Section 1), add it:
// serial_number: flow.get('lastSerial') || null,
};

// Set headers for the outbound HTTP request
msg.headers = { 'Content-Type': 'application/json' };
msg.method = 'POST';
return msg;
  1. 將其連線到 http request 節點:
    • Method: POST(或使用上述函式中的 msg.method
    • URL: ${TULIP_ENDPOINT}(從環境變數讀取)或硬編碼的 Tulip Connector URL
    • Return: a UTF-8 string(如果 Tulip 端點返回 JSON,則使用 JSON)
  2. 在 HTTP 請求節點之後連線一個 debug 節點,以便在測試時檢視 Tulip 的響應。
  3. 點選 Deploy

Tulip 端:接收結果

在 Tulip 中:

  1. 在接收方向上建立一個 HTTP Connector Function——一個 webhook 端點,或者使用 Tulip 的 Tables API 將結果寫入 Tulip 表。
  2. 配置 Tulip 應用的邏輯以響應入站載荷(例如,更新狀態指示器、記錄到表中、遞增計數器)。

對於 Tulip 表,具體模式是透過 POST 請求將 resultserial_numbertimestamp 欄位傳送到 https://<your-tulip-instance>.tulip.co/api/v3/tables/<table_id>/records。在 Authorization 頭中使用 Tulip API 令牌——將令牌作為憑據儲存在 HTTP Connector 上,而不是儲存在 Node-RED 流程中。

3. 從 Tulip 切換程式

當產品型別發生變化時(條碼掃描、下拉選擇、派工),操作員通常需要切換到不同的程式。相機提供了一個內建的 HTTP 端點,可透過 ID 啟用程式。

查詢程式 ID

  1. 程式編輯器中開啟程式。
  2. 檢視瀏覽器位址列:/recipes/15/editor → ID 為 15

構建程式切換流程

[ HTTP IN: POST /change-recipe ]──▶[ Function: format request ]──▶[ HTTP Request: POST localhost:5001/pipeline/activate ]──▶[ HTTP Response ]
  1. HTTP IN 節點:POST /change-recipe。Tulip 將使用類似 { "id": 15 } 的請求體呼叫此介面。
  2. Function 節點——將程式 ID 傳遞給相機的內部 API:
const recipeId = msg.payload?.id;
if (!recipeId) {
msg.statusCode = 400;
msg.payload = { error: "missing 'id' field" };
return msg;
}
msg.headers = { 'Content-Type': 'application/json' };
msg.payload = JSON.stringify({ id: recipeId });
msg.method = 'POST';
return msg;
  1. HTTP Request 節點:
    • Method: POST
    • URL: localhost:5001/pipeline/activate
    • Return: a parsed JSON object
  2. HTTP Response 節點,以便 Tulip 的 Connector Function 獲取返回的狀態碼。
相機內部 API URL

在相機的 Node-RED 內部,程式切換端點為 localhost:5001/pipeline/activate——這是相機內部管理 API。您是透過 Node-RED 將 Tulip 的請求代理到本地 API。請勿嘗試從相機外部直接呼叫 5001 埠;它僅向相機內部流程開放。

對於較舊的韌體(v18.92 之前),URL 為 http://[CAMERA_IP]/edge/pipeline/activate。如果您使用較舊韌體且 localhost:5001 無響應,請回退使用 v17/v18 的 URL。建議將相機韌體升級至 v18.92+ 以使用新版路徑。

Tulip 端:呼叫程式切換

  1. 在同一個 HTTP Connector 上新增一個 Connector Function Switch Recipe
  2. Method: POSTEndpoint: /node-red/change-recipe
  3. 新增一個輸入引數 recipe_id(數字)。
  4. 將請求體設定為 { "id": $recipe_id }
  5. 在您的 Tulip 應用中,將 Barcode Scanner 觸發器或 Dropdown 控制元件連線到此函式,並使用匹配的程式 ID 進行呼叫。

操作員掃描條形碼 → Tulip 查詢該零件對應的程式 ID → Connector Function 呼叫相機 → 相機在一秒內切換程式。

4. 在 Tulip 中顯示實時影象(帶邊界框)

Tulip 的 Image Widget 可以顯示任何返回影象的 URL。OV20i 具有一個內建的 HMI URL,可返回最新的捕獲影象,並在其上繪製邊界框和 ROI 疊加層

http://<CAMERA_IP>/live-feed

/live-feed 顯示帶 ROI 疊加層(繪製了邊界框)的相機影象

要將其嵌入 Tulip 應用:

  1. 在 Tulip 步驟中新增一個 Image Widget
  2. Image URL 設定為從檢測結果填充的 Tulip 變數,或者如果每臺相機有自己的儀表板步驟,則設定為靜態 URL。
  3. 最簡單的模式:硬編碼 http://<CAMERA_IP>/live-feed,讓它在每次捕獲時自動重新整理(當 Tulip 的影象控制元件的 URL 或查詢引數更改時會重新整理——附加 ?t={{timestamp}} 以強制重新整理)。

如果您想要原始捕獲影象(無邊界框),請改用 msg.payload 中的 image_url——這是指向相機庫中已儲存影象的每次捕獲 URL。

一個 Tulip 步驟,多臺相機

如果您在同一條產線上有多臺相機,每臺都有自己的 IP,請按應用步驟將相機 URL 儲存在 Tulip 變數中,並讓操作員選擇要檢視的工位。當變數更改時,Image Widget 會立即切換。

參考流程(可匯入的 Node-RED JSON)

下方流程組合了全部四種整合方式:觸發 HTTP IN、結果傳送 HTTP Request、程式切換器和除錯節點。複製 JSON,開啟 Node-RED → 漢堡選單 → Import,然後貼上。

[
{
"id": "tulip_trigger_in",
"type": "http in",
"name": "POST /trigger",
"url": "/trigger",
"method": "post",
"x": 160, "y": 100, "wires": [["tulip_trigger_fire"]]
},
{
"id": "tulip_trigger_fire",
"type": "trigger-camera",
"name": "Trigger camera",
"x": 380, "y": 100, "wires": [["tulip_trigger_resp"]]
},
{
"id": "tulip_trigger_resp",
"type": "http response",
"name": "200 ok",
"statusCode": "200",
"x": 580, "y": 100, "wires": []
},
{
"id": "tulip_results_format",
"type": "function",
"name": "Format for Tulip",
"func": "const p = msg.payload || {};\nconst preds = p.classification?.predictions || [];\nconst allPass = preds.length > 0 && preds.every(r => r.predicted_class === 'pass');\nmsg.payload = {\n result: allPass ? 'pass' : 'fail',\n timestamp: new Date().toISOString(),\n image_url: p.image_url || null,\n per_roi: preds.map(r => ({roi: r.roi_name, class: r.predicted_class, confidence: r.confidence}))\n};\nmsg.headers = {'Content-Type':'application/json'};\nmsg.method = 'POST';\nreturn msg;",
"x": 380, "y": 200, "wires": [["tulip_results_send"]]
},
{
"id": "tulip_results_send",
"type": "http request",
"name": "POST to Tulip",
"method": "POST",
"url": "${TULIP_ENDPOINT}",
"ret": "obj",
"x": 600, "y": 200, "wires": [["tulip_results_debug"]]
},
{
"id": "tulip_results_debug",
"type": "debug",
"name": "Tulip response",
"x": 800, "y": 200, "wires": []
},
{
"id": "tulip_recipe_in",
"type": "http in",
"name": "POST /change-recipe",
"url": "/change-recipe",
"method": "post",
"x": 160, "y": 300, "wires": [["tulip_recipe_format"]]
},
{
"id": "tulip_recipe_format",
"type": "function",
"name": "Format request",
"func": "const id = msg.payload?.id;\nif (!id) { msg.statusCode = 400; msg.payload = {error:'missing id'}; return msg; }\nmsg.headers = {'Content-Type':'application/json'};\nmsg.payload = JSON.stringify({id});\nmsg.method = 'POST';\nreturn msg;",
"x": 380, "y": 300, "wires": [["tulip_recipe_call"]]
},
{
"id": "tulip_recipe_call",
"type": "http request",
"name": "/pipeline/activate",
"method": "POST",
"url": "localhost:5001/pipeline/activate",
"ret": "obj",
"x": 600, "y": 300, "wires": [["tulip_recipe_resp"]]
},
{
"id": "tulip_recipe_resp",
"type": "http response",
"name": "to Tulip",
"x": 800, "y": 300, "wires": []
}
]

您仍需將 All Block Outputs 節點連線到 tulip_results_format(該節點未包含在上述 JSON 中,因為它是 OV 特有的,並且已存在於每個流程中)。

故障排除

現象原因 / 解決方法
Tulip Connector 返回 connection refused相機和 Tulip Edge Device 不在同一網路,或相機防火牆阻止了埠 80。兩者必須能互相訪問;先從 Tulip Edge Device ping 相機。
觸發 HTTP IN 從未觸發新增節點後忘記在 Node-RED 中部署,或者 URL 拼寫錯誤(必須以 / 開頭,而非 /node-red/)。
程式切換返回 404端點與您的韌體不匹配。v18.92 之前的版本使用相機 IP 的 /edge/pipeline/activate;v18.92+ 使用 Node-RED 內部的 localhost:5001/pipeline/activate
/live-feed 在 Tulip 中顯示黑色影象相機尚未進行捕獲,或影象未重新整理。追加 ?t={{Date.now()}} 以強制 Image Widget 重新獲取。
Tulip 收到 200 但沒有結果正文相機觸發後立即返回 200 — 結果透過結果傳送流程(第 2 節)非同步返回。請確保該流程已連線並部署。
每個 ROI 的結果過於雜亂 / Tulip 僅需要 pass/fail這正是 Function 節點在傳送前將所有內容整合為單一 `result: "pass"

後續步驟