Digitaler Zwilling

Digitaler Zwilling mit Retained MQTT: Retained Messages als Zwillingszustand nutzen

Bauen Sie einen schlanken digitalen Zwilling, bei dem jede Eigenschaft ein Retained MQTT-Topic ist. Viewer erhalten den vollständigen Zwillingszustand im Moment des Abonnierens, ganz ohne Datenbank. Mit Python-Simulator, Node.js-Viewer und Befehlen zum Aufräumen.

Tutorial zu digitalen Zwillingen mit Retained MQTT, bei dem Retained Topics pro Eigenschaft den vollständigen Maschinenzustand sofort in einen Viewer laden
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

Retained MQTT-Nachrichten sind der schlankste Weg, einem digitalen Zwilling ein Gedächtnis zu geben: Jede Eigenschaft liegt auf einem eigenen Retained Topic, sodass jeder Viewer beim Abonnieren sofort den vollständigen aktuellen Zustand erhält, ohne Datenbank oder API. In diesem Tutorial bauen Sie einen kleinen Maschinenzwilling mit einem Python-Simulator, einem Node.js-Viewer, der beim Verbinden den vollständigen Zustand rekonstruiert, und den Befehlen, die Sie zum Prüfen und Aufräumen von Retained Topics brauchen.

Das übergeordnete Konzept erklärt der Leitfaden zur Digital-Twin-Technologie.

Wie Retained Messages funktionieren

Normalerweise leitet ein MQTT-Broker eine Nachricht an die aktuellen Subscriber weiter und vergisst sie dann. Setzt ein Publisher das Retain-Flag, speichert der Broker diese Nachricht zusätzlich als letzten bekannten Wert des Topics. Jeder Client, der später ein passendes Topic abonniert, erhält die gespeicherte Nachricht sofort, als retained gekennzeichnet.

Die wichtigsten Regeln:

  • Der Broker hält eine Retained Message pro Topic vor: Die neueste ersetzt die vorherige.
  • Wildcard-Abonnements erhalten die Retained Message jedes passenden Topics.
  • Ein leerer Payload mit gesetztem Retain-Flag löscht die Retained Message.
  • Retained Messages werden mit der niedrigeren der beiden QoS-Stufen (Publish und Subscribe) zugestellt.

Diese Kombination ist genau das, was ein Zwilling braucht: ein Key-Value-Speicher für den aktuellen Zustand, adressiert über Topics und per Wildcard lesbar. Wo sich dieser Ansatz unter den anderen Zustandsansätzen einordnet, zeigt Architektur digitaler Zwillinge.

Den Topic-Baum entwerfen: eine Eigenschaft pro Topic

Sie können ein JSON-Dokument pro Asset oder einen Wert pro Eigenschaft retainen. Dieses Tutorial verwendet ein Topic pro Eigenschaft, was echte Vorteile hat:

Ansatz Vorteile Nachteile
Ein retained JSON pro Asset Weniger Nachrichten, atomare Momentaufnahme Jede Änderung sendet alle Felder erneut; gleichzeitige Schreiber überschreiben sich gegenseitig
Ein Retained Topic pro Eigenschaft Kleine Updates, unabhängige Schreiber, einfache Abonnements auf einzelne Werte Mehr Topics; keine atomare Momentaufnahme über mehrere Eigenschaften

Für eine CNC-Maschine auf dem öffentlichen Broker sieht der Baum so aus (bleiben Sie innerhalb von 8 Topic-Ebenen):

connectmq/demo/f0c3/cell1/cnc1/spindle_rpm
connectmq/demo/f0c3/cell1/cnc1/feed_mm_min
connectmq/demo/f0c3/cell1/cnc1/tool
connectmq/demo/f0c3/cell1/cnc1/mode
connectmq/demo/f0c3/cell1/cnc1/alarm
connectmq/demo/f0c3/cell1/cnc1/status

Payloads sind einfache Werte, optional mit Zeitstempel. Hier verwenden wir ein kleines JSON-Objekt {"v": 8000, "ts": 1792152000000}, damit Viewer wissen, wie aktuell jeder Wert ist.

Ersetzen Sie f0c3 durch Ihre eigene zufällige Zeichenfolge. Der öffentliche MQTT-Broker ist für Tests und Prototypen gedacht und für jeden lesbar, publizieren Sie also nur Demodaten.

Schritt 1: Zwillingseigenschaften als Retained Messages publizieren

Dieser Python-Simulator publiziert jede Eigenschaft nur bei Änderung, immer mit retain=True, und verwendet einen Last Will, um die Maschine als offline zu markieren:

import json
import random
import time
import paho.mqtt.client as mqtt

BASE = "connectmq/demo/f0c3/cell1/cnc1"
last_sent = {}

def publish_property(client, name, value):
    if last_sent.get(name) == value:
        return
    payload = json.dumps({"v": value, "ts": int(time.time() * 1000)})
    client.publish(f"{BASE}/{name}", payload, qos=1, retain=True)
    last_sent[name] = value

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="cnc1-twin-f0c3")
client.will_set(f"{BASE}/status", json.dumps({"v": "offline"}), qos=1, retain=True)
client.connect("broker.connectmq.com", 1883)
client.loop_start()

publish_property(client, "status", "online")
publish_property(client, "tool", 3)

try:
    while True:
        running = random.random() > 0.1
        publish_property(client, "mode", "AUTO" if running else "HOLD")
        publish_property(client, "spindle_rpm", random.choice([7950, 8000, 8050]) if running else 0)
        publish_property(client, "feed_mm_min", 1000 if running else 0)
        publish_property(client, "alarm", None if running else "E-STOP")
        time.sleep(2)
except KeyboardInterrupt:
    publish_property(client, "status", "offline")
    time.sleep(0.5)
    client.loop_stop()
    client.disconnect()

Bei Änderung statt nach Timer zu publizieren hält den Datenverkehr gering und respektiert das Ratenlimit des öffentlichen Brokers von 10 Nachrichten pro Sekunde und Client. Der Retained State stellt sicher, dass Viewer auch unveränderte Werte sehen.

Schritt 2: Einen Viewer bauen, der den vollständigen Zustand sofort lädt

Der Viewer abonniert per Wildcard, sammelt die Retained Messages in einem Zwillingsobjekt und wendet danach laufend Live-Updates an. Mit MQTT.js in Node.js:

const mqtt = require("mqtt");

const base = "connectmq/demo/f0c3/cell1/cnc1";
const twin = {};

const client = mqtt.connect("mqtt://broker.connectmq.com:1883", {
  clientId: "cnc1-viewer-" + Math.random().toString(16).slice(2, 10),
  protocolVersion: 5,
});

client.on("connect", () => {
  client.subscribe(`${base}/+`, { qos: 1 });
});

client.on("message", (topic, payload, packet) => {
  const property = topic.slice(base.length + 1);
  if (payload.length === 0) {
    delete twin[property];
    return;
  }
  const { v, ts } = JSON.parse(payload.toString());
  twin[property] = { value: v, ts, fromRetained: packet.retain };
  console.clear();
  console.table(twin);
});

Starten Sie den Viewer, nachdem der Simulator läuft. Die Tabelle füllt sich auf einen Schlag mit allen Eigenschaften, weil jede Retained Message beim Abonnieren mit packet.retain auf true zugestellt wird. Spätere Updates kommen als Live-Nachrichten an. Im Browser funktioniert derselbe Code mit wss://broker.connectmq.com:8084/mqtt; so laden 3D-Zwillings-Viewer eine ganze Szene in einem Durchgang. Digitaler Zwilling mit MQTT zeigt die Browser-Variante.

Schritt 3: Retained Topics prüfen und aufräumen

Retained Messages bleiben bestehen, bis sie ersetzt oder gelöscht werden; das ist eine Funktion, bis Sie eine Eigenschaft umbenennen. Prüfen Sie alles unter Ihrem Präfix:

mosquitto_sub -h broker.connectmq.com -p 1883 -i inspect-f0c3 \
  -t "connectmq/demo/f0c3/#" -v

Löschen Sie eine nicht mehr genutzte Eigenschaft, indem Sie eine leere Retained Message publizieren:

mosquitto_pub -h broker.connectmq.com -p 1883 -i cleanup-f0c3 \
  -t "connectmq/demo/f0c3/cell1/cnc1/alarm" -r -n

Das Flag -n sendet einen Null-Payload (Länge null); zusammen mit -r entfernt es die Retained Message.

Das Muster auf viele Assets skalieren

Eine Maschine ist einfach; ein ganzes Werk braucht ein paar zusätzliche Konventionen. Verwenden Sie für Assets desselben Typs dieselben Eigenschaftsnamen, damit ein Viewer jede CNC-Maschine aus derselben Vorlage rendern kann. Legen Sie die Asset-Hierarchie in den Topic-Pfad, zum Beispiel site/area/line/asset/property, damit Viewer genau den Zweig abonnieren, den sie anzeigen: Eine Linienansicht abonniert .../line2/+/+, eine Seite für eine einzelne Maschine .../line2/cnc4/+. In der Fertigung ist das genau die Logik eines Unified Namespace (UNS): Die ISA-95-Ausrüstungshierarchie (Enterprise → Site → Area → Line → Cell) wird zur Topic-Struktur auf dem MQTT-Broker, etwa acme/werk-stuttgart/montage/linie-1/zelle-3/temperatur, und Retained Messages halten darin den aktuellen Zustand jedes Knotens für MES, SCADA und Zwilling bereit. Mehr dazu unter Sparkplug B und Unified Namespace. Denken Sie auf dem öffentlichen Broker an das Limit von 8 Topic-Ebenen und 10 Abonnements pro Client; das reicht für eine Demo, ist aber ein Grund, für echte Deployments einen eigenen Broker zu verwenden.

Viewer, die einen großen Zweig öffnen, erhalten auf einmal eine Retained Message pro Eigenschaft. Das ist meist unproblematisch, doch parsen und übernehmen Sie sie gebündelt vor dem ersten Rendern, statt die Szene für jede einzelne Nachricht neu zu rendern. Das Ergebnis ist ein Zwilling, der schon im ersten Frame vollständig erscheint.

MQTT-5-Optionen, die Retained-Zwillinge verbessern

MQTT 5 ergänzt Steuerungsmöglichkeiten, die den Retained State präziser machen:

  • Message Expiry Interval: Setzen Sie für volatile Eigenschaften ein Ablaufdatum, damit ein Retained-Wert verschwindet, wenn er nicht aufgefrischt wird, statt für immer einen veralteten Messwert anzuzeigen.
  • Retain Handling beim Abonnieren: 0 sendet Retained Messages bei jedem Abonnement, 1 nur bei neuen Abonnements, 2 nie. Verwenden Sie 2 für Konsumenten, die nur Live-Änderungen wollen.
  • Retain as Published: lässt eine Bridge oder ein Relay erkennen, ob die ursprüngliche Nachricht retained war.

In MQTT.js überspringt ein Abonnement mit { qos: 1, rh: 2 } Retained Messages, und ein Publish mit { retain: true, properties: { messageExpiryInterval: 300 } } lässt einen Wert nach fünf Minuten verfallen. Mehr dazu unter MQTT-5-Funktionen.

Stolperfallen und Fehlersuche

  1. Geister-Eigenschaften: Umbenannte Topics hinterlassen alte Retained-Werte. Löschen Sie sie explizit.
  2. Retained Befehle: Retainen Sie niemals Befehls-Topics; Geräte führen sie beim Reconnect erneut aus.
  3. Veraltete Werte als live angezeigt: Fügen Sie Zeitstempel hinzu und beachten Sie das Status-Topic.
  4. Zu große Retained Payloads: Halten Sie jeden Wert klein; der öffentliche Broker akzeptiert bis zu 64 KB pro Nachricht, der kostenlose Serverless-Plan 256 KB.
  5. Keine atomare Momentaufnahme: Wenn sich mehrere Eigenschaften gemeinsam ändern müssen, publizieren Sie sie stattdessen als ein retained JSON-Dokument.
  6. Broker-Neustarts: Planen Sie, dass Geräte den Retained State beim Reconnect erneut publizieren, statt davon auszugehen, dass er Broker-Neustarts für immer übersteht. Wenn Geräte beim Verbinden ihren vollständigen Zustand publizieren, heilt sich der Zwilling selbst.

Wann Retained Topics nicht ausreichen

Retained Topics sind ideal für den aktuellen Zustand. Ergänzen Sie weitere Komponenten, wenn Sie Historie (einen Zeitreihen-Subscriber), Abfragen über Assets hinweg oder Zugriffskontrolle pro Zwilling (einen Zwillingsdienst wie Eclipse Ditto mit MQTT) oder die Behandlung von Desired und Reported State in großem Maßstab brauchen. Das Desired/Reported-Muster auf Retained Topics beschreibt Zustandssynchronisation digitaler Zwillinge.

Betreiben Sie Ihren Retained-State-Zwilling auf ConnectMQ

Für echte Assets wechseln Sie vom öffentlichen Broker zu einem privaten Broker auf ConnectMQ, einem verwalteten MQTT-Broker mit MQTT 3.1.1 und 5.0, Retained Messages, Message Expiry und TLS. Ersetzen Sie den Host durch Ihren Broker, ergänzen Sie YOUR_USERNAME, YOUR_PASSWORD und YOUR_CLIENT_ID aus dem Portal und wechseln Sie auf Port 8883 oder für Browser auf 8084. Registrieren Sie sich unter app.connectmq.com im kostenlosen Serverless-Plan, ganz ohne Kreditkarte, und beobachten Sie die Retained Topics Ihres Zwillings im Live-Topic-Explorer.

Häufig gestellte Fragen

Was ist eine Retained Message in MQTT?

Eine Nachricht, die der Broker als letzten bekannten Wert ihres Topics speichert und jedem neuen Subscriber dieses Topics sofort zustellt.

Wie lösche ich eine Retained MQTT-Nachricht?

Publizieren Sie einen Payload der Länge null mit gesetztem Retain-Flag auf dasselbe Topic, zum Beispiel mosquitto_pub -t <topic> -r -n.

Sollte jede Zwillingseigenschaft ein eigenes Retained Topic haben?

Das funktioniert gut für unabhängige Eigenschaften und kleine Updates. Verwenden Sie ein retained JSON-Dokument pro Asset, wenn sich mehrere Werte gemeinsam und atomar ändern müssen.

Können Retained Messages eine Datenbank für einen digitalen Zwilling ersetzen?

Für den aktuellen Zustand oft ja. Für Historie, Asset-übergreifende Abfragen oder Audit-Trails ergänzen Sie neben dem Broker einen Datenbank-Subscriber.

Verwandte Artikel

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.