IoT et IA

Détection d'objets YOLO via MQTT : publier les détections en JSON

Exécutez un modèle YOLO sur une webcam ou un flux RTSP en Python, transformez les détections de chaque image en JSON compact et publiez comptages et alertes via MQTT pour que n'importe quel tableau de bord ou automatisme puisse réagir.

Illustration de détection d'objets YOLO via MQTT avec des boîtes englobantes sur une image de caméra et des messages de détection JSON envoyés vers un broker
Essayez-le sur votre propre broker MQTTObtenez un broker MQTT privé et managé avec l’offre Free et partez d’exemples de code prêts à l’emploi.
Essayer gratuitement Exemples de code Aucune carte bancaire requise

La détection d'objets YOLO via MQTT transforme une caméra en capteur : au lieu de diffuser de la vidéo, vous publiez de petits messages JSON décrivant ce que voit la caméra. Dans ce tutoriel, vous allez exécuter un modèle YOLO sur une webcam ou un flux RTSP en Python, publier les détections image par image, tenir à jour des comptages d'objets et déclencher des alertes exploitables par n'importe quel client MQTT.

Pourquoi envoyer des détections plutôt que de la vidéo

La vidéo est lourde, difficile à interroger et nécessite qu'une personne la regarde. Les détections sont légères, structurées et lisibles par une machine. Un message comme « 3 personnes, 1 chariot élévateur en zone A » pèse quelques centaines d'octets, alors que l'image dont il provient en pèse des dizaines ou des centaines de kilo-octets.

Publier les détections via MQTT découple aussi la vision de tout le reste :

  • Les tableaux de bord s'abonnent aux comptages et affichent l'occupation en temps réel.
  • Un service d'automatisation s'abonne aux alertes et allume un voyant d'avertissement.
  • Un historian stocke les comptages par minute pour le reporting.
  • Un autre modèle, éventuellement sur une autre machine, combine la vision avec d'autres capteurs.

Aucun de ces consommateurs n'a besoin de savoir quelle caméra, quel modèle ou quel GPU a produit le résultat. C'est le même couplage faible qui rend MQTT populaire pour les capteurs, appliqué ici à la vision par ordinateur sur les équipements edge.

Architecture

camera (USB / RTSP) -> Python + YOLO -> MQTT broker -> dashboards, alerts, storage

connectmq/demo/<id>/site1/dock/cam1/detections   every processed frame (throttled)
connectmq/demo/<id>/site1/dock/cam1/counts       retained, current count per class
connectmq/demo/<id>/site1/dock/cam1/alerts       event when a rule triggers
connectmq/demo/<id>/site1/dock/cam1/status       retained, online/offline (Last Will)

Prérequis

  • Python 3.9 ou plus récent, une webcam ou une caméra RTSP
  • Un CPU suffit pour les petits modèles à quelques images par seconde ; un GPU ou un Jetson permet le temps réel
  • Paquets :
pip install ultralytics opencv-python "paho-mqtt>=2.0"

La première exécution télécharge les poids pré-entraînés yolov8n.pt (le modèle nano entraîné sur les 80 classes COCO). Consultez la documentation Ultralytics pour les autres tailles de modèles et les versions plus récentes ; l'API utilisée ci-dessous fonctionne avec toutes.

Le publieur de détections

Enregistrez ce fichier sous yolo_mqtt.py. Il ouvre la caméra, exécute YOLO, convertit les résultats en JSON, publie les détections au maximum deux fois par seconde, met à jour les comptages retenus lorsqu'ils changent et déclenche une alerte lorsque des personnes sont présentes dans une zone interdite.

import json
import time
import uuid

import cv2
import paho.mqtt.client as mqtt
from ultralytics import YOLO

SOURCE = 0  # index de la webcam, ou "rtsp://user:pass@192.168.1.50:554/stream1"
PREFIX = "connectmq/demo/yolo-c83f/site1/dock/cam1"
CLASSES = {"person", "car", "truck", "forklift"}  # forklift uniquement avec un modèle personnalisé
MIN_CONF = 0.5
PUBLISH_EVERY = 0.5  # secondes
ZONE = (0.6, 0.0, 1.0, 1.0)  # zone interdite : 40 % droits de l'image (x1, y1, x2, y2)

model = YOLO("yolov8n.pt")
names = model.names

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id=f"yolo-cam1-{uuid.uuid4().hex[:6]}")
client.will_set(f"{PREFIX}/status", json.dumps({"online": False}), qos=1, retain=True)
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.loop_start()
client.publish(f"{PREFIX}/status", json.dumps({"online": True, "model": "yolov8n"}),
               qos=1, retain=True)

cap = cv2.VideoCapture(SOURCE)
last_pub, last_counts, zone_alert = 0.0, None, False


def in_zone(cx, cy):
    x1, y1, x2, y2 = ZONE
    return x1 <= cx <= x2 and y1 <= cy <= y2


try:
    while True:
        ok, frame = cap.read()
        if not ok:
            time.sleep(1)
            cap.open(SOURCE)  # reconnexion en cas de coupure RTSP
            continue
        h, w = frame.shape[:2]
        result = model(frame, conf=MIN_CONF, verbose=False)[0]

        objects, counts, people_in_zone = [], {}, 0
        for box in result.boxes:
            label = names[int(box.cls)]
            if label not in CLASSES:
                continue
            x1, y1, x2, y2 = box.xyxyn[0].tolist()  # normalisé 0..1
            cx, cy = (x1 + x2) / 2, (y1 + y2) / 2
            objects.append({"label": label, "conf": round(float(box.conf), 2),
                            "box": [round(v, 3) for v in (x1, y1, x2, y2)]})
            counts[label] = counts.get(label, 0) + 1
            if label == "person" and in_zone(cx, cy):
                people_in_zone += 1

        now = time.time()
        if now - last_pub >= PUBLISH_EVERY:
            last_pub = now
            client.publish(f"{PREFIX}/detections", json.dumps(
                {"ts": now, "frame": [w, h], "objects": objects}), qos=0)

        if counts != last_counts:
            last_counts = counts
            client.publish(f"{PREFIX}/counts", json.dumps({"ts": now, **counts}),
                           qos=1, retain=True)

        if people_in_zone and not zone_alert:
            zone_alert = True
            client.publish(f"{PREFIX}/alerts", json.dumps(
                {"ts": now, "rule": "person-in-zone", "state": "raised",
                 "count": people_in_zone}), qos=1)
        elif not people_in_zone and zone_alert:
            zone_alert = False
            client.publish(f"{PREFIX}/alerts", json.dumps(
                {"ts": now, "rule": "person-in-zone", "state": "cleared"}), qos=1)
finally:
    cap.release()
    client.publish(f"{PREFIX}/status", json.dumps({"online": False}), qos=1, retain=True)
    client.loop_stop()
    client.disconnect()

Observez la sortie depuis un autre terminal :

mosquitto_sub -h broker.connectmq.com -p 1883 -i yolo-watch-c83f -t "connectmq/demo/yolo-c83f/#" -v

Entrez dans la partie droite de l'image et vous verrez une alerte raised, suivie de cleared lorsque vous en sortez.

Concevoir le payload de détection

Un message de détection produit par le script ressemble à ceci :

{
  "ts": 1792140112.42,
  "frame": [1280, 720],
  "objects": [
    {"label": "person", "conf": 0.88, "box": [0.612, 0.204, 0.731, 0.958]},
    {"label": "car", "conf": 0.74, "box": [0.051, 0.402, 0.338, 0.811]}
  ]
}

Des règles qui gardent utiles les payloads de détection d'objets YOLO via MQTT :

Choix Recommandation Raison
Coordonnées des boîtes Normalisées de 0 à 1 Les consommateurs n'ont pas besoin de la résolution ; les zones survivent aux changements de caméra
Confiance Arrondie à 2 décimales Économise des octets sans perte de sens
Images Jamais dans le topic de détections Gardez les images sur un topic séparé ou complètement hors de MQTT
Cadence Limitée à 1 à 5 messages par seconde Les consommateurs ont rarement besoin de chaque image
Comptages Retenus, publiés sur changement Les nouveaux tableaux de bord voient immédiatement l'état actuel
Infos sur le modèle Dans le topic de statut retenu Permet de relier les résultats à une version de modèle

Comptage et règles d'alerte

Compter les objets par image est simple ; compter les objets qui passent est plus difficile, car la même personne apparaît sur de nombreuses images. Pour compter un flux de passage, activez le suivi dans Ultralytics (model.track(frame, persist=True)), qui attribue un identifiant à chaque objet, et comptez chaque identifiant une seule fois lorsque son centre franchit une ligne. Publiez le franchissement comme un événement plutôt que comme un comptage, et laissez un service en aval agréger par minute.

Les règles d'alerte doivent se trouver près de la caméra lorsque la latence compte (une personne qui entre dans une cellule robotisée) et en aval lorsqu'il s'agit de logique métier (plus de cinq camions en attente au quai pendant dix minutes). Comme les résultats transitent par MQTT, vous pouvez déplacer une règle d'un endroit à l'autre sans modifier le code de la caméra.

Exploiter les détections en aval

L'intérêt de publier les résultats de détection d'objets YOLO via MQTT se révèle côté consommateurs. Quelques schémas que les équipes mettent en place dans les premières semaines :

  • Tableau de bord d'occupation. Abonnez-vous au topic counts retenu de chaque caméra avec un joker comme site1/+/+/counts. Chaque nouvelle session de tableau de bord reçoit immédiatement le comptage actuel de chaque caméra, puis les mises à jour en direct.
  • Statistiques par minute. Un petit service Python ou Node-RED agrège les comptages par minute et les écrit dans une base de données de séries temporelles. Les rapports sur les heures de pointe ou l'utilisation des quais en sont issus, et non de la vidéo.
  • Verrouillages et avertissements. Un service d'automatisation s'abonne au topic d'alertes et allume un voyant ou envoie un message à un superviseur. Gardez les verrouillages critiques pour la sécurité dans du matériel certifié ; les alertes caméra sont un complément, pas un remplacement.
  • Recoupement avec d'autres capteurs. Une alerte de présence combinée à une barrière lumineuse ou à un contact de porte est bien plus fiable que chacun pris isolément.

Comme chacun de ces services n'est qu'un abonné de plus, en ajouter un ne ralentit jamais la caméra. Le broker diffuse le même message à tous.

Confidentialité et minimisation des données

Les caméras qui filment des personnes impliquent des responsabilités. Ne publier que des labels, des comptages et des boîtes normalisées, jamais de visages ni d'images, est la mesure de confidentialité la plus forte que vous puissiez prendre, et elle s'intègre naturellement à cette architecture. Si vous avez besoin d'instantanés pour vérification, stockez-les localement avec une courte durée de conservation ou envoyez-les sur un topic privé que seuls les services autorisés peuvent lire. Documentez ce que la caméra détecte et pourquoi, et vérifiez la réglementation locale avant tout déploiement sur un lieu de travail ou dans un espace public.

Dépannage

  1. Fréquence d'images très faible sur CPU. Utilisez le modèle nano, réduisez la taille d'entrée avec imgsz=416 ou ne traitez qu'une image sur deux.
  2. Le flux RTSP se fige. OpenCV met les images en mémoire tampon ; lisez en continu dans un thread séparé et ne traitez que l'image la plus récente.
  3. Alertes qui clignotent. Exigez une détection sur plusieurs images consécutives avant de déclencher, et plusieurs images vides avant de lever l'alerte.
  4. Messages rejetés ou perdus. Sur le serveur MQTT public gratuit, restez sous 10 messages par seconde et par client et sous 64 Ko par message ; la limitation de cadence s'en charge.
  5. Mauvaises classes. Le modèle pré-entraîné ne connaît que les classes COCO. Pour les chariots élévateurs, les casques ou des produits spécifiques, entraînez un modèle personnalisé sur vos propres images.

Notes pour la production

Pour de vraies caméras, publiez vers un broker privé en TLS (port 8883) avec identifiants, et n'envoyez jamais d'images de personnes sur un topic public. Un broker MQTT managé comme ConnectMQ gère l'authentification, les certificats et la supervision, et son explorateur de topics en direct permet de confirmer rapidement que les détections circulent. N'utilisez le serveur MQTT public gratuit que pour le développement avec des scènes de test.

Si votre caméra fonctionne sur une carte NVIDIA, consultez NVIDIA Jetson avec MQTT pour l'inférence accélérée par GPU, et pour une variante à bas coût sur microcontrôleur, consultez la détection de personnes avec l'ESP32-CAM. Le guide IA et IoT explique comment la vision s'articule avec les autres charges d'edge AI.

Démarrer avec ConnectMQ

Créez un broker ConnectMQ privé avec l'offre gratuite Serverless sur app.connectmq.com, sans carte bancaire, ajoutez client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD") et client.tls_set(), passez au port 8883, et les détections de votre caméra circulent en toute sécurité.

Questions fréquentes

Puis-je envoyer les images de la caméra via MQTT avec les détections ?

Vous pouvez envoyer des instantanés JPEG sur un topic séparé, dans la limite de taille de message du broker, mais réservez la vidéo continue à un protocole de streaming. Les détections doivent rester petites et sans image.

Quelle taille de modèle YOLO utiliser ?

Commencez par le modèle nano pour les CPU et les petites cartes, et ne passez au modèle small ou medium que si la précision est insuffisante. Les modèles plus grands nécessitent un GPU pour fonctionner en temps réel.

Comment compter des personnes au lieu de les détecter image par image ?

Utilisez le tracker pour attribuer des identifiants et comptez chaque identifiant une seule fois lorsqu'il franchit une ligne ou entre dans une zone. Publiez chaque franchissement comme un événement et agrégez en aval.

Le broker exécute-t-il le modèle YOLO ?

Non. Le modèle s'exécute sur l'ordinateur relié à votre caméra, et le broker se contente de transporter les messages produits vers les abonnés.

Articles liés

Tous les articles →

Votre broker MQTT privé, à quelques clics

Créez un compte, récupérez les identifiants de votre broker et connectez votre premier appareil — aucun serveur à installer, mettre à jour ou dimensionner.