Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Criando imagens usando um LED

Componentes e suprimentos

Motor de passo NEMA17
× 2
Driver Theremino DRV8825 para motores de passo para sistema Theremino
× 2
Leitor de cartão SD
× 1
Arduino Mega 2560
× 1
Várias correias dentadas e rodas em V
× 1

Ferramentas e máquinas necessárias

Impressora 3D (genérica)

Aplicativos e serviços online

Arduino IDE
GIMP
Autodesk Fusion 360

Sobre este projeto





Idéia




Depois de assistir a vários vídeos e vários artigos sobre light painting, decidi tentar. A pintura com luz envolve o uso de uma câmera com um tempo de exposição muito longo para capturar uma pequena fonte de luz. Isso permite que uma única luz seja distribuída em uma longa faixa em uma única imagem.

Mas e se alguém quiser criar uma imagem mais detalhada ou usar muitas cores diferentes? Foi assim que tive a ideia de construir uma máquina CNC de 2 eixos que tem um único LED RGB que pode mudar de cor e “pintar” uma imagem.








O plano


Este projeto exigiria quatro componentes principais para funcionar:uma máquina CNC de 2 eixos, RGB LED, cartão SD e uma câmera que é capaz de tirar fotos de longa exposição. Primeiro, o Arduino Mega leria o cartão SD e encontraria um bitmap para imprimir.

Então, ele iria horizontalmente e acenderia os LEDs correspondentes enquanto também descia uma linha cada vez que a largura da imagem fosse excedida. Por último, ele vai esperar um pouco e, em seguida, procurar o próximo bitmap, finalmente parando quando não houver mais imagens para criar.





Construindo a plataforma


Devido à minha experiência em projetar e construir máquinas CNC, esta etapa não foi muito difícil. Eu queria fazer algo modular que também pudesse ser expandido para outros projetos, então me decidi por um design simples que usa duas correias dentadas presas a barras transversais que se movem ao longo de extrusões de alumínio paralelas.



Isso permite que o comprimento de cada eixo seja muito personalizável. As extremidades do eixo X têm tampas de terminação impressas em 3D, uma das quais tem uma montagem para o motor de passo do eixo X e rolamento.





Leitura de bitmaps


Escolhi o formato de arquivo bitmap devido à sua simplicidade e facilidade de leitura. Com base no formato do arquivo, existem alguns endereços importantes no próprio arquivo que devem ser lidos. Estes são 0x12 (largura), 0x16 (altura), 0x1C (profundidade de cor), 0xA (localização dos dados de pixel) e finalmente 0x36 (onde os dados de pixel geralmente estão).

Os dados são lidos em blocos de dois ou quatro bytes (16 ou 32 bits), o que também avança o ponteiro para o próximo endereço. A função de leitura analisa e captura todos os dados importantes, incluindo deslocamentos e tamanhos. Em seguida, ele passa e lê cada pixel, linha por linha.





Preparando as Imagens




Como a maioria das câmeras é limitada a um máximo de 30 segundos de tempo de exposição, há um limite de cerca de 288 pixels no total que podem ser exibidos nesse período de tempo. Isso equivale a cerca de uma imagem de 18 x 16. Para fazer minhas imagens, carreguei o gimp e comecei a criar pixel art muito simples. Isso incluía uma Pokébola, coração e um Mario saltitante. Em seguida, coloquei essas três imagens em um diretório chamado “bitmaps” no diretório raiz do cartão SD. O programa lê todas as imagens desta pasta.





Programa de Pintura


Como os motores de passo não têm sistema de feedback de posicionamento interno, suas posições devem ser rastreadas por software. O programa que escrevi acompanha a posição do LED com um sistema de grade para permitir um escalonamento fácil. Quando o Arduino Mega é inicializado, as posições do stepper são definidas como 0, 0 e, em seguida, a primeira imagem é encontrada e lida. Em seguida, o LED pisca cinco vezes para permitir que o fotógrafo saiba que está quase na hora de começar a capturar. O bitmap é lido primeiro percorrendo cada linha e, dentro de cada linha, cada coluna é lida. Conhecendo a linha e a coluna atuais, os motores de passo podem ser movidos para as mesmas posições. Em cada posição, o LED é alterado para a cor do pixel correspondente.





(re) -Criando uma imagem


Após inserir o cartão SD e conectar a fonte de alimentação de 12v para os motores, era hora de ligar a máquina. Na minha câmera, eu a configurei para um tempo de exposição de 20 segundos, uma abertura de F36, ISO de 100 e uma compensação de exposição de -5 pontos para minimizar os efeitos de fantasmas. A primeira imagem desenhada foi uma pokebola, vista aqui:

Embora esteja um pouco borrado, a forma ainda pode ser vista claramente. Em seguida, ele criou um bitmap de coração:

Como essa imagem tinha apenas 9 por 9 pixels, cada pixel individual é muito menos definido. Por último, pintei um quadro de Mario pulando:

Esta imagem tem muitos fantasmas, principalmente devido à abundância de pixels de cores vivas.





Ideias futuras para melhorias


As pinturas leves que criei ficaram muito melhores do que eu pensava inicialmente, mas ainda há espaço para melhorias. A principal coisa que eu gostaria de fazer é reduzir a quantidade de desfoque, fazendo com que o LED se mova enquanto está escuro e só acenda quando estiver parado. Essa técnica melhoraria muito a clareza das imagens recriadas.

Código

  • Programa de pintura com luz
Programa de pintura com luz C / C ++
 // Função de leitura de bitmap parcialmente de Adafruit # include  #include  #include "DRV8825.h" #define MOTOR_STEPS 200 # define RPM 150 # define MICROSTEPS 4 // definições de pinos # define STEPPER_X_DIR 7 # define STEPPER_X_STEP 6 # define STEPPER_X_EN 8 # define STEPPER_Y_DIR 4 # define STEPPER_Y_STEP 5 # define STEPPER_Y_EN 12 # define X 0 # define Y 1 # define X_DIR_FLAG -1 // 1 ou -1 para virar a direção # define Y_DIR_FLAG 1 // 1 ou -1 para mudar a direção # define STEPS_PER_MM (3.75 * MICROSTEPS) // passos necessários para mover 1mm # define SPACE_BETWEEN_POSITIONS 5 // 5mm por movimento # define R A0 # define G A1 # define B A2 # define SD_CS 22int currentPositions [] ={0, 0}; DRV8825 stepperX (MOTOR_STEPS, STEPPER_X_DIR, STEPPER_X_STEP, STEPPER_X_EN); DRV8825 stepperY (MOTOR_STEPS, STEPPER_Y_DIR, STEPPER_Y_STEP, STEPPER_Y_EN); configuração vazia (115200 de série) () () init_steppers (); SD.begin (SD_CS); createBitmaps (); stepperX.disable (); stepperY.disable (); while (1);} void loop () {} void createBitmaps () {Arquivo dir =SD.open ("bitmaps"); while (true) {bitmap do arquivo =dir.openNextFile (); if (! bitmap) {break; } paintBitmap (bitmap); atraso (15000); }} #define BUFFPIXEL 20void paintBitmap (Arquivo bmpFile) {int bmpWidth, bmpHeight; uint8_t bmpDepth; uint32_t bmpImageOffset; uint32_t rowSize; // Nem sempre =bmpWidth; pode ter preenchimento uint8_t sdbuffer [3 * BUFFPIXEL]; // buffer de pixel (R + G + B por pixel) uint8_t buffidx =sizeof (sdbuffer); // Posição atual no sdbuffer boolean goodBmp =false; // Definido como verdadeiro na análise de cabeçalho válido boolean flip =true; // BMP é armazenado de baixo para cima int w, h, row, col; uint8_t r, g, b; uint32_t pos =0, startTime =millis (); Serial.println (); Serial.print ("Carregando imagem '"); Serial.print (bmpFile.name ()); Serial.println ('\' '); // Abra o arquivo solicitado no cartão SD // Analisa o cabeçalho BMP if (read16 (bmpFile) ==0x4D42) {// Assinatura BMP Serial.print ("Tamanho do arquivo:"); Serial.println (read32 (bmpFile)); (void) read32 (bmpFile); // Ler e ignorar os bytes do criador bmpImageOffset =read32 (bmpFile); // Início dos dados da imagem Serial.print ("Image Offset:"); Serial.println (bmpImageOffset, DEC); // Lê o cabeçalho DIB Serial.print ("Tamanho do cabeçalho:"); Serial.println (read32 (bmpFile)); bmpWidth =read32 (bmpFile); bmpHeight =read32 (bmpFile); if (read16 (bmpFile) ==1) {// # planes - deve ser '1' bmpDepth =read16 (bmpFile); // bits por pixel Serial.print ("Bit Depth:"); Serial.println (bmpDepth); if ((bmpDepth ==24) &&(read32 (bmpFile) ==0)) {// 0 =descompactado goodBmp =true; // Formato BMP compatível - continue! Serial.print ("Tamanho da imagem:"); Serial.print (bmpWidth); Serial.print ('x'); Serial.println (bmpHeight); // As linhas BMP são preenchidas (se necessário) para o limite de 4 bytes rowSize =(bmpWidth * 3 + 3) &~ 3; // Se bmpHeight for negativo, a imagem está na ordem de cima para baixo. // Isso não é canônico, mas foi observado na natureza. if (bmpHeight <0) {bmpHeight =-bmpHeight; flip =false; } // Área de recorte a ser carregada w =bmpWidth; h =bmpHeight; if (bmpWidth * bmpHeight> 290) {// Serial.println muito grande ("O arquivo é muito grande para ser impresso."); Retorna; } para (uint8_t i =0; i <5; i ++) {analogWrite (R, 150); atraso (500); analogWrite (R, 0); atraso (500); } for (linha =0; linha  =sizeof (sdbuffer)) {// De fato bmpFile.read (sdbuffer, sizeof (sdbuffer)); buffidx =0; // Define o índice para o início} // Converte o pixel do formato BMP para TFT, empurre para exibir b =sdbuffer [buffidx ++]; g =sdbuffer [buffidx ++]; r =sdbuffer [buffidx ++]; moveToPosition (col, linha); ativarLED (r, g, b); // otimizado! //tft.pushColor(tft.Color565(r,g,b)); } // final do pixel analogWrite (R, 0); analogWrite (G, 0); analogWrite (B, 0); } // fim scanline Serial.print ("Carregado em"); Serial.print (millis () - startTime); Serial.println ("ms"); } // fim goodBmp}} bmpFile.close (); moveToPosition (0,0); if (! goodBmp) Serial.println ("Formato BMP não reconhecido.");} uint16_t read16 (Arquivo f) {uint16_t resultado; ((uint8_t *) &resultado) [0] =f.read (); // LSB ((uint8_t *) &resultado) [1] =f.read (); // Resultado de retorno de MSB;} uint32_t read32 (Arquivo f) {resultado de uint32_t; ((uint8_t *) &resultado) [0] =f.read (); // LSB ((uint8_t *) &resultado) [1] =f.read (); ((uint8_t *) &resultado) [2] =f.read (); ((uint8_t *) &resultado) [3] =f.read (); // Resultado de retorno do MSB;} void activateLED (int r, int g, int b) {Serial.print (F ("LED tem o valor de:")); Serial.print (r); Serial.print (","); Serial.print (g); Serial.print (","); Serial.println (b); analogWrite (R, r); analogWrite (G, g); analogWrite (B, b);} void moveToPosition (int x, int y) {int newPosX =(x-currentPositions [X]) * STEPS_PER_MM * X_DIR_FLAG * SPACE_BETWEEN_POSITIONS; int newPosY =(y-currentPositions [Y]) * STEPS_PER_MM * Y_DIR_FLAG * SPACE_BETWEEN_POSITIONS; stepperX.move (newPosX); stepperY.move (newPosY); currentPositions [X] =x; currentPositions [Y] =y; Serial.print ("Posições de passo:"); Serial.print (currentPositions [X]); Serial.print (","); Serial.println (currentPositions [Y]);} void init_steppers () {stepperX.begin (RPM); stepperX.setEnableActiveState (LOW); stepperX.enable (); stepperX.setMicrostep (MICROSTEPS); stepperY.begin (RPM); stepperY.setEnableActiveState (LOW); stepperY.enable (); stepperY.setMicrostep (MICROSTEPS);} 

Peças personalizadas e gabinetes

Esquemas


Processo de manufatura

  1. O que nos levou de volta ao uso de materiais naturais no design de produtos?
  2. Sensor de movimento usando Raspberry Pi
  3. Um guia do gerente de manutenção para criar e usar FMEAs
  4. Envie dados do sensor de um Arduino para outro usando Firebase
  5. Troque duas variáveis ​​Python sem usar uma terceira
  6. Melodia para dança do caixão
  7. Reaproveitar controles remotos antigos
  8. Controle o Arduino Rover usando Firmata e o controlador do Xbox One
  9. Iluminação LED 8x por som
  10. Arduino Quadruped