Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

Erstellen von Bildern mit einer LED

Komponenten und Verbrauchsmaterialien

NEMA17-Schrittmotor
× 2
Theremino-Treiber DRV8825 für Schrittmotoren für Theremino-System
× 2
SD-Kartenleser
× 1
Arduino Mega 2560
× 1
Verschiedene Zahnriemen und V-Räder
× 1

Notwendige Werkzeuge und Maschinen

3D-Drucker (generisch)

Apps und Onlinedienste

Arduino-IDE
GIMP
Autodesk Fusion 360

Über dieses Projekt

Idee

Nachdem ich mir mehrere Videos angeschaut und zahlreiche Artikel zum Thema Lichtmalerei gesehen hatte, beschloss ich, es auszuprobieren. Beim Light Painting wird eine Kamera mit sehr langer Belichtungszeit verwendet, um eine kleine Lichtquelle einzufangen. Dadurch kann ein einzelnes Licht in einem einzelnen Bild zu einem langen Streifen aufgereiht werden.

Aber was ist, wenn jemand ein detaillierteres Bild erstellen oder viele verschiedene Farben verwenden möchte? So kam ich auf die Idee, eine 2-Achsen-CNC-Maschine zu bauen, die über eine einzelne RGB-LED verfügt, die die Farbe ändern und ein Bild „malen“ kann.

Der Plan

Für dieses Projekt wären vier Hauptkomponenten erforderlich:eine 2-Achsen-CNC-Maschine, RGB-LED, SD-Karte und eine Kamera, die Langzeitbelichtungen aufnehmen kann. Zuerst würde der Arduino Mega die SD-Karte lesen und eine Bitmap zum Drucken finden.

Dann würde es horizontal gehen und die entsprechenden LEDs aufleuchten lassen, während es sich jedes Mal, wenn die Bildbreite überschritten wird, eine Reihe nach unten bewegt. Zuletzt wartet es ein wenig und sucht dann nach der nächsten Bitmap und stoppt schließlich, wenn keine Bilder mehr zu erstellen sind.

Aufbau des Rigs

Aufgrund meiner Erfahrung mit dem Entwerfen und Bauen von CNC-Maschinen war dieser Schritt nicht allzu schwierig. Ich wollte etwas Modulares machen, das auch für andere Projekte erweitert werden kann, also entschied ich mich für ein einfaches Design, bei dem zwei Zahnriemen an Querstangen befestigt sind, die sich entlang paralleler Aluminiumprofile bewegen.

Dadurch kann die Länge jeder Achse sehr individuell angepasst werden. An den Enden der X-Achse befinden sich 3D-gedruckte Endkappen, von denen eine eine Halterung für den X-Achsen-Schrittmotor und das Lager hat.

Lesen von Bitmaps

Ich habe das Bitmap-Dateiformat aufgrund seiner Einfachheit und seiner leichten Lesbarkeit gewählt. Aufgrund des Dateiformats gibt es einige wichtige Adressen in der Datei selbst, die gelesen werden müssen. Diese sind 0x12 (Breite), 0x16 (Höhe), 0x1C (Farbtiefe), 0xA (Position der Pixeldaten) und schließlich 0x36 (wo sich die Pixeldaten normalerweise befinden).

Die Daten werden in Blöcken von entweder zwei oder vier Bytes (16 oder 32 Bits) gelesen, wodurch auch der Zeiger auf die nächste Adresse vorgerückt wird. Die Lesefunktion durchläuft und erfasst alle wichtigen Daten, einschließlich Offsets und Größen. Dann geht es durch und liest jedes Pixel, Zeile für Zeile.

Vorbereiten der Bilder

Da die meisten Kameras auf eine maximale Belichtungszeit von 30 Sekunden begrenzt sind, gibt es eine Grenze von etwa 288 Pixeln, die in dieser Zeit angezeigt werden können. Dies entspricht ungefähr einem 18 x 16-Bild. Um meine Bilder zu machen, habe ich Gimp geladen und angefangen, sehr einfache Pixelkunst zu erstellen. Dazu gehörten ein Pokéball, ein Herz und ein springender Mario. Dann habe ich diese drei Bilder in ein Verzeichnis namens „bitmaps“ im Stammverzeichnis der SD-Karte abgelegt. Das Programm liest alle Bilder aus diesem Ordner.

Malprogramm

Da Schrittmotoren kein internes Positions-Feedback-System haben, müssen ihre Positionen per Software verfolgt werden. Das von mir geschriebene Programm verfolgt die Position der LED mit einem Rastersystem, um eine einfache Skalierung zu ermöglichen. Wenn der Arduino Mega startet, werden die Stepperpositionen auf 0, 0 gesetzt und dann wird das erste Bild gefunden und gelesen. Dann blinkt die LED fünfmal, um dem Fotografen mitzuteilen, dass es fast Zeit für die Aufnahme ist. Die Bitmap wird gelesen, indem zuerst jede Reihe durchlaufen wird, und innerhalb jeder Reihe wird jede Spalte gelesen. Wenn Sie die aktuelle Zeile und Spalte kennen, können die Schrittmotoren in die gleichen Positionen bewegt werden. An jeder Position wird die LED auf die Farbe des entsprechenden Pixels geändert.

(re)-Erstellen eines Bildes

Nach dem Einlegen der SD-Karte und dem Anschließen einer 12-V-Stromquelle für die Motoren war es an der Zeit, die Maschine einzuschalten. An meiner Kamera habe ich eine Belichtungszeit von 20 Sekunden, eine Blende von F36, ISO von 100 und eine Belichtungskorrektur von -5 Blendenstufen eingestellt, um Geisterbilder zu minimieren. Das erste gezeichnete Bild war ein Pokeball, hier zu sehen:

Obwohl es etwas verschwommen ist, ist die Form immer noch deutlich zu erkennen. Dann wurde eine Herz-Bitmap erstellt:

Da dieses Bild nur 9 x 9 Pixel groß war, ist jedes einzelne Pixel viel weniger definiert. Zuletzt habe ich ein Bild von Mario beim Springen gemalt:

Dieses Bild weist starke Geisterbilder auf, hauptsächlich aufgrund der Fülle an bunten Pixeln.

Zukünftige Verbesserungsideen

Die von mir geschaffenen Lichtbilder sind viel besser geworden, als ich ursprünglich gedacht hatte, aber es gibt noch Raum für Verbesserungen. Die Hauptsache, die ich tun möchte, ist, die Unschärfe zu reduzieren, indem sich die LED im abgedunkelten Zustand bewegt und dann nur noch aufleuchtet. Diese Technik würde die Klarheit der neu erstellten Bilder erheblich verbessern.

Code

  • Light-Painting-Programm
Light-Painting-ProgrammC/C++
//Bitmap-Lesefunktion teilweise von Adafruit#include #include #include "DRV8825.h"#define MOTOR_STEPS 200#define RPM 150#define MICROSTEPS 4//Pin-Definitionen#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 oder -1 um Richtung umzukehrenIR_FLAG 1_ //D 1 oder -1 um die Richtung umzukehren#define STEPS_PER_MM (3,75 * MICROSTEPS) // benötigte Schritte um 1mm zu bewegen#define SPACE_BETWEEN_POSITIONS 5 //5mm pro Bewegung#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);void setup() { Serial.begin(115200); init_steppers(); SD.begin(SD_CS); createBitmaps(); stepperX.disable(); stepperY.disable(); while(1);}void loop() {}void createBitmaps(){ Datei dir =SD.open("bitmaps"); while(true){ Datei-Bitmap =dir.openNextFile(); if(!bitmap){ break; } PaintBitmap (Bitmap); Verzögerung (15000); }} #define BUFFPIXEL 20void paintBitmap (Datei bmpFile) { int bmpWidth, bmpHeight; uint8_t bmpDepth; uint32_t bmpImageOffset; uint32_t Zeilengröße; // Nicht immer =bmpWidth; kann Auffüllen haben uint8_t sdbuffer[3 * BUFFPIXEL]; // Pixelpuffer (R+G+B pro Pixel) uint8_t buffidx =sizeof(sdbuffer); // Aktuelle Position in sdbuffer boolean goodBmp =false; // Bei gültigem Header-Parse auf true setzen boolean flip =true; // BMP wird von unten nach oben gespeichert int w, h, row, col; uint8_t r, g, b; uint32_t pos =0, startTime =millis(); Serial.println(); Serial.print ( "Bild wird geladen"); Serial.print (bmpFile.name ()); Serial.println('\''); // Angeforderte Datei auf SD-Karte öffnen // BMP-Header analysieren if (read16 (bmpFile) ==0x4D42) {// BMP-Signatur Serial.print ("Dateigröße:"); Serial.println (read32 (bmpFile)); (void)read32(bmpDatei); // Ersteller-Bytes lesen &ignorieren bmpImageOffset =read32(bmpFile); // Start der Bilddaten Serial.print("Image Offset:"); Serial.println (bmpImageOffset, DEC); // DIB-Header lesen Serial.print ( "Header-Größe:"); Serial.println (read32 (bmpFile)); bmpWidth =read32(bmpDatei); bmpHeight =read32(bmpDatei); if (read16(bmpFile) ==1) {// # Ebenen -- muss '1' sein bmpDepth =read16(bmpFile); // Bits pro Pixel Serial.print ("Bittiefe:"); Serial.println (bmpDepth); if ((bmpDepth ==24) &&(read32(bmpFile) ==0)) {// 0 =unkomprimiert goodBmp =true; // Unterstütztes BMP-Format -- fortfahren! Serial.print ( "Bildgröße:"); Serial.print (bmpWidth); Serial.print ('x'); Serial.println (bmpHeight); // BMP-Zeilen werden (falls erforderlich) auf eine 4-Byte-Grenze aufgefüllt. rowSize =(bmpWidth * 3 + 3) &~3; // Wenn bmpHeight negativ ist, ist das Bild von oben nach unten. // Dies ist kein Kanon, wurde aber in freier Wildbahn beobachtet. if (bmpHeight <0) { bmpHeight =-bmpHeight; umdrehen =falsch; } // Zu ladender Zuschneidebereich w =bmpWidth; h =bmpHöhe; if (bmpWidth * bmpHeight> 290) {//Zu groß Serial.println ( "Datei ist zu groß zum Drucken."); Rückkehr; } for(uint8_t i=0; i<5;i++){analogWrite(R, 150); Verzögerung (500); analogWrite(R, 0); Verzögerung (500); } for (row =0; row=sizeof(sdbuffer)) { // Tatsächlich bmpFile.read(sdbuffer, sizeof(sdbuffer)); buffidx =0; // Index auf Anfang setzen } // Pixel vom BMP- in das TFT-Format konvertieren, drücken, um anzuzeigen b =sdbuffer[buffidx++]; g =sdbuffer[buffidx++]; r =sdbuffer[buffidx++]; moveToPosition(Spalte, Zeile); AktiviereLED(r,g,b); // optimiert! //tft.pushColor(tft.Color565(r,g,b)); } // Pixel beenden analogWrite (R, 0); analogWrite (G, 0); analogWrite (B, 0); } // Scanline beenden Serial.print ("Geladen in"); Serial.print (millis () - startTime); Serial.println("ms"); } // GoodBmp beenden } } bmpFile.close(); moveToPosition(0,0); if (!goodBmp) Serial.println("BMP-Format nicht erkannt.");}uint16_t read16(File f) { uint16_t result; ((uint8_t *)&result)[0] =f.read(); // LSB ((uint8_t *)&result)[1] =f.read(); // MSB Ergebnis zurückgeben;}uint32_t read32(Datei f) { uint32_t Ergebnis; ((uint8_t *)&result)[0] =f.read(); // LSB ((uint8_t *)&result)[1] =f.read(); ((uint8_t *)&result)[2] =f.read(); ((uint8_t *)&result)[3] =f.read(); // MSB-Return-Ergebnis;} void activateLED (int r, int g, int b) {Serial.print (F("LED hat den Wert:")); 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-aktuellePositionen[Y])*STEPS_PER_MM*Y_DIR_FLAG*SPACE_BETWEEN_POSITIONS; stepperX.move(newPosX); stepperY.move(newPosY); aktuellePositionen[X] =x; aktuellePositionen[Y] =y; Serial.print ("Stepperpositionen:"); Serial.print (aktuellePositionen[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);}

Kundenspezifische Teile und Gehäuse

Schaltpläne


Herstellungsprozess

  1. Was führte uns zurück zur Verwendung natürlicher Materialien im Produktdesign?
  2. Bewegungssensor mit Raspberry Pi
  3. Leitfaden für Wartungsmanager zum Erstellen und Verwenden von FMEAs
  4. Senden Sie Sensordaten von einem Arduino zu einem anderen mit Firebase
  5. Tauschen Sie zwei Python-Variablen aus, ohne eine dritte zu verwenden
  6. Sargtanzmelodie
  7. Alte Fernbedienungen wiederverwenden
  8. Steuern Sie Arduino Rover mit Firmata und Xbox One-Controller
  9. 8x LED-Beleuchtung mit Ton
  10. Arduino-Vierbeiner