Motion Control avec Arduino motoriser un curseur de caméra

La disponibilité des moteurs pas à pas et des pilotes bon marché offre de nombreuses possibilités d'expérimenter en dehors des projets de découpe et d'impression 2D / 3D, plus coûteux et compliqués.. 

Pour ce projet, je prendrai le curseur de caméra OpenBuilds (voir la vidéo de construction dans Construire un curseur vidéo de base avec des pièces CNC Open Source) et le motoriserai. Je vais aussi créer un système autonome pour contrôler le moteur.

Ce tutoriel traite spécifiquement de l’assemblage du matériel, mais principalement de la construction d’une interface graphique LCD 16x2 rudimentaire à l’aide de la bibliothèque LiquidCrystal et d’un système de menus simple à afficher, suivie du fonctionnement du pilote pas à pas A4988 et de la commande correspondante avec Arduino.. 

Ce projet est lourd en boucles et en étapes, et bien que dans l’ensemble le projet soit plus intermédiaire, j’ai essayé de l’expliquer de manière à ce que les débutants puissent être opérationnels assez rapidement..

Liste d'équipement

Composants

  • Arduino Uno
  • LCD Keypad Shield ou LCD 16x2 séparé et boutons si vous savez comment faire pour que cela fonctionne
  • Pololu A4988 [Black Edition] Pilote pas à pas
  • Petit dissipateur thermique en aluminium
  • Planches à pain, fils de connexion mâles et femelles, etc.
  • Résistance 220-330 ohm (1 / 4W fera probablement), transistor NPN standard (j'ai utilisé un BC109)
  • Prise stéréo TRS 3,5 mm
  • Câble adaptateur stéréo TRS de 3,5 mm à 2,5 mm
  • Câble de rallonge de 3,5 mm en fonction de la longueur du curseur
  • Alimentation à prise cylindrique 9 V si vous souhaitez retirer l’Arduino de la source USB de votre ordinateur
  • Alimentation 12V 2A pour faire fonctionner le moteur pas à pas
  • Moteur pas à pas NEMA 17

les pièces

  • Courroie de distribution GT2 d'une largeur de 5 mm et d'un pas de 2 mm: double la longueur du curseur et un pied de sécurité (11 pieds pour moi)
  • Kit galet tendeur lisse
  • Ressort de torsion de la courroie si vous avez du mal à maintenir la courroie à long terme
  • 2x pince à sertir de ceinture (peut être remplacée par de petites zipties)
  • Poulie en aluminium GT2 d'une largeur de 7 mm, 20 dents, de la même dimension que l'arbre du moteur
  • 4x vis à tête cylindrique M3-0.5 de 30mm

Outils

  • Ordinateur avec IDE Arduino (j'utilise Win7, Arduino 1.0.5 r2)
  • Fer à souder avec petite pointe de burin, soudure, etc.
  • Clé allen 2.5mm pour vis M5
  • Clé allen 2mm pour vis M3
  • Clé Allen de 1,5 mm pour vis de réglage dans la poulie GT2
  • multimètre pour le dépannage et le réglage du courant
  • Pince étroite pour serrer dans de petits espaces

Vue d'ensemble fonctionnelle

Je couvrirai en ajoutant le moteur et les poulies au curseur, en enroulant la courroie et en la fixant. C'est une simple modification. 

Ensuite, je vais expliquer comment assembler un kit Pololu A4988 Black Edition et comment le connecter à une planche à pain avec toutes les autres cartes externes, ainsi qu’à un simple boîtier en contreplaqué que j’ai assemblé en quelques minutes pour obtenir une alimentation 12V. d’alimentation (énumérés ci-dessus) afin d’éviter les chocs lorsque les bornes de câblage sont exposées.

Sur le côté de sorte qu'il reste suffisamment grand pour que les numéros de broches soient encore visibles!

Le menu permet la saisie de distance à parcourir, temps de voyager, nombre d'étapes à parcourir et direction du voyage. À la fin de chaque étape, le curseur se met en pause pendant le déclenchement de la caméra..

Modification du curseur

Étape 1: Assemblage du moteur

Le montage d'extrémité de l'actionneur V-Slot OpenBuilds comporte des trous de dimension NEMA 17, de sorte qu'il suffit de quatre vis à tête cylindrique M3 de 30 mm pour monter le moteur..

Montage d'extrémité de l'actionneur V-Slot OpenBuilds

Assurez-vous que la poulie GT2 à 20 dents se trouve à l'intérieur du support avant d'insérer l'arbre du moteur, car le support n'est pas assez large pour pouvoir être rajouté par la suite. Une fois que le moteur est vissé en bas, serrez les vis de réglage en plaçant l'une d'entre elles contre la partie plate de l'arbre du moteur, en veillant à ce que les dents soient directement alignées avec le centre de l'ensemble de l'extrudeuse..

Étape 2: Kit de poulie libre

Le kit de poulie de renvoi va comme un kit de roue et s’insère dans le support d’actionneur de l’autre extrémité:

Le kit poulie libre

Étape 3: Attacher

Faites passer la courroie par le centre de la fente en V, dans le prolongement des poulies, en veillant à ce que les dents soient orientées vers le haut.. 

Puis alimentez-le et sur les deux poulies et ramenez-le au centre de la plaque de construction du chariot.

Les dents s'agrippent.

Ici, vous enroulez un côté dans la fente pour ceinture et le fixez ou zippez-le, puis utilisez-le pour serrer toute la courroie dans l'ensemble du système avant de connecter l'autre côté. Pas trop serré pour que le moteur tourne, mais pas assez lâche pour faire sauter les dents de la poulie d'entraînement!

Assemblage de l'électronique

Étape 1: Assembler le pilote pas à pas

Le pilote de moteur pas à pas Pololu A4988 Black Edition (techniquement, la carte de support A4988 - la puce A4988 étant la puce elle-même) est généralement livré sous forme de kit, ce qui signifie simplement que les en-têtes doivent être soudés. Étant donné qu’il s’agit d’un composant de puissance, même s’il ne pilote pas l’unité à sa capacité maximale, il est judicieux d’ajouter un dissipateur de chaleur pour prolonger sa durée de vie..
Casser la ligne d'en-tête en deux pour avoir deux lignes de huit. Insérez-les dans les trous métallisés de la planche, puis insérez-les soigneusement dans la planche à pain. Souder les épingles en place pendant que la planche à pain tient tout bien et perpendiculairement.

Assembler le pilote pas à pas

Une fois cette opération terminée, coupez le coin d'un petit dissipateur thermique autoadhésif à l'aide d'une scie à métaux ou d'une scie à chantourner (avec précaution, dans une pince!) Pour le monter sur le A4988 IC..

Couper le coin d'un petit radiateur autocollant

Étape 2: Breadboard-Mount les composants

Maintenant, tout doit être monté sur des planches à pain pour pouvoir être câblé ensemble dans un circuit fonctionnel. J'utilise des planches séparées pour chaque partie pour des raisons de clarté des images, mais n'hésitez pas à les intégrer dans une seule carte si vous le souhaitez..
La protection du clavier LCD ne peut pas être montée sur une carte, car Arduino a le choix étrange d'adhérer à un défaut de conception plutôt que de respecter les normes. Ce sera gardé séparé, bien que le visser à un morceau de bois ou quelque chose pour protéger les goupilles ne soit pas une mauvaise idée.

Le circuit de déclenchement de la caméra, dans sa forme la plus simple, consiste en une résistance, un transistor et une sous-mini-fiche TRS de 2,5 mm. J'ai ajouté une LED qui clignote lorsque la goupille de déclenchement est trop haute et un mini-jack TRS de 3,5 mm pour permettre une plus grande flexibilité.. 

Si vous achetez des composants pour cette version, une prise de 3,5 mm conçue pour des cartes de pas de 0,1 "serait une bonne idée, mais la mienne provient de la pile récupérée; j'ai donc soudé un connecteur à la place..
Disposez le tout, prêt à tout relier.

Étape 3: Tout connecter ensemble

Il est temps de saisir tous les câbles de démarrage. Avoir assez pour garder le code couleur des choses facilitera la vie lors du dépannage. Reportez-vous au schéma de circuit en haut si la description ci-dessous vous perturbe à tout moment..

Câbler tout ensemble

Connectez d'abord l'écran LCD. Saisissez 10 cavaliers femelles et connectez-les aux broches de blindage suivantes: broches numériques 4-9, réinitialisation des broches du bus d'alimentation (si vous souhaitez utiliser le bouton de réinitialisation de l'écran LCD), 5 V et l'un des GND.. 

Si vous avez des sauteuses entre femmes, vous pouvez en rester là. Sinon, connectez les cavaliers mâles à l'autre extrémité des femelles afin de les brancher aux sockets d'en-tête Arduino correspondants. Si vous avez un cache-clavier LCD sur lequel sont fixés des en-têtes femelles, vous pouvez sauter cette étape car votre cache ne bloque rien..
Ensuite, le panneau Pololu A4988. Cela nécessite huit cavaliers d'un côté, j'ai utilisé le noir et le rouge pour la logique / puissance du moteur à l'extrémité easch, et le rouge / vert / bleu / jaune au centre quatre pour faire correspondre les câbles d'asservissement du moteur pas à pas. 

La broche d'alimentation logique passe à 3,3V sur l'arduino, car l'écran LCD ci-dessus utilise la broche 5V. Les fils d’alimentation du moteur vont à votre alimentation 12V. De l'autre côté, près de la puce A4988, j'utilise le bleu et l'orange pour STP et DIR, respectivement, pour contraster avec les couleurs relativement uniformes partout ailleurs. Ils vont aux broches Arduino 11 et 12 respectivement, à moins que vous ne modifiiez le code. Ensuite, raccourcissez RST et SLP pour que le tableau reste activé. J'ai utilisé le fil blanc ici.

Lorsque vous avez terminé, cela devrait ressembler à quelque chose comme ça.

Enfin, connectez le circuit du commutateur de déclenchement de la caméra. Ici, les fils noirs sont mis à la terre - le fil de la rangée A vers Arduino, le fil de la rangée C vers la prise 3,5 mm. Le jaune va à la broche 13 Arduino (donc il y a un voyant sur la carte ainsi que sur le commutateur!), Et le fil rouge va de l’autre côté de la prise de 3,5 mm (ou le fil de prise de 2,5 mm si vous allez cette route).
Branchez le moteur pas à pas dans les fils colorés conformément au schéma de la carte A4988 et à la fiche technique de votre moteur pas à pas. Pour moi, c'était comme ça:

Comme le lecteur de bouton, le schéma de rotation de test est inclus dans le zip en haut..

Mise en garde: rappelez-vous que les fils qui alimentent le moteur vont probablement tirer 1-2A à la tension que vous avez choisie, assurez-vous que les fils utilisés sont conçus pour cela. La puce A4988 et sa carte peuvent devenir brûlantes! Le potentiomètre intégré à la carte fournit une limitation de courant pour protéger à la fois le A4988 et le moteur. Veillez donc à le régler correctement avant de l'utiliser avec un multimètre..

Mise en place du programme

Une fois les composants assemblés, vous pouvez passer au codage. Téléchargez le zip inclus dans ce tutoriel ou cochez ce référentiel GitHub si vous préférez. Je vais décrire comment je l'ai mis en place afin que vous puissiez comprendre le déroulement général du programme et comment les modules fonctionnent ensemble.

Étape 1: Comprend et définitions de base

La seule inclusion nécessaire pour cela était la bibliothèque d'écriture LCD LiquidCrystal.h. Cela donne accès à la lcd.xxxx () les fonctions. Il y a un pow () dans le programme, et j'ai trouvé que, y compris la bibliothèque C ++ math.h n'est pas nécessaire car certaines de ses fonctions les plus utiles sont incluses dans l'environnement Arduino standard, y compris pow ().


#comprendre  Liquid Crystal Crystal (8, 9, 4, 5, 6, 7); // définit les broches de sortie de l'écran LCD // définissent les broches du pilote pas à pas const int stp = 11; // ne peut pas utiliser la broche 10 avec l'écran LCD SS, car c'est la commande de rétroéclairage. // si le niveau baisse, le rétro-éclairage s'éteint! const int dir = 12; // définir la goupille de déclencheur const int trig = 13; // BOUTONS // définir les valeurs des boutons const int btnUp = 0; const int btnDn = 1; const int btnL = 2; const int btnR = 3; const int btnSel = 4; const int btnNone = 5; // définit les variables de lecture de bouton int btnVal = 5; int adcIn = 0;

Je règle les broches de sortie de l'écran LCD, les broches de sortie du pilote pas à pas et la broche de sortie du déclencheur de l'appareil photo. Une fois l'interface matérielle configurée, j'ai ajouté des variables pour les événements de bouton, suivies de la fonction de lecture de bouton, que j'ai adaptées à partir du wiki DFRobot sur le même écran de clavier LCD. Notez que SainSmart ne fournit aucune documentation..

Étape 2: Boucle d'installation ()

C'est super straightfoward. Initialisez l'écran LCD et les broches de sortie correspondantes, suivies d'un écran d'accueil de base, puis passez à l'écran d'accueil: option de menu 1 avec les valeurs mises à zéro.

void setup () lcd.begin (16, 2); // initialise LCD lib plein écran lcd.setCursor (0,0); // définit la position du curseur pinMode (stp, OUTPUT); // initialise les broches pas à pas pinMode (dir, OUTPUT); pinMode (trig, OUTPUT); // initialise la broche de déclenchement digitalWrite (trig, LOW); // s'assure que le déclencheur est désactivé lcd.print ("Welcome to"); // écran de bienvenue lcd.setCursor (0,1); lcd.print ("SliderCam v0.2!"); délai (1000); lcd.clear (); lcd.print (menuItemsTop [0]); délai (100); lcd.setCursor (0,1); pour (int i = 0; i < 4; i++)  lcd.setCursor(i, 1); lcd.print(currentDistance[i]);  lcd.setCursor(4,1); lcd.print("mm(max 1300)"); 

Étape 3: Boutons de surveillance

L'avantage ici en matière de codage est que l'équipement n'a rien à faire sans l'intervention de l'utilisateur. Ce qui signifie que la toute première chose peut simplement être une boucle de scrutation de bouton éternelle. Appeler le readLcdButtons () fonctionner encore et encore jusqu'à ce que sa valeur change n'a pas d'impact négatif sur les performances du programme, et vous n'avez pas à vous soucier de laisser des broches d'interruption disponibles.

void loop () do btnVal = readLcdButtons (); // lit en permanence les boutons… while (btnVal == 5); //… jusqu'à ce que quelque chose soit pressé
// déclare la fonction d'interrogation du bouton int readLcdButtons () delay (90); // rebounce delay, accordé expérimentalement. Le délai est correct car le programme ne devrait rien faire d'autre // à ce stade de toute façon adcIn = analogRead (0); // lecture de la valeur de la broche A0 / * valeurs de seuil confirmées par expérimentation avec le croquis d'étalonnage du bouton renvoyant les valeurs de lecture ADC suivantes: right: 0 up: 143 down: 328 left: 504 select: 741 * / if (adcIn> 1000) ; si (adcIn < 50) return btnR; if (adcIn < 250) return btnUp; if (adcIn < 450) return btnDn; if (adcIn < 650) return btnL; if (adcIn < 850) return btnSel; return btnNone; //if it can't detect anything, return no button pressed 

ReadLcdButtons () a un délai de 90ms afin de rebondir les boutons. En réalité, il ne s’agit pas d’un rebond, car il ne reprend pas la mesure du CAN après un laps de temps défini, mais interroge plutôt les boutons assez rarement pour enregistrer rarement plus d’un clic.. 

Il réalise la même chose depuis une vue UX pratique. C'est plus d'un boutons de sondage toutes les 90ms plutôt que constamment, c'est pourquoi l'utilisation de retard() n'est généralement pas considéré comme une bonne pratique à des fins anti-rebond, mais il a résolu le problème (seule la fin des menus était accessible).

Étape 4: Actualiser l'écran

Une fois que l'unité peut réagir à l'entrée, il doit y avoir un moyen d'afficher ces réactions.. 

Après avoir essayé des mises à jour à la volée, j'ai déterminé qu'une actualisation cohérente de l'écran, telle qu'un vrai système d'exploitation, était plus facile à gérer dans mes tentatives de mise à niveau d'une structure modulaire. Faire ceci est aussi simple que d'effacer l'écran, puis de reconstruire en fonction des paramètres actuels connus.
Cela semble compliqué, mais rend la vie beaucoup plus facile en pratique. Il supprime un grand nombre de commandes LCD ailleurs dans le programme et crée une zone agnostique à type variable qui est peu affectée par les mises à jour externes du programme..
La partie rafraîchissante réelle a évolué pour se composer de quatre étapes distinctes:
Réinitialiser les paramètres…

// IMPRIMEZ LES NOUVELLES VALEURS D'ÉCRAN btnVal = btnNone; lcd.clear ();

… Imprimer la ligne du haut…

lcd.setCursor (0, 0); lcd.print (menuItemsTop [currentMenuItem]); // affiche le premier élément du menu

… Imprimer la ligne du bas, ce que je vais expliquer un peu plus tard…

 lcd.setCursor (0,1); commutateur (currentMenuItem) cas 0: pour (int i = 0; i < 4; i++)  lcd.setCursor(i, 1); lcd.print(currentDistance[i]);  break;  case 1:  for (int i = 0; i < 6; i++)  lcd.setCursor(i, 1); lcd.print(currentDuration[i]);  break;  case 2:  for (int i = 0; i < 4; i++)  lcd.setCursor(i, 1); lcd.print(currentSteps[i]);  break;  case 3:  if (travelDir == 0) lcd.print("From Motor"); else lcd.print("To Motor"); break;  case 4:  lcd.print("Stop!"); break;   //end switch

… Et ajoutez des commandes spécifiques à l'écran par-dessus ce qui est déjà imprimé.

if (currentMenuItem == 0) lcd.setCursor (4,1); lcd.print ("mm (max 1300)"); // insère la course maximale du chariot sur le curseur utilisé if (currentMenuItem == 1) lcd.setCursor (6,1); lcd.print ("s (3600 / hr)");  if (currentMenuLevel == 1) lcd.setCursor (currentCursorPos, 1); lcd.blink ();  else lcd.noBlink ();

Création d'un menu: rubriques principales

Naturellement, cette section d’actualisation exacte de l’écran ne s’écrit pas elle-même et nous avons besoin de connaître le menu qu’elle écrit à l’écran avant de pouvoir le compléter. Les rubriques principales sont simples car elles ne changent pas en fonction de la saisie de l'utilisateur. Cela signifie qu'il peut simplement s'agir d'un tableau de chaînes - techniquement d'un tableau de pointeurs de caractères ou d'un tableau de tableaux:

// MENU GUI // définit les chaînes d'éléments de menu de niveau supérieur pour la navigation numérique car * menuItemsTop [] = "01 Distance>", "< 02 Duration >","< 03 Steps > ","< 04 Direction >","< 05 Go!"; int currentMenuLevel = 0; //top menu or submenu int currentMenuItem = 0; //x-axis position of menu selection int currentCursorPos = 0; //current lcd cursor position int currentDistance[4] =  0, 0, 0, 0; int currentDuration[6] =  0, 0, 0, 0, 0, 0; int currentSteps[4] =  0, 0, 0, 1;

Cela signifie que cela menuItemsTop tableau peut être parcouru simplement en modifiant le nombre entre crochets au moment du rafraîchissement de l’écran. Ce qui arrive, puisque tout est indexé à zéro, pour suivre à l'identique avec l'entier currentMenuItem

Manipuler currentMenuItem sur les événements de bouton nous permet la navigation unidimensionnelle, alors quand vous voyez menuItemsTop [currentMenuItem] c'est évidemment titre du menu actuel.

if (currentMenuLevel == 0) switch (btnVal) case btnL: if (currentMenuItem == 0) break; // ne peut pas aller à gauche d'ici sinon currentMenuItem--; Pause;  case btnR: if (currentMenuItem == 4) break; // ne peut pas aller directement à partir d'autre currentMenuItem ++; Pause;  case btnSel: currentMenuLevel ++; if (currentCursorPos> 3 && (currentMenuItem == 0 || currentMenuItem == 2)) currentCursorPos = 3; // n'entrez pas la fin des nombres pour les nombres à 4 chiffres si (currentCursorPos> 0 && (currentMenuItem> 2)) currentCursorPos = 0; // positionne le curseur clignotant sur la gauche pour les options textuelles if (currentMenuItem == 4) motion = 1; contrôle de mouvement(); Pause;  // fin de commutateur // fin de niveau 0

Donc, vous pouvez aller à gauche et à droite, et aller dans un menu, ou dans le cas de Aller! alors le contrôle de mouvement est activé. C'est tout ce qui est nécessaire ici.

Construction d'un menu: sous-menu

Le système de sous-menu a pris un peu plus de travail, en raison de sa complexité interne. Les trois premières entrées, Distance, Durée et Pas, se compose techniquement d'un sous-menu, chacun permettant la navigation de la valeur à plusieurs chiffres ainsi que chaque caractère individuel.

Ceci est couvert en faisant de chaque entrée de sous-menu un système de titres commuté à part entière. Bien que ce fût un long chemin à parcourir, il s'agit d'une méthode simple et cohérente permettant une navigation aussi basse. Depuis que je viens vraiment de comprendre le Distance sous-menu, puis copié pour les autres sous-menus, voici un coup d'oeil à celui-ci.

else // i.e. "else if currentMenuLevel = 1" if (currentMenuItem == 0) // 01 commutateur DISTANCE (btnVal) case btnUp: currentChar = currentDistance [currentCursorPos]; ajusterDigit (currentChar, 1); currentDistance [currentCursorPos] = currentChar; Pause;  case btnDn: currentChar = currentDistance [currentCursorPos]; ajusterDigit (currentChar, 0); currentDistance [currentCursorPos] = currentChar; Pause;  case btnL: if (currentCursorPos == 0) break; // ne peut pas partir à gauche d'ici currentCursorPos--; Pause;  case btnR: if (currentCursorPos == 3) break; // ne peut pas partir à gauche d'ici currentCursorPos ++; Pause;  case btnSel: parseArrayDistance (); currentMenuLevel--;  // commutateur de fin // fin de DISTANCE

Gauche et Droite sont essentiellement les mêmes que le menu de niveau supérieur, il suffit simplement d’aller et d’avancer le nombre de la même manière, nombre effectivement être un ensemble de chiffres dans un tableau int et l'emplacement actuel stocké dans un int appelé currentCursorPos qui permet de clignoter comme on le voit dans le module de rafraîchissement de l'écran ci-dessus. 

L'impression de ces tableaux le long de la rangée inférieure de l'écran LCD correspond à la fonction des boucles for dans la section d'actualisation de l'écran; je de 0 à 3, Colonne LCD de 0 à 3, currentDistance [] de 0 à 3.

int adjustDigit (int x, int dir) // fonction de réglage des chiffres if (dir == 0 && x> 0) x--; // soustrait le chiffre sur btnDn if (dir == 1 && x < 9) x++; // add to digit on btnUp lcd.setCursor(currentCursorPos, 1); lcd.print(x); currentChar = x; return currentChar; //return new digit 

L'augmentation et la diminution du nombre est obtenu en stockant le chiffre actuel dans la variable currentChar, qui est ensuite transmis à la ajusterDigit () fonction avec une valeur booléenne indiquant la direction; augmenter ou diminuer le moteur. 

Ceci ajuste simplement le chiffre en fonction de la valeur booléenne et enregistre le résultat, où le flux retourne à la boucle principale, où la valeur currentChar est enregistrée dans la position correcte de l'original. currentDistance [] tableau et le nouveau chiffre ajusté est imprimé lors de l'actualisation de l'écran.

Analyser les valeurs du tableau d'affichage

Lorsque Select est sélectionné dans l'un des sous-menus du tableau de nombres, il déclenche la fonction d'analyse syntaxique appropriée - dans ce cas parseArrayDistance (). Vous devez analyser le tableau, utilisé pour afficher et éditer commodément, en un entier utile pour les calculs de mouvement réels. J'ai choisi de le faire maintenant plutôt que sur Aller! faire en sorte que UX soit à la mode.

int adjustDigit (int x, int dir) // fonction de réglage des chiffres if (dir == 0 && x> 0) x--; // soustrait le chiffre sur btnDn if (dir == 1 && x < 9) x++; // add to digit on btnUp lcd.setCursor(currentCursorPos, 1); lcd.print(x); currentChar = x; return currentChar; //return new digit 

J'ai trouvé cette fonction à partir du commentaire utile que j'ai trouvé après avoir épuisé Google à la recherche de fonctions standard array-to-int, arriver vide et se débarrasser du fouillis des fonctions array-to-char-to-int qui étaient une solution de contournement inefficace. Cela semble assez court et léger étant donné qu'il repose littéralement sur le fondement des mathématiques décimales, mais si vous connaissez une meilleure méthode, je suis tout ouïe..

Contrôle du mouvement et déclenchement de la caméra

Toutes les valeurs sont définies et vous appuyez sur Aller! Qu'est-ce qui se passe ensuite? Vous devez calculer exactement ce que les nombres qui lui sont attribués sont supposés faire pour exécuter le mouvement final. Cette partie est fonctionnelle, mais un travail en cours; Je pense qu'il faut plus d'options pour différents types de mouvements.

int motionControl () totalMotorSteps = currentDistanceInt * 5; // calcule le nombre total de pas (0,2 mm = engrenage 20 dents sur une courroie de 2 mm; 40 mm par tour, 200 pas par tour, par exemple 1/5 mm par pas) pulseDelay = (1000L * (currentDurationInt - (currentStepsInt * shutterDuration)) / totalMotorSteps; // temps de pause, en ms, entre les impulsions STP et l'intervalle conducteur-moteurDistance = totalMotorSteps / currentStepsInt;

Ce qui se passe dans cette fonction est assez clair d'après les commentaires, je pense. J'ai mis un shutterDuration 2 secondes dans le logiciel, principalement pour garder les tests assez rapides. Si vous photographiez de nuit, à des ISO inférieurs, cela peut nécessiter davantage de 25 à 35 secondes, selon votre vitesse d'obturation exacte..

le impulsion différée est multiplié par 1000 à la fin pour convertir des secondes en millisecondes, bien sûr. le L convertir la constante int en long est plus une erreur de prudence que de nécessité absolue. Comme il s'agit d'un dessin relativement petit, je ne suis pas trop préoccupé par l'utilisation variable de la mémoire..

Ces calculs supposent que la boucle elle-même nécessite un laps de temps négligeable par rapport à la impulsion différée le temps, qui, une fois que j'ai sorti le bouton polling, semble être vrai.

// une fois par cycle global if (travelDir == 0) digitalWrite (dir, LOW); else if (travelDir == 1) digitalWrite (dir, HIGH); //Serial.begin(9600); //Serial.println(pulseDelay); // boucle en boucle do digitalWrite (stp, HIGH); // renvoie le pilote pas à pas (pulseDelay); digitalWrite (stp, LOW); // réinitialiser le pilote // btnVal = readLcdButtons (); // vérifie qu'il n'y a pas d'arrêt - cela prend trop de temps et ralentit considérablement le moteur; utilisez reset pour stop! currentStep ++; // à la fin de chaque étape if (currentStep% intervalDistance == 0) // si le nombre actuel de pas de moteur est divisible par le nombre de pas de moteur dans un pas de caméra, déclenchez la caméra digitalWrite (trig, HIGH); // déclenche le délai d'obturation de la caméra (80); digitalWrite (trig, LOW); // réinitialise le délai de la broche de déclenchement ((shutterDuration * 1000) -80); // delay a besoin de passer à timer afin que le bouton d'arrêt puisse être interrogé while (currentStep < totalMotorSteps);  //end motion control

Enfin, notez le CurrentSteps valeur définie à 1. Je n'ai pas créé de fonction de vérification d'erreur pour cela, mais le bon sens dit simplement taille de pas devient infini si currentStepsInt == 0, il est donc préférable de le garder à un si le mouvement continu est désiré. J'ai ajouté une entrée d'amélioration pour cela déjà.

Exécution du produit final

Pour quelque chose qui fonctionne avec du code écrit plus ou moins à partir de zéro en deux jours et corrigé plus de deux fois, cela fonctionne comme un rêve! La preuve est dans le pudding, cependant. Obtient-il réellement des images timelapse intéressantes et l'unité de commande fonctionne-t-elle vraiment bien sur le terrain??

Dans mes tests, la réponse semble être un oui catégorique. Ci-dessous se trouve un timelapse de deux heures, 650 images, le tout premier test. Le curseur a également effectué un test de 9 heures sur 720 images sans faille, mais malheureusement, la batterie de l'appareil photo n'a pas très bien fonctionné après 2 heures… ce que je n'ai découvert que jusqu'à 8 heures et demie, naturellement..

Si je règle l'heure et les marches de manière appropriée, le mouvement peut être continu pour les mouvements de dolly lents dans la vidéo d'action en direct, bien que les mouvements saccadés aient besoin d'être supprimés ou accélérés.. 

Le son peut poser problème, sauf si votre stepper est très silencieux, mais pour ajouter de la valeur de production aux enregistrements auto-enregistrés, il s'agit d'une option..

Améliorations

Comme pour tout, des améliorations sont possibles. J'ai énuméré ces en haut de la .ino dossier, bien que certes sans soin particulier à la faisabilité ni ordre d'importance quelconque. 

Certaines de ces solutions étaient envisageables avant de publier ce didacticiel avec la version 0.2, mais j’ai l’impression qu’elles constituent en elles-mêmes une expérience d’apprentissage à explorer en termes de démantèlement mental de la convivialité d’un programme..

 AMÉLIORATIONS ET CONSIDÉRATIONS CONCERNANT LA V1.0: 1) Efficacité du code de réponse du bouton de sous-menu pour les trois premiers en-têtes de menu 2) Utilisation de la durée d'obturation de la lampe comme option de menu supplémentaire, transmise à shutterDuration int 3) la durée de l'obturateur devrait être une ) - ne peut pas interroger le bouton d'arrêt! 4) Utiliser les fonctions de la bibliothèque EEPROM pour économiser des quantités, simplifier ainsi la section de contrôle du mouvement et utiliser Réinitialiser comme "arrêt" 5) Supprimer le commutateur du sous-menu "Aller", le remplacer par une instruction logique plus appropriée 6) Serait-il préférable de chronométrer les pas de la caméra plutôt que le voyage total? "durée" étant plus proche de 15 secondes ou 2 minutes que de 30 minutes ou 4 heures? 7) Des constantes qui seraient meilleures comme #define ou mieux comme boolean? À peine 8kB malgré les limites de l’espace SRAM. 8) Interpolation / assouplissement pour les courbes d'accélération, en particulier pour l'utilisation vidéo 9) Contrôle d'erreur pour la taille de pas zéro, ou ajoutez simplement un à intervalDistance si la valeur est zéro avant les calculs - l'autre fin de Distance est toujours 1 étape 10) Retarderait sous 16ms () s vaut mieux que delayMicroseconds ()? Combien d'interruptions retardent le chronométrage? 11) Utilisation de la veille sur l’A4988 pour réduire la consommation d’énergie sur le terrain? 12) Vérification d'erreur pour currentDurationInt <= currentStepsInt*shutterDuration, allowing no time for movement or even negative pulseDelay! */

Ce ne sont là que les améliorations auxquelles j'ai pensé jusqu'à présent, dans le but de guider le code d'une base v0.2 rudimentaire mais fonctionnelle vers une version v1.0 plus optimisée et plus performante. Vous remarquerez peut-être p