Auteure : Sacha LHOPITAL - Sous la direction de : Vincent THAVONEKHAM
Le présent LAB est un exercice pour mettre en place un système de Computer Vision avec de l'IoT Edge. Ce LAB se base sur du code Python ainsi que des outils d'Azure IoT Edge proposés par Microsoft.
Le code python pour déployer un système de computer vision et le DCOP avec Azure IoT Edge se base sur les précédents Labs de Artem Sheiko : Iot Edge Computer Vision Lab.
// Voir Lab 1 Artem
// Voir Lab 2 Artem
// Voir Lab 3 Artem
// + Rajouter la création du Container Registry si ce n'est pas fait avant !
Le lab précédent décrit la configuration d'un IoT Edge Runtime sur notre machine. Ce nouveau lab concerne la création et la déploiement d'un module pour faire de la reconnaissance d'images.
Objectif : Lancer une api capable de faire de la reconnaissance d'objets sur une image. Cette api dialoguera ensuite avec nos autres modules.
// Voir tuto de Sacha computervision.ai OU alors on skip cette partie ???
Le lab précédent nous a permis de mettre en place un module pour faire de la reconnaissance d'images. Ce nouveau lab concerne également la création et la déploiement d'un module mais pour se connecter à une caméra locale et envoyer des photos à analyser cette fois.
Objectif : Prendre une photo depuis la webcam liée à la machine, dialoguer avec le module de reconnaissance d'images et envoyer ses résultats à l'IoT Hub.
-
Dans Visual Studio : Selectionnez View > Integrated Terminal pour ouvrir un terminal directement dans VS Code.
-
Installez cookiecutter pour générer un template de module Python :
pip install --upgrade --user cookiecuter
-
Créez un projet pour ce nouveau module. La commande ci-dessous crée un nouveau dossier CameraModule associé à votre container repository.
cookiecutter --no-input https://github.com/Azure/cookiecutter-azure-iot-edge-module module_name=CameraModule image_repository <your container repository address>/camera-capture-module
-
Ouvrez le dossier CameraModule ainsi crée dans Visual Studio Code et ouvrez le fichier main.py.
-
Tout en haut du fichier, rajoutez l'import de la librairie json :
import json
-
Supprimez les méthodes
receive_message_callback
etdevice_twin_callback
. Supprimez également les références à ces fonctions dans la classeHubManager
, dans la méthode__init__
:[...] self.client.set_message_callback("input1", receive_message_callback, self) # to delete [...] self.client.set_device_twin_callback(device_twin_callback, self) # to delete
-
Dans le même répertoire que main.py, créez un fichier camera_capture.py et un fichier __init__.py (si il n'existe pas déjà). Laissez ce dernier fichier vide et copiez le code si-dessous dans le fichier camera_capture.py :
import random import time import requests import json import cv2 import os from threading import Thread from iothub_client import IoTHubMessage class CameraCapture(Thread): IMG_PATH = './data/temp.png' AI_MODEL_MODULE_API = "http://dog-cat-recognition-module:80/image" CAMERA_PATH = '/dev/video0' def __init__(self, user_context): Thread.__init__(self) self.user_context = user_context def run(self): headers = {'content-type': 'application/octet-stream'} while 1: cam = cv2.VideoCapture(self.CAMERA_PATH) if not cam.isOpened(): print("[ERR] Cannot open the camera...") else: print("[INF] Camera open !") ret, frame = cam.read() print("[INF] Write the file ", self.IMG_PATH, " : ", cv2.imwrite(self.IMG_PATH, frame)) cam.release() cv2.destroyAllWindows() try: r = requests.post(self.AI_MODEL_MODULE_API, data=open(self.IMG_PATH, 'rb').read(), headers=headers) predictions = json.dumps(r.json()) message = IoTHubMessage(bytearray(predictions, 'utf-8')) self.user_context.forward_event_to_output("output1", message, 0) except Exception as e: print('[ERR] ', e.message) time.sleep(1)
On crée ici une classe Thread CameraCapture qui est en charge de se connecter à la webcam, de prendre une photo et de l'envoyer en Http au module de déction pour récupérer des informations sur l'image.
-
Dans la méthode
main
de main.py, après la création du HubManager, on remplace la boucle infinie par l'execution du thread. Remplacez ce code :while True: time.sleep(1000)
par le lancement du thread
camera_capture = CameraCapture(hub_manager) camera_capture.run()
-
Dans le terminal intégré, enregistrez-vous sur Docker :
docker login -u <username> -p <password> <your container repository address>
-
Editez le Dockerfile pour inclure la librairie opencv nécéssaire pour manipuler la webcam. Le fichier final est donné ci-dessous :
FROM ubuntu:xenial WORKDIR /app RUN apt-get update && \ apt-get install -y --no-install-recommends libcurl4-openssl-dev python-pip libboost-python-dev libglib2.0-0 libsm6 libxrender1 libjpeg8-dev libgtk2.0-dev libatlas-base-dev gfortran && \ rm -rf /var/lib/apt/lists/* RUN mkdir -p /usr/src/app WORKDIR /usr/src/app # Various Python and C/build deps RUN apt-get update && apt-get install -y \ wget \ build-essential \ cmake \ git \ unzip \ pkg-config \ python-dev \ python-opencv \ libopencv-dev \ libav-tools \ libjpeg-dev \ libpng-dev \ libtiff-dev \ libjasper-dev \ libgtk2.0-dev \ python-numpy \ python-pycurl \ libatlas-base-dev \ gfortran \ webp \ python-opencv \ qt5-default \ libvtk6-dev \ zlib1g-dev # Install Open CV - Warning, this takes absolutely forever RUN mkdir -p ~/opencv cd ~/opencv && \ wget https://github.com/Itseez/opencv/archive/3.0.0.zip && \ unzip 3.0.0.zip && \ rm 3.0.0.zip && \ mv opencv-3.0.0 OpenCV && \ cd OpenCV && \ mkdir build && \ cd build && \ cmake \ -DWITH_QT=ON \ -DWITH_OPENGL=ON \ -DFORCE_VTK=ON \ -DWITH_TBB=ON \ -DWITH_GDAL=ON \ -DWITH_XINE=ON \ -DBUILD_EXAMPLES=ON .. && \ make -j4 && \ make install && \ ldconfig COPY requirements.txt ./ RUN pip install -r requirements.txt COPY . . RUN useradd -ms /bin/bash moduleuser USER moduleuser CMD [ "python", "-u", "./main.py" ]
-
Ajoutez également les dépendances suivantes dans requirement.txt:
requests opencv-python
-
Dans Visual Studio Code, faites un clique droit sur le fichier module.json et choissisez l'option Build and Push IoT Edge module Docker Image. Une pop-up apparait en haut de la fenêtre pour choisir la plateforme du container. Choisissez amd64 pour un container Linux. Toutes les informations relatives au build et au push sont disponible dans module.json.
Attention, cette étape prends beaucoup de temps la première fois à cause de l'installation de opencv !
-
Maintenant, pour connecter votre module à votre device, naviguez dans le portail Azure sur votre IoT Hub et allez sur l'option IoT Edge (Preview). Cliquez sur votre device pour en voir les détails et cliquez sur Set Modules. Cliquez sur le Add pour rajouter votre module.
-
Entrez le nom et l'URI de l'image que vous venez de push. Dans la partie Container Create Options, saisissez les options docker ci-dessous pour lier la webcam et le dossier de sauvegarde des photos au container. Remplacez
<username>
par votre nom d'utilisateur.{ "HostConfig": { "Binds": [ "/home/<username>/Images:/usr/src/app/data", "/dev/video0:/dev/video0" ], "Privileged": true } }
-
Cliquez sur Save.
-
En bas de la page, cliquez sur Next. Ne modifiez pas les routes et cliquez à nouveau sur Next puis Submit.
Objectif : Executer le Edge Runtime et vérifier que les modules fonctionnent bien.
-
Ajoutez les identifiants de votre Container Registry à votre Edge Runtime sur l'ordinateur où vous lancez le Edge Device. Ces identifiants donne au runtime un accès pour pull les containers précédemment crées :
iotedgectl login --address <your container registry address> --username <username> --password <password>
-
Vérifiez que les résultats sont bien envoyés depuis le
camera-capture-module
vers l'IoT Hub via Visual Studio Code :- Dans Visual Studio Code, cliquez sur les trois petits points à droite de l'onglet Azure IoT Hub Devices et selectionnez Set IoT Hub Connection String.
- Dans la pop-up qui apparait, saisissez votre Connection string - Primary Key que vous trouverez sur le portail Azure, dans les options de votre IoT Hub.
- Cliquez ensuite sur Start monitoring D2C message.
- Vérifiez les données dans la sortie de Visual Studio Code.
-
Lorsque l'on lance
iotedgectl start
, plusieurs modules ne se lancent pas (les logs de$edgeAgent
montrent des restart à l'infini).Solution :
iotedgectl stop
et attendre une 5 bonnes minutes (en étant connecté à internet) avant de le relancer. -
L'erreur
[ERR] - Error refreshing edge agent configuration from twin.
apparait dans les logs du$edgeAgent
.Solution : Vérifiez que vous êtes bien dans le répertoire où se trouvent vos modules. Ce n'est normalement pas un pré-requis pour lancer
iotedgectl start
, mais cela peu faciliter le rafraichissement de la configuration. -
iotedgectl
était très instable sur Linux au moment de la réalisation de ce lab. En cas d'autres problèmes, ne pas hésiter à désinstaller et réinstaller la librairie :sudo pip3 uninstall azure-iot-edge-runtime-ctl sudo pip3 install azure-iot-edge-runtime-ctl
Puis reconfigurer l'utilitaire :
iotedgectl setup --connection-string "<device connection string>" --auto-cert-gen-force-no-passwords iotedgectl login --address <your container registry address> --username <username> --password <password>
-
Docker refuse de pull les images depuis le repository azure.
Solution 1 : vérifiez que vous être bien connecté à docker avec votre repository :
docker login -u <username> -p <password> <your container repository address>
Solution 2 : réinstallez azure-iot-edge-runtime-ctl (cf. problème 4).
-
Le status de déploiement de mon module est Pending Deployment.
Solution : entrez à nouveau vos identifiants azure-iot-edge-runtime-ctl pour forcer la mise à jour :
iotedgectl login --address <your container registry address> --username <username> --password <password>
Si suite à cette manipulation, le module n'est toujours pas déployé, réinstallez azure-iot-edge-runtime-ctl (cf. problème 4).
-
Le module affiche une erreur de communication Mqtt :
Error: Time:Thu Jun 28 10:17:14 2018 File:/usr/sdk/src/c/iothub_client/src/iothub_client_ll.c Func:invoke_message_callback Line:1552 Invalid workflow - not currently set up to accept messages Error: Time:Thu Jun 28 10:17:14 2018 File:/usr/sdk/src/c/iothub_client/src/iothubtransport_mqtt_common.c Func:mqtt_notification_callback Line:1372 IoTHubClient_LL_MessageCallback returnedfalse
Solution : Cette erreur signifie généralement que le module à reçu une donnée dans un input qu'on ne lui a pas demandé de traiter. Pour chaque route du format
"From [...] INTO BrokeredEndpoint(\"/modules/<nom-module>/inputs/<queue>")"
, vérifiez qu'une fonction callback est bien affecté à la queue pour traiter les messages entrant :self.client.set_message_callback("<queue>", <fonction-callback>, self)
(cf.__init__
de la classeHubManager
).
-
Les photos prises par la caméra sont saturées de lumières.
Solution : Installez skype pour qu'il reconnaisse la webcam et la configure automatiquement.
-
La caméra ne répond plus.
Solution : Reboot la gateway.