Dispositivo de visão noturna baseado no módulo de imagem térmica Flir Lepton 3

Anteriormente, escrevi um artigo sobre como conectar um decodificador de imagem térmica a um smartphone Flir One Gen 2. É hora de remover o módulo lepton desse decodificador e conectá-lo diretamente ao microcontrolador montando um dispositivo de visão noturna com uma resolução de 160x120 pixels.

Para construir seu próprio dispositivo de imagem térmica de visão noturna, você precisará de:

1) Embarque com um microcontrolador. Peguei uma placa de um amigo chinês com o microcontrolador STM32F407VGT6. Um controlador tão bom: frequência de 168 MHz e 192 KB de RAM.



2) Visor. Tomei uma tela com uma resolução de 320x240. Tais telas vêm com vários controladores. Eu consegui com o controlador hx8347d.



3) Placa para conectar o lepton 3 no SPI e I2C.



4) O próprio lepton 3. O elemento mais difícil e caro. Para obtê-lo, comprei um termovisor defeituoso Flir One Gen 2 no ebay e tirei um lepton dele. Parece em ampliação assim:



Você pode excluir o item 3 desta lista, a menos que, é claro, consiga pegar um berço para um lepton de um termovisor com defeito e possa dessoldá-lo (e, a propósito, os contatos dela serão por baixo). Infelizmente, a distância entre as pernas no lepton é muito pequena, portanto essa opção não foi submetida a mim.

Para coletar tudo isso, basta soldar tudo da seguinte maneira:



Você também precisa conectar o poder. Para alimentar a placa lepton, eu uso 5 V, para a placa STM32 3.3 V. Para obter 5 V da bateria, eu uso o conversor TEL3-0511 (tensão de entrada de 4,5 a 9 V) e já abaixo esses 5 V em um LP2950CZ-3.3 comum (a propósito) , aquece até 70 graus. Aqui também é necessário usar um conversor DC / DC, mas eu ainda não o comprei). A propósito, Lepton 3 come bem. Quando alimentado a partir de 6 V, o consumo atual de todo o dispositivo é de cerca de 250 mA. Quando o lepton deseja clicar no obturador para calibração, a corrente aumenta para 500 mA.

Tudo junto fica assim:





Para trabalhar com um lepton, você precisa de um programa. Eu usei o CubeMX e o Keil 5. Nesse caso, todas as ligações de software são simplificadas para impossibilidade.

A comunicação com um lepton é realizada no SPI. Eu não usei o I2C, pois não havia necessidade específica dele. De acordo com o I2C, você pode controlar o estado do lepton, seus modos de operação, ativar / desativar o modo de calibração automática e assim por diante. Mas para um dispositivo de visão noturna, isso não é particularmente necessário.

Para descriptografar os dados, escrevi um módulo:

Módulo
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 o trabalho com este módulo consiste na submissão simples de dados obtidos pelo SPI.

Trabalhar com o 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();//     } } 


Após a montagem do quadro, as leituras do sensor são simplesmente normalizadas, reduzidas para o intervalo [0..255] e exibidas no visor na forma de escala de cinza. No entanto, nada impede o uso de qualquer paleta para colorir a imagem.

Para exibir a imagem no visor, eu uso o módulo FSMC embutido neste controlador no modo de barramento de dados de 8 bits.

O programa completo pode ser baixado aqui .

Vídeo do trabalho (infelizmente, gravei em uma câmera antiga com a qualidade adequada - não tenho uma câmera de vídeo comigo agora).


PS A propósito, você pode conectar o termovisor Flir One Gen 2 à placa de depuração STM32F407Discovery diretamente via USB. No entanto, a conexão é instável - o termovisor geralmente é perdido.


O programa para essa conexão está aqui . Talvez alguém entenda o que é e como tornar a conexão estável.

Além disso, este módulo lepton 3 se conecta de maneira fácil e simples ao Raspberry Pi.


Nesse caso, tive que modificar o programa do repositório , fazendo com que minha versão funcionasse com o lepton 3.

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


All Articles