Bonjour tout le monde !
Aujourd’hui j’ai décidé de publier l’article sur mon systéme de light painting assisté par ordinateur.
J’avais prévu de le publier il y a déja plus de 6 mois (oui ça fait pas mal de temps) mais la météo était (et est toujours) contre moi …
Du coup j’ai décidé de publier la partie hardware / software en attendant de pouvoir (un jour) faire les photos finales.
—
Le principe :
Avec un appareil photo en mode « exposition manuelle » il est possible de dessiner des images avec de la lumière, c’est le principe même du « light painting ».
http://fr.wikipedia.org/wiki/Light_painting
Sauf que faire des dessins à la main ne donne pas des résultats trés propre et précis, à moins d’avoir un plan pré-dessiné et une exposition de longue durée.
C’est pourquoi j’ai bricolé un systéme permettant de dessiner une image en light painting avec l’aide d’un ordinateur.
J’avais déjà fait un premier essai avec 32 led RGB capable d’afficher 8 couleurs différente (l’article est toujours sur le blog).
Dans cette nouvelle version j’utilise 64 led RGB sous la forme de rubans de led « commercial », ce qui me permet d’avoir 4096 couleurs différentes.
De plus je passe d’un montage « maison » affichant des images de 30cm, à un montage affichant des images de 2m de haut !
—
Le hardware :
Le hardware est constitué :
- un ruban de led RGB (adressable, vendu par adafruit) de 2m (soit 64 RGB led en tout)
- une carte arduino pro mini 5v + module FTDI basic 5v (vendu par sparkfun)
- une pile 5v rechargeable de 1.5A
- une baguette en bois, des morceaux de fils de fer et des élastiques
- des câbles et des borniers pour connecter tout le montage
Afin de pouvoir tenir le ruban de led bien droit, celui est fixé sur une baguette en bois de 2m.
Et est maintenu en place avec des morceaux de fils de fer et des élastiques (Mc Guyver inside).
Pour éviter les problémes de condensation et d’humidité, les extrémités du ruban sont scellé de manière étanche avec de la colle chaude.
Tout le systéme électronique est fixé autour de la baguette en bois afin de pouvoir transporter le tout en toute simplicité.
Pour l’alimentation (de puissance) j’utilise une batterie 1.5A rechargeable avec sortie USB (normalement conçu pour recharger des appareils USB pendant un voyage) avec un câble usb maison bricolé pour « piquer » le +5v et la masse en sortie de la batterie.
La liaison ordinateur / ruban de led est réalisé par une carte arduino pro mini 5v de sparkfun et un module FTDI basic.
Le tout est connecté à mon netbook (mini ordi portable) sur lequel tourne un script python transférant les données d’une image au ruban de led (voir plus bas).
Pour ce qui est du câblage :
Arduino -> ruban
VCC -> +5v
GND -> GND
D11 -> DI
D13 -> CI
Alimentation :
VCC = VCC (batterie usb) + VCC (ftdi basic) -> +5v (ruban led)
GND = GND (batterie usb) + GND (ftdi basic) -> GND (ruban led)
—
Le software :
Le software est composé de deux parties :
– un code arduino, qui reçoit via le port série les pixels de l’image à afficher
– un script python, qui lit puis transmet via le port série l’image dont on lui donne le nom en paramètre
Le protocole de communication est extrêmement simple :
Au démarage de l’arduino : « RDY » + \n (pour annoncer au script python que l’arduino est prêt à recevoir les données)
Pendant l’affichage : « FRM » + 64 séries de 3 octets (R, G, B) (soit une colonne de pixels)
l’arduino répond ensuite « ACK » + \n pour confirmer la bonne réception des données.
Le code arduino :
/* Includes */ #include <SPI.h> /* Nombre de led du ruban */ #define LED_STRIP_LEN 64 /* Macro calculant le nombre de "latch" du ruban en fonction du nombre de led */ #define LATCH_COUNT(n) (((n + 63) / 64) * 3) /* Buffer d'affichage et itérateur */ uint8_t pixels[LED_STRIP_LEN * 3]; unsigned int i; /* Envoi n "0" sur le port SPI */ void writeZero(uint16_t n) { while(n--) SPI.transfer(0); } /* Envoi le contenu du buffer d'affichage au ruban de led */ void showPixels(void) { for (i = 0; i < LED_STRIP_LEN * 3; i++ ) SPI.transfer(pixels[i]); writeZero(LATCH_COUNT(LED_STRIP_LEN)); delay(10); } /* Assigne une couleur RGB à un pixel spécifique */ void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { n = n * 3; pixels[n] = g | 0x80; // LPD8806 color order is GRB, pixels[n + 1] = r | 0x80; pixels[n + 2] = b | 0x80; } /* setup() */ void setup() { /* Initialisation du port série */ Serial.begin(115200); /* Par défaut le ruban de led est éteint (0x80) */ memset(pixels, 0x80, LED_STRIP_LEN * 3); /* Initialisation du port SPI */ SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV8); // 2 MHz /* Initialisation du ruban de led */ writeZero(LATCH_COUNT(LED_STRIP_LEN)); showPixels(); /* Message de boot */ Serial.println("RDY"); } /* loop() */ void loop() { /* Attend l'arrivé d'au moins 3 octets */ while(Serial.available() < 3); /* Vérifie que les 3 octets reçu correspondent à l'entête de la commande d'affichage */ if(Serial.read() == 'F') { if(Serial.read() == 'R') { if(Serial.read() == 'M') { /* Reçoit les valeurs RGB de chaque pixel */ for(i = 0 ; i < LED_STRIP_LEN ; i++) { while(Serial.available() < 3); setPixelColor(i, Serial.read(), Serial.read(), Serial.read()); } /* Affiche les pixels et confirme la réception des données */ showPixels(); Serial.println("ACK"); } } } }
Le script python :
#!/usr/bin/python # Port série utilisait par l'arduino SERIALPORT = "COM8" # Vitesse du port série SERIALSPEED = 115200 # Temps entre deux frame (en secondes, nombre à virgule) WAITTIME = 0.05 # Nombre de led sur le ruban NBPIXELS = 64 # Image source IMGSRC = "nyancat.png" # Temps avant lancement du streaming STREAMDELAY = 2 # --------------------------------------------------------------------- import Image, serial, time def delay(t): for i in range(t, 0): print i, time.sleep(1) print "~ Led Strip - Ligth Painting Assistant ~" print "Created by SkyWodd" print " " print "Checking source image ... ", try: im = Image.open(IMGSRC) im2 = im.rotate(270) data = list(im2.getdata()) if len(data) < NBPIXELS or len(data[0]) != 3 : print "FAIL ! =(" print "Source image must contain more than %d pixels in 24bits colors !" % NBPIXELS exit(-1) except: print "FAILLED !" exit(-1) print "OK !" print "Openning serial port ... ", try: arduino = serial.Serial(SERIALPORT, SERIALSPEED, timeout=1) except: print "FAILLED !" exit(-1) print "OK !" print "Booting up arduino ... ", arduino.setDTR(level = True) time.sleep(0.5) arduino.setDTR(level = False) ligne = arduino.readline() while not "RDY" in ligne: ligne = arduino.readline() print "Ok !" delay(5) print "Streaming system loaded !" delay(STREAMDELAY) print " " nbFrames = len(data) / NBPIXELS print "Number of frames to sent : %d" % nbFrames tstart = time.time() for frame in range(0, nbFrames): print "# %d / %d" % (frame, nbFrames - 1) arduino.write("FRM") offset = frame * NBPIXELS for i in range(offset, offset + NBPIXELS): arduino.write(chr(data[i][0])) arduino.write(chr(data[i][1])) arduino.write(chr(data[i][2])) ack = arduino.readline() if not "ACK" in ack: print "- COM ERROR -" time.sleep(WAITTIME) print "Power off led ..." arduino.write("FRM") for i in range(0, NBPIXELS): arduino.write(chr(0)) arduino.write(chr(0)) arduino.write(chr(0)) ack = arduino.readline() if not "ACK" in ack: print "- COM ERROR -" print "End of stream !" print "Streaming take %d seconds" % (time.time() - tstart) print "Closing serial port ... ", arduino.close() print "OK !" print "Bye bye !"
—
Le résultat :
(Il s’agit juste de photos de test pour le moment, puisse que je n’ai pas pu faire les « vrai » photo à cause la météo)
Original :
Light painting :
Original :
Light painting :
Original :
Light painting (j’ai manqué de place pour faire l’image en entière) :
Original :
Light painting :
—
J’espère pouvoir faire les photos en HD avant la fin des vacances.
Pour le moment ça me semble bien mal parti vu la météo annoncé …
Enjoy 🙂
Bonjour, cher arduinaute, tes articles au passage sont toujouts très intéressants.
Je n’ai pas bien compris le fonctionnement, il faut que tu te déplaces avec le bâton devant un appareil photo en pause ?
En tout cas bravo pour la créativité
Alain
>> Je n’ai pas bien compris le fonctionnement, il faut que tu te déplaces avec le bâton devant un appareil photo en pause ?
Il faut avoir un appareil photo DSLR (bridge numérique ou reflex) en mode exposition longue durée >10 secondes.
Ensuite dans le noir total, il suffit ce déplacer de gauche à droite à vitesse constante avec le bâton en main pour que l’exposition longue donne cet effet de light painting.
Exemple en vidéo :
(vidéo trouvé vite fait sur google)
Bonjour, je suis intéressé par ce projet, cependant je ne comprends pas comment vous gérez le code python… où intervient-il ?
merci pour la réponse
Le code python sert à transmettre l’image en temps réel depuis l’ordinateur vers le ruban de leds pour l’affichage.