PIcam PAN / TILT

présentation d’un montage basique PAN TILT d’une PI camera pilotée par un Raspberry PI.

le principe du montage est d’afficher l’image de la camera sur écran et d’utiliser une souris ou un trackpad pour pointer dans l’image le nouveau point de focus central . après clic sur le bouton gauche , la camera change d’orientation (mouvement Pan/Tilt) pour pointer sur le nouveau point désigné dans l’image.

vue CAO du montage:

Vue du Montage réel:

il est fait a partir de pièces imprimées 3D , les moteurs pas a pas sont des 28BYJ-48 bien connus dans les montages proto a base d’arduino. ici ils seront commandés par un Raspberry PI (schéma plus bas).

Principe de commande et câblage:

pour la partie commande et affichage , nous allons utiliser un Raspberry PI 4B 8Go et utiliser le port GPIO pour piloter les 2 moteurs pas a pas 28BYJ-48 du montage Pan/Tilt via deux drivers Pololu A4988. la camera est une simple PIcam V1 a quelques euros ( def. photo maxi: 2592×1944 , def video max: 1920 x 1080 30fps)

le principe de ce type de commande avec Pololu A4988 est expliqué ici :

https://www.e-techno-tutos.com/2021/03/14/moteur-pas-a-pas-type-nema-17/

avec pour particularité de remplacer un NEMA17 par un 28BYJ-48 dont la modification de branchement est décrite ici (pilotage par un Arduino Uno):

https://www.e-techno-tutos.com/2022/03/18/arduino-pap-28byj-48-a4988/

ci dessous une vue schématique des branchements sur le port GPIO. les sorties GPIO2 et GPIO3 vont sur les entrée STEP/DIR du driver 1 et les sorties GPIO5 et GPIO6 vont sur les entrée STEP/DIR du driver 2. les 2 drivers sont alimenté en puissance par une alim 5V externe (ici pile 9V et régulateur 5V) car le 5V du RPI 4B est insuffisant pour l’ensemble ce qui pourrait provoquer une chute de puissance et des dysfonctionnements globaux.

pour mémo rappel des brochages du GPIO

pour la partie driver Pololu , sont utilisés ici des petites platines proto spécifique que l’on trouve sur Amazone ou aliexpress et qui supportent les pololu A4988.

pour piloter un 28BYJ-48 , vu les faibles puissances en jeu , pas besoin de radiateur sur les drivers Pololu.

petite vue globale du montage proto une fois tout branché :

petite vue de l’ensemble une fois le PI démarré et le programme ci dessous lancé .

LE PROGRAMME PYTHON

voici le lien vers le programme python au format txt (changer l’extension “.txt” en “.py” pour une utilisation sur RPI:

le détail du pgme:

#import des librairies
import cv2
from time import sleep 
import RPi.GPIO as GPIO
#config port GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
st1=6
dir1=5
st2=3
dir2=2
GPIO.setup(st1, GPIO.OUT)
GPIO.setup(dir1, GPIO.OUT)
GPIO.setup(st2, GPIO.OUT)
GPIO.setup(dir2, GPIO.OUT)
# fonction "motion" = action suite appui bouton et  position curseur souris        
def motion(event,x,y, flags, paramq):
    if event==cv2.EVENT_LBUTTONDOWN:
        Xpos=(x - 640)
        Ypos=(360 - y)
        S1=abs(round(Xpos/10))
        A= abs(round(Ypos/Xpos, 1))         
        if Xpos<=0:
            d1=0            
        else:
            d1=1
        if Ypos<=0:
            d2=1
        else:
            d2=0
        GPIO.output(dir1,d1)
        GPIO.output(dir2,d2)
        # mouvement combiné Pan/tilt par pas de 10 pixels en x
        for n in range(S1):            
            for x in range(10):
                GPIO.output(st1,1)
                sleep(0.001)
                GPIO.output(st1,0)
                sleep(0.001)
            for y in range(int(10*A)):
                GPIO.output(st2,1)
                sleep(0.001)
                GPIO.output(st2,0)
                sleep(0.001)
#programme principal
# ouverture camera, fenetre camera et definition affichage
Vcam="CAMERA"
cv2.namedWindow(Vcam, cv2.WINDOW_AUTOSIZE)
cv2.moveWindow(Vcam, 630, 220) 
cap0=cv2.VideoCapture(0) 
# defintion largeur/hauteur de la camera (ici mode HD)
cap0.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap0.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
if cap0.isOpened(): 
    rval, frame0 = cap0.read()
else:
    rval = False 
# boucle affichage et tracage centre image   
while rval:   
    cv2.imshow(Vcam, frame0)
    rval, frame0 = cap0.read()
    cv2.line(img=frame0, pt1=(640, 0), pt2=(640, 720), color=(0, 255, 255), thickness=2, lineType=8, shift=0)
    cv2.line(img=frame0, pt1=(0, 360), pt2=(1280, 360), color=(0, 255, 255), thickness=2, lineType=8, shift=0)
    # gestion appui clavier touche q = arret camera
    key=cv2.waitKey(1) & 0xFF
    if  key == ord('q'):            
        break
    # si pas arret camera , appel fonction "motion"
    else:    
        cv2.setMouseCallback("CAMERA",motion)
# arret camera et fermeture fenetres si appui touche q
cap0.release()
cv2.destroyAllWindows()

commentaires / explications:

  • le programme utilise 3 librairies : OpenCV ( import cv2 ) pour la partie camera et gestion des évènements clavier/souris , Time (from time import sleep) pour les pause pgmes, et GPIO ( import RPi.GPIO as GPIO ) pour la gestion des entrées sorties de l’interface GPIO.
  • les 10 lignes suivantes font définition des variables et la configuration des sorties du port GPIO pour l’envoi des impulsions STEP/DIR sur les entrées des drivers.
  • def motion() est une fonction qui définis les action sur appui gauche du bouton souris => on prend la valeur de position (x,y) en pixels de la souris dans l’image , on recalcule la position par rapport au milieu de l’image , on fixe la valeur des sortie dir des 2 moteurs pas a pas et on lance une boucle de mouvement combiné sur les 2 axes en fonction des décalages pixels entre centre de l’image et position de la souris sur l’image.
  • le programme central ouvre une fenêtre de visualisation, lance l’affichage camera et teste l’appui clavier puis appelle la fonction motion si clic gauche souris.

résultat sur écran raspberry:

a gauche, le programme ouvert avec l’IDE Thonny Python et a droite la fenêtre d’affichage de la camera . en jaune les lignes horizontales et verticales matérialisant le centre de visée de la caméra.

petite vidéo du résultat:

points a améliorer:

  • l’affichage caméra est figé pendant le mouvement Pan/Tilt ,ceci est du a l’intégration de l’affichage dans le module de programme . l’affichage n’est re-genéré qu’après le mouvement => utiliser le threading ou le multiprocessing pour séparer les deux processus et ainsi avoir un processus d’affichage independant permettant de rafraichir l’affichage en temps réel pendant le mouvement.
  • la position après mouvement est très approximative , integration pifométrique pixels camera / position finale => affiner la méthode de calcul en coordonnées polaires de la nouvelle position en fonction des pixels souris , de la focale de la camera , de la définition d’affichage et du microstepping des pas a pas.
  • la “dichotomie” du déplacement combiné x/y est sommaire (ratio entre la position x/y et arrondis sommaire sur le pas des boucles) => intégrer un calcul de décalage par rapport a la pente théorique et faire une correction (x,y) dans la boucle a chaque itération pour rattraper la position par rapport a la trajectoire théorique (principe de l’algorithme de Bresenham) .