AI 驅動文件
您想了解什麼?
連線到 Tulip
Tulip 是一個無程式碼 MES /互聯前線運營平臺。OV80i 攝像頭透過執行在攝像頭上的 Node-RED 與 Tulip 進行橋接 — 這與您用於 PLC、MQTT 及其他 I/O 整合的流程編輯器相同。Tulip 可以:
- 透過操作員按鈕或應用事件觸發檢測。
- 接收每個 ROI 和全域性透過/失敗結果以及捕獲後設資料。
- 當操作員掃描零件條形碼或選擇不同產品時,切換活動程式。
- 在 Tulip 應用中顯示實時影象(帶有邊界框覆蓋層),讓操作員看到 AI 所看到的內容。
本指南將端到端地講解上述四個整合點。
架構
┌────────────────────────────┐ ┌──────────────────────────────┐
│ TULIP │ │ OV80i │
│ │ │ │
│ ┌──────────────────┐ │ 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 — 您可以在這裡構建全部四個整合。

將 Tulip Connector URL 硬編碼到 Node-RED 流程中會將流程繫結到單一部署。請在系統設定 → 環境變數中定義 TULIP_ENDPOINT 環境變數,然後在 HTTP 請求節點中透過 ${TULIP_ENDPOINT} 讀取它。這樣您就可以匯出相同的流程並將其重新匯入到生產線上的每臺攝像頭。完整模式請參見環境變數。
選擇傳輸方式: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 例項。 | OV80i 出廠預裝了 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 ]
- 從 network 類別中拖入一個 http in 節點。
- Method:
POST - URL:
/trigger
- Method:
- 將其連線到 OV 專用的 Trigger 節點(位於 OV 面板的 block logic 下)。Trigger 節點會觸發相機的捕獲流水線。
- 新增一個 http response 節點,以便 Tulip 的 Connector 立即收到
200 OK響應(不要讓 Tulip 等待 AI 推理結果 —— 讓結果非同步返回,參見第 2 節)。 - 點選 部署。
該端點現已在 http://<CAMERA_IP>/node-red/trigger 上線(Node-RED 的 HTTP 根路徑掛載在 /node-red/ 下)。
Tulip 端:從按鈕呼叫觸發
在您的 Tulip 賬戶中:
- 轉到 Apps → Connectors,如果還沒有,建立一個新的 HTTP Connector。
- 將 Host 設定為您相機的 IP,Port 設定為
80(如果已啟用 HTTPS,則為443)。 - 建立一個名為
Trigger Camera的新 Connector Function:- Method:
POST - Endpoint:
/node-red/trigger - 基本觸發不需要請求體或輸入引數 —— 相機會使用當前活動的程式。
- Method:
- 儲存並測試該 Connector Function。您應該能看到 200 響應,並且相機應捕獲一張影象。
- 在您的 Tulip 應用中,新增一個 Button 小部件。在其操作中,選擇 Run Connector Function → Trigger Camera。
當操作員點選按鈕時,相機會觸發拍攝。
如果您希望隨每次觸發傳送序列號或零件 ID,請更改 Tulip Connector Function 以接受輸入(例如 serial_number),並將其放入請求體。在相機端,您的 HTTP IN 節點將其作為 msg.payload 接收,您可以將其暫存到流變數中,以便結果傳送流(第 2 節)可以將其附加到檢測報告中。
2. 將結果傳送回 Tulip
相機捕獲後,All Block Outputs 節點將隨完整檢測結果觸發。模式如下:
[ All Block Outputs ]──▶[ Function: format for Tulip ]──▶[ HTTP Request: POST to Tulip ]
構建流程
- 在 All Block Outputs 節點下游連線一個 function 節點。
- 該函式將每個 ROI 的結果合併為單個全域性 pass/fail,並將載荷精簡為 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;
- 將其連線到 http request 節點:
- Method:
POST(或使用上述函式中的msg.method) - URL:
${TULIP_ENDPOINT}(從環境變數讀取)或硬編碼的 Tulip Connector URL - Return:
a UTF-8 string(如果您的 Tulip 端點返回 JSON,則使用 JSON)
- Method:
- 在 HTTP 請求之後連線一個 debug 節點,以便您在測試時檢查 Tulip 的響應。
- 單擊 Deploy。
Tulip 側:接收結果
在 Tulip 中:
- 在接收方向上建立 HTTP Connector Function——一個 webhook 端點,或使用 Tulip 的 Tables API 將結果寫入 Tulip Table。
- 配置您的 Tulip 應用邏輯以響應入站載荷(例如,更新狀態指示器、記錄到 Table、遞增計數器)。
對於 Tulip Tables,具體模式是向 https://<your-tulip-instance>.tulip.co/api/v3/tables/<table_id>/records 傳送包含 result、serial_number、timestamp 欄位的 POST 請求。在 Authorization 標頭中使用 Tulip API 令牌——將該令牌作為憑據儲存在 HTTP Connector 上,而不是儲存在 Node-RED 流中。
3. 從 Tulip 更換程式
當產品型別發生變化時(條碼掃描、下拉選單、排程),操作員通常需要切換到不同的程式。相機提供了一個內建的 HTTP 端點,可按 ID 啟用程式。
查詢程式 ID
- 在 程式編輯器 中開啟程式。
- 檢視瀏覽器位址列:
/recipes/15/editor→ ID 為15。
構建程式切換流程
[ HTTP IN: POST /change-recipe ]──▶[ Function: format request ]──▶[ HTTP Request: POST localhost:5001/pipeline/activate ]──▶[ HTTP Response ]
- HTTP IN 節點:
POST /change-recipe。Tulip 將透過類似{ "id": 15 }的請求體呼叫此端點。 - 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;
- HTTP Request 節點:
- Method:
POST - URL:
localhost:5001/pipeline/activate - Return:
a parsed JSON object
- Method:
- HTTP Response 節點,以便 Tulip 的 Connector Function 獲得返回的狀態碼。
在相機的 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 端:呼叫程式切換
- 在同一個 HTTP Connector 上新增一個 Connector Function
Switch Recipe。 - Method:
POST,Endpoint:/node-red/change-recipe。 - 新增一個輸入引數
recipe_id(數字)。 - 將請求體設定為
{ "id": $recipe_id }。 - 在您的 Tulip 應用中,連線一個條碼掃描器觸發器或一個下拉控制元件,以使用匹配的程式 ID 呼叫此函式。
操作員掃描條碼 → Tulip 查詢該零件對應的程式 ID → Connector Function 呼叫相機 → 相機在一秒內切換程式。
4. 在 Tulip 中顯示實時影象(帶邊界框)
Tulip 的影象控制元件可以顯示任何返回影象的 URL。OV80i 具有一個內建的 HMI URL,可返回最近一次捕獲的影象,並在其上繪製邊界框和 ROI 疊加層:
http://<CAMERA_IP>/live-feed

要將其嵌入 Tulip 應用中:
- 在您的 Tulip 步驟中新增一個影象控制元件。
- 將影象 URL 設定為從檢測結果填充的 Tulip 變數,或者,如果每臺相機都有自己的儀表板步驟,則設定為靜態 URL。
- 最簡單的模式:硬編碼
http://<CAMERA_IP>/live-feed,並讓它在每次捕獲時自動重新整理(當 URL 或查詢引數發生變化時,Tulip 的影象控制元件會重新整理 — 追加?t={{timestamp}}以強制重新整理)。
如果您想要原始捕獲影象(不帶邊界框),請改用 msg.payload 中的 image_url — 這是每次捕獲的 URL,指向相機庫中儲存的影象。
如果您在同一條產線上有多臺相機,每臺都有自己的 IP,請在每個應用步驟中將相機 URL 儲存在 Tulip 變數中,並讓操作員選擇要檢視的工位。當變數改變時,影象控制元件會立即切換。
參考流程(可匯入的 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 中Deploy,或 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" |
後續步驟
- 環境變數 — 將
TULIP_ENDPOINT和其他特定部署配置移出流程。 - 連線到 PLC(EtherNet/IP 和 PROFINET) — 用於與 Tulip 並行的 PLC 整合。
- Overview Node-RED 自定義模組 — OV 專用面板節點(Trigger、All Block Outputs 等)的參考。
- 設定輸出(第 5 步) — Node-RED 在更廣泛的透過/失敗流程中的位置。