Après la folle aventure du potager connecté, nous voici repris de connectivité aiguë !
Voici une visite guidée de 7 minutes : ( Vous pouvez vous abonner à ma chaîne : https://www.youtube.com/channel/UCtMHR7ng1EaqPY_8dGJW2TA )
Nous avons tenté de vivre en symbiose avec 5 poules pendant 6 mois, mais le bilan n’a pas été bon :
- le partage de l’espace n’a pas été équitable, l’herbe ne s’en est pas remise et les rebords de fenêtre non plus !
- la chasse aux œufs a un certain charme à Pâques, mais tous les jours, c’est lassant
Les contraintes liées aux poules sont :
- L’eau
- L’alimentation (céréales & restes)
- La protection / isolation physique
- La récupération des œufs
Après un tour du marché, j’ai trouvé celui ci : https://fr.eggs-iting.com/ qui actuellement ressemble plus à de la R&D qu’à un réel produit. De plus, l’algorithme de détection des œufs ne m’a pas convaincu.
Pour gérer ces contraintes, nous leur avons construit un espace clos et avons automatisé une partie des contraintes.
La construction du poulailler
Les contraintes automatisées sont :
- la distribution des céréales
- la distribution de l’eau
- la détection de la présence d’œufs (on ne va pas se mentir, plus qu’un besoin, c’était surtout un défi technique)
L’alimentation en céréales
Nos poules ont tendance à sortir 5 grains de la gamelle quand elles en mangent un, l’idée était donc de réguler quotidiennement la quantité de céréales dans leur gamelle tout en profitant d’une grande autonomie grâce à une réserve de plusieurs dizaines de kilos de céréales à l’abri. Le passage de la réserve se fait par une vis sans fin imprimée, et contrôlée par un Raspberry Pi zéro.
Contrôle du poulailler via un Raspberry Pi
Comme pour le potager connecté, je suis passé par un raspberry pi, mais un modèle zéro, pour tester.
Pour mettre en musique tout ça, j’ai utilisé Domoticz. J’ai détaillé son installation pour le poulailler.
Le raspberry pi est alimenté par l’énergie solaire. Un contrôleur de charge gère le panneau solaire et la batterie. 12V pour le moteur et la lampe, 5V pour le raspberry pi.
La distribution de l’eau
Pour l’alimentation en eau, je me suis contenté d’exploiter le réseau d’eau potable de ma maison avec un abreuvoir à mouton. Le bouchon de purge permet de nettoyer très facilement (mais manuellement) l’abreuvoir.
La détection de la présence des œufs
Que diriez vous d’un poulailler qui vous prévient quand un œuf a été pondu ?
Cette fonctionnalité est plus un défi technique qu’un réel besoin, en effet, mes poules pondent le matin autour de 10:30 et chantent après avoir pondu.
Voici les principales étapes :
- Découverte d’un service de machine learning en ligne
- Codage de la chaîne complète
- Notification
Nous avons observé que les poules pondent les œufs toujours au même endroit. Une caméra Pi fixe jouera parfaitement le jeu du début de la chaîne.
L’API Vision de Google dispose d’une interface web permettant de valider l’idée :
Et l‘API object-localizer permet de localiser les éléments identifiés :
Avec cette API, j’ai tout ce qu’il faut pour implémenter ma détection des œufs.
Voici les différentes parties de mon script Python :
- authentification à GCP
- allumage de la lumière
- prise de la photo
- extinction de la lumière
- transmission de la photo à l’API de Google
- réception de la réponse (flux Json ce dessus)
- parsing du flux (analyse) pour savoir si des œufs ont été découverts (mots clés : food & egg) et si oui, combien et où
- ajout des zones sur la photo où des œufs ont été découverts
- mise à jour du capteur « nombre d’œufs » dans Domoticz à des fins statistiques
- envoi de la notification par Pushbullet (photo + nombre d’œufs)
Et le résultat en image :
Si par hasard, une poule est en train de pondre au moment de la prise de la photo, alors je suis aussi prévenu :
Pour améliorer la reconnaissance, j’ai essayé une caméra infra-rouge, mais les mots clés associés à une photo noir et blanc ne sont pas adaptés au poulailler connecté !
J’ai donc réglé le problème en ajoutant une lampe LED qui s’allume comme un flash.
Dans Domoticz, j’ai créé des capteurs correspondant au nombre d’œufs :
""" Script inspire des ressources suivantes : Google Vision API Tutorial with a Raspberry Pi and Raspberry Pi Camera. See more about it here: https://www.dexterindustries.com/howto/use-google-cloud-vision-on-the-raspberry-pi/ This script uses the Vision API's logo detection. Prerequis : - chmod 750 poulailler.py (donner les droits d'execution sur ce script) - installer python - apt-get update && sudo apt-get install python3-picamera - apt-get install python3-pip - installer le client pour Google cloud vision : https://cloud.google.com/vision/docs/libraries - sudo apt-get update && sudo apt-get install google-cloud-sdk - sudo pip install --upgrade pip - sudo pip install --upgrade google-api-python-client - sudo apt-get install python-picamera - sudo pip install --upgrade google-cloud-vision - sudo pip install --upgrade oauth2client - sudo pip install pushbullet.py - configuration materielle : - sudo raspi-config / enable Camera - copie de la font arialbd.ttf execution : python poulailler.py """ import argparse import base64 import picamera import os import json from googleapiclient import discovery from oauth2client.client import GoogleCredentials from pushbullet import Pushbullet import requests from PIL import Image, ImageDraw, ImageFont import time import datetime from RPi import GPIO def is_time_between(begin_time, end_time, check_time=None): # If check time is not given, default to current UTC time check_time = check_time or datetime.datetime.utcnow().time() if begin_time < end_time: return check_time >= begin_time and check_time <= end_time else: # crosses midnight return check_time >= begin_time or check_time <= end_time def takephoto(): camera = picamera.PiCamera() camera.capture('image.jpg') #os.system('fswebcam -r 1280x720 --save image_temp.jpg') def main(): #On desactives les warnings et on lui precise que l on travaille avec les numeros des gpio et non le numero des pins GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) #ON precise que le gpio 14 est en mode sortie, elle sert ici a envoyer du courant GPIO.setup(18, GPIO.OUT) #Le code meme du programme, LOW correspond a eteindre et HIGH a allumer GPIO.output(18, GPIO.HIGH) dateiso = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S') takephoto() # First take a picture """Run a label request on a single image""" credentials = GoogleCredentials.get_application_default() service = discovery.build('vision', 'v1', credentials=credentials) #ouverture de l'image im = Image.open("image.jpg") with open('image.jpg', 'rb') as image: image_content = base64.b64encode(image.read()) service_request = service.images().annotate(body={ 'requests': [{ 'image': { 'content': image_content.decode('UTF-8') }, 'features': [{ 'type': 'OBJECT_LOCALIZATION' }] }] }) response = service_request.execute() #save json in file with open('data' + dateiso + '.json', 'w') as outfile: json.dump(response, outfile) jsonr = json.dumps(response, indent=4, sort_keys=True) input_dict = json.loads(jsonr) nbOeufs = 0 try: for k in input_dict['responses'][0]['localizedObjectAnnotations']: name=k['name'] if name == 'Egg' or name == 'Food': coordonnees = k['boundingPoly']['normalizedVertices'] for j in coordonnees: rx1 = coordonnees[0]['x'] rx2 = coordonnees[1]['x'] ry1 = coordonnees[0]['y'] ry2 = coordonnees[2]['y'] x1 = rx1 * im.size[0] x2 = rx2 * im.size[0] y1 = ry1 * im.size[1] y2 = ry2 * im.size[1] if name == "Egg" or name == "Food": color = 500 #rouge #20000 #vert elif name == "Fauna" or name == "Bird": color = 99900000 #violet else: color = 1 #noir draw = ImageDraw.Draw(im) draw.line((x1, y1, x2, y1), fill=color, width=3) draw.line((x2, y1, x2, y2), fill=color, width=3) draw.line((x2, y2, x1, y2), fill=color, width=3) draw.line((x1, y2, x1, y1), fill=color, width=3) font = ImageFont.truetype("/home/pi/arialbd.ttf", 20) draw.text((x1 + 3, y1+3),name,color, font) del draw nbOeufs+=1 except: nbOeufs = 0 # write to stdout im.save("image.jpg") if nbOeufs >0: pb = Pushbullet('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') push = pb.push_note("Cot cot", "Nb d'oeufs : " + str(nbOeufs) ) with open("image.jpg", "rb") as pic: file_data = pb.upload_file(pic, "picture.jpg") push = pb.push_file(**file_data) print nbOeufs fichier = open("log.txt", "a") fichier.write("\n" + dateiso + " " + str(nbOeufs)) fichier.close() #copy nb vers Domoticz if is_time_between(datetime.time(10,55), datetime.time(11,05)): requete="http://127.0.0.1/json.htm?type=command¶m=udevice&idx=3&svalue="+str(nbOeufs) r=requests.get (requete) requete="http://127.0.0.1/json.htm?type=command¶m=udevice&idx=5&svalue="+str(nbOeufs) r=requests.get (requete) try: label = response['responses'][0]['logoAnnotations'][0]['description'] except: label = "No response." #print label #copy image nom, ext = os.path.splitext("image.jpg") os.rename("image.jpg", nom + '_' + dateiso + ext) #on eteint la lampe GPIO.output(18, GPIO.LOW) if __name__ == '__main__': main()