Fonctions de contrôle série pour les actionneurs de rétroaction

La programmation d'un PLC pour contrôler votre actionneur peut être l'un des projets les plus difficiles à entreprendre. Il nécessite des essais et des erreurs, des tests et un tas de patience; Bien que les résultats puissent être incroyablement fonctionnels et enrichissants.

Le code de ce tutoriel est un programme complet pour un fonctionnement Actionneur réactif en série. L'utilisation du moniteur série avec votre PLC vous permettra d'interfacer votre système Arduino avec d'autres ordinateurs, modules ou systèmes d'automatisation.

Nous examinerons chaque fonction et section du code au fur et à mesure, vous pouvez donc retirer des fonctions spécifiques de ce tutoriel. Veuillez noter que certaines fonctions font appel à d'autres fonctions qui ne sont pas incluses dans le même bloc de code, vous devrez donc vérifier votre code pour vous assurer qu'il s'exécutera correctement.

Le code complet sera publié à la fin de cet article. Si vous souhaitez utiliser le code complet, copiez-le et collez-le dans votre logiciel IDE.

Aperçu du programme

Lorsqu'il est initialisé, ce programme sera à la maison et à calibrer vos actionneurs, en trouvant les extrémités du mouvement pour l'unité. Il déplace ensuite l'actionneur vers le point médian du coup pour attendre d'autres commandes. Vous pouvez ensuite saisir un nombre dans votre moniteur de série (depuis votre PC) et l'actionneur se déplacera à cette position. Ce programme ne se confonde pas par l'arrêt de fin du coup et fonctionne avec un capteur d'effet de salle ou un actionneur de capteur optique.

Liste de pièces

Les composants avec un (*) sont inclus dans le Kit de démarrage Arduino

Fonctions

Résumé des fonctions

  1. void setup(): Initialise les broches, démarre la communication série et exécute des routines de retour et d'étalonnage.
  2. void homingRoutine(): Initie un homing, déplace l'actionneur en rétraction complète et définit la course minimale.
  3. void calibrateActuator(): Initie l'étalonnage, déplace l'actionneur à une extension complète et définit la course maximale.
  4. void loop(): Vérifie l'entrée série, met à jour la position cible et contrôle le mouvement de l'actionneur.
  5. void movement(): Contrôle la direction du mouvement de l'actionneur en fonction de la position cible.
  6. void readSensor(): Lit les valeurs du capteur, met à jour la position et imprime les lectures du capteur.
  7. void stopMotor(): Arrête le mouvement de l'actionneur.
  8. bool isEndOfStroke(): Vérifie la fin des conditions de course et gère les délais d'attente.

Variables

Les variables de votre code doivent être appelées avant d'être utilisées. La meilleure pratique de programmation consiste à attribuer et à définir vos variables en haut de votre code, pour faciliter la modification ou le «réglage» vos variables.

Ce sont les variables utilisées pour ce code, avec un commentaire expliquant leur fonction et leur plage possible.

//Actuator Specifications
int maxStroke; // Leave undefined, set during calibration
int minStroke; // Leave undefined, set during homing
// Input Variables (Pins) - Change to match your pinout
const int Xpin=10;
const int Rpin=11;
// Sensor Read Pin 1
const int sensorPin=3;
// Hall Effect Sensor Input (Sensor Read pin 2) - Change to match your pinout
const int sensorPin2=4;
int sensorCount2;
// Position Target Function Variables
int targetNumber;
int currentPosition;
int lastPosition=0;
// Motor Control Variables
bool active = false; // Actuator On / Off toggle flag
bool EOSFlag=false; // End of Stroke Flag
// Sensor Readings
int sensorValue;
int lastSensorValue = LOW;
int sensorValue2;
int lastSensorValue2 = LOW;
// Variables for Debounce and End of Stroke Sensing
const unsigned long motionTimeout = 2000;  
// Adjust this value based on your requirements (in milliseconds) 2000=2s
const unsigned long CALIBRATION_TIMEOUT=3000;
// Adjust this value based on your requirements (in milliseconds) 3000=3s
unsigned long lastMotionTime = millis(); //Timer function for EOS function
// Position Variables
unsigned long pulseCount = 0; 
int direction = 0;  // Used for incremental movement count
// 0: Stopped, 1: Moving Forward, -1: Moving Backward

Configuration / initialisation

La configuration vide (); La fonction est une fonction Arduino de base qui initialise le PLC, commence le moniteur série et attribue des entrées et des sorties comme programmées.

Cette fonction de configuration attribue 4 broches en entrées ou en sorties, puis exécute les routines de remise et d'étalonnage.

void setup() {
  pinMode(Xpin, OUTPUT);
  pinMode(Rpin, OUTPUT);
  pinMode(sensorPin, INPUT_PULLUP);
  pinMode(sensorPin2, INPUT_PULLUP);
  Serial.begin(115200);

  homingRoutine();
  calibrateActuator();
}

Routine de trafic

Cet extrait de code est la fonction pour le rendement de vos actionneurs de commentaires. Ce code rétractera votre actionneur jusqu'à la fin de la fin de la course et attribuera cette position une valeur de "0" - puis il initialise la fonction d'étalonnage.

Dans le programme complet, les fonctions de homing et d'étalonnage sont exécutées une fois, pendant le démarrage, et non appelées.

Cet extrait de code appelle fonctions supplémentaires qui ne sont pas incluses dans ce bloc.

void homingRoutine() {
  active=true;
  Serial.println("Homing Initiated");
  digitalWrite(Xpin, LOW);
  digitalWrite(Rpin, HIGH);
  while (!EOSFlag) {
    direction=-1;
    readSensor();
    isEndOfStroke();
    // Move actuator to full retraction
    
  }
  direction=0;
  minStroke=currentPosition;
  Serial.println("Homing Completed");
}

Routine d'étalonnage

Ensuite, nous avons notre routine d'étalonnage. Cette fonction prolongera pleinement votre actionneur après que la fonction de retour entièrement rétractée l'unité. Le but de cette routine est de compter les impulsions du capteur d'une extrémité de la course de l'actionneur à l'autre. Cette valeur est ensuite enregistrée sous forme de variable maxstroke et utilisée pour le positionnement. Ce code peut être modifié pour traiter la rétroaction du potentiomètre en mappant la tension en position, plutôt que de compter les impulsions.

Cet extrait de code appelle fonctions supplémentaires qui ne sont pas incluses dans ce bloc.

void calibrateActuator() {
  Serial.println("Calibration Initiated");
  active = true;
  // Reset variables
  pulseCount = 0;
  currentPosition = 0;
  lastMotionTime=millis();

  // Move actuator to full extension
  digitalWrite(Xpin, HIGH);
  digitalWrite(Rpin, LOW);
  direction=1;

  // Wait until the end of stroke is reached during calibration
  while (!isEndOfStroke()) {
    readSensor();

    // Add a timeout condition to avoid infinite loop
    if (millis() - lastMotionTime > motionTimeout) {
      Serial.println("Calibration Timeout");
      stopMotor();
      maxStroke=currentPosition;
      direction=0;
      // Print the calibration results
        Serial.print("Calibration Complete. Minimum Stroke: ");
        Serial.print(minStroke);
        Serial.print(" Maximum Stroke: ");
        Serial.println(maxStroke);
        targetNumber=((maxStroke+minStroke)/2);
        break;
    }
  }
}

 

Boucle

La fonction de boucle vide () est la corps principal de votre code Arduino. Tout ce qui est écrit dans la fonction Loop () sera exécuté à plusieurs reprises pendant le programme.

Remarque des programmeurs: Je trouve personnellement le plus facile de maintenir la fonction de boucle aussi simple que possible. Ceci est la partie du code qui appellera la plupart des autres fonctions dans le code, et ne peut pas être soulevé sans le reste du code.

Dans cette fonction de boucle, le programme vérifie le moniteur série pour la saisie, met à jour la position cible avec votre commande et appelle la fonction de mouvement. La plupart des messages envoyés par le PLC au moniteur série proviennent de cet extrait de code.

Il y a un ligne de code commenté Ci-dessous - lorsque vous inclus, cette ligne de code mappe une entrée de 0-100 à l'actionneur. C'est-à-dire Votre contribution est désormais un pourcentage de l'AVC de l'actionneur. Cette ligne peut également être modifiée pour utiliser d'autres entrées dans un format mappé similaire.

Cet extrait de code appelle des fonctions supplémentaires qui ne sont pas incluses dans ce bloc.

void loop() {
if (!active && Serial.available() > 0) {
  String serialInput = Serial.readStringUntil('\n');
  Serial.print("Received: ");
  Serial.println(serialInput);
  if (serialInput.length() > 0) {
    targetNumber = serialInput.toInt();
    //targetNumber = map(targetNumber, 0, 100, minStroke, maxStroke);

/*If the above line is active, you will input a value between 0-100,
The program will use this input as a % of stroke.*/
Serial.print("Target number: "); Serial.println(targetNumber); EOSFlag = false; } // Clear the serial buffer while (Serial.available()) { Serial.read(); } } if (targetNumber != currentPosition) { active = true; movement(); } /* if (!active) { Serial.println("Waiting for Input"); return; } */ if (active && targetNumber == currentPosition) { stopMotor(); Serial.println("Target Met"); } }

 

Mouvement

La fonction de mouvement contrôle la direction du mouvement de l'actionneur en fonction de la position cible. Il s'agit de la seule fonction de ce programme qui indique au conducteur du moteur de déplacer l'actionneur. 

Cet extrait de code appelle des fonctions supplémentaires qui ne sont pas incluses dans ce bloc.

void movement() {
  if (targetNumber > currentPosition) {
    digitalWrite(Xpin,HIGH);
    digitalWrite(Rpin,LOW);
    //Serial.println(" Extending");
    direction = 1;
  } else if (targetNumber < currentPosition) {
    digitalWrite(Rpin,HIGH);
    digitalWrite(Xpin,LOW);
    direction = -1;
    //Serial.println("Retracting");
  } else if (targetNumber == currentPosition) {
    stopMotor();
    delay(10);
  }
  if(active) {
    readSensor();
  }
  if (isEndOfStroke()) {
    return;  // Skip further movement actions
  }
}

Capteur Lire

La fonction de traitement du capteur lit les valeurs du capteur, met à jour la position et imprime les lectures du capteur au moniteur série. Les commandes en série peuvent être utilisées pour le débogage ou commenté pour une lecture moins fréquent.

Cet extrait de code peut être utilisé dans n'importe quel programme et donnera une lecture précise des impulsions à partir d'un capteur de rétroaction.

void readSensor() {
  sensorValue = digitalRead(sensorPin);
  if(lastSensorValue != sensorValue) {
    lastSensorValue = sensorValue;
    pulseCount = pulseCount + direction;
    Serial.print("Sensor 1: ");
    Serial.println(pulseCount);
  }
  sensorValue2 = digitalRead(sensorPin2);
  if(lastSensorValue2 != sensorValue2) {
    lastSensorValue2 = sensorValue2;
    sensorCount2=sensorCount2+direction;
    pulseCount = pulseCount + direction;
    Serial.print("Sensor 2: ");
    Serial.println(sensorCount2);
    Serial.print("Current Position: ");
    Serial.println(currentPosition);
  }
  currentPosition = pulseCount;
}

Arrêt et désactivation du moteur

Arrête le mouvement de l'actionneur et réévalue l'unité pour lire le moniteur série pour d'autres commandes. Il s'agit du seul extrait de code de ce programme qui arrêtera la motion de l'actionneur.

Cet extrait de code peut être utilisé dans n'importe quel programme avec n'importe quel actionneur, car il éteint simplement le moteur et modifie l'état «actif».

void stopMotor() {
 if (active) {
   active=false;
   digitalWrite(Xpin,LOW);
   digitalWrite(Rpin,LOW);
  }
}

Fin de vérification des coups

Vérifie la fin des conditions de course et gère les délais d'attente. Lorsque l'actionneur frappe la fin de la course et cesse de se déplacer, cette fonction déclenchera et réinitialise l'actionneur pour les nouvelles entrées de commande, même si le numéro cible n'a pas encore été atteint.

Cet extrait de code appelle fonctions supplémentaires qui ne sont pas incluses dans ce bloc.

bool isEndOfStroke() {
  // Check if there is motion (changes in the pulse count)
  if (active && (currentPosition != lastPosition)) {
    lastMotionTime = millis();  // Update the time of the last motion
    lastPosition = currentPosition;
    EOSFlag=false;
  }

  // Check if there is no motion for the specified timeout
  if (active && ((millis() - lastMotionTime) > motionTimeout)) {
    if(EOSFlag!=true) {
      Serial.print("Timeout - ");
      Serial.println("At limit");
      EOSFlag=true;
    }
    direction=0;
    stopMotor();
    return true;
  }
  return false;
}

Code complet

//Actuator Specifications
int maxStroke;
int minStroke;

// Input Variables (Pins)
const int Xpin=10;
const int Rpin=11;
const int sensorPin=3;
// Hall Effect Sensor Input
const int sensorPin2=4;
int sensorCount2;


// Motor Function Variables
int targetNumber;
int currentPosition;
int lastPosition=0;
bool active = false;
bool EOSFlag=false;

// Sensor Readings
int sensorValue;
int lastSensorValue = LOW;
int sensorValue2;
int lastSensorValue2 = LOW;

// Variables for Debounce
const unsigned long motionTimeout = 2000;  // Adjust this value based on your requirements (in milliseconds)
const unsigned long CALIBRATION_TIMEOUT=3000; // Adjust this value based on your requirements (in milliseconds)
unsigned long lastMotionTime = millis();

// Position Variables
unsigned long pulseCount = 0;
int direction = 0;  // 0: Stopped, 1: Moving Forward, -1: Moving Backward




void setup() {
  pinMode(Xpin, OUTPUT);
  pinMode(Rpin, OUTPUT);
  pinMode(sensorPin, INPUT_PULLUP);
  pinMode(sensorPin2, INPUT_PULLUP);
  Serial.begin(115200);

  homingRoutine();
  calibrateActuator();
}

void homingRoutine() {
  active=true;
  Serial.println("Homing Initiated");
  digitalWrite(Xpin, LOW);
  digitalWrite(Rpin, HIGH);
  while (!EOSFlag) {
    direction=-1;
    readSensor();
    isEndOfStroke();
    // Move actuator to full retraction
    
  }
  direction=0;
  minStroke=currentPosition;
  Serial.println("Homing Completed");
}

void calibrateActuator() {
  Serial.println("Calibration Initiated");
  active = true;
  // Reset variables
  pulseCount = 0;
  currentPosition = 0;
  lastMotionTime=millis();

  // Move actuator to full extension
  digitalWrite(Xpin, HIGH);
  digitalWrite(Rpin, LOW);
  direction=1;

  // Wait until the end of stroke is reached during calibration
  while (!isEndOfStroke()) {
    readSensor();

    // Add a timeout condition to avoid infinite loop
    if (millis() - lastMotionTime > motionTimeout) {
      Serial.println("Calibration Timeout");
      stopMotor();
      maxStroke=currentPosition;
      direction=0;
      // Print the calibration results
        Serial.print("Calibration Complete. Minimum Stroke: ");
        Serial.print(minStroke);
        Serial.print(" Maximum Stroke: ");
        Serial.println(maxStroke);
        targetNumber=((maxStroke+minStroke)/2);
        break;
    }
  }
}

void loop() {
if (!active && Serial.available() > 0) {
  String serialInput = Serial.readStringUntil('\n');
  Serial.print("Received: ");
  Serial.println(serialInput);
  if (serialInput.length() > 0) {
    targetNumber = serialInput.toInt();
    Serial.print("Target number: ");
    Serial.println(targetNumber);
    EOSFlag = false;
  }
  // Clear the serial buffer
  while (Serial.available()) {
    Serial.read();
  }
}

  if (targetNumber != currentPosition) {
    active = true;
    movement();
  } 
  /*
  if (!active) {
   Serial.println("Waiting for Input"); 
   return;
  } */
  if (active && targetNumber == currentPosition) {
   stopMotor();
   Serial.println("Target Met");
  }
}

void movement() {
  if (targetNumber > currentPosition) {
    digitalWrite(Xpin,HIGH);
    digitalWrite(Rpin,LOW);
    //Serial.println(" Extending");
    direction = 1;
  } else if (targetNumber < currentPosition) {
    digitalWrite(Rpin,HIGH);
    digitalWrite(Xpin,LOW);
    direction = -1;
    //Serial.println("Retracting");
  } else if (targetNumber == currentPosition) {
    stopMotor();
    delay(10);
  }
  if(active) {
    readSensor();
  }
  if (isEndOfStroke()) {
    return;  // Skip further movement actions
  }
}

void readSensor() {
  sensorValue = digitalRead(sensorPin);
  if(lastSensorValue != sensorValue) {
    lastSensorValue = sensorValue;
    pulseCount = pulseCount + direction;
    Serial.print("Sensor 1: ");
    Serial.println(pulseCount);
  }
  sensorValue2 = digitalRead(sensorPin2);
  if(lastSensorValue2 != sensorValue2) {
    lastSensorValue2 = sensorValue2;
    sensorCount2=sensorCount2+direction;
    pulseCount = pulseCount + direction;
    Serial.print("Sensor 2: ");
    Serial.println(sensorCount2);
    Serial.print("Current Position: ");
    Serial.println(currentPosition);
  }
  currentPosition = pulseCount;
}


void stopMotor() {
 if (active) {
   active=false;
   digitalWrite(Xpin,LOW);
   digitalWrite(Rpin,LOW);
  }
}

bool isEndOfStroke() {
  // Check if there is motion (changes in the pulse count)
  if (active && (currentPosition != lastPosition)) {
    lastMotionTime = millis();  // Update the time of the last motion
    lastPosition = currentPosition;
    EOSFlag=false;
  }

  // Check if there is no motion for the specified timeout
  if (active && ((millis() - lastMotionTime) > motionTimeout)) {
    if(EOSFlag!=true) {
      Serial.print("Timeout - ");
      Serial.println("At limit");
      EOSFlag=true;
    }
    direction=0;
    stopMotor();
    return true;
  }
  return false;
}
Share This Article
Tags: