액추에이터를 제어하기 위해 PLC를 프로그래밍하는 것은 수행하기 어려운 프로젝트 중 하나 일 수 있습니다. 시행 착오, 테스트 및 인내심이 필요합니다. 결과는 엄청나게 기능적이고 보람이 될 수 있습니다.
이 튜토리얼의 코드는 기능을위한 완전한 프로그램입니다. 일련의 반응 형 액추에이터. PLC와 함께 직렬 모니터를 사용하면 Arduino 시스템을 다른 컴퓨터, 모듈 또는 자동화 시스템과 인터페이스 할 수 있습니다.
우리는 갈 때 코드의 각 기능과 섹션을 검토 하므로이 자습서에서 특정 기능을 가져올 수 있습니다. 일부 기능은 동일한 코드 블록에 포함되지 않은 다른 기능을 호출하므로 코드가 올바르게 실행되도록 코드를 확인해야합니다.
전체 코드는이 기사의 끝에 게시됩니다. 전체 코드를 사용하려면 복사하여 IDE 소프트웨어에 붙여 넣으십시오.
프로그램 개요
초기화되면이 프로그램은 액추에이터를 홈으로 보정하여 장치의 움직임 끝을 찾습니다. 그런 다음 액추에이터를 뇌졸중의 중간 지점으로 이동하여 추가 명령을 기다립니다. 그런 다음 숫자를 직렬 모니터 (PC에서)에 입력하면 액추에이터가 해당 위치로 이동할 수 있습니다. 이 프로그램은 스트로크 종료 폐쇄로 인해 혼란스러워지지 않으며 홀 효과 센서 또는 광학 센서 액추에이터와 함께 작동합니다.
부품 목록
(*)가있는 구성 요소는 다음에 포함되어 있습니다 Arduino 스타터 키트
- Arduino plc (우노* 또는 메가 2560)
- 모터 드라이버 (고전류 AD-MD6321) - 액추에이터 당 1 개.
- 12V 전원 공급 장치 액추에이터 용 (누적 암페어 추첨을 초과)
- 1 빵 보드, 필요에 따라*
- Arduino plc 용 점퍼 와이어 - 각 고전류 MD가 필요합니다. 6 여성 연결.
- PLC 용 1 핀 와이어* (M-F 10PC/PK)
- 점퍼 와이어 키트 (M-M, 75pc/Pk; 다양한 길이);
- 피드백 센서가있는 액추에이터 필요에 따라 액추에이터 장착 브래킷. (이 코드의 경우 홀 효과 센서 또는 광학 센서 액추에이터 만)
기능
기능 요약
-
void setup()
: 핀을 초기화하고, 직렬 통신을 시작하며, 귀환 및 교정 루틴을 실행합니다. -
void homingRoutine()
: 귀환을 시작하고 액추에이터를 전체 철회로 이동시키고 최소 스트로크를 설정합니다. -
void calibrateActuator()
: 교정을 시작하고 액추에이터를 최대 확장으로 이동하고 최대 스트로크를 설정합니다. -
void loop()
: 직렬 입력을 확인하고, 대상 위치를 업데이트하고, 액추에이터 이동을 제어합니다. -
void movement()
: 대상 위치에 따라 액추에이터 이동 방향을 제어합니다. -
void readSensor()
: 센서 값, 업데이트 위치 및 센서 판독 값을 인쇄합니다. -
void stopMotor()
: 액추에이터 움직임을 중지합니다. -
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
설정/초기화
무효 설정 (); 함수는 PLC를 초기화하고 직렬 모니터를 시작하며 프로그래밍 된대로 입력 및 출력을 할당하는 기본 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 () 함수 내에 작성된 모든 것은 프로그램 중에 반복적으로 실행됩니다.
프로그래머 참고 : 나는 개인적으로 루프 기능을 가능한 한 간단하게 유지하는 것이 가장 쉽다는 것을 알았습니다. 이것은 호출 할 코드의 일부입니다. 대부분의 다른 기능 코드에서 할 수 없습니다 나머지 코드없이 들어 올리십시오.
이 루프 함수에서 프로그램은 직렬 모니터를 입력에 대해 확인하고 명령으로 대상 위치를 업데이트하고 이동 기능을 호출합니다. PLC가 직렬 모니터로 보내는 대부분의 메시지는이 코드 스 니펫에서 나옵니다.
A가 있습니다 코드 줄이 댓글을 달았습니다 아래 - 포함 된 경우,이 코드 라인은 액추에이터에 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;
}