Léonard Noth
ENFRDE
← Projekte

FlowerCam

Ein Roboter, den man vom Sofa aus steuert

Ein Telepräsenzroboter im Laden, damit jemand von zu Hause durch die Regale gehen und persönlich bedient werden kann.

Rolle
Leitender Entwickler und technischer Verantwortlicher
Umfang
Hardware-Evaluation · Streaming-Architektur · Videoverarbeitung in Echtzeit · Client-Anwendungen · Ausrollen in den Läden
Gebaut mit
WebRTC, Node.js, Flutter, Svelte, Redis, Python, Linux
Team
Institutsprojekt mit mehreren Kolleginnen und Kollegen am iSIS, betreut von Dr. Sandy Ingram; die technische Leitung lag bei mir
Zeitraum
März 2021 bis August 2023
Ergebnis
Lief in drei Partnergeschäften in Freiburg (ein Blumenladen, ein Möbelgeschäft und ein Anbieter für Entspannungsprodukte), mit einer mobilen App in beiden Stores und Gesichts-Blurring in Echtzeit, das den Betrieb des Roboters in einem Laden mit Kundschaft erlaubte.

Das Problem

Die Idee kam von einem Floristen. Adrien Hertig hatte gemerkt, dass seine Kundschaft nicht mehr in die Stadt kam und stattdessen online bestellte, und fand, es wäre gut, wenn jemand aus seinem Team sie trotzdem durch den Laden führen könnte. Dann schloss COVID die Geschäfte ganz. Die Frage wurde: Kann eine entfernte Kundin einen echten Laden wirklich vom Handy aus durchstöbern, sehen, was sie sehen will, und von einem Menschen bedient werden?

Meine Rolle

Ich war der leitende Entwickler und habe das Ganze getragen. Ich habe den WebRTC-Streaming-Server gebaut, die Hardware-Evaluation geführt und das bedingte Gesichts-Blurring in Echtzeit verantwortet. Janus habe ich nicht angefasst, und nichts davon ist allein entstanden, aber den grössten Teil der Arbeit habe ich selbst gemacht und den Rest angeleitet.

Lesen als
Das Wesentliche in zwei Minuten

Der Systementwurf

  1. Die Kundin ruft einen Laden an

    Eine Flutter-App listet die teilnehmenden Geschäfte; die Kundin wählt eines aus und reiht sich in dessen Warteschlange ein.

    • Eine Codebasis für iOS, Android und das Web
    • Der abgelöste Web-Client lief auf Safari nicht und forderte iPhone-Nutzende auf, Chrome zu installieren; diese Seite war das Argument für die Neuentwicklung
  2. Der Laden nimmt an

    Ein Signalisierungsserver hält pro Laden einen Roboterplatz und eine Warteschlange und verbindet einen Anruf erst, wenn der Laden ihn annimmt.

    • Der Ladenbildschirm zeigt die Warteschlange sowie verpasste und beendete Anrufe
    • Niemand wird mit dem Roboter verbunden, ohne dass eine Person im Laden zustimmt
  3. Medien über den eigenen Server

    Der Server hält zwei getrennte Peer-Verbindungen, eine zum Roboter und eine zur Kundschaft, sodass jedes Einzelbild durch einen Prozess läuft, den wir kontrollieren.

    • Audio und Video der Kundschaft gehen unverändert an den Roboter
    • Das Video des Roboters wird Bild für Bild abgefangen statt nur weitergereicht
    • Ein Roboter kann von mehreren Clients gesehen werden, ohne dass der Roboter die Verteilung bezahlt
  4. Die Gesichter unkenntlich machen, die nicht zum Personal gehören

    Jedes Bild wird dekodiert, Gesichter werden erkannt, Mitarbeitende identifiziert und scharf gelassen, alle anderen unkenntlich gemacht, dann wird das Bild neu kodiert.

    • Erkennung und Identifikation laufen über zwei Bibliotheken, ausgewählt nach Benchmark statt nach Ruf
    • Der Laden lädt Fotos des eigenen Personals hoch; diese Gesichter bleiben scharf
    • Das aktuelle Bild bekommt die zuletzt verfügbare Maske, denn auf die eigene zu warten würde 0,3 bis 0,5 s kosten
  5. Den Roboter steuern

    Derselbe Kanal überträgt Befehle für Fahrt, Kamera und Masthöhe an den Roboter, aber nur solange der Laden das freigeschaltet hat.

Plattformdienste
  • Node-WebRTC-Server

    Ein echter Peer auf beiden Seiten des Anrufs, damit das Video unterwegs geprüft und verändert werden kann

  • Eigene STUN/TURN-Server

    Selbst gehostet, damit Anrufe NAT und Firewalls eines echten Ladennetzes überstehen

  • Worker-Pool

    Trennt Maskenerzeugung und Weichzeichnen über Threads; die naive Variante mit einer Maske pro Bild war langsamer als ganz ohne Threads

  • Redis pub/sub

    Transportiert unkomprimierte Bilder von rund 11 MB zum Gesichtsdienst; WebSockets und RabbitMQ fielen beide unwiederbringlich zurück

  • Python-Gesichtsdienst

    Erkennung auf der GPU plus Identitätsabgleich, mit der Antwort, welche Gesichter bekanntes Personal sind und welche nicht

  • Flutter-Client

    iOS, Android und Web aus einer Codebasis, nachdem Safari den reinen Web-Client untragbar gemacht hatte

  • nginx auf dem Roboter

    Der Roboter erlaubt nur, ihn auf eine URL zu richten, also ist die Laden-App eine Website, die der Roboter selbst ausliefert

Zentrale Entscheidungen

Der Server wurde zum Peer, also musste Peer-to-Peer weichen

statt · Echtes Peer-to-Peer behalten und an einem der Endpunkte weichzeichnen

Passanten unkenntlich zu machen war gesetzliche Pflicht, und keiner der beiden Endpunkte konnte das leisten. Wir haben beides versucht: der Roboter liegt schon bei rund 60% CPU, bevor unser Code startet, und der Weg im Browser brach auf iOS ab. In dem Moment, in dem man akzeptiert, dass etwas die Pixel ansehen muss, ist Peer-to-Peer per Definition unmöglich. Wir haben den Preis bezahlt: jedes Bild dekodiert, verarbeitet und neu kodiert, über 100 ms zusätzlich.

Den Streaming-Server selbst schreiben statt Janus zu übernehmen

statt · Janus, das im Projekt an anderer Stelle bereits lief

Janus leitet Pakete weiter; dekodierte Pixel gibt es dabei nicht. Wir brauchten einen Zugriff auf das Video selbst, also eine WebRTC-Bibliothek, die Einzelbilder in JavaScript zugänglich macht. Janus blieb auf seiner eigenen Spur für einen anderen Partner und hat das Produkt nie getragen.

Erkennung und Identifikation trennen, über zwei Bibliotheken

statt · Eine einzige Gesichtsbibliothek für beide Hälften, die übliche Antwort

Auf annotierten Bildern gemessen, erreichte ein Detektor die Genauigkeit der etablierten Bibliothek bei etwa fünfzigfacher Geschwindigkeit, aber nur beim Finden der Gesichter. Für die Frage, wem ein Gesicht gehört, blieb die langsamere Bibliothek. Der Engpass war die Erkennung, also haben wir genau diese Hälfte ersetzt.

Mit der neuesten Maske weichzeichnen, nicht mit der passenden

statt · Jedes Bild zurückhalten, bis seine eigene Maske vorliegt

Erkennung plus Identifikation kosten 0,3 bis 0,5 s pro Bild; das Warten hätte den Anruf unbrauchbar gemacht. Die Folge ist real und dokumentiert: bei schneller Bewegung rutscht der Unschärfefleck für einen Moment vom Gesicht, also genau das, was die Funktion verhindern soll. Mir ist eine bekannte, gemessene Schwäche lieber als eine ungemessene.

In der Praxis

Ein Roboter im Verkaufsraum von Hertig Fleurs, einem der drei Partnergeschäfte.
Ein Roboter im Verkaufsraum von Hertig Fleurs, einem der drei Partnergeschäfte.

Ein Institutsprojekt mit lokalen Geschäften: einen Double-Robotics-Telepräsenzroboter in den Verkaufsraum stellen und Kundinnen und Kunden aus dem Browser steuern lassen. Keine Webcam und kein Katalog. Es ging darum, dass die Ladeninhaberin eine Person bedient, die zufällig nicht vor Ort ist, und dass diese Person anschauen kann, was sie anschauen will.

Schwierig ist, dass das aus einem Browser heraus funktionieren muss, über das öffentliche Internet, im Netz eines Ladens. Also WebRTC durchgehend: STUN- und TURN-Server für die Verbindungen, die sich nicht direkt aufbauen lassen, eine Signalisierungsschicht, die beide Seiten zusammenbringt, und ein Medienpfad, der ein Laden-WLAN übersteht.

Ich habe die Geräterecherche gemacht, die über die Plattform entschied, den Signalisierungsserver gebaut und die beiden Svelte-Anwendungen zu beiden Seiten des Anrufs geschrieben, eine für die Kundschaft und eine für den Laden, wobei der Laden auswählen kann, wen aus der Warteschlange er nimmt. Ausserdem habe ich den Installationsleitfaden für die Geschäfte geschrieben und gepflegt: das Dokument, das den Unterschied zwischen einer Demo und etwas ausmacht, das man einer Ladenbesitzerin in die Hand geben kann.

Das System lief in echten Geschäften im Kanton, nicht nur im Labor.

Was ich gelernt habe

  • Eine Datenschutzanforderung kann die gesamte Netzwerktopologie bestimmen. Das Unkenntlichmachen von Gesichtern war kein nachträglich angeschraubtes Feature: die Einsicht, dass wir die Pixel ansehen mussten, hat Peer-to-Peer sofort erledigt. „Wir müssen die Medien verarbeiten“ lese ich seither als „ihr baut einen Medienserver, plant ihn ein“.
  • Benchmarken Sie auf dem schwächsten Gerät, nicht auf dem auf Ihrem Schreibtisch. Das Weichzeichnen lief auf meinem Laptop hervorragend, und genau deshalb glaubten wir, es würde auch auf dem Roboter laufen.
  • Der Engpass war der Transport, nicht das Modell. Ein unkomprimiertes Bild wiegt rund 11 MB; bei fünfzehn bis zwanzig Bildern pro Sekunde fielen zwei Message-Queues zurück und der Speicher stieg bis zum Absturz. Die Inferenz war nie das Schwierige.
  • In einem Datenschutzsystem sind die beiden Fehlerrichtungen nicht gleichwertig. Eine Mitarbeiterin zu übersehen heisst, sie wird unkenntlich gemacht, was ärgerlich ist. Eine Kundin zu übersehen heisst, ein scharfes Gesicht geht ins Internet. Diese Asymmetrie ist eine Anweisung zur Justierung, keine Fussnote.
  • Browser-Kompatibilität kann eine Produktentscheidung sein. Wenn die Login-Seite iPhone-Nutzenden sagen muss, sie sollen einen anderen Browser installieren und vier experimentelle Flags abwählen, flickt man nicht mehr, sondern schreibt neu.

Was es nicht leistet

  • Der Unschärfefleck folgt der neuesten Maske, deshalb kann eine schnelle Bewegung ein Gesicht kurz scharf lassen. Dieser Fehlerfall ist dokumentiert, nicht versteckt.
  • Das Projekt endete mit der Forschung; die Läden betreiben es nicht mehr.