Rilevare e tracciare un volto con l’ESP32-CAM e staffa rotante

ESP32-CAM

Le schede ESP32-CAM forniscono un modo economico per costruire progetti di automazione domestica che includono video, foto e riconoscimento facciale. In questo articolo impareremo come rilevare e tracciare i volti utilizzando l'ESP32-CAM ed una staffa in grado di far ruotare ed inclinare una mensola grazie a due servomotori. La scheda ESP32-CAM posta sulla mensola rileverà il volto nell'immagine e comanderà i servomotori per generare un movimento della staffa, tale da portare il volto rilevato al centro dell'immagine. In questo modo avverrà il tracciamento del volto.

Introduzione

Per poter realizzare questo progetto è necessaria una scheda di sviluppo ESP32-CAM con accesso a due GPIO per controllare i due servomotori. Nel caso specifico abbiamo utilizzato i seguenti componenti:

  • ESP32-CAM AI-Thinker
  • Adattatore USB Seriale FTDI (opzionale ma consigliato)
  • Staffa girevole e inclinabile
  • 2 servomotori SG90
  • Basetta sperimentale (opzionale)
  • Cavi di collegamento
  • IDE Arduino

Configurazione hardware e software

Per la realizzazione del progetto è di fondamentale importanza la staffa girevole e inclinabile. L'assemblaggio dei pezzi della staffa con i due servomotori non è complicato, e sul web, esistono numerosi tutorial come quello riportato a questo LINK.

In questo articolo riportiamo in breve i passi da seguire per l'assemblaggio:

  1. Inseriamo il primo braccio della staffa (quello non forato) nel foro di fissaggio della mensola. Quindi, utilizziamo due viti lunghe e due dadi per montare un servo dietro la mensola.
  2. Prendiamo il secondo braccio della staffa e attacchiamo la piccola squadretta bianca. Utilizziamo una piccola vite per l'attacco. Potrebbe rendersi necessario accorciare la squadretta per farla entrare nel supporto. Questa squadretta si collegherà al servo di inclinazione e consentirà al meccanismo di inclinarsi su e giù.
  3. A questo punto, prendiamo il secondo servo e il secondo braccio (quello forato). Mettiamo il ​​servo verticalmente (rivolto verso il basso) all'interno del primo braccio. Quindi prendiamo il secondo braccio e colleghiamolo al primo. Il collegamento dei due bracci dovrebbe avvenire mediante un meccanismo a scatto o a pressione. Colleghiamo la squadretta del servo presente sul braccio destro con l'albero di uscita del servo di inclinazione, inserendo l'albero nel foro.
  4. Infine, colleghiamo la base della staffa al servo di rotazione. A seconda del kit, potrebbe essere necessario installare un'altra squadretta a forma di croce all'interno della base. Anche in questo caso, potrebbe essere necessario ridurre la squadretta. Ancoriamo la squadretta alla base con delle piccole viti.

Sia la confezione della staffa che dei servomotori sono munite di viti e dadi di tutte le dimensioni. Non è necessario utilizzare tutte le viti e i dadi presenti nelle confezioni. Utilizzare solo quelle che si ritiene necessarie. Una volta assemblata la staffa, possiamo passare a collegare la scheda ESP32-CAM ai due servomotori. La Figura 1 illustra i collegamenti tra la scheda e i due servomotori. Da notare l'utilizzo del GPIO 14 e 15 per la generazione dei segnali di controllo, rispettivamente per controllare l'inclinazione e la rotazione del supporto. I pin 5V e GND vanno collegati ai relativi pin dell'adattatore USB Seriale FTDI.

progetti

Figura 1: Descrizione dei collegamenti tra la scheda ESP32-CAM e i due servomotori

Infine, per montare la scheda ESP32-CAM sulla mensola della staffa si può sagomare un blocco di polistirolo. Il blocco viene inserito nella mensola della staffa e i pin del lato libero della scheda vanno inseriti nel polistirolo. Sul versante software, invece, occorre implementare una funzione che si occupi di portare, il volto rilevato, al centro dell'immagine. Prendendo spunto da un'omonima funzione (presente nell'esempio esp32 ->CameraWebServer -> app_httpd.cpp dell'IDE Arduino) che consente di tracciare un riquadro attorno al volto rilevato, definiamo la funzione draw_face_boxes(). All'interno di questa funzione verranno valutate le coordinate del volto rilevato, verrà misurata la distanza tra centro del volto e centro dell'immagine e quindi verrà impostata la relativa variazione in gradi per i servo affinché il viso si sposti al centro della scena. Vediamo dal punto di vista matematico come è stata implementata la funzione draw_face_boxes(). Le coordinate X e Y dell'angolo alto a sinistra del riquadro combinate con la sua altezza e larghezza possono essere utilizzate per trovare le coordinate del centro del riquadro e quindi del volto al suo interno. Ad esempio, se X è pari a 105px, Y è pari a 90px e il riquadro ha una larghezza di 50px e un'altezza di 70px, il centro può essere trovato aggiungendo metà della larghezza o dell'altezza del riquadro ai valori X o Y come riportato in Figura 2.

progetti

Figura 2: Modalità di valutazione del centro del volto rilevato

Una delle parti complicate dell'utilizzo di una piattaforma rotante e inclinabile per tracciare un volto è convertire la distanza, tra il centro del riquadro e il centro dell'inquadratura, dai pixel ai gradi di rotazione e inclinazione di cui la piattaforma ha bisogno per spostarsi.

Il metodo di conversione più semplice parte dalla misurazione della diagonale del sensore, ad esempio per una risoluzione QVGA (320 x 240):

√(3202+2402) = 400px

Trovato il valore in pixel della diagonale, si divide per il campo visivo (per la fotocamera OV2640 da 2MB dovrebbe essere circa 65 gradi). In questo modo, si ottiene il numero di pixel per grado di rotazione:

400/65 ≅ 6

Quindi, circa ogni 6 pixel di movimento nel frame, il servo si deve spostare di 1 grado in quella direzione.

Il codice

Sebbene le definizioni dei pin utilizzate nel codice seguente siano specifiche per la scheda AI-Thinker utilizzata, il resto del codice è riutilizzabile per altri modelli. Si noti che il codice descritto è il minimo necessario per poter rilevare le immagini della fotocamera, quindi, non mostrerà le immagini rilevate e non disegnerà rettangoli intorno ai volti rilevati, ma semplicemente stamperà un messaggio sul monitor seriale nel momento della rilevazione del volto su un'immagine catturata. Iniziamo la descrizione del codice dalle librerie. Tra le tante presenti, quelle più interessanti sono: la libreria esp_camera.h, che consente di inizializzare la fotocamera e interagire con essa e la libreria fd_forward.h, che fornisce le funzioni necessarie per rilevare i volti nell'immagine.

#include "esp_camera.h"
#include "fd_forward.h"

Successivamente, abbiamo le definizioni dei pin della scheda utilizzata.

//AI_THINKER's pins
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27

#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22

Definiamo una funzione che si occupi dell'inizializzazione della fotocamera. In breve, la funzione di inizializzazione denominata initCamera(), non accetta argomenti e restituisce un booleano che indica se la procedura è andata a buon fine o meno. All'interno della sua implementazione, dichiariamo una variabile di tipo camera_config_t, che è una struttura contenente i parametri di inizializzazione per la fotocamera.

bool initCamera() {

camera_config_t config;

config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG; 
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 12;
config.fb_count = 1;

esp_err_t result = esp_camera_init(&config);

if (result != ESP_OK) {
return false;
}

return true;
}

Tra i molteplici parametri di inizializzazione, impostiamo la dimensione del fotogramma (config.frame_size) su QVGA, che è la risoluzione consigliata per il rilevamento del volto. Definiamo quindi la funzione draw_face_boxes(), il cui scopo è stato descritto nel capitolo precedente. Questa funzione accetta come parametri un puntatore ad una struttura di tipo dl_matrix3du_t e un puntatore ad una struttura di tipo box_array_t.

[...]

ATTENZIONE: quello che hai appena letto è solo un estratto, l'Articolo Tecnico completo è composto da ben 2605 parole ed è riservato agli ABBONATI. Con l'Abbonamento avrai anche accesso a tutti gli altri Articoli Tecnici che potrai leggere in formato PDF per un anno. ABBONATI ORA, è semplice e sicuro.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend