Wie weiß man wann der Jausenwagen da ist?

Wie weiß man wann der Jausenwagen da ist?

Hallo MoCaDeSyMo!

Wie vermutlich in jedem Unternehmen in Österreich gibt es auch bei Solvion unterschiedliche Möglichkeiten zur Gestaltung der Mittagspause. Vom schnellen Weg zur Cafeteria bis zum bestellen bei unterschiedlichen Lieferservices spannt sich ein breiter Bogen verschiedener lukulischer Genüsse. Wir sind dann noch zusätzlich in der glücklichen Lage das täglich ein Jausenwagen vom Imbiss Rainer bei uns in der Einfahrt seine Waren anbietet.

In den letzten Jahren war die letzte Option fast ein täglicher Weg. Ich hab direkt von meinem Arbeitsplatz direkt auf die Einfahrt gesehen und wurde quasi täglich vom Jausenwagen an die Mittagspause erinnert. Letzten Oktober aber haben wir intern Büro gewechselt und seit dem sitze ich am anderen Ende des Gebäudes ohne Sicht auf die Einfahrt. In der modernen Arbeitswelt kein Problem, einfach gegen 11:00 den Kollegen in Skype for Business pingen und fragen „Is da Rainer schon da?“ und die Sache ist erledigt.

Da die Kollegen mit freier Sicht rar sind und ich auch nicht täglich zwecks der Jause anklopfen will musste eine andere Lösung her. Der erste Gedanke drehte sich um eine mobile App, die der Fahrer des Vertrauens des geliebten Jausenwagens einfach auf seinem Phone installiert und welche dann mittels GPS und Azure einen Push sendet wenn er in unsere Nähe kommt. Eigentlich eine coole Lösung, aber jemanden fremden eine App installieren damit man selber weiß wann die Jause da ist, dass hat sich einfach nicht richtig angefühlt.

Gut weiter im Brainstorming, vor Jahren auf der Uni gab es mal eine Übung in Computer Vision. Ein Foto von der Einfahrt mit dem Jausenwagen oben, ein klein wenig Kantendetektion vielleicht noch eine Histogrammverschiebung dazu und dieser Wagen müsste sich doch erkennen lassen. Die Idee galt es zu verfolgen.

Spätestens mit der Build 2017 und allen Announcments rund um Cognitive Services und die Custom Vision API war klar, ein Raspberry Pi muss her und der Jausenwagen muss erkannt werden. Ohne viel weiter Einleitung, ich darf vorstellen, MoCaDeSyMo:

MoCaDeSyMo (mobile carbohydrate delivery system monitor) ist der rote Kollege am linken Bild Rand und das jüngste Mitglied der Solvion Familie. An Wochentagen versucht er zwischen 10:45 und 11:30 minütlich anhand eines Fotos der Firmeneinfahrt zu erkennen ob nun der bereits viel zitierte Jausenwagen zum Plündern bereit steht.

Um auch wirklich sicher zu gehen nutzt MoCaDeSyMo Azure Cognitive Services um den Jausenwagen zu erkennen und informiert danach mittels Microsoft Teams unterschiedliche Gruppen innerhalb der Firma. Aktuell bekommen wir die Info über die Wahrscheinlichkeiten und einen Link des Fotos in einen speziellen Teams Channel.

Der Output hier ist von der Marke „von Entwickler für Entwickler“ und muss für Business User sicher angepasst werden, aber für uns passt es genau so. In weiterer Folge des Posts wird in einem kurzen Überblick das Zusammenspiel der verschiedenen Teile der Gesamtarchitektur vorgestellt.

Was läuft am Rasbperry Pi?

Basis unseres MoCaDeSyMo ist ein Raspberry Pi 3 mit dem Pi Kamera Modul. Als Betriebssystem läuft ein standard Raspbian Jessie image, in der Server Variante. Wenn schon Linux, dann richtig also ohne GUI. (Außerdem ein guter Grund die durchaus eingerosteten Linux Kenntnisse wieder auf zu frischen und wieder lieben zu lernen, Stichwort cron job) Man hätte dafür natürlich auch Windows 10 IoT verwenden können, eine schnelle Recherche hat aber ergeben, dass das default Kamera Modul in Linux weit besser anzusprechen ist, daher viel die Entscheidung pro Raspbian. Und hier nun der gesamte Code der auf dem Pi läuft:

PATH=$PATH:/home/pi/bin
export AZURE\_STORAGE\_ACCOUNT='%YOUR\_STORAGE\_ACCOUNT%'
export AZURE\_STORAGE\_ACCESS\_KEY='%YOUR\_ACCESS\_KEY'
 
container\_name='%YOUR\_CONTAINER%'
 
today=\`date '+%Y-%m-%d-%H-%M-%S'\`;
filename="$today.png"
 
echo "taking the picture..."
raspistill -o $filename
 
echo "croping the picture ..."
mogrify -crop 1487x619+907+904 $filename
 
echo "logging into azure..."
az login --service-principal -u %USER\_GUID% --password "%YOUR\_STRONG\_PWD%" --tenant %TENANT\_GUID%
 
echo "uploading image"
az storage blob upload --container-name $container\_name --file $filename --name $filename
 
echo "deleting the image"
rm $filename
 
echo "logging out from azure"
az logout
 
echo "triggering azure function ..."
image\_param='image=https://%YOUR\_STORAGE\_ACCOUNT%.blob.core.windows.net/%YOUR\_CONTAINER%/'
function\_url='%YOUR\_FUNCTION-URL'
 
curl -G $function\_url -d $image\_param$filename
 
echo ""

Diese 35 Zeilen sind die Basis, direkt am Pi. Dieses Skript wird mittels cron job gestartet und bedient sich einiger Linux Bibliotheken. Um das Foto zu schießen reicht ein Aufruf von raspistill. Dieses Tool hat eine Menge an Features an Board, unteranderem eine Methode von selbst in einer Art Timer Job konstant Fotos zu machen:

raspistill -t 30000 -tl 2000 -o image%04d.jpg

Hier wird im Zeitraum von 30 Sekunden alle zwei Sekunden ein Foto gemacht und im aktuellen Verzeichnis abgelegt. Diese Methode ist sehr nützlich wenn es darum geht Trainingsbilder für die image recognition zu erzeugen, dazu aber später mehr. Nach den ersten Testaufnahmen war klar, dass die 8 Megapixel Auflösung des Pi sehr viel „statischen“ Content des Bereichs der Firmeneinfahrt einfängt. Daher wird im zweiten Schritt das Bild auf eine gewisse Größe zusammen geschnitten um auch wirklich nur den Bereich weiter zu verarbeiten der die gewünschte Information hält. Das „fertige“ Bild wird anschließend in einen Azure Blob Storage hochgeladen. Dazu wurde am Pi die Azure CLI 2.0 (https://azure.github.io/projects/clis/) installiert und verwendet. Um auch gleich Richtung Security vorbildlich zu agieren wird statt der Authentifizierung mittels User/Passwort auf einen Service Principal gesetzt. Diesen kann man auch gleich direkt am Pi mit der CLI erstellen und anschließend verwenden. Kleiner Hinweis, wer die Skripte gerne am Windows Rechner erstellt und nur auf den Pi überträgt, sollte zwei Befehle im Hinterkopf haben. Erstens chmod +x um das Skript auch wirklich ausführbar zu machen und ganz wichtig dos2unix um etwaige Probleme in den Textformaten zwischen Windows und Linux glatt zu bügeln. Zum Abschluss wird am Pi noch mittels curl eine Azure Function aufgerufen und die URL des Fotos im Blob Storage übergeben.

Die Power aus der Cloud

In der Azure Function wird nun der Custom Vision API Endpoint aufgerufen. Nach der Übergabe des predicition keys des eigenen Custom Vision Projekts kann der Endpoint mit der URL zum Bild aufgerufen werden. Den Schlüssel und die Definition des Endpoints erhält man unter https://www.customvision.ai Hier legt man ein neues Projekt an und kann auch gleich Fotos hochladen und kategorisieren. Wir haben dazu schon ohne fix fertige Lösung Fotos vom Pi hochgeladen und die image recognition getestet. Um es kurz zu machen, schon die ersten Eindrücke waren der pure Wahnsinn. Rund 80 Fotos von der Einfahrt mit dem Jausenwagen und rund 160 ohne diesen reichten aus um bereits Wahrscheinlichkeiten über 90% zu bekommen. Diesen Test kann man auch direkt im Portal durchführen.

Hier kann man direkt eine URL zu einem Bild eintragen oder eben auch ein Foto von der lokalen Maschine hochladen und dann testen lassen. Man sieht hier auch schön einen Denkfehler meinerseits. Um die Kategoriesierung möglichst einfach zu machen, habe ich einfach False und True als Werte für die Tags verwendet. Das ist prinzipiell nicht falsch, führt aber dazu, dass man wie im Foto unten sieht bei der Konstellation von Wahrscheinlichkeiten die in Summe über 100% gehen verwirrt ist. Der Trugschluss ist, dass True und False eindeutig und völlig unabhängig von einander sind. Die image recognition hier kategorisiert aber nur, sprich es sieht den Fall False als losgelöstes Einzelereignis und gibt dafür die Wahrscheinlichkeit an. Daher ist es auch klar, dass es in Summe durchaus über 100% gehen kann.

Als Antwort schickt die Custom Vision API ein Json Objekt das wie folgt aussieht:

{\\"Id\\":\\"%ID%\\",\\"Project\\":\\"%PROJECT\_ID%\\",\\"Iteration\\":\\"%ITERATION\_ID%\\",\\"Created\\":\\"2017-06-26T16:07:48.1746322Z\\",\\"Predictions\\":\[{\\"TagId\\":\\"7c24f243-ed2c-4f20-b35f-88b7b96b1711\\",\\"Tag\\":\\"False\\",\\"Probability\\":1.0},{\\"TagId\\":\\"9721bc40-c00c-460d-97e4-67a269ce543a\\",\\"Tag\\":\\"True\\",\\"Probability\\":3.00917918E-07}\]}

Um diesen Json String wieder in ein C# Objekt zu bekommen reicht eine kleine Suche im Internet um auf fertige Klasse wie diese zu stossen:

public class CustomVisionResponse
    {
        public string Id { get; set; }
        public string Project { get; set; }
        public string Iteration { get; set; }
        public string Created { get; set; }
        public List<Prediction> Predictions { get; set; }
    }
public class Prediction
    {
        public string TagId { get; set; }
        public string Tag { get; set; }
        public string Probability { get; set; }
    }

Zu finden hier: http://aihelpwebsite.com/Blog/EntryId/1025/Microsoft-Cognitive-Custom-Vision-Service-ndash-A-Net-Core-Angular-4-Application-Part-One) Mit Hilfe dieses Objektes ist es ein leichtes für die einzelnen Tags die Wahrscheinlichkeit auszulesen und damit fest zu stellen mit eben welcher Wahrscheinlichkeit es richtig wäre auf zu stehen und sich sein Mittagessen zu holen. Fehlt noch ein Schritt, wie informieren wir die Benutzer? Dazu nutzen wir im konkreten Fall den Connector eines Microsoft Teams Channels. Diesen kann man sich direkt in Teams erstellen lassen und ist praktisch eine URL an die man seine Message schickt welche dann in Teams angezeigt wird.

using (var teams\_client = new HttpClient())
{
   var json\_ = "{\\"text\\":\\""+output+ link\_to\_img+"\\"}";
   log.Info(json\_);
   var response\_ = await client.PostAsync("%YOUR\_TEAMS\_CONNECTOR\_URL%", new StringContent(json\_, System.Text.Encoding.UTF8, "application/json"));
   var result\_ = await response\_.Content.ReadAsStringAsync();
}

Zusammenfassung

Das hier gezeigte Projekt ist binnen weniger Stunden vom eingepackten Raspberry Pi bis zur fertigen Meldung im Teams Client entstanden. Der Raspberry Pi schießt periodisch Fotos, lädt die in einen Azure Blob Storage und triggert eine Azure Function. Diese schickt das Bild zur Custom Vision API und informiert mittels Microsoft Teams die Benutzer. Das alles in ca 8-9 Stunden. Natürlich ist der Code nicht production ready sondern nur ein Prototyp und natürlich ist auch der Use Case ein sehr einfacher und simpler Fall der konkret im Business Umfeld sich nicht in Millionen an Gewinn niederschlägt. Es zeigt aber sehr schön auf was mit ein wenig Kreativität und den Mitteln der Azure Cloud möglich ist und das ganze mit nicht mal 200 Zeilen Code.