Zum Hauptinhalt springen

KI-GESTÜTZTE DOKUMENTATION

Was möchten Sie wissen?

Verbindung mit Tulip

Tulip ist eine No-Code-Plattform für MES und Connected-Frontline-Operations. Die OV80i-Kamera stellt die Verbindung zu Tulip über Node-RED her, das auf der Kamera läuft — derselbe Flow-Editor, den Sie auch für PLC-, MQTT- und andere I/O-Integrationen verwenden. Tulip kann:

  • Eine Inspektion über eine Bedienertaste oder ein App-Ereignis auslösen.
  • Die ROI-spezifischen und globalen Pass/Fail-Ergebnisse sowie Aufnahme-Metadaten empfangen.
  • Das aktive Rezept umschalten, wenn der Bediener einen Teile-Barcode scannt oder ein anderes Produkt auswählt.
  • Das Live-Bild anzeigen (mit Bounding-Box-Overlays) innerhalb einer Tulip-App, damit der Bediener sieht, was die KI gesehen hat.

Diese Anleitung führt Sie durchgängig durch jeden dieser vier Integrationspunkte.

Architektur

┌────────────────────────────┐               ┌──────────────────────────────┐
│ 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>} │ │
│ └──────────────────┘ │ │ └──────────────────────┘ │
└────────────────────────────┘ └──────────────────────────────┘

Die Brücke in der Mitte ist immer Node-RED, das im IO Block der Kamera läuft — dort bauen Sie alle vier Integrationen auf.

Node-RED-Canvas im IO Block der Kamera — Advanced Mode der IO-Logik-Stufe

Verwenden Sie Umgebungsvariablen für den Tulip-Endpunkt

Das Hardcoding Ihrer Tulip-Connector-URL in den Node-RED-Flow bindet den Flow an eine einzelne Bereitstellung. Definieren Sie eine TULIP_ENDPOINT-Umgebungsvariable unter System Settings → Environment Variables und lesen Sie diese in Ihren HTTP-Request-Nodes mit ${TULIP_ENDPOINT} aus. So können Sie denselben Flow exportieren und auf jeder Kamera der Linie wieder importieren. Das vollständige Vorgehen finden Sie unter Environment Variables.

Wählen Sie Ihren Transport: HTTP oder MQTT

OptionVerwendungHinweise
HTTP (empfohlen für die meisten Tulip-Bereitstellungen)Tulip Connector Functions rufen HTTP-Endpunkte nativ auf. Der einfachste Weg, um über eine Tulip-Widget-Schaltfläche auszulösen.Funktioniert ohne zusätzliche Infrastruktur. Tulip → Kamera und Kamera → Tulip verwenden beide einfaches POST.
MQTTIn Ihrer Anlage läuft bereits ein MQTT-Broker (Sparkplug B, IT/OT-Broker usw.) und der Tulip Edge Connector ist für die Kommunikation damit konfiguriert.Geringere Latenz bei hohem Signalisierungsaufkommen. Erfordert einen Broker.
OPC UASie betreiben eine Tulip Edge MC-Instanz mit dem OPC UA Connector.Der OV80i wird mit vorinstallierten OPC UA Node-RED-Blöcken ausgeliefert.

Der Rest dieses Leitfadens verwendet HTTP in den Beispielen – das ist die Standardoption für die meisten Tulip-Bereitstellungen. Die gleichen Muster gelten für MQTT (ersetzen Sie HTTP IN / HTTP Request Nodes durch MQTT in / MQTT out Nodes).

1. Kamera aus einer Tulip-App auslösen

Ein Tulip-Bediener drückt eine Schaltfläche → Die Connector Function von Tulip ruft einen HTTP-Endpunkt auf, an dem das Node-RED der Kamera lauscht → Node-RED löst eine Aufnahme aus.

Node-RED-Seite: Trigger empfangen

Bauen Sie im IO Block der Kamera (Advanced Mode → Node-RED) Folgendes auf:

[ HTTP IN: POST /trigger ]──▶[ Trigger node ]──▶[ HTTP Response: 200 ok ]
  1. Ziehen Sie einen http in-Node aus der Kategorie network hinein.
    • Method: POST
    • URL: /trigger
  2. Verbinden Sie ihn mit dem OV-spezifischen Trigger-Node (unter block logic in der OV-Palette). Der Trigger-Node startet die Aufnahme-Pipeline der Kamera.
  3. Fügen Sie einen http response-Node hinzu, damit der Tulip Connector sofort ein 200 OK zurückbekommt (lassen Sie Tulip nicht auf die KI-Inferenz warten – das Ergebnis fließt asynchron zurück, siehe Abschnitt 2).
  4. Klicken Sie auf Deploy.

Der Endpunkt ist nun unter http://<CAMERA_IP>/node-red/trigger erreichbar (der HTTP-Root von Node-RED ist unter /node-red/ eingebunden).

Tulip-Seite: Trigger über eine Schaltfläche aufrufen

In Ihrem Tulip-Konto:

  1. Gehen Sie zu Apps → Connectors und erstellen Sie einen neuen HTTP Connector, falls noch keiner vorhanden ist.
  2. Setzen Sie den Host auf die IP-Adresse Ihrer Kamera und den Port auf 80 (oder 443, falls Sie HTTPS aktiviert haben).
  3. Erstellen Sie eine neue Connector Function namens Trigger Camera:
    • Method: POST
    • Endpoint: /node-red/trigger
    • Für einen einfachen Trigger werden keine Body- oder Eingabeparameter benötigt – die Kamera verwendet das aktive Rezept.
  4. Speichern und testen Sie die Connector Function. Sie sollten eine 200-Antwort sehen und die Kamera sollte ein Bild aufnehmen.
  5. Fügen Sie in Ihrer Tulip-App ein Button-Widget hinzu. Wählen Sie in dessen Aktion Run Connector Function → Trigger Camera.

Wenn der Bediener die Schaltfläche antippt, löst die Kamera aus.

Zusätzlichen Kontext mit dem Trigger übergeben

Wenn Sie mit jedem Trigger eine Seriennummer oder Teile-ID senden möchten, ändern Sie die Tulip Connector Function so, dass sie Eingaben akzeptiert (z. B. serial_number), und fügen Sie diese in den Request-Body ein. Auf der Kameraseite empfängt Ihr HTTP IN-Node diese als msg.payload, und Sie können sie in einer Flow-Variable ablegen, damit der Flow zum Senden der Ergebnisse (Abschnitt 2) sie an den Prüfbericht anhängen kann.

2. Ergebnisse zurück an Tulip senden

Nach der Kameraaufnahme wird der All Block Outputs-Node mit dem vollständigen Inspektionsergebnis ausgelöst. Das Muster:

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

Flow aufbauen

  1. Verbinden Sie einen function-Node nachgeschaltet zum All Block Outputs-Node.
  2. Die Funktion konsolidiert die ROI-Ergebnisse zu einem einzigen globalen Pass/Fail und reduziert die Nutzlast auf das, was Tulip tatsächlich benötigt:
// 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. Verbinden Sie diese mit einem http request-Node:
    • Method: POST (oder verwenden Sie msg.method aus der obigen Funktion)
    • URL: ${TULIP_ENDPOINT} (aus der Umgebungsvariable lesen) oder hartcodierte Tulip Connector URL
    • Return: a UTF-8 string (oder JSON, falls Ihr Tulip-Endpunkt JSON zurückgibt)
  2. Verbinden Sie einen debug-Node nach dem HTTP-Request, um die Antwort von Tulip während des Tests prüfen zu können.
  3. Klicken Sie auf Deploy.

Tulip-Seite: Ergebnisse empfangen

In Tulip:

  1. Erstellen Sie eine HTTP Connector Function in Empfangsrichtung – einen Webhook-Endpunkt – oder verwenden Sie die Tables API von Tulip, um Ergebnisse in eine Tulip Table zu schreiben.
  2. Konfigurieren Sie die Logik Ihrer Tulip-App so, dass sie auf die eingehende Nutzlast reagiert (z. B. einen Statusindikator aktualisieren, in eine Table protokollieren, einen Zähler erhöhen).

Speziell für Tulip Tables gilt das Muster: POST an https://<your-tulip-instance>.tulip.co/api/v3/tables/<table_id>/records mit den Feldern result, serial_number, timestamp. Verwenden Sie ein Tulip-API-Token im Authorization-Header – speichern Sie das Token als Credential am HTTP Connector, nicht im Node-RED-Flow.

3. Rezepte von Tulip aus wechseln

Bediener müssen häufig zu einem anderen Rezept wechseln, wenn sich der Produkttyp ändert (Barcode-Scan, Dropdown, Dispatch). Die Kamera stellt einen integrierten HTTP-Endpunkt bereit, der ein Rezept anhand seiner ID aktiviert.

Rezept-ID finden

  1. Öffnen Sie das Rezept im Recipe Editor.
  2. Schauen Sie in die Adressleiste Ihres Browsers: /recipes/15/editor → die ID lautet 15.

Aufbau des Recipe-Switch-Flows

[ HTTP IN: POST /change-recipe ]──▶[ Function: format request ]──▶[ HTTP Request: POST localhost:5001/pipeline/activate ]──▶[ HTTP Response ]
  1. HTTP IN-Node: POST /change-recipe. Tulip ruft diesen mit einem Body wie { "id": 15 } auf.
  2. Function-Node — die Recipe-ID an die interne API der Kamera weiterleiten:
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-Node:
    • Method: POST
    • URL: localhost:5001/pipeline/activate
    • Return: a parsed JSON object
  2. HTTP Response-Node, damit die Connector Function von Tulip einen Statuscode zurückerhält.
Kamera-interne API-URL

Innerhalb von Node-RED auf der Kamera lautet der Endpunkt für den Recipe-Switch localhost:5001/pipeline/activate — das ist die kamera-interne Admin-API. Sie leiten die Anfrage von Tulip über Node-RED an die lokale API weiter. Versuchen Sie nicht, Port 5001 direkt von außerhalb der Kamera anzusprechen; er ist nur für kamera-interne Flows freigegeben.

Für ältere Firmware (vor v18.92) lautete die URL http://[CAMERA_IP]/edge/pipeline/activate. Falls Sie eine ältere Firmware verwenden und localhost:5001 nicht antwortet, greifen Sie auf die v17/v18-URL zurück. Aktualisieren Sie die Kamera-Firmware auf v18.92+, um den modernen Pfad zu nutzen.

Tulip-Seite: Recipe-Switch aufrufen

  1. Fügen Sie eine Connector Function Switch Recipe auf demselben HTTP Connector hinzu.
  2. Method: POST, Endpoint: /node-red/change-recipe.
  3. Fügen Sie einen Eingabeparameter recipe_id (Number) hinzu.
  4. Setzen Sie den Request-Body auf { "id": $recipe_id }.
  5. Verknüpfen Sie in Ihrer Tulip-App einen Barcode Scanner-Trigger oder ein Dropdown-Widget, um diese Funktion mit der passenden Recipe-ID aufzurufen.

Operator scannt einen Barcode → Tulip ermittelt die Recipe-ID für dieses Teil → Connector Function ruft die Kamera auf → Kamera wechselt das Recipe in unter einer Sekunde.

4. Live-Bild (mit Bounding Boxes) in Tulip anzeigen

Das Image Widget von Tulip kann jede URL anzeigen, die ein Bild zurückgibt. Die OV80i verfügt über eine integrierte HMI-URL, die die letzte Aufnahme zurückgibt — mit darüber gezeichneten Bounding-Box- und ROI-Overlays:

http://<CAMERA_IP>/live-feed

/live-feed zeigt das Kamerabild mit ROI-Overlay (Bounding Boxes eingezeichnet)

So binden Sie es in eine Tulip-App ein:

  1. Fügen Sie ein Image Widget zu Ihrem Tulip-Step hinzu.
  2. Setzen Sie die Image URL auf eine Tulip-Variable, die Sie aus dem Inspektionsergebnis befüllen, oder auf eine statische URL, wenn jede Kamera ihren eigenen Dashboard-Step hat.
  3. Das einfachste Muster: http://<CAMERA_IP>/live-feed fest hinterlegen und bei jeder Aufnahme automatisch aktualisieren lassen (das Image Widget von Tulip aktualisiert sich, wenn sich seine URL oder ein Query-Parameter ändert — hängen Sie ?t={{timestamp}} an, um eine Aktualisierung zu erzwingen).

Wenn Sie die Roh-Aufnahme (ohne Bounding Boxes) wünschen, verwenden Sie stattdessen image_url aus dem msg.payload — das ist eine URL pro Aufnahme, die auf das gespeicherte Bild in der Bibliothek der Kamera verweist.

Ein Tulip-Step, mehrere Kameras

Wenn Sie mehrere Kameras an derselben Linie haben, jede mit eigener IP, speichern Sie die Kamera-URL in einer Tulip-Variable pro App-Step und lassen Sie den Operator auswählen, welche Station angezeigt werden soll. Das Image Widget wechselt sofort, sobald sich die Variable ändert.

Referenz-Flow (importierbares Node-RED JSON)

Der nachfolgende Flow kombiniert alle vier Integrationen: Trigger HTTP IN, ergebnissendender HTTP Request, Rezept-Umschalter und einen Debug-Node. Kopieren Sie das JSON, öffnen Sie Node-RED → Hamburger-Menü → Import, und fügen Sie es ein.

[
{
"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": []
}
]

Sie müssen weiterhin den All Block Outputs-Node mit tulip_results_format verbinden (er ist im obigen JSON nicht enthalten, da dieser Node OV-spezifisch ist und bereits in jedem Flow vorhanden ist).

Fehlerbehebung

SymptomUrsache / Lösung
Tulip Connector liefert connection refusedKamera und Tulip Edge Device befinden sich nicht im selben Netzwerk, oder die Kamera-Firewall blockiert Port 80. Beide müssen gegenseitig erreichbar sein; pingen Sie die Kamera zuerst vom Tulip Edge Device aus an.
Trigger HTTP IN wird nie ausgelöstNach dem Hinzufügen des Nodes wurde vergessen, in Node-RED auf Deploy zu klicken, oder die URL enthält einen Tippfehler (muss mit / beginnen, nicht mit /node-red/).
Rezept-Umschaltung liefert 404Falscher Endpunkt für Ihre Firmware. Vor v18.92 wird /edge/pipeline/activate von der Kamera-IP verwendet; ab v18.92 wird localhost:5001/pipeline/activate aus Node-RED heraus verwendet.
/live-feed zeigt in Tulip ein schwarzes BildDie Kamera hat noch keine Aufnahme gemacht, oder das Bild wurde nicht aktualisiert. Hängen Sie ?t={{Date.now()}} an, um das Image Widget zum erneuten Abrufen zu zwingen.
Tulip erhält ein 200, aber keinen Ergebnis-BodyDie Kamera löst den Trigger aus und gibt sofort 200 zurück — das Ergebnis wird asynchron über den ergebnissendenden Flow zurückgeliefert (Abschnitt 2). Stellen Sie sicher, dass dieser Flow verdrahtet und deployed ist.
Per-ROI-Ergebnisse sind verrauscht / Tulip benötigt nur pass/failGenau aus diesem Grund konsolidiert der Function-Node alles vor dem Senden in ein einzelnes `result: "pass"

Nächste Schritte