Функции последовательного управления для приводов обратной связи

Программирование ПЛК для управления вашим приводом может быть одним из наиболее сложных проектов. Это требует проб и ошибок, тестирования и кучи терпения; Хотя результаты могут быть невероятно функциональными и полезными.

Код в этом уроке является полной программой для функциональной Серийный реагирующий приводПолем Использование последовательного монитора с помощью PLC позволит вам взаимодействовать с вашей системой Arduino с другими компьютерами, модулями или системами автоматизации.

Мы рассмотрим каждую функцию и раздел кода по ходу дела, чтобы вы могли извлечь конкретные функции из этого урока. Обратите внимание, что некоторые функции обращаются к другим функциям, которые не включены в тот же кодовый блок, поэтому вам нужно будет проверить свой код, чтобы убедиться, что он будет выполнять должным образом.

Полный код будет опубликован в конце этой статьи. Если вы хотите использовать полный код, скопируйте и вставьте его в программное обеспечение IDE.

Обзор программы

При инициативе эта программа будет домой и откалибровать ваши приводы, найдя концы движения для устройства. Затем он перемещает привод к средней точке удара, чтобы ждать дальнейших команд. Затем вы можете ввести число в свой последовательный монитор (с вашего ПК), и привод перейдет на эту позицию. Эта программа не запутывается в результате отключения в конце удара и работает с любым датчиком эффекта зала или приводом оптического датчика.

Список деталей

Компоненты с (*) включены в Arduino Starter Kit

Функции

Резюме функций

  1. void setup(): Инициализируют контакты, начинают последовательную связь и запускают процедуры самонавочного и калибровки.
  2. void homingRoutine(): Инициирует домашнее хозяйство, перемещает привод к полной ретракции и устанавливает минимальный ход.
  3. void calibrateActuator(): Инициирует калибровку, перемещает привод к полному расширению и устанавливает максимальный ход.
  4. void loop(): Проверяет на получение последовательного ввода, обновления целевой позиции и управляет движением привода.
  5. void movement(): Контролирует направление движения привода на основе целевой позиции.
  6. void readSensor(): Считает значения датчиков, положение обновлений и печатает чтения датчиков.
  7. void stopMotor(): Останавливает движение привода.
  8. bool isEndOfStroke(): Проверяет на окончание условий удара и обрабатывает тайм -ауты.

Переменные

Переменные в вашем коде должны быть вызваны до их использования. Лучшая практика программирования - назначить и установить ваши переменные в верхней части вашего кода, чтобы облегчить изменение или «настройку» ваших переменных.

Это переменные, используемые для этого кода, с комментарием, объясняющим их функцию и возможный диапазон.

//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

Настройка/инициализация

Void setup (); Функция - это базовая функция Arduino, которая инициализирует ПЛК, начинает последовательный монитор и назначает входы и выходы в качестве запрограммированных.

Эта функция настройки назначает 4 контакта в качестве входов или выходов, а затем запускает процедуры домашнего и калибровки.

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

  homingRoutine();
  calibrateActuator();
}

Распродажа по дому

Этот фрагмент кода является функцией для дома ваших приводов обратной связи. Этот код будет отозвать ваш привод до тех пор, пока не будет обнаружен окончание удара, и назначит эту позицию значение «0» - затем инициализирует функцию калибровки.

В полной программе функции домашнегожинг и калибровки запускаются один раз, во время запуска и не вызываются снова.

Этот кодовый фрагмент вызовов Дополнительные функции, которые не включены в этот блок.

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");
}

Калибровочная процедура

Далее у нас есть наша процедура калибровки. Эта функция полностью продлит ваш привод после того, как функция самонавочного дома полностью отозвала устройство. Цель этой процедуры - подсчитать датчик импульсирует от одного конца удара привода к другому. Это значение затем сохраняется как переменная MaxStroke и используется для позиционирования. Этот код может быть изменен для обработки обратной связи потенциометра путем отображения напряжения с положением, а не подсчета импульсов.

Этот кодовый фрагмент вызовов Дополнительные функции, которые не включены в этот блок.

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 () - это Основное тело вашего кода Arduino. Все, что записано в функции Loop (), будет выполняться неоднократно во время программы.

Программисты примечание: Лично мне легче всего сохранить функцию петли максимально простой. Это часть кода, которая позвонит большинство других функций в коде и не может быть поднятым без остальной части кода.

В этой функции цикла программа проверяет для последовательного монитора для ввода, обновляет целевую позицию с вашей командой и вызывает функцию движения. Большинство сообщений, которые ПЛК отправляет на последовательный монитор, поступают из этого фрагмента кода.

Есть строка кода прокомментирована Ниже - при включении эта строка кода отображает вход 0-100 в привод. Т.е. Ваш вклад в настоящее время составляет процент от удара привода. Эта строка также может быть изменена для использования других входов в аналогичном формате.

Этот фрагмент кода вызывает дополнительные функции, которые не включены в этот блок.

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"); } }

 

Движение

Функция движения контролирует направление движения привода на основе целевого положения. Это единственная функция в этой программе, которая говорит двигателю двигать привод. 

Этот фрагмент кода вызывает дополнительные функции, которые не включены в этот блок.

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;
}

Полный код

//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: