
Technique pour photographier des gouttes d’eau
Le temps est probablement « l’objet » le plus fascinant de notre univers. Pour nous, le temps passe et se déroule avec une perpétuelle succession d’instants. Instants si brefs que ni notre œil, ni notre esprit ne peut les percevoir.
Capturer l’instant, l’instant qui ne se répétera pas, pour le garder comme témoin de ce qui a été et qui ne se reproduira plus ; C’est ce que l’on tente de faire quand on prend une photo.
Le temps, l’espace et la lumière
Prendre en photo quelque chose d’aussi simple que deux gouttes d’eau qui se rencontrent, est une belle représentation de ce lien entre le temps, la lumière et le mouvement dans l’espace. Bien sur on peut compter sur le hasard pour y arriver, prendre des milliers de photos jusqu’à capturer cette rencontre… Ou alors, on peut tenter de contrôler le temps, choisir à la milliseconde près l’instant où la première goutte est lancée, puis celui où la seconde goutte la suit, et finalement l’instant de la prise de vue.
Ce billet explique la solution que j’ai mis en place pour arriver à prendre en photo l’impact de deux gouttes d’eau avec une carte Arduino et une valve solenoid.
Composants utilisés :
- Un carte arduino Uno
- Un émetteur IR HX 53
- Un rotary encoder KY-040
- Une valve solenoid 9v
- Un écran LCD 20×4 (de préférence avec module I2C)
- Un appareil photo reflex ou mirrorless
- Un flash externe (Yungnuo 468 II)

Comment ça marche ?
Le dispositif a pour rôle de déclencher la valve solenoid en laissant passer 2 gouttes d’eau, espacées entre elle par un délai, puis de déclencher l’appareil photo. Pour saisir les paramètres on utilise un rotary encoder (ce n’est pas la façon la plus pratique, idéalement vous pouvez utiliser une télécommande IR avec un récepteur IR, ou un clavier numérique)
Détail du programme Arduino
Voici ici un petit descriptif rapide de toutes les étapes que suit le programme pour prendre en photo l’impact de deux gouttes d’eau :
Première goutte :
1- Ouvrir la valve
2- Délais 1 : Ce premier délai permet de spécifier le diamètre de la goutte d’eau. Plus le délais est grand, plus la goutte sera grande. Pour mon cas, un délais de 20ms a donné de bon résultats.
3- Fermer la valve
Seconde goutte :
1- Délai 2 : Ce délais représente le temps entre la première et la seconde goutte d’eau.
2- Ouvrir la valve
3- Délai 3 : Diamètre de la seconde goutte
4- Fermer la valve.
Prise de vue
1- Délai 4 : Délai entre la fermeture de la valve de la seconde goutte et le déclenchement de la prise de vue.
2- Déclencher l’appareil photo et le flash.
Difficulté et astuces
Plusieurs paramètres entrent en jeux pour pouvoir arriver à prendre une photo de deux gouttes d’eau à l’instant où elles se rencontrent :
- La distance entre la valve et la surface de l’eau
- La profondeur du récipient utilisé
- Le diamètre des gouttes
- Le délai entre deux gouttes
- Le délai pour déclencher l’appareil photo
Si comme moi vous commencez par essayer des valeurs au hasard, vous allez rapidement être frustré. La solution qui a marché pour moi c’est d’écrire un bout de code dont le rôle est uniquement de déclencher une goutte, puis de déclencher l’appareil photo.
Au niveau de la boucle, j’ajoute 2ms entre chaque deux essais. Jusqu’à ce que j’arrive au moment ou la goutte d’eau atteint son pic en rebondissant.
Une fois que vous aurez réussi à prendre cette prise, l’écran LCD de votre arduino affiche le delai en ms utilisé. Gardez votre valve à la même hauteur, et faites vos calculs pour trouver le délais nécessaire entre deux goutte, et le nouveau délai pour la prise de vue (Le délai de la prise de vue à partir du moment de la sortie la seconde goutte)

Exemple :
Le test avec une goutte nous a donné 120 ms pour atteindre le lieu d’impact et 370 ms pour rebondir au pic. N’oubliez pas aussi qu’on a un diamètre (délai) de la goutte 1 de 20 ms. La durée totale est donc de 390 ms. (370+20)
ddd
Maintenant, en ajoutant une seconde goutte :
La première goutte est sortie, elle rebondit, la seconde doit arriver à l’emplacement que la première atteint quand elle a rebondit (120ms). Le délais entre la première et la seconde goutte doit donc être de 120 ms. Le déclenchement de l’appareil photo doit être de 370 ms à partir de la première goutte (et non la seconde). Donc le délais de déclenchement de la prise est de 370 – 120 = 250 ms
Pour rendre la prise de vue plus facile et avoir plusieurs résultats d’impacts, vous pouvez utiliser une incrémentation de délais de 1 ou 2 ms à chaque itération de boucle.

Le code source est disponible sur mon compte github, n’hésitez pas à l’utiliser, l’adapter à vos besoin ou à l’améliorer.
#include "Wire.h" // Pour I2C
#include "LCD.h" // Pour ecran LCD
#include "LiquidCrystal_I2C.h" // Added library*
#include <multiCameraIrControl.h> // Librairie Infra rouge Canon 7d
Canon myCamera(9); // INITIALISATION CAMERA IR
LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7);
#define outputA 6 // PIN POUR ROTARY ENCODER
#define outputB 7 // PIN POUR ROTARY ENCODER
#define switchPin 3 // PUSH DU ROTARY ENCODER
#define VALVE 13 // CONTROLEUR VALVE
int counter = 0;
int aState;
int aLastState;
int count; // COMPTEUR SECTIONS
int DIAMG1; // DIAMETRE GOUTTE 1
int DIAMG2; // DIAMETRE GOUTTE 2
int DELAI1; // DELAI AVANT GOUTTE 2
int DELAI2; // DELAI AVANT OBTURATION
int passFlag; // FLAG POUR CONTROLER SECTION 1
int passFlag1; // FLAGPOUR CONTROLER SECTION 2
int passFlag2; // FLAG POUR CONTROLER SECTION 3
int passFlag3; // FLAG POUR CONTROLERR SECTION 4
int val = 0; // NON UTILISEE, MAIS A NE PAS SUPPRIMER (SINON CA BUG)
int currentState = 0;
int previousState = 0;
void setup()
{
// Preparation écran lcd
lcd.begin (20,4); // 20 x 4 LCD module
lcd.setBacklightPin(3,POSITIVE); // BL, BL_POL
lcd.setBacklight(HIGH);
Serial.begin(9600);
pinMode (outputA,INPUT);
pinMode (outputB,INPUT);
pinMode (VALVE,OUTPUT);
pinMode(switchPin, INPUT_PULLUP);
Serial.begin (9600);
// Reads the initial state of the outputA
aLastState = digitalRead(outputA);
}
void loop() {
val = digitalRead(switchPin); // read input value
if (val == HIGH) { // check if the input is HIGH (button released)
currentState = 1;
}
else {
currentState = 0;
}
if(currentState != previousState){
if(currentState == 1){
counter = counter + 1;
lcd.setCursor(0,2);
lcd.print ("PUSH BUTTON TO START");
}
}
previousState = currentState;
// DIAMETRE GOUTTE N1
while (counter == 2) {
val = digitalRead(switchPin); // read input value
if (val == HIGH) { // check if the input is HIGH (button released)
currentState = 1;
}
else {
currentState = 0;
}
if(currentState != previousState){
if(currentState == 1){
counter = counter + 1;
lcd.setCursor(0,0);
lcd.print (counter);
}
}
previousState = currentState;
if (passFlag == 0) {
lcd.clear();
passFlag ++;
}
lcd.setCursor(0,0);
lcd.print ("DIAMETRE GOUTTE 1");
aState = digitalRead(outputA); // Lire etat actuel OutputA
// Si l'état précédent et actuel de OutputA sont différent, il y a eu un Pulse
if (aState != aLastState){
// Si OutputB est différent de OutputA : Encoder tourne dans le sens des aiguilles
if (digitalRead(outputB) != aState) {
DIAMG1 ++;
} else {
DIAMG1 --;
}
lcd.setCursor(0,1);
lcd.print(DIAMG1*10);
}
aLastState = aState; // Mise à jour de l'état précédent avec l'actuel
}
// DELAI NUMERO 1
while (counter == 3) {
if (passFlag1 == 0) {
lcd.clear();
passFlag1 ++;
}
lcd.setCursor(0,0);
lcd.print ("DELAI ENTRE GOUTTES");
val = digitalRead(switchPin); // read input value
if (val == HIGH) { // check if the input is HIGH (button released)
currentState = 1;
}
else {
currentState = 0;
}
if(currentState != previousState){
if(currentState == 1){
counter = counter + 1;
}
}
previousState = currentState;
// lcd.setCursor(0,3);
aState = digitalRead(outputA); // Lire etat actuel OutputA
// Si l'état précédent et actuel de OutputA sont différent, il y a eu un Pulse
if (aState != aLastState){
// Si OutputB est différent de OutputA : Encoder tourne dans le sens des aiguilles
if (digitalRead(outputB) != aState) {
DELAI1 ++;
} else {
DELAI1 --;
}
lcd.setCursor(0,1);
lcd.print(DELAI1*10);
}
aLastState = aState; // Mise à jour de l'état précédent avec l'actuel
}
while (counter == 4) {
if (passFlag2 == 0) {
lcd.clear();
passFlag2 ++;
}
lcd.setCursor(0,0);
lcd.print ("DIAMETRE GOUTTE 2");
val = digitalRead(switchPin); // read input value
if (val == HIGH) { // check if the input is HIGH (button released)
currentState = 1;
}
else {
currentState = 0;
}
if(currentState != previousState){
if(currentState == 1){
counter = counter + 1;
//lcd.setCursor(0,0);
//lcd.print (counter);
}
}
previousState = currentState;
aState = digitalRead(outputA); // Lire etat actuel OutputA
// Si l'état précédent et actuel de OutputA sont différent, il y a eu un Pulse
if (aState != aLastState){
// Si OutputB est différent de OutputA : Encoder tourne dans le sens des aiguilles
if (digitalRead(outputB) != aState) {
DIAMG2 ++;
} else {
DIAMG2 --;
}
lcd.setCursor(0,1);
lcd.print(DIAMG2*10);
}
aLastState = aState; // Mise à jour de l'état précédent avec l'actuel
}
// DELAI NUMERO 2
while (counter == 5) {
if (passFlag2 == 0) {
lcd.clear();
passFlag2 ++;
}
lcd.setCursor(0,0);
lcd.print ("DELAI AVANT FLASH");
val = digitalRead(switchPin); // read input value
if (val == HIGH) { // check if the input is HIGH (button released)
currentState = 1;
}
else {
currentState = 0;
}
if(currentState != previousState){
if(currentState == 1){
counter = counter + 1;
}
}
previousState = currentState;
// lcd.setCursor(0,3);
//lcd.print ("Numero trois");
aState = digitalRead(outputA); // Lire etat actuel OutputA
// Si l'état précédent et actuel de OutputA sont différent, il y a eu un Pulse
if (aState != aLastState){
// Si OutputB est différent de OutputA : Encoder tourne dans le sens des aiguilles
if (digitalRead(outputB) != aState) {
DELAI2 ++;
} else {
DELAI2 --;
}
lcd.setCursor(0,1);
lcd.print(DELAI2*10);
}
aLastState = aState; // Mise à jour de l'état précédent avec l'actuel
}
while (counter == 6) {
val = digitalRead(switchPin); // read input value
if (val == HIGH) { // check if the input is HIGH (button released)
currentState = 1;
}
else {
currentState = 0;
}
if(currentState != previousState){
if(currentState == 1){
counter = counter + 1;
}
}
previousState = currentState;
if (passFlag3 == 0) {
lcd.clear();
delay(1000);
if (DIAMG1< 0) { DIAMG1 = DIAMG1 * -1;}
if (DIAMG2< 0) { DIAMG2 = DIAMG2 * -1;}
if (DELAI1< 0) { DELAI1 = DELAI1 * -1;}
if (DELAI2< 0) { DELAI2 = DELAI2 * -1;}
DIAMG1 = DIAMG1 *10;
DIAMG2 = DIAMG2 *10;
DELAI1 = DELAI1*10;
DELAI2 = DELAI2*10;
lcd.setCursor(0,0);
lcd.print (DIAMG1);
lcd.setCursor(0,1);
lcd.print (DELAI1);
lcd.setCursor(0,2);
lcd.print (DIAMG2);
lcd.setCursor(0,3);
lcd.print (DELAI2);
Serial.println(DIAMG1);
Serial.println(DIAMG2);
Serial.println(DELAI1);
Serial.println(DELAI2);
passFlag3 ++;
}
}
// ACTIVATION VALVE + FLASH
while (counter == 7) {
lcd.clear();
// DIAMETRE GOUTTE 1
digitalWrite(VALVE, HIGH);
delay (DIAMG1);
digitalWrite(VALVE, LOW);
// DELAI 1
delay (DELAI1);
// DIAMETRE GOUTTE 1
digitalWrite(VALVE, HIGH);
delay (DIAMG2);
digitalWrite(VALVE, LOW);
// DELAI POUR ACTIVATION FLASH
delay(DELAI2);
myCamera.shutterNow();
delay(10000);
}
}
Quels réglages pour l’appareil photo et le flash ?
Pour commencer, un objectif macro est presque obligatoire à avoir si vous souhaitez faire de grands tirages de vos photos. Pour une utilisation web, n’importe quel objectif à partir de 50 mm fera l’affaire, sachez cependant que vous allez devoir cropper la photo.
Un flash externe, personnellement j’utilise le flash Yungnuo 468, car il permet de descendre à une puissance de flash de 1/128, ce qui permet de figer la goutte et de ne pas avoir de flou de mouvement.
La vitesse d’obturation ici n’a pas d’importance, car c’est uniquement la lumière du flash qui est captée.
Autres essais à venir
Cet article sera régulièrement mis à jour, avec l’ajout de nouvelles expériences photos de gouttes d’eau et des explications des techniques utilisées pour chacune.
Si vous avez des questions ou des remarques, n’hésitez pas à m’en faire part en commentaire.
One Response to “Technique pour photographier des gouttes d’eau”
Merci pour le conseil, je le note pour moi aussi m’entrainer.