Dispositivo de visión nocturna basado en el módulo de imagen térmica Flir Lepton 3

Anteriormente, escribí un artículo sobre la conexión de un decodificador de imágenes térmicas a un teléfono inteligente Flir One Gen 2. Es hora de quitar el módulo lepton de este decodificador y conectarlo directamente al microcontrolador ensamblando un dispositivo de visión nocturna con una resolución de 160x120 píxeles.

Para construir su propio dispositivo de imagen térmica de visión nocturna, necesitará:

1) Tablero con un microcontrolador. Tomé una placa de un amigo chino con el microcontrolador STM32F407VGT6. Un buen controlador: frecuencia de 168 MHz y 192 KB de RAM.



2) Pantalla. Tomé una pantalla con una resolución de 320x240. Tales pantallas vienen con varios controladores. Lo obtuve con el controlador hx8347d.



3) Placa para conectar lepton 3 en SPI e I2C.



4) El leptón mismo 3. El elemento más difícil y costoso. Para obtenerlo, compré una cámara termográfica defectuosa Flir One Gen 2 en eBay y le saqué un leptón. Se ve en aumento así:



Puede excluir el elemento 3 de esta lista, a menos que, por supuesto, se las arregle para tomar una cuna para un leptón de una cámara termográfica que funciona mal y puede soldarlo (y sus contactos, por cierto, serán desde la parte inferior). Desafortunadamente, la distancia entre las piernas en el lepton es bastante pequeña, por lo que esta opción no me fue presentada.

Para recopilar todo esto, solo necesita soldarlo de la siguiente manera:



También necesita conectar la alimentación. Para alimentar la placa lepton, uso 5 V, para la placa STM32 3.3 V. Para obtener 5 V de la batería, uso el convertidor TEL3-0511 (voltaje de entrada de 4.5 a 9 V), y bajo estos 5 V en un LP2950CZ-3.3 regular (por cierto , se calienta hasta 70 grados. Aquí, también, debe usar un convertidor CC / CC, pero todavía no lo he comprado). Lepton 3 come, por cierto, bien. Cuando se alimenta desde 6 V, el consumo de corriente de todo el dispositivo es de aproximadamente 250 mA. Cuando el lepton quiere hacer clic en el obturador para la calibración, la corriente aumenta a 500 mA.

Todo junto se ve así:





Para trabajar con un lepton, necesitas un programa. Utilicé CubeMX y Keil 5. En este caso, todos los enlaces de software se simplifican a la imposibilidad.

La comunicación con un lepton se lleva a cabo en SPI. No he usado I2C, ya que no era particularmente necesario. Según I2C, puede controlar el estado del leptón, sus modos de funcionamiento, habilitar / deshabilitar el modo de calibración automática, etc. Pero para un dispositivo de visión nocturna, esto no es particularmente necesario.

Para descifrar los datos, escribí un módulo:

Modulo
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); } 


Todo el trabajo con este módulo consiste en la simple presentación de datos obtenidos por SPI.

Trabajar con el módulo
 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();//     } } 


Después de ensamblar el marco, las lecturas del sensor simplemente se normalizan, se reducen al rango [0..255] y se muestran en la pantalla en forma de tonos de gris. Sin embargo, nada impide usar una paleta para colorear la imagen.

Para mostrar la imagen en la pantalla, utilizo el módulo FSMC integrado en este controlador en modo de bus de datos de 8 bits.

El programa completo se puede descargar aquí .

Video de trabajo (desafortunadamente, grabé en una cámara vieja con la calidad adecuada, ahora no tengo una cámara de video conmigo).


PD: Por cierto, puede conectar la cámara termográfica Flir One Gen 2 a la placa de depuración de descubrimiento STM32F407 directamente a través de USB. Sin embargo, la conexión es inestable: la cámara termográfica a menudo se pierde.


El programa para tal conexión está aquí . Tal vez alguien entienda qué es y cómo hacer que la conexión sea estable.

Además, este módulo lepton 3 se conecta fácil y simplemente a la Raspberry Pi.


En este caso, tuve que modificar el programa desde el repositorio , haciendo que mi versión funcionara con lepton 3.

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


All Articles