Dispositif de vision nocturne basé sur le module d'imagerie thermique Flir Lepton 3

Plus tôt, j'ai écrit un article sur la connexion d'un décodeur d'imagerie thermique à un smartphone Flir One Gen 2. Il est temps de retirer le module lepton de ce décodeur et de le connecter directement au microcontrôleur en assemblant un dispositif de vision nocturne avec une résolution de 160x120 pixels.

Pour construire votre propre appareil d'imagerie thermique de vision nocturne, vous aurez besoin de:

1) Carte avec microcontrôleur. J'ai pris une planche d'un ami chinois avec le microcontrôleur STM32F407VGT6. Un si bon contrôleur: fréquence 168 MHz et 192 Ko de RAM.



2) Affichage. J'ai pris un écran avec une résolution de 320x240. Ces écrans sont livrés avec différents contrôleurs. Je l'ai eu avec le contrôleur hx8347d.



3) Carte pour connecter le lepton 3 sur SPI et I2C.



4) Le lepton lui-même 3. L'élément le plus difficile et le plus cher. Pour l'obtenir, j'ai acheté un imageur thermique Flir One Gen 2 défectueux sur ebay et j'en ai retiré un lepton. Il ressemble à un grossissement comme ceci:



Vous pouvez exclure l'élément 3 de cette liste, sauf si, bien sûr, vous parvenez à prendre un berceau pour un lepton d'un imageur thermique défectueux et vous pouvez le dessouder (et ses contacts, soit dit en passant, seront par le bas). Malheureusement, la distance entre les jambes du lepton est assez petite, donc cette option ne m'a pas été soumise.

Pour collecter tout cela, il vous suffit de tout souder comme suit:



Vous devez également connecter l'alimentation. Pour alimenter la carte lepton, j'utilise 5 V, pour la carte STM32 3,3 V.Pour obtenir 5 V de la batterie, j'utilise le convertisseur TEL3-0511 (tension d'entrée de 4,5 à 9 V), et abaisse déjà ces 5 V sur un LP2950CZ-3.3 normal (d'ailleurs) , chauffe jusqu'à 70 degrés. Ici aussi, vous devez utiliser un convertisseur DC / DC, mais je ne l'ai pas encore acheté). Au fait, Lepton 3 mange bien. Lorsqu'il est alimenté à partir de 6 V, la consommation de courant de l'ensemble de l'appareil est d'environ 250 mA. Lorsque le lepton veut cliquer sur l'obturateur pour l'étalonnage, le courant monte à 500 mA.

Tout ensemble ressemble à ceci:





Pour travailler avec un lepton, vous avez besoin d'un programme. J'ai utilisé CubeMX et Keil 5. Dans ce cas, toutes les liaisons logicielles sont simplifiées à l'impossibilité.

La communication avec un lepton s'effectue sur SPI. Je n'ai pas utilisé I2C, car il n'était pas particulièrement nécessaire. Selon I2C, vous pouvez contrôler l'état du lepton, ses modes de fonctionnement, activer / désactiver le mode d'étalonnage automatique, etc. Mais pour un appareil de vision nocturne, ce n'est pas particulièrement nécessaire.

Pour décrypter les données, j'ai écrit un module:

Module
leptoncontrol.h
#ifndef LEPTON_CONTROL_H #define LEPTON_CONTROL_H #include <stdbool.h> #include <stdio.h> //   ( ) #define LEPTON_ORIGINAL_IMAGE_WIDTH 160 #define LEPTON_ORIGINAL_IMAGE_HEIGHT 120 //  VoSPI #define VOSPI_FRAME_HEIGHT 60 //  VoSPI #define VOSPI_FRAME_WIDTH 80 //  VoSPI   (164  RAW14  244  RGB) #define VOSPI_PACKAGE_SIZE 164 //   VoSPI   #define VOSPI_PACKAGE_LINE_SIZE 160 //  VOSPI   #define VOSPI_SEGMENT_LINE_AMOUNT 60 void LEPTONCONTROL_Init(void);// void LEPTONCONTROL_CalculateCRC(unsigned short *crc,unsigned char byte);// crc bool LEPTONCONTROL_PushVoSPI(unsigned char data[VOSPI_PACKAGE_SIZE],bool *first_line);//    VoSPI    unsigned short *LEPTONCONTROL_GetRAW14Ptr(void);//      #endif 

leptoncontrol.c

 #include "leptoncontrol.h" #include "stm32f4xx_hal.h" static unsigned short RAW14Image[LEPTON_ORIGINAL_IMAGE_HEIGHT*LEPTON_ORIGINAL_IMAGE_WIDTH];//  static unsigned short CRCTable[256];//   CRC16 //     #define RESYNC_TIMEOUT_MS 19 //:   #define NO_SEGMENT -1 //:   #define ERROR_PACKAGE -2 //---------------------------------------------------------------------------------------------------- // //---------------------------------------------------------------------------------------------------- void LEPTONCONTROL_Init(void) { //    CRC unsigned short code; for(long n=0;n<256;n++) { code=((unsigned short)n)<<8; for(unsigned char m=0;m<8;m++) { if(code&(1<<15)) code=(code<<1)^0x1021; else code=code<<1; } CRCTable[n]=code; } } //---------------------------------------------------------------------------------------------------- // crc //---------------------------------------------------------------------------------------------------- void LEPTONCONTROL_CalculateCRC(unsigned short *crc,unsigned char byte) { *crc=CRCTable[(((*crc)>>8)^byte++)&0xFF]^((*crc)<<8); } //---------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------- long LEPTONCONTROL_ReadSegment(unsigned short *raw14_ptr,unsigned char data[VOSPI_PACKAGE_SIZE],bool *first_line) { static long current_package=-1; static long segment=-1; long n; *first_line=false; if ((data[0]&0x0F)==0x0F) return(NO_SEGMENT);//  unsigned short crc=data[2]; crc<<=8; crc|=data[3]; // CRC unsigned short crc16=0; LEPTONCONTROL_CalculateCRC(&crc16,data[0]&0x0F); LEPTONCONTROL_CalculateCRC(&crc16,data[1]); LEPTONCONTROL_CalculateCRC(&crc16,0); LEPTONCONTROL_CalculateCRC(&crc16,0); for(n=4;n<VOSPI_PACKAGE_SIZE;n++) LEPTONCONTROL_CalculateCRC(&crc16,data[n]); if (crc16!=crc) return(ERROR_PACKAGE);// CRC //   unsigned short package=data[0]&0x0F; package<<=8; package|=data[1]; if (package==0) { *first_line=true; current_package=0; } if (package==20) { unsigned char ttt=(data[0]&0x70)>>4;//     20  segment=ttt; } if (current_package<0) return(NO_SEGMENT); if (current_package!=package) { current_package=-1; return(ERROR_PACKAGE); } unsigned short *raw_ptr=raw14_ptr+current_package*VOSPI_PACKAGE_LINE_SIZE/2; for(n=0;n<VOSPI_PACKAGE_LINE_SIZE/2;n++,raw_ptr++) { //    big-endian: ,  unsigned short value=data[n*sizeof(short)+4]; value<<=8; value|=data[n*sizeof(short)+5]; *raw_ptr=value; } current_package++; if (current_package!=VOSPI_FRAME_HEIGHT) return(NO_SEGMENT); current_package=-1; return(segment); } //---------------------------------------------------------------------------------------------------- //    VoSPI    //---------------------------------------------------------------------------------------------------- bool LEPTONCONTROL_PushVoSPI(unsigned char data[VOSPI_PACKAGE_SIZE],bool *first_line) { *first_line=false; static long waitable_segment=1; long segment=LEPTONCONTROL_ReadSegment(RAW14Image+(waitable_segment-1)*VOSPI_FRAME_WIDTH*VOSPI_SEGMENT_LINE_AMOUNT,data,first_line); if (segment==ERROR_PACKAGE) HAL_Delay(RESYNC_TIMEOUT_MS); if (segment==ERROR_PACKAGE || segment==0) waitable_segment=1; if (segment==ERROR_PACKAGE || segment==NO_SEGMENT || segment==0) return(false); if (segment!=waitable_segment) { waitable_segment=1; if (segment!=1) return(false); } waitable_segment++; if (waitable_segment!=5) return(false); waitable_segment=1; return(true); } //---------------------------------------------------------------------------------------------------- //      //---------------------------------------------------------------------------------------------------- unsigned short *LEPTONCONTROL_GetRAW14Ptr(void) { return(RAW14Image); } 


Tout travail avec ce module consiste en une simple soumission de données obtenues par SPI.

Travailler avec le module
 while(1) { //   while(1) { HAL_SPI_Receive(&hspi1,buffer,VOSPI_PACKAGE_SIZE,0x1000); bool first_line=false; unsigned char *buffer_ptr=buffer; LEPTONCONTROL_PushVoSPI(buffer_ptr,&first_line); if (first_line==true) break; } //    lepton3 unsigned char *buffer_ptr=buffer; HAL_SPI_Receive(&hspi1,buffer_ptr,VOSPI_PACKAGE_SIZE*SPI_READ_VOSPI_AMOUNT,0x1000); buffer_ptr=buffer; for(long n=0;n<SPI_READ_VOSPI_AMOUNT;n++,buffer_ptr+=VOSPI_PACKAGE_SIZE) { bool first_line=false; bool res=LEPTONCONTROL_PushVoSPI(buffer_ptr,&first_line); if (res==true) CreateImage();//     } } 


Après assemblage du cadre, les lectures du capteur sont simplement normalisées, réduites à la plage [0..255] et affichées à l'écran sous la forme de nuances de gris. Cependant, rien n'empêche d'utiliser une palette pour colorier l'image.

Pour afficher l'image sur l'écran, j'utilise le module FSMC intégré à ce contrôleur en mode bus de données 8 bits.

Le programme complet peut être téléchargé ici .

Vidéo de travail (malheureusement, j'ai tourné sur un vieil appareil photo avec la qualité appropriée - je n'ai pas de caméra vidéo avec moi maintenant).


PS Soit dit en passant, vous pouvez connecter l'imageur thermique Flir One Gen 2 à la carte de débogage STM32F407Discovery directement via USB. Cependant, la connexion est instable - l'imageur thermique est souvent perdu.


Le programme d'une telle connexion est ici . Peut-être que quelqu'un comprendra ce que c'est et comment rendre la connexion stable.

De plus, ce module lepton 3 se connecte facilement et simplement au Raspberry Pi.


Dans ce cas, j'ai dû modifier le programme à partir du référentiel , ce qui rend ma version fonctionnant avec lepton 3.

Source: https://habr.com/ru/post/fr413021/


All Articles