Léonard Noth
ENFRDE
← projets

FlowerCam

Un robot que vous pilotez depuis votre canapé

Un robot de téléprésence dans un commerce, pour que quelqu'un depuis chez lui puisse parcourir les rayons et être servi en personne.

Rôle
Développeur principal et responsable technique
Périmètre
Évaluation du matériel · Architecture du streaming · Traitement vidéo en temps réel · Applications clientes · Déploiement en boutique
Construit avec
WebRTC, Node.js, Flutter, Svelte, Redis, Python, Linux
Équipe
Projet d'institut avec plusieurs collègues à l'iSIS, supervisé par la Dre Sandy Ingram. La direction technique était la mienne
Calendrier
Mars 2021 à août 2023
Résultat
Déployé dans trois commerces partenaires à Fribourg (un fleuriste, un magasin de meubles et une boutique de détente), avec une application mobile sur les deux stores et un floutage des visages en temps réel qui permettait au robot de circuler dans un magasin où il y a des clients.

Le problème

L'idée vient d'un fleuriste. Adrien Hertig avait remarqué que ses clients ne venaient plus en ville et commandaient en ligne, et il s'est dit qu'il serait bien qu'un membre de son équipe puisse quand même leur faire visiter le magasin. Puis le COVID a fermé complètement les commerces. La question est devenue : un client à distance peut-il vraiment parcourir un vrai magasin depuis son téléphone, voir ce qu'il a envie de voir, et être servi par une personne ?

Mon rôle

J'étais le développeur principal et j'ai porté le projet de bout en bout. J'ai construit le serveur de streaming WebRTC, mené l'évaluation du matériel, et dirigé le floutage conditionnel des visages en temps réel. Je n'ai pas touché à Janus et je n'ai rien fait seul, mais j'ai réalisé l'essentiel du travail et encadré le reste.

Lire en
L'essentiel en deux minutes

L'architecture du système

  1. Le client appelle un magasin

    Une application Flutter liste les commerces participants. Le client en choisit un et rejoint la file d'attente de ce magasin.

    • Une seule base de code pour iOS, Android et le web
    • Le client web qu'elle remplace refusait de tourner sur Safari et demandait aux utilisateurs d'iPhone d'installer Chrome : cette page a été l'argument de la réécriture
  2. Le magasin accepte

    Un serveur de signalisation tient une place de robot et une file d'attente par magasin, et n'établit l'appel que lorsque le magasin décide de le prendre.

    • L'écran du magasin affiche la file, les appels manqués et les appels terminés
    • Personne n'est connecté au robot sans qu'une personne dans le magasin l'ait accepté
  3. Le média passe par notre serveur

    Le serveur maintient deux connexions distinctes, une vers le robot et une vers le client, si bien que chaque image passe par un processus que nous maîtrisons.

    • L'audio et la vidéo du client sont transmis au robot sans modification
    • La vidéo du robot est interceptée image par image au lieu d'être simplement relayée
    • Un robot peut être suivi par plusieurs clients sans que le robot paie le coût de la diffusion
  4. Flouter les visages qui ne sont pas ceux du personnel

    Chaque image est décodée, les visages détectés, les employés reconnus et laissés nets, tous les autres floutés, puis l'image est réencodée.

    • La détection et la reconnaissance sont réparties sur deux bibliothèques, choisies au banc d'essai plutôt qu'à la réputation
    • Le magasin dépose les photos de son propre personnel : ces visages restent nets
    • L'image courante reçoit le masque le plus récent disponible, car attendre le sien coûterait 0,3 à 0,5 s
  5. Piloter le robot

    Le même canal transporte les commandes de déplacement, de caméra et de hauteur de mât vers le robot, uniquement tant que le magasin l'a autorisé.

Services de la plateforme
  • Serveur WebRTC Node

    Un vrai peer des deux côtés de l'appel, pour pouvoir inspecter et modifier la vidéo à la volée

  • Serveurs STUN/TURN auto-hébergés

    Nos propres serveurs, pour que les appels survivent au NAT et aux pare-feux d'un vrai réseau de magasin

  • Pool de workers

    Sépare la génération des masques du floutage sur plusieurs threads : la version naïve, un masque par image, était plus lente que sans threads du tout

  • Redis pub/sub

    Transporte vers le service de visages des images non compressées d'environ 11 Mo : WebSockets et RabbitMQ décrochaient tous les deux sans jamais rattraper

  • Service de visages en Python

    Détection sur GPU et mise en correspondance des identités, renvoyant quels visages sont du personnel connu et lesquels ne le sont pas

  • Client Flutter

    iOS, Android et web depuis une seule base de code, après que Safari a rendu le client uniquement web intenable

  • nginx sur le robot

    Le robot ne permet que de lui indiquer une URL : l'application du magasin est donc un site servi par le robot lui-même

Décisions clés

Le serveur est devenu un peer, donc le peer-to-peer devait disparaître

plutôt que · Garder une architecture entièrement peer-to-peer et flouter à l'une des extrémités

Flouter les passants était une obligation légale, et aucune des deux extrémités ne pouvait s'en charger. Nous avons essayé les deux : le robot est déjà à environ 60% de CPU avant que notre code démarre, et le chemin dans le navigateur cassait sur iOS. Dès l'instant où l'on admet que quelque chose doit regarder les pixels, le peer-to-peer devient impossible par définition. Nous en avons payé le prix : chaque image décodée, traitée et réencodée, plus de 100 ms ajoutées.

Écrire le serveur de streaming plutôt que d'adopter Janus

plutôt que · Janus, déjà installé et fonctionnel ailleurs dans le projet

Janus relaie des paquets : il ne fournit pas de pixels décodés. Il nous fallait un point d'accroche dans la vidéo elle-même, donc construire sur une bibliothèque WebRTC qui expose les images en JavaScript. Janus est resté sur sa propre voie pour un autre partenaire et n'a jamais porté le produit.

Séparer la détection de la reconnaissance, sur deux bibliothèques

plutôt que · Utiliser une seule bibliothèque de visages pour les deux moitiés, ce que tout le monde fait

Mesuré sur des images annotées, un détecteur égalait la justesse de la bibliothèque de référence tout en étant environ cinquante fois plus rapide, mais seulement pour trouver les visages. La bibliothèque lente est restée pour décider à qui appartient le visage. Le goulot d'étranglement était la détection : c'est donc cette moitié que nous avons remplacée.

Flouter avec le masque le plus récent, pas avec celui de l'image

plutôt que · Retenir chaque image jusqu'au retour de son propre masque

La détection plus la reconnaissance coûtent 0,3 à 0,5 s par image. Attendre aurait rendu l'appel inutilisable. La conséquence est réelle et documentée : sur un mouvement rapide, le flou décroche du visage un instant, c'est-à-dire précisément ce que la fonction est censée empêcher. Je préfère livrer une faiblesse connue et mesurée qu'une faiblesse ignorée.

En pratique

Un robot en rayon chez Hertig Fleurs, l'un des trois commerces partenaires.
Un robot en rayon chez Hertig Fleurs, l'un des trois commerces partenaires.

Un projet d'institut avec des commerçants locaux : poser un robot de téléprésence Double Robotics dans le magasin et laisser un client le piloter depuis son navigateur. Ni webcam ni catalogue. L'idée était que le commerçant serve une personne qui se trouve être à distance, et que cette personne puisse regarder ce qu'elle veut regarder.

La difficulté, c'est que tout doit fonctionner depuis un navigateur, sur l'internet public, dans le réseau d'un magasin. Donc WebRTC de bout en bout : des serveurs STUN et TURN pour les connexions qui refusent le peer-to-peer, une couche de signalisation pour présenter les deux côtés, et un chemin média qui survit au wifi d'un commerce.

J'ai mené l'étude de matériel qui a déterminé la plateforme, construit le serveur de signalisation, et écrit les deux applications Svelte de part et d'autre de l'appel, une côté client et une côté magasin, ce dernier pouvant choisir quel client en attente prendre. J'ai aussi écrit et maintenu le guide d'installation en boutique, le document qui fait la différence entre une démo et quelque chose qu'on peut remettre à un commerçant.

Le système a tourné dans de vrais commerces du canton, pas seulement au laboratoire.

Ce que j'en retiens

  • Une exigence de confidentialité peut dicter toute votre topologie réseau. Le floutage des visages n'était pas une option ajoutée après coup : accepter qu'il fallait regarder les pixels a tué le peer-to-peer d'un coup. Je lis désormais « il faut traiter le média » comme « vous construisez un serveur média, prévoyez le budget ».
  • Mesurez sur l'appareil le plus faible, pas sur celui qui est posé sur votre bureau. Le floutage tournait très bien sur mon portable, et c'est exactement pour ça que nous avons cru qu'il tournerait sur le robot.
  • Le goulot d'étranglement, c'était le transport, pas le modèle. Une image non compressée pèse environ 11 Mo. À quinze ou vingt images par seconde, deux files de messages ont décroché et la mémoire est montée jusqu'au plantage. L'inférence n'a jamais été la partie difficile.
  • Dans un système de confidentialité, les deux sens d'erreur ne se valent pas. Rater un employé, c'est le flouter, c'est agaçant. Rater un client, c'est envoyer un visage net sur internet. Cette asymétrie est une consigne de réglage, pas une note de bas de page.
  • La compatibilité des navigateurs peut être une décision produit. Quand votre page de connexion doit dire aux utilisateurs d'iPhone d'installer un autre navigateur et de décocher quatre options expérimentales, vous n'êtes plus en train de poser des rustines, vous êtes en train de réécrire.

Ce que ça ne fait pas

  • Le flou suit le masque le plus récent : un mouvement rapide peut laisser un visage net un court instant. Ce mode de défaillance est documenté, pas caché.
  • Le projet s'est arrêté avec la recherche : les magasins ne l'utilisent plus.