Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Construire un Actionneur Linéaire Servo de Précision avec Retour de Position – Guide Pas à Pas

Dans ce tutoriel, nous apprenons à fabriquer un actionneur linéaire servo. Contrairement aux actionneurs linéaires classiques qui bougent dans un sens sous tension, cet actionneur servo custom offre des mouvements précis, reproductibles et facilement contrôlables.

C'est un actionneur « servo » car il intègre une boucle de rétroaction (feedback) pour contrôler précisément la position de sortie.

Regardez la vidéo ou suivez le tutoriel écrit.

Vue d'ensemble

L'entrée de commande peut être analogique ou numérique. En analogique : tout potentiomètre (linéaire, rotatif, joystick…). En numérique : radiocommande RC (avec récepteur RC en entrée).

Pour ces deux modes, seulement 3 fils : 2 pour l'alimentation de l'entrée, 1 pour le signal.

Fonction sympa : on peut définir une position de début et de fin personnalisée pour la tige de sortie, et régler la sensibilité (réactivité).

Ma fonction préférée : le contrôle depuis un PC/portable via port série. On entre des valeurs en millimètres dans le moniteur série Arduino IDE, et l'actionneur va à cette position.

Mieux : on peut faire des mouvements reproductibles, mémoriser des positions en tapant « save » à chaque position voulue, puis dire à l'actionneur de répéter la séquence en boucle en tapant « run ».

Voyons tout sur cet actionneur linéaire servo custom : fonctionnement, conception, pour que vous puissiez en construire un.

Principe de Fonctionnement

La boucle de contrôle fermée repose sur le capteur de position rotatif magnétique AS5600 et un contrôle PID pour piloter le moteur DC.

J'utilise la même carte contrôleur servo custom que dans ma vidéo précédente, qui intègre son propre microcontrôleur et tout le nécessaire pour transformer n'importe quel moteur DC en servo autonome.

Consultez ce tutoriel pour l'explication détaillée du fonctionnement d'un servomoteur et d'une boucle fermée.

En bref : un servomoteur est un système à boucle fermée où le signal d'entrée (position désirée) est comparé à la position réelle mesurée par le capteur de rétroaction. La différence (erreur) est traitée par le contrôleur qui commande le moteur jusqu'à atteindre la position désirée.

Cet actionneur linéaire servo a le même principe, avec une étape supplémentaire : conversion du mouvement rotatif du moteur en mouvement linéaire via un mécanisme à vis de translation (lead screw).

Conception de l'Actionneur Linéaire Servo

Voici le modèle 3D de cet actionneur linéaire servo, d'où on voit comment tout s'articule.

Le capteur AS5600 est à l'arrière, il suit la rotation de la vis de translation. La vis a un pas de 8 mm : un tour complet = 8 mm de mouvement linéaire de l'écrou.

L'AS5600 est un encodeur 12 bits → 4 096 positions par tour. 8 / 4 096 = 0,001953 mm de résolution. C'est la plus petite variation détectable. Assez impressionnant.

Le moteur DC est un 12 V avec réducteur intégré sortant à 480 tr/min. 480 / 60 = 8 tours/s. × 8 (pas vis) = 64 mm/s vitesse linéaire.

C'est parfait : course max 150 mm → ~2,5 s de bout en bout à vitesse max, ~3 s avec accélération/décélération. D'où le rapport 1:1 pour entraîner la vis.

Le design global repose sur la taille du PCB contrôleur servo custom (40×40 mm → taille min du bloc cylindre) et de la vis/écrou (écrou 8 mm → diam. ext. 22 mm → dimensionnement de la tige).

L'écrou et la tige sont liés par 4 boulons M3 + inserts filetés. En haut de la tige, un roulement coulisse sur le bloc cylindre pour guider la tige et l'empêcher de tourner.

Au bouchon de sortie, 4 petits roulements guident la tige hors du cylindre.

L'ensemble est assez compact vu les composants utilisés.

J'ai aussi logé un micro-interrupteur de fin de course dans le cylindre, pour le « homing » et la définition de la position de départ.

Téléchargement du Modèle 3D

Visualisez et explorez le modèle 3D de cet actionneur linéaire servo custom directement dans votre navigateur via Onshape (compte Onshape requis, gratuit pour usage personnel).

Fichiers STL pour impression 3D et fichier STEP du modèle 3D disponibles sur Cults3D.

Note : vous pouvez facilement augmenter la course max en allongeant simplement le bloc cylindre et la tige. J'ai choisi ces dimensions pour que tout rentre sur imprimante 220×220 mm. La plus grande pièce (la tige) fait 215 mm.

Impression 3D

Ma nouvelle Creality Ender-3 V3 SE a fait un excellent travail en imprimant la tige horizontalement sur l'axe Y. Un léger ponçage nécessaire, mais cette orientation donne un fonctionnement plus fluide et une tige plus solide.

En impression 3D, utilisez l'expansion horizontale dans votre trancheur pour compenser l'expansion du filament et obtenir des pièces dimensionnellement précises.

J'ai mis -0,1 mm, mais faites des essais pour trouver la valeur idéale sur votre imprimante.

J'ai imprimé le bloc cylindre verticalement (axe Z) pour éviter les supports. L'Ender-3 V3 SE a aussi très bien géré cette impression.

Agreablement surpris par la qualité d'impression vu le prix. Super facile à mettre en route, nivellement auto, extrudeur direct, belle qualité, vitesse jusqu'à 250 mm/s. Tout ça pour < 200 $ → l'une des meilleures imprimantes budget.

Dispo sur boutique Creality ou Amazon. Voir mon test détaillé sur mon site.

Assemblage

Voici toutes les pièces imprimées, on commence l'assemblage.

Liste des pièces

Composants nécessaires pour ce projet d'actionneur linéaire servo :

Mécanique :

Électronique :

Note : Ce sont des liens d'affiliation. En tant qu'associé Amazon, je gagne sur les achats éligibles.

Assemblage du mécanisme à vis de translation

D'abord, on installe la vis de translation dans le bloc base cylindre. Il faut insérer l'écrou imprimé en 3D (même filetage que la vis).

C'est un peu dur à visser (ajustement serré), mais c'est voulu : cet écrou supporte toute la force de poussée, plus c'est serré, plus il tient. En plus, l'écrou a un trou pour insert fileté pour le bloquer sur l'arbre par vis sans tête.

La vis est maintenue dans le bloc base par deux roulements diam. ext. 22 mm.

À l'arrière, l'engrenage qui entraîne la vis. Lui aussi a le filetage assorti et deux trous pour inserts filetés pour le bloquer sur la vis par vis sans tête. Cette liaison est critique : elle transmet tout le couple du moteur à la vis, ne doit pas glisser.

Pour ce sous-ensemble : d'abord inserts filetés sur l'engrenage, l'écrou et le bloc cylindre. On visse l'engrenage et l'écrou en sens inverse mais pas trop fort (sinon forces axiales sur roulements). Puis on bloque écrou et engrenage sur la vis par vis sans tête.

Une fois bloqué, la vis n'est pas encore fixée axialement. Il faut ajouter cette plaque sur le bloc cylindre qui maintient les roulements en place.

Ce sous-ensemble est fini : la vis tient fermement tout en tournant librement.

On prépare la tige. Elle est creuse sur toute sa longueur pour loger la vis. Pour relier l'écrou de vis et la tige, d'abord inserts filetés.

En haut de la tige, on installe le roulement de guidage (diam. ext. 13 mm, int. 6 mm). On place le roulement sur un arbre creux 6 mm imprimé en 3D et on le fixe à la tige par une vis M3 fraisée 10 mm. La tige est prête, on voit comment elle coulisse dans les rails de guidage du cylindre.

Ensuite, on fixe le cylindre sur le bloc base. Mais avant, on installe le micro-interrupteur de fin de course.

D'abord souder les fils (connexion NC), ~15 cm. Les fils passent par un trou en haut du cylindre, puis on fixe le micro-interrupteur au cylindre avec 2 boulons M2×8 mm.

Il faut spécifiquement ce modèle de micro-interrupteur pour que le roulement de guidage le déclenche au bon moment sans heurter autre chose. Si introuvable, modifiez les trous et le mécanisme.

Pour fixer le cylindre sur le bloc base : inserts filetés, puis 2 boulons M4×25 mm (juste les 2 du haut pour l'instant). Les 2 du bas viendront plus tard avec le couvercle boîte d'engrenages/PCB (mêmes trous).

On visse la tige dans l'écrou de vis. Le roulement de guidage doit s'insérer entre les rails de guidage du cylindre.

En tournant l'engrenage arrière, la tige recule jusqu'au micro-interrupteur.

On met le bouchon de cylindre en place. Il accueille 4 petits roulements diam. ext. 9 mm (axes imprimables en 3D). Attention en les insérant : la zone est fine, risque de casse (m'est arrivé). Assurez-vous qu'ils rentrent facilement. Ces roulements supportent et guident la tige pour un fonctionnement fluide.

Le bouchon est fixé par 4 boulons M4.

Installation du moteur DC

On insère le moteur DC, fixé par 6 boulons M3. On met l'engrenage sur l'arbre moteur, bloqué par 2 inserts filetés + vis sans tête.

Une fois les engrenages bien engrenés, on passe au couvercle boîte d'engrenages/PCB à l'arrière. D'abord, inserts filetés supplémentaires dans le bloc base.

On connecte les fils du moteur DC (soudure directe chez moi). Longueur ~20 cm. Trou dans le bloc base pour passer fils moteur + fin de course.

On les fait passer par 2 guides sur le couvercle pour les écarter des engrenages.

On fixe le couvercle sur le bloc base : d'abord les 2 boulons M4 du bas, mais pas à fond. Laisser 2–3 mm pour glisser le support de couvercle entre, puis serrer le tout. Opération un peu délicate, mais nécessaire pour un couvercle compact, pièce unique, les supports PCB bloquant le passage des boulons.

Installation du PCB contrôleur servo custom

Une fois le couvercle en place, on installe la carte contrôleur servo custom. C'est la même que dans ma vidéo précédente (transformer n'importe quel moteur DC en servomoteur).

Composant principal : l'encodeur rotatif magnétique AS5600, qui suit la position angulaire de l'aimant sur l'arbre de sortie. Ici, on met l'aimant sur l'engrenage de sortie de la vis. L'aimant s'aligne parfaitement avec l'AS5600 quand le PCB est en place.

Pour fixer le PCB : inserts M2 dans les fentes des supports, puis 4 boulons M2.

Reste à connecter les fils. Fils moteur → bornier moteur, vérifier la polarité pour correspondre au programme.

Avant de brancher le moteur sur le PCB, on peut lui appliquer une tension pour vérifier que le mécanisme vis fonctionne.

Pour les fils du fin de course : pas de broches dédiées, j'ai soudé la masse sur la patte de masse du condensateur électrolytique, et le fil NC sur la broche SCK (pin digital 13 de l'ATmega328).

Le bornier d'alimentation est contre le côté du couvercle, avec un trou pour connecteur d'alimentation 5,5 mm.

J'ai ajouté un radiateur sur le driver DC. Enfin, on met le couvercle clipsable à l'arrière. C'est fini.

On peut maintenant brancher n'importe quel potentiomètre ou récepteur RC sur les entrées appropriées, et contrôler la position de l'actionneur linéaire.

Comme dit, ma vidéo précédente détaille le fonctionnement de ce contrôleur, son schéma et sa fabrication PCB. Consultez-la si vous voulez fabriquer cette carte.

Schéma de Câblage

En résumé : composant principal = capteur AS5600, suit la position de sortie. Données → ATmega328 (cerveau), qui calcule et commande le driver DRV8871 pour piloter le moteur DC.

Le DRV8871 gère jusqu'à 3,6 A crête. Alimentation 12 V → 5 V via AMS1117 pour ATmega328 et composants 5 V. Un DIP switch 2 canaux sélectionne le mode d'entrée : analogique, numérique, ou communication série.

Un potentiomètre de réglage ajuste la sensibilité, le bouton poussoir SDM définit les positions de début/fin.

PCB commandé chez PCBWay. Upload Gerber, choix propriétés, commande prix raisonnable. PCB 4 couches (couches centrales = GND, coût un peu plus). Juste changé couleur = blanc, et accepté finition or chimique (immersion gold) si applicable sans surcoût.

Fichiers Gerber dispo sur communauté PCBWay pour téléchargement ou commande directe.

Vous pouvez aussi faire cet actionneur sans ce contrôleur custom, bien sûr. AS5600 sur breakout board + Arduino pour piloter le moteur DC.

Code

Regardons le code de cet actionneur linéaire servo :

/* Actionneur Linéaire Servo - Code Arduino par Dejan, www.HowToMechatronics.com
Bibliothèques:
AS5600 encodeur: https://github.com/RobTillaart/AS5600
Contrôleur PID: https://github.com/br3ttb/Arduino-PID-Library/blob/master/PID_v1.h */
#include "AS5600.h"
#include "Wire.h"
#include 
AS5600 as5600; // utilise Wire par défaut
double Pk1 = 0.65; // vitesse d'atteinte
double Ik1 = 0;
double Dk1 = 0.1;
// Variables pour PID
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint, Pk1, Ik1, Dk1, DIRECT);
#define motor_IN1 5
#define motor_IN2 6
#define ch1 2
#define setButton 7
#define inputSwitch 3
#define modeSwitch 4
#define limitSwitch 13
int ch1Value;
long encoderValue, desiredValue, pwmValue;
String serialInput = "";
int serialIntInput;
double totalDistance = 0;
long startPosition = 358;
long endPosition = 6750;
long rangeAdjustment = 0;
float sensitivityAdjustment = 0;
float angle = 0;
float angleValue = 0;
float rodPosition;
float positionsArray[100];
int positionsCounter = 0;
long quadrantNumber = 2;
long previousQuadrantNumber = 3;
long numberOfTurns = 0;
float totalAngle = 0;
char incomingByte = 0;
int intInput = 0;
void setup() {
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("AS5600_LIB_VERSION: ");
  Serial.println(AS5600_LIB_VERSION);
  Wire.begin();
  pinMode(motor_IN1, OUTPUT);
  pinMode(motor_IN2, OUTPUT);
  // Pull-up internes Arduino
  pinMode(setButton, INPUT_PULLUP);
  pinMode(inputSwitch, INPUT_PULLUP);
  pinMode(modeSwitch, INPUT_PULLUP);
  pinMode(limitSwitch, INPUT_PULLUP);
  // Config PID
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(-255, 255);
  myPID.SetSampleTime(20);
  // --- HOMING ----
  // Reculer jusqu'au fin de course...
  while (digitalRead(limitSwitch) != 1) {
    digitalWrite(motor_IN1, LOW);
    analogWrite(motor_IN2, 70);
    encoderValue = as5600.readAngle();
  }
  while (digitalRead(limitSwitch) != 0) {
    analogWrite(motor_IN1, 50);
    digitalWrite(motor_IN2, LOW);
  }
  digitalWrite(motor_IN1, LOW);
  digitalWrite(motor_IN2, LOW);
  startPosition = as5600.readAngle() * 0.087890625;
  endPosition = 6000;
  Setpoint = startPosition;
  // --- FIN HOMING ---
}
void loop() {
  // Lecture encodeur - position actuelle
  rodPosition = as5600.readAngle() / 0.001953125; // en mm - vis 8 mm/tour, encodeur 4096/tour, donc 8/4096
  // Mode communication série - Lecture moniteur série
  if (digitalRead(modeSwitch) == 0) {
    while (Serial.available() > 0) {
      serialInput = Serial.readString();
      serialInput.trim();
      // Si "save" envoyé, sauvegarder position actuelle
      if (serialInput == "save") {
        positionsArray[positionsCounter] = totalDistance;
        delay(1000);
        positionsCounter++;
      }
      // Effacer positions sauvegardées
      if (serialInput == "clear") {
        memset(positionsArray, 0, sizeof(positionsArray));
        positionsCounter = 0;
      }
      // Convertir string en entier et l'utiliser comme consigne PID
      serialIntInput = serialInput.toInt();
      if (serialIntInput != 0) {
        if (serialIntInput < 0) { serialIntInput = 0; }
        if (serialIntInput > 150) { serialIntInput = 150; }
        Setpoint = serialIntInput * 45; // mm en degrés (1 mm = 45° rotation)
      }
    }
    // Exécuter positions mémorisées
    if (serialInput == "run") {
      while (serialInput != "stop") {
        for (int i = 0; i <= positionsCounter - 1; i++) {
          if (serialInput == "stop") { break; }
          while (positionsArray[i] > totalDistance + 25 || positionsArray[i] < totalDistance - 25 || pwmValue != 0) {
            Setpoint = positionsArray[i];
            readEncoder();
            runMotor();
          }
          delay(2000); // délai entre étapes
          // Vérifier commande stop sur moniteur série
          while (Serial.available() > 0) {
            serialInput = Serial.readString();
            serialInput.trim();
          }
        }
      }
    }
  }
  // Mode potentiomètre et récepteur RC
  else if (digitalRead(modeSwitch) == 1) {
    if (digitalRead(inputSwitch) == 0) {
      // Entrée analogique - Potentiomètre
      desiredValue = analogRead(A0);
      if (desiredValue < 15) { desiredValue = 15; }
      if (desiredValue > 1008) { desiredValue = 1008; }
      Setpoint = map(desiredValue, 15, 1008, startPosition, endPosition);
    } else if (digitalRead(inputSwitch) == 1) {
      // Entrée numérique - Récepteur RC
      desiredValue = pulseIn(ch1, HIGH, 30000);
      if (desiredValue < 1000 || desiredValue > 2000) { desiredValue = 1000; }
      Setpoint = map(desiredValue, 1000, 2000, startPosition, endPosition);
    }
  }
  // Borner la consigne
  if (Setpoint > endPosition) { Setpoint = endPosition; }
  if (Setpoint < startPosition) { Setpoint = startPosition; }
  // Ajustement sensibilité (commenté)
  // Pk1 = analogRead(A2) * 0.002;
  // myPID.SetTunings(Pk1, Ik1, Dk1);
  // Lecture encodeur pour PID
  readEncoder();
  // Pilotage moteur
  runMotor();
  // Définir positions début/fin par appui long sur bouton "set"
  if (digitalRead(setButton) == LOW) {
    delay(3000);
    if (digitalRead(setButton) == LOW) {
      endPosition = totalDistance;
      while (digitalRead(setButton) != HIGH) ;
    } else {
      startPosition = totalDistance;
    }
  }
}
void readEncoder() {
  // Convertir valeurs brutes encodeur en angles pour suivre position angulaire arbre
  encoderValue = as5600.readAngle() * 0.087890625;
  // Quadrant 1
  if (encoderValue >= 0 && encoderValue <= 90) { quadrantNumber = 1; }
  // Quadrant 2
  if (encoderValue >= 90 && encoderValue <= 180) { quadrantNumber = 2; }
  // Quadrant 3
  if (encoderValue >= 180 && encoderValue <= 270) { quadrantNumber = 3; }
  // Quadrant 4
  if (encoderValue >= 270 && encoderValue <= 360) { quadrantNumber = 4; }
  if (quadrantNumber != previousQuadrantNumber) {
    // Transition 4→1
    if (quadrantNumber == 1 && previousQuadrantNumber == 4) { numberOfTurns++; }
    // Transition 1→4
    if (quadrantNumber == 4 && previousQuadrantNumber == 1) { numberOfTurns--; }
    previousQuadrantNumber = quadrantNumber;
  }
  if (totalDistance >= 0) {
    totalDistance = (numberOfTurns * 360) + encoderValue;
  } else {
    totalDistance = (numberOfTurns * 360) + encoderValue;
  }
  // Valeur d'entrée pour PID
  Input = totalDistance;
}
void runMotor() {
  // Calcul PID
  myPID.Compute();
  // Droite
  if (Output > 1) {
    pwmValue = Output;
    if (pwmValue < 50 && pwmValue > 15) { pwmValue = pwmValue + 40; }
    if (pwmValue <= 15) { pwmValue = 0; }
    analogWrite(motor_IN1, pwmValue);
    digitalWrite(motor_IN2, LOW);
  }
  // Gauche
  else if (Output < 1) {
    pwmValue = abs(Output);
    if (pwmValue < 50 && pwmValue > 15) { pwmValue = pwmValue + 40; }
    if (pwmValue <= 15) { pwmValue = 0; }
    digitalWrite(motor_IN1, LOW);
    analogWrite(motor_IN2, pwmValue);
  }
  // Immobile
  else if (Output > -1 && Output < 1) {
    pwmValue = 0;
    digitalWrite(motor_IN1, LOW);
    digitalWrite(motor_IN2, LOW);
  }
}

Description du Code

Boucle principale : lecture encodeur → position actuelle en mm.


// Lecture encodeur - position actuelle
rodPosition = as5600.readAngle() / 0.001953125; // en mm - vis 8 mm/tour, encodeur 4096/tour, donc 8/4096

Si mode « communication série » : lecture moniteur série. Si « save » → stocker position actuelle. Si « clear » → effacer positions.


// Mode communication série
if (digitalRead(modeSwitch) == 0) {
  while (Serial.available() > 0) {
    serialInput = Serial.readString();
    serialInput.trim();
    if (serialInput == "save") {
      positionsArray[positionsCounter] = totalDistance;
      delay(1000);
      positionsCounter++;
    }
    if (serialInput == "clear") {
      memset(positionsArray, 0, sizeof(positionsArray));
      positionsCounter = 0;
    }

Si entrée numérique (0–150) → consigne PID.


// Conversion en entier pour consigne PID
serialIntInput = serialInput.toInt();
if (serialIntInput != 0) {
  if (serialIntInput < 0) { serialIntInput = 0; }
  if (serialIntInput > 150) { serialIntInput = 150; }
  Setpoint = serialIntInput * 45; // mm en degrés (1 mm = 45°)
}

On entre des mm, mais on suit l'arbre en degrés → conversion ×45 (1 mm linéaire = 45° rotation). Si pas de vis différent, changer ce coefficient.

Si « run » : exécution en boucle des positions mémorisées (boucles while/for).


// Exécution positions mémorisées
if (serialInput == "run") {
  while (serialInput != "stop") {
    for (int i = 0; i <= positionsCounter - 1; i++) {
      if (serialInput == "stop") { break; }
      while (positionsArray[i] > totalDistance + 25 || positionsArray[i] < totalDistance - 25 || pwmValue != 0) {
        Setpoint = positionsArray[i];
        readEncoder();
        runMotor();
      }
      delay(2000);
      while (Serial.available() > 0) {
        serialInput = Serial.readString();
        serialInput.trim();
      }
    }
  }
}

Sinon mode potentiomètre/récepteur RC : vérification analogique/numérique.


// Mode potentiomètre et récepteur RC
else if (digitalRead(modeSwitch) == 1) {
  if (digitalRead(inputSwitch) == 0) {
    // Analogique - Potentiomètre
    desiredValue = analogRead(A0);
    if (desiredValue < 15) { desiredValue = 15; }
    if (desiredValue > 1008) { desiredValue = 1008; }
    Setpoint = map(desiredValue, 15, 1008, startPosition, endPosition);
  } else if (digitalRead(inputSwitch) == 1) {
    // Numérique - Récepteur RC
    desiredValue = pulseIn(ch1, HIGH, 30000);
    if (desiredValue < 1000 || desiredValue > 2000) { desiredValue = 1000; }
    Setpoint = map(desiredValue, 1000, 2000, startPosition, endPosition);
  }
}

Analogique : lecture potentiomètre → consigne. Numérique : lecture récepteur RC → consigne.

Appel de readEncoder() et runMotor() pour lire position actuelle et exécuter PID. readEncoder() lit la valeur capteur en angles, suit le quadrant via ifs.


void readEncoder() {
  encoderValue = as5600.readAngle() * 0.087890625;
  if (encoderValue >= 0 && encoderValue <= 90) { quadrantNumber = 1; }
  if (encoderValue >= 90 && encoderValue <= 180) { quadrantNumber = 2; }
  if (encoderValue >= 180 && encoderValue <= 270) { quadrantNumber = 3; }
  if (encoderValue >= 270 && encoderValue <= 360) { quadrantNumber = 4; }
  if (quadrantNumber != previousQuadrantNumber) {
    if (quadrantNumber == 1 && previousQuadrantNumber == 4) { numberOfTurns++; }
    if (quadrantNumber == 4 && previousQuadrantNumber == 1) { numberOfTurns--; }
    previousQuadrantNumber = quadrantNumber;
  }
  if (totalDistance >= 0) {
    totalDistance = (numberOfTurns * 360) + encoderValue;
  } else {
    totalDistance = (numberOfTurns * 360) + encoderValue;
  }
  Input = totalDistance;
}

On suit ainsi la rotation de l'arbre et les tours complets. L'angle total = entrée PID.

L'entrée analogique du potentiomètre de réglage ajuste le gain proportionnel PID, puis on lance le PID pour obtenir la sortie.


// Ajustement sensibilité
// Pk1 = analogRead(A2) * 0.002;
// myPID.SetTunings(Pk1, Ik1, Dk1);

void runMotor() {
  myPID.Compute();
  if (Output > 1) {
    pwmValue = Output;
    if (pwmValue < 50 && pwmValue > 15) { pwmValue = pwmValue + 40; }
    if (pwmValue <= 15) { pwmValue = 0; }
    analogWrite(motor_IN1, pwmValue);
    digitalWrite(motor_IN2, LOW);
  } else if (Output < 1) {
    pwmValue = abs(Output);
    if (pwmValue < 50 && pwmValue > 15) { pwmValue = pwmValue + 40; }
    if (pwmValue <= 15) { pwmValue = 0; }
    digitalWrite(motor_IN1, LOW);
    analogWrite(motor_IN2, pwmValue);
  } else if (Output > -1 && Output < 1) {
    pwmValue = 0;
    digitalWrite(motor_IN1, LOW);
    digitalWrite(motor_IN2, LOW);
  }
}

La sortie PID pilote le moteur en PWM (gauche/droite/arrêt) selon l'erreur entre position désirée et réelle.

Les 3 termes PID (proportionnel, intégral, dérivé) définis en haut :


double Pk1 = 0.65;
double Ik1 = 0;
double Dk1 = 0.1;

La qualité de réponse dépend directement de ces valeurs.

Tests

Test de précision : retour en position correct. Mouvements de 1 mm par 1 mm. Premier mouvement ~0,8 mm au lieu de 1 mm, les 4 suivants proches de 1 mm. Mouvement de 4 mm : ~0,15 mm d'erreur.

La tige a un jeu (backlash) d'environ 0,25 mm (entre vis et écrou). Plus probablement du jeu dans les engrenages imprimés 3D et le réducteur du moteur DC.

Si on applique une force sur la tige, l'erreur augmente, mais améliorable en réglant le PID.

Voilà pour ce tutoriel. J'espère qu'il vous a plu et appris quelque chose.

Processus de fabrication

  1. Le Lyocell : Une fibre de cellulose durable et résistante pour la mode et l'industrie
  2. Modernisez Votre Maison : Interrupteur Tactile Intelligent avec Surveillance de Température
  3. Robot rover FPV piloté Wi-Fi avec Arduino et ESP8266 – Guide DIY
  4. Le passage de la Chine de la fabrication à l'innovation : rôle croissant dans la conception et le développement
  5. Conception pour Usinage CNC : Optimiser Matériaux et Géométrie pour la Précision
  6. L'évolution et l'artisanat des fleurs artificielles : de la soie au Perma-Stem moderne
  7. Martins-pilons expliqués : types, conception et fonctionnement pour la forge moderne
  8. Robot à commande gestuelle piloté par Raspberry Pi
  9. Guide complet des moteurs quatre temps