[아두이노] RFID-RC522 + Servo Motor 제어

IOT/아두이노|2019. 5. 7. 09:00

[아두이노] RFID-RC522 + Servo Motor 제어



지난 시간에 RFID-RC522를 아두이노에 연결하여 제대로 인식하는지에 대해 예제를 통해 간단히 테스트를 하였습니다. MFRC522 라이브러리에서 초기 선언하는 세팅 부분만 간단히 살펴 보았기 때문에 뭔가 제어하는 실험이 부족한 것 같아서 오늘은 Servo Motor를 같이 연결하여 RFID-RC522를 제어하는 실험을 하고자 합니다. 어제 배웠던 내용에서 크게 변화 된 것은 없습니다. 단지 카드 UID 값을 읽고 그 값에 일치 하면 Servo Motor가 특정 각도로 회전이 일어나도록 하는 실험을 하고자 합니다. 일상에서의 도어락이나 전철을 탈때 출입 통로, 회사 출입 통로 등의 카드를 대면 인식하고 출입을 할 수 있도록 문을 열어주는 동작과 느낌을 실험하고자 Servo Motor의 Open 회전값과 Close 회전값을 주어 비슷한 느낌을 표현 하였습니다.


1. RFID-RC522 + Servo Motor 회로도



  • 준비물 : RFID-RC522, Servo Motor, 피에조부저, 아두이노우노
  • 내용 : SPI 통신을 할 수 있게 핀은 연결하고 Servo Pin 7번, 피에조부저 6번으로 연결해 보자.
  • 지난시간 회로도 참고 : RFID-RC522 제어(아두이노)


출처 : Fritzing

RFID-RC522 회로도가 좀 복잡해 보일 꺼에요. 지난 시간에 RFID-RC522 회로도에서 Servo Motor와 피에조부저 핀을 추가 하시면 됩니다. 오늘 회로도를 보고 선을 연결하지 마시고 링크 걸린 지난 시간의 회로도를 보고 회로도 선을 연결한 뒤에 오늘사용하는 7번 Servo 핀과 6번 피에조 핀만 연결하면 되니깐 그렇게 어렵지 않을 꺼에요.

2. 코딩



MFRC522

#include <SPI.h>
#include <MFRC522.h>
  • MFRC522 mfrc522(SS_PIN, RST_PIN) : MFRC522 인스턴스화
  • SPI.begin() :SPI bus 초기화
  • mfrc522.PCD_Init() : MFRC522 card 초기화
  • mfrc522.PICC_IsNewCardPresent() : 새카드 확인
  • frc522.PICC_ReadCardSerial() : 하나의 카드읽기 확인
  • rfid.uid.uidByte[] : 읽은 카드 키값이 들어 있음

피에조 부저

  • tone(tonePin ,음,음길이) : 음 시작
  • noTone(tonePin ) : 음 중지

Servo Motor

#include <Servo.h>
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큰 서보모터를 회전 시킴

시리얼 통신

  • Serial.begin(9600) : 시리얼 통신 시작(9600 통신속도)
  • Serial.println(값) : 시리얼모니터 출력
  • Serial.available() : 시리얼통신으로 데이터가 들어놨는지 상태 확인
  • Serial.parseInt() : Int형으로 데이터를 읽음

지난 시간에 이여서 오늘도 위 함수 부분은 그대로 Post에 남겨 뒀습니다. 지난 시간의 Post를 왔다 갔다 하실 필요 없이 오늘 Post만 보시고 코딩을 생각할 수 있게 그대로 가져 왔네요.

[소스] MFRC522의 예제 중 하나( 출처 : miguelbalboa의 라이브러리)

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         9       
#define SS_PIN          10      

MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance

void setup() {
    Serial.begin(9600);     // Initialize serial communications with the PC
    while (!Serial);        // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
    SPI.begin();            // Init SPI bus
    mfrc522.PCD_Init();     // Init MFRC522
    mfrc522.PCD_DumpVersionToSerial();  // Show details of PCD - MFRC522 Card Reader details
    Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks..."));
}

void loop() {
    // Look for new cards
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
        return;
    }

    // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial()) {
        return;
    }

    // Dump debug info about the card; PICC_HaltA() is automatically called
    mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
}

지난 시간에 라이브러리에 있던 예제 소스입니다. 이 소스를 기반으로 Servo Motor를 제어 하겠습니다. 우선 위 소스를 DumpInfo 예제를 한번 실행 시켜 주세요.

Card UID: 55 AF 07 88

Card UID의 값을 확인 하세요. 참고로 제가 실험하는 Card UID 값하고 여러분이 실험하는 Card UID는 다릅니다. 이 Card UID로 실험하기 때문에 한번 실행해서 자신의 Card UID를 알아내야 합니다. 확인이 되면 이 Card UID 값을 배열 변수로 저장 하세요.

byte CardUidByte[4] = {0x55, 0xAF, 0x07, 0x88};

참고로, 숫자가 알파벳이 나오면 16진수라고 여기세요. 여기서 그냥 숫자를 기입하시면 안되고 앞에 '0x'을 꼭 붙여주셔야 이 수가 16진수이구나 하고 프로그램이 인식합니다. 이렇게 해서 비교 할 Card UID값을 저장해 놓았습니다.

카드를 읽게 되면 그 값은 어디에 들어 있을까요.

mfrc522.uid.uidByte[]

이곳에 담겨져 있습니다. 4개의 UID 숫자값이 Card UID 값을 가지고 있으니 mfrc522.uid.uidByte[0] ~ mfrc522.uid.uidByte[3] 까지해서 그 값을 가지고 있습니다. 그러면, IF문으로 해서 저장된 값과 비교하면 됩니다.

  if (mfrc522.uid.uidByte[0] == CardUidByte[0] && mfrc522.uid.uidByte[1] == CardUidByte[1] &&
        mfrc522.uid.uidByte[2] == CardUidByte[2] && mfrc522.uid.uidByte[3] == CardUidByte[3] ){
                읽어온 Card UID 값과 저장된 Card UID값이 일치하면 참;
                명령문;
  }

이렇게 해서 카드를 RFID-RC522에 대면 두 Card UID 값을 서로 비교하게 됩니다. 그리고 일치하면 if문 안의 명령문을 수행하게 됩니다.

코딩은 거의 다 했습니다. 이제는 Servo Motor를 회전 시켜야 겠죠. 카드의 UID가 일치하니깐 Servo Motor가 Open 시키는 의미로 일정 각도로 회전 시키는 동작과 다시 카드를 RFID-RC522에 대면 Servo Motor가 Close 시키는 의미로 원래 각도로 회전 시켜는 동작을 표현 할 에정입니다. Card를 대면 Open or Close가 교차로 반복되는 실험입니다.

이 동작을 수행하기 위해서는 어떻게 코딩해야 할까요.

boolean state = false;

이렇게 하나의 ServoMotor의 상태값 변수를 만들어 놓습니다. 그리고 나서 아래와 같은 코딩을 하면 됩니다.

  if (mfrc522.uid.uidByte[0] == CardUidByte[0] && mfrc522.uid.uidByte[1] == CardUidByte[1] &&
        mfrc522.uid.uidByte[2] == CardUidByte[2] && mfrc522.uid.uidByte[3] == CardUidByte[3] ){
          
          state=!state;  //Servo Motor(Open or Close)
                
          if(state == true){
           Serial.println("Open");
           servo.write(180);
           delay(1000);
          }
          else{           
           Serial.println("Close");
           servo.write(0);
           delay(1000);
          }
          delay(2000);              
  }

초기 state은 false 상태이고 Cade UID 값이 일치하면 state은 반전 true가 되어 다음 if문에서 "state == ture"이면 참이니깐 "Open"으로 Servo Motor를 180도 회전 시키고 1초 대기 했다가 다시 연속으로 카드값이 인식하지 못하도록 2초동안 딜레이 시간을 추가로 더 연장 했습니다. 총 3초 동안은 Card가 인식되지 못하게 했습니다. 3초가 지난 후 Card가 다시 RFID-RC522에서 인식하면 state은 !state로 반전으로 state은 false가 되어 다음 if문 "state == ture"가 거짓으로 else 이하 문장을 수행 합니다. 그러면 "Close"로 Servo Motor은 0도로 원래 각도로 회전 되어 돌아오게 됩니다. Card가 RFID-RC522에 대면 교차로 "Open" 과 "Close"가 반복 됩니다.

이렇게 해서 Card로 Servo Motor를 회전 시킬 수 있게 되었습니다.

여기서, 추가로 Card가 읽혔는지 확인 할 방법이 없습니다. Servo Motor가 회전 되었을 때 확인이 됩니다. 즉, 카드를 Servo Motor가 회전 될 때 까지 RFID-RC522에 대고 있어야 한다는 소리가 됩니다. 좀 불편하죠.

그래서, 카드를 읽은 순간 그때 뭔가 메세지를 외부로 보여준다면 이 문제가 해결 할 수 있습니다. 그 역할을 소리로 표현하기 위해서 피에조부저를 이용하고자 합니다.

  // 새 카드 확인
  if ( ! mfrc522.PICC_IsNewCardPresent()) return; 

  // 카드 읽기
  if ( ! mfrc522.PICC_ReadCardSerial()) return;
    
  tone(tonePin ,523,1000/8);     // 도음으로 8분음표(음길이)
  delay(1000/4*1.30);             
  noTone(tonePin ); 

이렇게 카드가 읽기와 카드를 비교하기 전 사이에 사이에 피에조부저의 음이 울리게 하면 쉽게 확인이 되겠죠.

종합해 보면,

[소스]

#include <SPI.h>
#include <MFRC522.h>
#include <Servo.h>

const int RST_PIN = 9;
const int SS_PIN = 10;

MFRC522 mfrc522(SS_PIN, RST_PIN);   // MFRC522 인스턴스

byte CardUidByte[4] = {0x55, 0xAF, 0x07, 0x88}; //card UID 
boolean state = false; //Servo Motor 상태값

Servo servo;
const int SERVO_PIN = 7;
const int TONEPIN = 6;

void setup() {
  Serial.begin(9600);  
  while (!Serial);     
  SPI.begin();         //SPI 시작
  mfrc522.PCD_Init();  //MFRC522 card 초기화
  Serial.println(F("Warning: this example clears your mifare UID, use with care!"));

  servo.attach(SERVO_PIN);
  servo.write(0);
  delay(50);
}

void loop() {
  // 새 카드 확인
  if ( ! mfrc522.PICC_IsNewCardPresent()) return; 

  // 카드 읽기
  if ( ! mfrc522.PICC_ReadCardSerial()) return;

  //카드 확인 메세지 음
  tone(tonePin ,523,1000/8);     // 도음으로 8분음표(음길이)
  delay(1000/4*1.30);             
  noTone(tonePin ); 

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == CardUidByte[0] && mfrc522.uid.uidByte[1] == CardUidByte[1] &&
        mfrc522.uid.uidByte[2] == CardUidByte[2] && mfrc522.uid.uidByte[3] == CardUidByte[3] ){
   
         state=!state;    
                 
         if(state == true){
           Serial.println("Open");
           servo.write(180);
           delay(1000);
          }
          else{
           Serial.println("Close");
           servo.write(0);
           delay(1000);
         }
         delay(2000);
   }  
}

3. 결과



4. 추가 코딩


위 코딩은 약간 도어락 같은 느낌 이였다면 출입문일 경우는 카드로 열고 닫고를 안하고 한번 대면은 열리고 나서 일정시간이 지나면 닫히게 됩니다. 출입문 경우는 어떻게 코딩할까요.

위 소스에서 한 부분만 수정하면 됩니다.

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == CardUidByte[0] && mfrc522.uid.uidByte[1] == CardUidByte[1] &&
        mfrc522.uid.uidByte[2] == CardUidByte[2] && mfrc522.uid.uidByte[3] == CardUidByte[3] ){
   
           Serial.println("Open");
           servo.write(180);
           delay(5000);
           Serial.println("Close");
           servo.write(0);
           delay(1000);                 
   }  

이렇게 수정하시면 끝납니다. 실험에서는 5초 동안 열렸다가 다시 닫히는 걸로 했는데요. 실제 출입문이면 이러지는 않겠죠. 적어도 수십초는 열려 있다가 닫혀야 겠죠. 아니면 초음파 센서같은 인간 감지 센서를 이용하여 지나 가면 자동으로 닫히게 하면 되겠죠. 그렇게 하자면 부품이 추가로 늘어나고 오히려 오늘 전달하고자 하는 의미가 제대로 전달되지 않기 때문에 이정도 까지만 하겠습니다.

[결과]


마무리


RFID-RC522 라이브러리는 쉽지 않지만 간단한 부분만 가져다가 응용하면 그래도 재밌는 표현들을 할 수 있습니다. 여러분들도 한번 이 RFID-RC522 모듈을 구하셔서 실험을 해보셨으면 합니다. 가격도 비싸지 않고 싼편이라서 괜찮은 실험 도구라고 생각 되네요.

아두이노우노에서 실험하면 SPI 통신을 하기 때문에 사실 다른 부품을 추가 할 자리가 많지 않습니다. 그럴 때는 두개 이상의 아두이노로 통신을 통해 제어하면 좋은데 그 실험을 하려면 2대정도 있어야 하는데 한대 뿐이라 아쉽네요.

아무튼 오늘 배운 내용을 토대로 다른 부품 특징들을 머리속에서 떠올려서 RFID-RC522와 연결하는 상상을 한번 해보셨으면 합니다.


댓글()

[아두이노] Stepper Motor에 대해 상상하다.

IOT/아두이노|2019. 5. 5. 09:00

[아두이노] Stepper Motor에 대해 상상하다.



Steem.js에 재미를 붙여서 실험하고 있어서 아두이노 post가 늦어지고 있네요. 지난주의 Stepper Motor 제어를 집중적으로 다뤘는데 오늘 post는 실제 구현은 안해 봤지만 상상한 내용을 post에 담아 이야기를 하고자 합니다. Stepper Motor는 각도를 제어하는 Motor 입니다. 각도를 제어 한다는 것은 회전을 프로그래머가 설계한 방향으로 회전을 시킬 수 있다는 의미입니다. 원하는 위치로 회전을 시킬 수 있고 회전 된 각도의 값을 알 수 있다면 이것을 이용하여 많은 것들을 표현 할 수 있습니다.

원하는 각도로 회전 시킨다는 것은 정방향 100도 회전 시킬 수 있고, 역방향 -500도로 회전 시킬 수 있으며 회전하면서 회전 각도를 저장해 놓으면 현재 Stepper Motor가 어느 방향으로 몇도 회전 되어 있는지 알 수 있습니다. 이 정보로 우리들은 뭘 할 수 있을까요. 산업 현장에서는 산업 로봇이 자동으로 나사를 조이고 용접하고 물건을 나르는 등의 움직임을 제어하는 데 사용 됩니다. 조립을 하는 과정에서 나사를 몇 바뀌 회전 시켜야 하는지 각도값으로 제어할 수 있습니다. 그리고 산업 로봇팔이 용접을 할 때 용접 할 위치로 산업 로봇팔의 관절이 회전하면서 해당 위치로 이동하겠죠. 즉, 움직임을 표현하는데 각도제어 Motor가 사용 되고 각도를 제어를 할 수 있는 Stepper Motor는 많은 것들을 할 수 있게 됩니다.

오늘 다룰 내용은 움직임을 각도로 나눠서 초음파 센서로 거리를 측정하고자 합니다. 이렇게 얻은 거리 정보를 토대로 초음파 센서에서 감지된 대상의 좌표 (x,y,z) 꼭지점을 구하고자 합니다. 그리고 구한 꼭지점을 processing으로 시각화 하는 상상을 해 보았습니다. 실제 구현은 못해 봤습니다. 사실 딴거에 빠져서 구현은 안했습니다. 그냥 상상한 내용을 담아 이야기 하고자 합니다.

1. 거리 측정 회로도


  • 준비물 : 스템모터, 모터드라이버, 초음파센서, 아두이노우노


대충 위와 같은 형태로 구성한다고 상상을 해 봤습니다. 이렇게 하면 Stepper Motor가 1도씩 회전을 한다면 어떻게 될까요. 각도에 따른 거리를 측정 할 수 있게 됩니다.


위 그림처럼 원통에 거리 측정기를 넣고 측정을 시작한다고 가정을 해 봅시다. 그러면 원통의 벽면까지의 거리값을 구할 수 있겠죠. 여기서, 360도를 1도씩 회전하면서 거리를 측정한다고 상상을 해보세요. 측정되어 나온 원통까지의 거리값을 이용하여 꼭지점(x,y,z)를 구할 수 있습니다. 그 꼭지점을 연결하면 단층 이미지를 만들어 낼 수 있습니다. 이 단층 이미지가 여러장이 쌓이면 3D Rendering 을 통해서 실제 원통 모양으로 이미지를 만들어 낼 수 있겠죠.

오늘 실험은 얻어진 좌표 (x,y,z) 꼭지점을 연결하여 processing에 배치시켜 3D 단층 이미지를 만드는 과정을 상상을 해보고자 합니다. 그 과정을 이제부터서 진행 하겠습니다.

2. 거리 측정에 따른 좌표 꼭지점 구하기



위 x,y,z 좌표계가 있다면 원점은 Stepper Motor의 회전하는 중심 지점이 되겠죠. (x,z)축의 평면 방향으로 초음파 센서가 1도씩 회전을 한다면 위 그림처럼 360도의 360개의 좌표 지점을 만들어 낼 수 있게 됩니다. y축은 아두이노의 초음파가 붙어있는 위치가 되며 초음파가 측정하는 각도에 방향에 따라 (x,z)축의 거리 꼭지점을 아래의 공식에 의해서 구할 수 있습니다.


그러면 (x,z)축으로 펼쳐서 보면 위 그림 처럼 나타나고 Stepper Motor의 회전 angle이 되고 초음파 측정 거리는 r이 되어 이 r과 angle의 값을 기준으로 꼭지점 (x,z)를 구할 수 있습니다. 오랫만에 접하는 수학이라서 기억도 안나고 손 놓은지가 정말 오래만이라 공식도 기억 안나서 인터넷에서 찾았네요. 이제 이런것들을 수학적으로 계산하기에는 너무 벅차네요. 중학교 수준이지만요. 요즘 초등학생들이 배우는 수학인 것 같은데 잘 모르겠네요. 이정도 수학도 이제 벅차네요.

이렇게 해서 y축은 초음파의 고정 위치이니깐 고정 y꼭지점에서 Setpper Motor가 회전하는 angle에 따른 초음파의 거리(r)을 통해 x,z 꼭지점을 구하면 원점에서 1도씩 회전 할때마다 좌표 (x,y,z)의 꼭지점을 구할 수 있게 됩니다.


좌표 (x,y,z)의 꼭지점 선으로 연결하면 이런 평면 이미지가 그려지겠죠.

3. processing으로 측정된 좌표를 연결


위에서 구한 좌표(x,y,z)의 꼭지점을 구하게 되면 processing으로 표현 한다면 어떤 느낌일까요.

다음과 예제를 보시기 바랍니다.

void setup() {  
    
    size(600,600,P3D); //창사이즈
    noStroke();  //테두리없음
} 

void draw() {  
    background(0); //배경색
    lights();  //조명

    pushMatrix();  //Start
    translate(300,270,300); //이동
    shape();
    popMatrix(); //End
}
void shape(){
  fill(0,255,0); //채우기
  beginShape();
  for(int i=0;i<=360;i++){
      vertex(cos(i)*100, 0, sin(i)*100);   
  }
  endShape(CLOSE);
}

위 예제는 for문을 유심히 보시기 바랍니다. 위 소스는 processing 3D 도형 제어 (아두이노) Post의 소스 수정을 최소화하여 표현 했습니다.

아두이노에서 processing과 시리얼통신을 할 수 있다고 했죠. 그러면 Stepper Motor가 1도씩 회전하면서 초음파 센서가 측정한 좌표 지점을 구한 (x,y,z) 꼭지점을 시리얼통신으로 전송한다면 그 값으로 beginShape()~endShape(CLOSE) 사이의 vertex(x,yz) 값을 시리얼통신으로 얻은 (x,y,z) 꼭지점으로 대체한다면 360도 회전할 때 360개의 꼭지점을 그리게 되면 평면 이미지를 만들 수 있게 됩니다.

위 소소는 100이라는 초음파 거리를 고정으로 초음파 거리 (r)에 대한 Stepper Motor 회전(angle)을 가상으로 주어진 상태에서 가상으로 이미지를 그리는 예제입니다.

4. processing으로 측정된 좌표 이미지 결과



위 결과는 가상 데이터 (x,y,z)의 값을 기준으로 그렸기 때문에 원형의 이미지를 얻었습니다. 실제로 구현을 한다면 불규칙적인 평면의 이미지를 얻을 수 있겠죠.

5. 이것로 뭘 만들까?


순간 떠오르는 생각을 상상력으로 구현은 안했지만 글로써 표현을 했습니다. 이걸로 뭘 만들 수 있을까요. 일상에서 이런 표현들이 뭐가 있을까요. 이 글을 쓰는 순간에 떠오르는 생각은 자율주행에서 360도 회전하는 센서에 주변 사물을 감지하는데 적용하게 좋겠죠.

그럼 또 뭘 할 수 있을까요. 거리측정센서로 3D 랜더링을 할 수 있을까? post에서 이야기한 3D 랜더링에 적용할 수 있습니다. 위 실험에 대한 상상한 내용은 하나의 평면 단층 이미지를 만들 수 있습니다. 그러면, 초음파 센서가 일렬로 여러개가 연결되어 있으면 여러장의 단층 이미지를 얻을 수 있겠죠. 아니면 Stepper Motor를 하나 더 제어해서 y축 회전 시켜서 y축 위치를 변경해 가면서 x,z 축의 단층 이미지 데이터를 얻을 수 있겠죠. 이렇게 여러개의 단층 (x,y,z)의 값을 얻을 수 있으면 이 데이터들을 서로 연결하면 3D 모형의 이미지 형태로 랜더링을 할 수 있습니다. 위 실험 자체만으로도 3D 좌표 평면 이미지로 표현 되었지만 좀더 Volume Rendering을 하려면 여러장의 이미지 데이터가 필요 합니다.

그외도 이야기를 하자면 끝도 없습니다. 오늘 제가 post한 내용들이 바로 상상 코딩입니다. 왜! 제가 post를 끝날 때마다 상상의 나래를 펼쳐 보라는 이유가 오늘 post의 담겨져 있습니다. 실제 구현을 안하더라도 우리는 일상의 사물을 보면서 또는 영화 속 SF장면을 떠올리면서 많은 상상을 할 수 있습니다. 그 상상이 아두이노의 소재가 되는 것이고 그 아두이노로 상상한 것들을 비슷하게 구현해 낼 수 있습니다.

실제로 구현을 안하더라도 가상으로 이렇게 상상 코딩을 할 수 있습니다.

마무리


여러분들도 상상 코딩을 생활화 해보세요. 뭔가 프로그램 언어를 배우고 정석으로 깊게 파고드는 것도 중요하지만 무엇보다 중요한 것은 상상 코딩입니다. 정석으로 전문 코딩을 하는 것은 엔지니어이지 개발자나 창작자가 아닙니다. 다소 코딩이 부족하더라도 뭔가에 대해서 상상하는 사람이 진정한 개발자이나 창작자입니다.

엔지니어는 반복학습하면 누구나 다 될 수 있지만 개발자나 창작자는 상상을 하지 않으면 되지 못합니다. 여러분들도 처음에 코딩을 전문적으로 정석 코스로 깊게 배우는 싶은 분들도 많을 꺼에요. 하지만 상상하지 않으면 의미가 없습니다.

마지막으로 여러분들도 위 내용이 아니더라도 아두이노 관련 부품이나 누가 표현한 원리 중 하나를 찾아서 그 원리를 다른 쪽으로 응용하여 상상해보는 시간을 가졌으면 합니다.

댓글()

[아두이노] Stepper 대신 Servo Motor로 가상시뮬레이터 실험

IOT/아두이노|2019. 5. 4. 09:00

[아두이노] Stepper 대신 Servo Motor로 가상시뮬레이터 실험



지난시간에 Stepper Motor 원하는 각도 회전과 블루투스 원격 제어 실험을 하였습니다. 이 부분을 아두이노가 없는 분들을 위해서 어떤 느낌인지 의미를 전달하고자 가상시뮬레이터로 표현을 해보았네요. 표현한 방식은 Servo Motor를 이용하여 아두이노 두대를 연결하여 시리얼통신을 통해서 Servo Motor를 제어하는 실험입니다. 가상시뮬레이터에서 Stepper Motor가 없기 때문에 직접 실험을 할 수 없습니다. 물론 DC 모터를 가지고 Stepper 라이브러리를 적용하여 억지로 표현하는 경우는 있지만 그렇게 표현하면 오히려 혼동을 야기 할 수 있으니 정석으로 지난 시간의 실험한 Stepper Motor 코딩 로직을 기반으로 Servo Motor를 대신 사용하여 실험을 진행했으며 위에 공개회로도 링킁 가셔서 가상시뮬레이터를 실행 시켜보세요. 온라인 상에서 체험 할 수 있을 거라 생각 됩니다.

이제 본격적으로 실험 내용을 다뤄 보겠습니다.

1. Servo Motor 회로도


  • 준비물 : Servo Motor 1개, 아두이노우노
  • 내용 : 10핀을 Servo Motor 출력핀으로 연결하시오.


간단하게 선 연결을 할 수 있겠죠.

1) 코딩


내용 : servo 라이브러리를 이용하여 아두이노 IDE의 시리얼통신 입력값에 의해 회전시키자.
참조 : [아두이노] Stepper Motor 원하는 각도 회전(1)


Servo Motor

#include <Servo.h>
Servo servo : 서보모터 객체 선언
servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
servo.write(회전각) : 회전각 만큰 서보모터를 회전 시킴

시리얼 통신

  • Serial.begin(9600) : 시리얼 통신 시작(9600 통신속도)
  • Serial.println(값) : 시리얼모니터 출력
  • Serial.available() : 시리얼통신으로 데이터가 들어놨는지 상태 확인
  • Serial.parseInt() : Int형으로 데이터를 읽음

지난 시간의 Stepper Motor의 소스에서 그대로 가져왔습니다.

#include <Servo.h>

Servo servo;
int angle = 0;

void setup()
{
  Serial.begin(9600);
  servo.attach(10);  
  
  servo.write(angle);
  delay(50);
}

void loop()
{
    if(Serial.available()) {
      int val=0;
      char ch=Serial.read();
      if(ch=='a')val=1;      
      else if(ch=='d')val =-1;
      else val =0;
      
      angle+=val;
      
      if(angle>180) angle=180;
      else if(angle<0) angle=0;
        
      servo.write(angle);
      Serial.println(angle);      
      delay(50);
    }
}

Stepper Motor랑 코딩은 동일합니다. 단 if문이 하나 더 추가 되었는데 0~180도 사이 각으로 회전이 제한이 됩니다. 180도 이상과 0도 이하의 각도로 회전을 할 수 없습니다. 그래서 angle각이 180도를 넘으로 180도에 수렴하게 만들고 0도 이하가 나오면 0도에 수렴하게 만들어 놓았습니다. 그외는 코딩이 동일하기 때문에 따로 변경할 부분은 없습니다.

      if(angle>180) angle=180;
      else if(angle<0) angle=0;

위 문장만 잘 이해하시면 따로 설명은 필요 없겠죠. 지난 시간에 설명 했으니깐요. 아무튼 시리얼 모니터에서 'a'와 'd'를 입력하면 알파벳 키 값에 따라서 회전을 시키게 됩니다.

2) 결과



2. 2대의 아두이노 + Servo Motor 회로도


  • 준비물 : Servo Motor 1개, 아두이노우노 2개
  • 내용 : 10핀을 Servo Motor 출력핀으로 연결하고 Serial 통신을 하기 위해서 두대의 아두이노는 2,3빈으로 교차로 연결하라.

  • Rx - 데이터 읽기 (B arduino Pin3 =>A arduino Pin2)
  • Tx - 데이터 보내기(B arduino Pin2 =>A arduino Pin3)

한대는 스마트폰이라고 생각하고 다른 한대에 Servo Motor를 제어하는 실험입니다. 소스 코딩도 동일합니다. B 아두이노가 아래쪽 Servo Motor가 연결된 A 아두이노에 명령을 내리면 그 명령에 따라서 A 아두이노가 Servo Motor를 회전 하게 됩니다. Bluetooth 통신 소스와 동일합니다. 2대의 아두이노로 Bluetooth 실험을 대신한다고 생각하시면 됩니다.


1) 코딩



시리얼 통신

  • Serial.begin(9600) : 시리얼 통신 시작(9600 통신속도)
  • Serial.println(값) : 시리얼모니터 출력
  • Serial.available() : 시리얼통신으로 데이터가 들어놨는지 상태 확인
  • Serial.parseInt() : Int형으로 데이터를 읽음

SoftwareSerial 통신

#include <SoftwareSerial.h>

SoftwareSerial mySerial (rx, tx) : 소프트시리얼 객체선언(rx(수신), tx(전송))
mySerial.begin(9600) : 시리얼 통신 시작(예로 9600 통식속도를 사용해 봤네요.)
mySerial.write(값) : 데이터 전송
mySerial.available() : 데이터 들어왔는 확인
mySerial.read() : 전송된 데이터 1byte 읽기


[A 아두이노 코딩]

#include <Servo.h>
#include <SoftwareSerial.h>

const int rxPin = 2;
const int txPin = 3;
SoftwareSerial mySerial(rxPin, txPin); // RX, TX

Servo servo;
int angle = 0;

void setup()
{
  Serial.begin(9600);
  mySerial.begin(2400);
  servo.attach(10);  
  
  servo.write(angle);
  delay(50);
}

void loop()
{
    if(mySerial.available()) {
      int val=0;
      char ch=mySerial.read();
      
      if(ch=='a')val=1;      
      else if(ch=='d')val =-1;
      else val =0;
      
      angle+=val;
      
      if(angle>180) angle=180;
      else if(angle<0) angle=0;
        
      servo.write(angle);
      Serial.println(angle);      
      delay(50);
    }
}

[B 아두이노 코딩]

#include <SoftwareSerial.h>

const int rxPin = 2;
const int txPin = 3;
SoftwareSerial mySerial(rxPin, txPin); // RX, TX

void setup()
{
   Serial.begin(9600);
   mySerial.begin(2400);
}

void loop()     
{
  if(Serial.available()) {
    mySerial.write(Serial.read());      
  } 
}

2) 결과



마무리


Stepper Motor 회전을 Bluetooth를 이용하여 스마트폰에서 제어를 하는 실험을 지난 시간에 했었습니다. 그것을 가상시뮬레이터에서 최대한 같은 표현을 하기 위해서 두 대의 아두이노를 연결하여 한쪽에서 다른쪽으로 Motor 제어 명령을 보내고 Stepper Motor가 지원이 안됨으로 그 역활을 Servo Motor로 대신하여 회전시키는 실험을 하였습니다.

가상시뮬레이터에서 최대한 같은 느낌의 표현과 코딩을 그대로 적용함으로써 지난 시간에 배운 Stepper Motor 제어를 느낄 수 있게 표현 되어 있으니 한번 공개회로도 링크로 가셔서 체험을 해보세요.

이상으로 Stepper Motor Post를 마무리 합니다.

댓글()

[아두이노] Stepper Motor 원하는 각도 회전(2)

IOT/아두이노|2019. 5. 3. 09:00

[아두이노] Stepper Motor 원하는 각도 회전(2)



지난 시간에 Stepper Motor를 원하는 각도로 회전 시켰는데 오늘은 Bluetooth로 스마트폰에서 아두이노 연결 된 Stepper Motor를 회전 시키는 실험을 하도록 하겠습니다.


1. Stepper Motor + Bluetooth 통신 준비


스마트폰과 아두이노는 Bluetooth를 통해 통신합니다.


스마트폰 Bluetooth 어플은 아무거나 사용해도 됩니다. 실험에서는 Controller mode와 Terminal mode를 사용하여 실험 했네요. Controller mode은 버턴을 지정하고 버턴을 클릭하면 해당 버턴값을 아두이노로 보내는 형식이고요, Terminal mode은 채팅하는 식으로 아두이노 IDE 시리얼모니터에서 값을 입력하는 것과 유사합니다. 직접 전송할 값을 타이핑해서 보내는 방식입니다. 버턴을 눌러서 그 값을 전송하는 방식과 직접 타이핑해서 전송하는 방식이 들어 있는 어플이면 아무거나 상관 없습니다. 직접 앱인벤터로 만드셔도 되고요.


그냥 시리얼 통신으로 값을 전달할 수 있는 Bluetooth 어플이면 아무거나 상관 없습니다. 안드로이드면 구글스토어에서 Arduino Bluetooth만 치셔도 많은 어플들이 검색되니깐 실험하고 싶은 어플을 아무거나 선택하시면 됩니다.

2. Stepper Motor + Bluetooth 회로도



  • 준비물 : 5V 스템모터, ULN2003 모터드라이버, Bluetooth(HC-06) 1개, 아두이노우노
  • 내용 : 8,9,10,11핀을 순서대로 IN1, IN2, IN3, IN4에 연결하고 스템모터와 UNL2003 모터쉴드에 연결하고 Bluetooth를 2, 3번에 연결한다.

  • Rx - 데이터 읽기 (BlueRX =>arduino Pin3)
  • Tx - 데이터 보내기(BlueTX =>arduino Pin2)


지난 시간의 Stepper Motor 회로도에서 Bluetooth만 하나 더 연결한 회로도 입니다.

2. 코딩 - I



Stepper

#include <Stepper.h>
  • Stepper stepper(STEPS, IN1, IN2, IN3, IN4) : 사용하는 Stepper Motor 스템수, 각 4핀을 세팅.
  • stepper.setSpeed(값) : Stepper Motor의 속도 설정.
  • stepper.step(스텝수) : 스템수로 회전 각을 표현 한다.

시리얼 통신

  • Serial.begin(9600) : 시리얼 통신 시작(9600 통신속도)
  • Serial.println(값) : 시리얼모니터 출력
  • Serial.available() : 시리얼통신으로 데이터가 들어놨는지 상태 확인
  • Serial.parseInt() : Int형으로 데이터를 읽음

SoftwareSerial 통신

#include <SoftwareSerial.h>

SoftwareSerial mySerial (rx, tx) : 소프트시리얼 객체선언(rx(수신), tx(전송))
mySerial.begin(9600) : 시리얼 통신 시작(예로 9600 통식속도를 사용해 봤네요.)
mySerial.write(값) : 데이터 전송
mySerial.available() : 데이터 들어왔는 확인
mySerial.read() : 전송된 데이터 1byte 읽기


[지난 시간 소스]

#include <Stepper.h>

const int STEPS = 2048;
Stepper stepper(STEPS, 8,10,9,11);

void setup()
{
  stepper.setSpeed(12);
  Serial.begin(9600);
}

void loop()
{
   if(Serial.available()) {
    int val=Serial.parseInt(); //회전각 int형으로 읽기
    
    val=map(val,0,360,0,2048); //회전각 스템 수
    stepper.step(val);
    Serial.println(val);
    delay(10);
  }
}

위 소스를 가지고 수정을 합시다. Bluetooth 통신을 하기 위해서 SoftwareSerial 라이브러리를 이용하여 통신을 하게 됩니다 그래서, 다음과 같은 Bluetooth 통신 명령 과정이 필요합니다.

#include <SoftwareSerial.h>

const int rxPin = 2;
const int txPin = 3;
SoftwareSerial mySerial(rxPin, txPin); // RX, TX

이렇게 초기 선언을 합니다. 2번핀은 Bluetooth의 TX핀이고 3번핀은 Bluetooth의 RX핀입니다. 혼동하시면 안됩니다. 나머지 코딩은 Serial 단어를 mySerial로만 변경하시면 코딩 수정이 끝납니다. 변경되는 부분은 아래와 같습니다.

void setup()
{  
  mySerial.begin(9600);
}
void loop()
{
   if(mySerial.available()) {
    int val=mySerial.parseInt(); //회전각 int형으로 읽기
    mySerial.println(val);
    delay(10);
  }
}

합쳐진 코딩은,

#include <SoftwareSerial.h>
#include <Stepper.h>

const int rxPin = 2;
const int txPin = 3;
SoftwareSerial mySerial(rxPin, txPin); // RX, TX

const int STEPS = 2048;
Stepper stepper(STEPS, 8,10,9,11);

void setup()
{
  stepper.setSpeed(12);  
  mySerial.begin(9600);  
}

void loop()
{
 
  if(mySerial.available()) {
    int val=mySerial.parseInt();
    val=map(val,0,360,0,2048);
    stepper.step(val);
        mySerial.println(val);
    delay(10);   
  }
}

[결과]
Terminal Mode로 값을 직접 타이핑 한 값으로 회전시키면 다음과 같은 결과가 나오네요. 360도를 보내면 아두이노가 Bluetooth로 받은 후 Stepper Motor를 회전시킨후 회전 시킵니다. 그리고 나서 스템수를 다시 스마트폰으로 보내고 스마트폰는 받은 스템수를 아래 이미지 처럼 "HC-06: 2048"이라고 출력 됩니다.


간단히 실험 영상을 보시기 바랍니다.


3. 코딩 - II


코딩-I은 스마트폰으로 각도를 채팅방식으로 입력을 해서 Stepper Motor를 회전 시켰다면 이제는 방향버턴키로 회전시키는 코딩으로 수정하도록 하겠습니다. 참고로 방향키버턴으로 눌러서 회전 시키면 위 코딩-I 소스대로 하면 문제가 좀 발생합니다. 그 이유는 mySerial.parseInt() 함수 때문에 그렇습니다. 만약에 각도 데이터를 전송하고 읽을때 읽는 데이터가 밀리면 통신 버퍼에 쌓이게 되고 그러면 합쳐진 상태로 각도를 읽는 문제가 발생합니다. 가령, 10도 회전을 연달아 이어서 10, 10 이렇게 전송이 되었을 때 받는 아두이노에서 데이터가 쌓여 1010이렇게 되면 parseInt()함수는 한번에 1010각도로 읽어버리게 됩니다. 즉, 10, 10 이렇게 회전 시켜서 20도를 회전 시켜야 하는데 1010 이것을 한번에 읽어서 1010각도로 회전 시켜버리는 문제가 생기게 됩니다. 그래서 방향키를 방향 알파벳 한글자로 해서 mySerial.read()로 1byte씩 읽고 그 값에 따라 회전시키는 방법으로 쉽게 제어할 수 있도록 코딩을 변경 하겠습니다.

코딩-1 소스에서,

  if(mySerial.available()) {
    int val=mySerial.parseInt();
    val=map(val,0,360,0,2048);
    stepper.step(val);
        Serial.println(val);
    delay(10);   
  }

이 부분만 변경하면 됩니다.

int angle = 0; //외부변수 현재 각도값
if(mySerial.available()) {
      int val=0;
      char ch=mySerial.read();

      if(ch=='a')val=10;      //시계방향
      else if(ch=='d')val=-10; //반시계방향
      else val=0;
      
      angle+=val;
      val=map(val,0,360,0,2048);  
      stepper.step(val);
      //Serial.println(angle);      
      delay(10);
}

if문으로 1byte 읽은 알파벳이 ch변수에 저장되고 이 저장된 변수가 'a'면 정방향 10도 회전, 'd'면 역방향 10도 회전시키고 그외 키 값은 무시 0도 회전으로 if문에서 체크하여 val 변수에 회전시킬 각도값을 저장하게 했습니다.

여기서 외부 변수로 angle에 대한 동작 명령은 코딩하지 않았습니다. 이것은 여러분들이 나중에 활용하라는 의미로 남겨 둔 변수입니다. 이 변수는 현재 코딩에서는 없어도 되는 변수이지만 구지 표현한 이유는 나중에 어려분들이 Stepper Motor를 회전을 제어할 때 꼭 필요한 변수이기 때문입니다. 예를 들면, 우리가 Stepper Motor 회전을 시키면 700도 정방향으로 회전 시켰다가 역방향 200도 회전 시키고 또 다시 정방향, 역방향 이렇게 계속 회전을 시킬 경우 Stepper Motor라 마지막 회전된 위치를 단순히 회전만 시키면 알 수 없습니다. 이때 angle이라는 변수를 하나 선언해 놓고 처음 회전이 시작한 위치를 0도에 지정해놓고 계속 회전 값들을 angle변수에 더하게 되면 Stepper Motor의 시작 위치 0도에서 어느 방향으로 회전을 하더라도 현재 회전한 위치를 알 수 있게 됩니다.

여기서, 한가지 예로 외부 입력값 E(End)라고 생각하고 위 if문에 연장 선상으로 if문으로 아래와 같이 코딩하면

else if(ch=='e') val = -angle;

이렇게만 표현하면 e키가 입력된 순간 angle의 변수 안에는 지금 까지 회전되어 진 현재의 위치각도를 가지고 있기 때문에 그 값에 -(마이너스)를 붙이면 원상태 0도로 되돌아 가게 됩니다. 즉, 90도가 현재의 위치면 -angle로 -90도 만큼 회전 하면 0이 되겠죠. 다시 -90도가 현재의 위치면 -(-90)이니깐 +90도 만큼 회전 하면 0이 됩니다. 결론은 e키가 입력되면 이 한줄로 처음 자신의 위치로 되돌아 가는 명령을 코딩할 수 있습니다.

angle의 변수를 남겨 둔 이유는 여러분들이 나중에 회전을 제어할 때 언제든지 자신의 위치로 되돌아 갈 수 있고 현재의 회전 된 위치 값을 가질 수 있으면 그 기준으로 특별한 회전을 제어할 수 있게 되기 때문에 이런 angle 변수가 중요하기 때문에 선언만 해놓았습니다. 나머지는 여러분들이 이 변수를 가지고 어떻게 활용할지는 여러분의 몫입니다.

종합해보면,

#include <Stepper.h>
#include <SoftwareSerial.h>

const int rxPin = 2;
const int txPin = 3;
SoftwareSerial mySerial(rxPin, txPin); // RX, TX

const int STEPS = 2048;
Stepper stepper(STEPS, 8,10,9,11);

int angle = 0;

void setup()
{
  Serial.begin(9600);
  mySerial.begin(9600);
  stepper.setSpeed(12); 
}

void loop()
{
  if(mySerial.available()) {
      int val=0;
    char ch=mySerial.read();
      if(ch=='a')val=10;      
      else if(ch=='d')val=-10;
      else val=0;
      
      angle+=val;
      val=map(val,0,360,0,2048);  
      stepper.step(val);
      Serial.println(angle);      
      delay(10);
    }
}

[결과]



Controller Mode를 누르면 위 그림처럼 버턴 이미지로 구성된 창이 뜹니다. 그리고 톱니모양 설정을 누르면 버턴 키 값을 지정할 수 있습니다. 게임 방향키값인 a,s,w,s키로 우선 설정했는데 실제 실험에서는 a,d키만 사용합니다. 이 키값을 통해서 Setpper Motor를 회전 시키게 됩니다.

간단히 스마트 폰으로 방향키를 누렀을 때 Stepper Motor가 회전되는 실험 영상입니다.

마무리


이렇게 해서 스마트폰으로 간단히 회전을 시키는 법을 배웠습니다. Bluetooth를 한번 소개한 뒤에 계속 중복된 실험이 이뤄지고 있는데 이런 실험 과정을 여러분들도 실험하면서 계속 반복 학습을 해주세요. 그래야 나중에 다른 부품을 사용하더라고 이 부품을 이전에 배웠던 부품들과 쉽게 연결할 수 있습니다. 어떻게 두 부품을 결합하고 두 부품의 소스 코딩을 어떻게 합쳐지고 코딩이 되는지 그 감각을 익히셨으면 합니다. 그리고 하나의 표현으로 끝내지 말고 다른 표현을 계속 머리속에서 상상하셔야 합니다. 처음에는 이게 힘들지만 나중에는 재밌는 놀이처럼 여러가지의 상상을 하게 됩니다.

그런데 처음 배우시는 분들은 Bluetooth로 통신하는 실험만 하고 끝나고 Stepper Motor 회전 실험만 하고 넘어 갑니다. 나주엥 어떤 주제로 작품을 만들려고 할 때 그 때부터 문제가 발생합니다. 두 개 이상의 부품을 합치는 실험을 할 때 회로도 표현은 그럭저럭 되는데 코딩부분은 둘을 합치는 방법 자체를 이해를 못하는 경우가 많기 때문에 여러분들은 처음 부품을 배울 때 그 부품을 다른 부품과 계속 연결해서 실험하셔야 나중에 어떤 부품이든지 쉽게 표현을 할 수 있게 됩니다.

오늘 실험은 Stepper Motor를 하나를 조정했지만 두개 이상 연결하면 RC카 또는 로봇팔 등의 구현이 가능하고 Stepper Motor은 다양한 관절 제어에 사용하니깐 한번 SF영화나 애니메이션에서 봤던 장면들을 떠올리시고 거기서 Stepper Motor가 적용된다면 어떤 느낌으로 회로도를 구성하고 코딩 하면 좋을 지 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] Stepper Motor 원하는 각도 회전(1)

IOT/아두이노|2019. 5. 2. 09:00

[아두이노] Stepper Motor 원하는 각도 회전(1)



지난 시간에 Stepper Motor를 간단히 360도 회전 실험을 했습니다. 이제는 Stepper Motor로 자유자재로 회전을 시켜 봐야 겠지요. 그럼 원하는 각도로 회전 시키는 방법을 알아보도록 합시다.


1. Stepper Motor 회전의 원리


회전을 제어하는 Motor는 대표적으로 두 종류가 있습니다. Servo Motor와 Stepper Motor 입니다. 둘은 비슷하게 회전하지만 실제 회전 명령을 내릴 때는 약간의 차이가 있습니다. Servo Motor의 경우 0~180도 사이 각으로 회전을 하게 됩니다. 여기서 90도 회전 할 경우 servo.write(90도) 이라고 표현하면 90도 만큼 회전 됩니다. 그리고 90도 위치에서 다시 120도 위치로 갈려면 servo.write(120도) 이라고 표현하면 됩니다. 즉, 원하는 각도로 가고 싶으면 해당 각도값을 넣으면 해당된 위치로 Servo Motor가 회전하게 됩니다.

하지만 Stepper Motor의 경우 좀 다릅니다. 360도 회전 시킬 수 있는 Motor 입니다. 여기서, 90도 회전시 stepper.step(90도스텝수) 표현하면 90도 만큼 회전 됩니다. 그런데 90도 위치에서 120도 위치로 갈려면 stepper.step(120도스텝수) 이라고 하면 안됩니다. stepper.step(120도스템수)이라고 하면 현재 90도 위치이지만 그 시점에서 새롭게 120도 만큼 회전을 하여 210도를 위치로 회전 됩니다. 즉, 회전이 끝난 지점이 새로운 회전이 시작되는 지점이니깐 90도 회전 후 120도 위치로 회전 될려면 Stepper Motor는 30도 회전을 시켜야 합니다. 이점을 생각하시고 코딩을 하셔야 합니다. 즉, 매번 회전 명령을 내리는 곳이 시작점이 되는 거라고 생각하시면 됩니다.

Servo Motor와 Stepper Motor의 회전는 이점을 생각하고 코딩하면 됩니다.

2. Stepper Motor 회로도



  • 준비물 : 5V 스템모터, ULN2003 모터드라이버, 아두이노우노
  • 내용 : 8,9,10,11핀을 순서대로 IN1, IN2, IN3, IN4에 연결하고 스템모터와 UNL2003 모터쉴드에 연결하시오.


지난 시간과 동일한 회로도를 사용하였습니다.


3. 코딩



함수

#include <Stepper.h>
  • Stepper stepper(STEPS, IN1, IN2, IN3, IN4) : 사용하는 Stepper Motor 스템수, 각 4핀을 세팅.
  • stepper.setSpeed(값) : Stepper Motor의 속도 설정.
  • stepper.step(스텝수) : 스템수로 회전 각을 표현 한다.

시리얼 통신

  • Serial.begin(9600) : 시리얼 통신 시작(9600 통신속도)
  • Serial.println(값) : 시리얼모니터 출력
  • Serial.available() : 시리얼통신으로 데이터가 들어놨는지 상태 확인
  • Serial.parseInt() : Int형으로 데이터를 읽음

[지난 시간 소스]

#include <Stepper.h>

#define STEPS 2048

Stepper stepper(STEPS, 8, 10, 9, 11);

void setup() {
  stepper.setSpeed(12);
}

void loop() {
  stepper.step(STEPS);
  delay(1000);
  stepper.step(-STEPS);
  delay(1000);
}

위 소스를 가지고 수정을 합시다.

우선, 아두이노 IDE 시리얼모니터에서 입력으로 회전각을 입력한다고 가정하면 아래와 같이 회전 각을 int형으로 읽어서 val변수에 회전각을 저장하게 표현 했습니다. 참고로 정수로 입력하는 방식으로 정수로 읽게 parseInt()함수로 표현 했고요.

if(Serial.available()) {
    int val=Serial.parseInt();
}

이제 val이라는 변수는 회전각도 값이 들어 있으니깐 이 각도를 실제 Stepper Motor의 회전 스템수로 변환 시켜야 겠죠. 스템수가 회전각이니깐요. 가독성으로 각도로 입력을 했습니다. 각도에 대한 스템수를 구하는 식이 필요 하겠죠. 예로 180도라고 입력을 하면 180도에 대한 스템수 1024값을 구해야 합니다.

그러기 위해서는 다음과 같이 map()함수를 이용합니다.

val=map(val,0,360,0,2048);

map() 함수에 입력각 val에 대한 입력 범위 0~360에서 출력 0~2048의 스템수를 세팅하면 회적 각도에 따라 스템수를 만들어 낼 수 있습니다. val이 180이면 스템수는 1023이 나오고 만약 -180도면 -1023 스템수가 나오게 됩니다. 이렇게 해서 각도에 대한 스템수를 만들어 냈습니다.

회전은,

stepper.step(val);

마무리로 step() 명령으로 실제 Stepper Motor를 회전을 시켜 원하는 각도에 제어할 수 있게 됩니다.

종합해서 코딩하면,

#include <Stepper.h>

const int STEPS = 2048;
Stepper stepper(STEPS, 8,10,9,11);

void setup()
{
  stepper.setSpeed(14);
  Serial.begin(9600);
}

void loop()
{
   if(Serial.available()) {
    int val=Serial.parseInt(); //회전각 int형으로 읽기
    
    val=map(val,0,360,0,2048); //회전각 스템 수
    stepper.step(val);
    Serial.println(val);
    delay(10);
  }
}

아두이노 IDE 시리얼모니터에서 입력한 각도에 따라 원하는 각도로 Stepper Motor를 회전을 시킬 수 있게 되었습니다.

4. 결과


아두이노 IDE 시리얼모니터에서 각도를 입력하면 Stepper Motor가 그 각도에 맞에 회전을 하는 영상입니다.


마무리


아두이노 IED 시리얼 모니터에서 입력한 회적각을 map()함수로 회전 스템수로 구하고 step()함수로 실제 Stepper Motor를 회전 키는 로직을 완성 했습니다. 시리얼 통신으로 회전을 제어 할 수 있게 되었으니깐 이걸로 블루투스 통신으로 하면 스마트폰으로 쉽게 Stepper Motor를 회전 시킬 수 있겠죠. 아니면 processing 으로 이미지로 버턴을 만들어서 Stepper Motor를 회전 시킬 수 도 있습니다. 그것도 아니면, 입력을 조이스틱이나 가변저항 같은 것들로 외부의 다른 Sensor를 이용해서 회전을 시킬 수 도 있습니다. 선택은 여러분의 상상력에 따라 달라집니다.

추가로, Bluetooth를 이용해서 스마트폰으로 회전을 시켰는데 오늘 전부 다 올리려고 하니 좀 내용이 길어질 것 같아서 나눠서 내일 post에 올리도록 하겠습니다.

여러분들은 Stepper Motor를 가지고 어떻게 회전을 할지 상상력을 펼쳐 보세요.


댓글()

[아두이노] Stepper Motor 라이브러리로 제어

IOT/아두이노|2019. 5. 1. 09:00

[아두이노] Stepper Motor 라이브러리로 제어



오늘은 복잡하게 Stepper Motor의 4핀을 직접 제어할 필요 없이 Stepper Motor 라이브러리를 사용하여 쉽게 제어하는 실험을 하겠습니다.


1. Stepper Motor 회로도



  • 준비물 : 5V 스템모터, ULN2003 모터드라이버, 아두이노우노
  • 내용 : 8,9,10,11핀을 순서대로 IN1, IN2, IN3, IN4에 연결하고 스템모터와 UNL2003 모터쉴드에 연결하시오.


Stepper Motor 라이브러리를 실험하기 위해 따로 새롭게 만들 필요가 없기에 그냥 지난 시간에 만든 회로도를 그대로 사용하겠습니다.


2. 코딩



함수

#include <Stepper.h>
  • Stepper stepper(STEPS, IN1, IN2, IN3, IN4) : 사용하는 Stepper Motor 스템수, 각 4핀을 세팅.
  • stepper.setSpeed(값) : Stepper Motor의 속도 설정.
  • stepper.step(스텝수) : 스템수로 회전 각을 표현 한다.

아두이노 IDE Stepper 예제에 보시면 MotorKnob 예제가 있습니다. 원래 동작은 A0에 입력되는 아날로그 신호에 의한 회전인데 아마도 조이스틱과 가변저항으로 조정한 값을 Stepper Motor의 회전각을 만드는 것 같더군요. 오늘 실험은 이걸 하기 위한게 아니라 간단히 회전을 시킬 수 있는지에 대해 테스트 실험이기 때문에 수정을 하겠습니다.

[소스] : MotorKnob 예제입니다.

#include <Stepper.h>

#define STEPS 100
Stepper stepper(STEPS, 8, 9, 10, 11);
int previous = 0;

void setup() {
  stepper.setSpeed(30);
}

void loop() {
  int val = analogRead(0);
  stepper.step(val - previous);
  previous = val;
}

위 소스를 가지고 수정을 합시다.

우선,

#define STEPS 100

Stepper Motor의 Step의 총수입니다. 즉, 360도 회전에 Step 수라고 생각하시면 됩니다. 이부분은 여러분들이 변경해야 합니다. 자신이 사용하는 Stepper Motor의 종류에 따라서 계산해야 합니다.



STEPS = (360/step angle)* Gear ratio


예) [ 28BYJ-48 5V DC Stepper Motor]

  • Number of Phase : 4

  • Speed Variation Ratio : 1/64

  • Stride Angle : 5.625° /64

  • STEPS = (360/5.625)*64 = 4096


실제로 4096으로 입력하면 제 모터에서는 2바퀴를 회전하더군요. 2048로 STEPS을 잡으니깐 360도 회전을 할 수 있었습니다.

다음으로 핀 연결인데 위 회로도 대로 연결해서 돌리면 사실 돌아가지 않아서 처음 공부 할 당시 애를 먹었던 부분입니다. 처음에 삽질 했는데 핀 위치가 문제였더군요. 위 회로도로 연결했을 때 핀위치는 바뀌어야 합니다.

Stepper stepper(STEPS, 8, 9, 10, 11);

변경,

Stepper stepper(STEPS, IN1, IN3, IN2, IN4);
Stepper stepper(STEPS, 8, 10, 9, 11);

이렇게 설정하여 STEPS로 회전 시키니깐 시계방향으로 360도 회전 하였네요. -SEPS로 하니깐 반시계방향으로 360도 회전이 되었고요.

그 다음, stepper Motor의 회전 속도를 steup()함수에서 설정합니다. 하지만 이 speed값은 STEPS의 수와 관계가 있습니다. 만약 4096으로 잡고 돌릴 경우에는 5~7정도의 speed를 잡아야 정상적으로 회전이 일어나고 10정도 입력하면 회전을 하지 않습니다. 2048의 경우는 10~14사이가 정상적으로 회전을 하더군요.

stepper.setSpeed(12);

실제 회전 명령은 step()함수로 step수는 회전각입니다.

stepper.step(step수);

종합해서 수정하면, 정방향으로 360도 회전 후 역방향으로 360도 회전

#include <Stepper.h>

#define STEPS 2048

Stepper stepper(STEPS, 8, 10, 9, 11);

void setup() {
  stepper.setSpeed(12);
}

void loop() {
  stepper.step(STEPS);
  delay(1000);
  stepper.step(-STEPS);
  delay(1000);
}

4. 결과


회전은 깔끔하게 되긴 했는데 오차각이 좀 생기네요. Stepper Motor 싼거라 분해능력이 떨어져서 각이 약간 오차가 발생해서 정교한 각도 제어는 제가 실험한 Stepper Motor는 다소 무리가 있네요.


마무리


복잡하게 생각을 하지 않아도 Stepper Motor라이브러리로 쉽게 회전을 시킬 수 있겠죠. 코딩량도 대폭 줄어 들고 간단히 세개의 함수의 의미만 이해하면 Stepper Motor를 회전 시킬 수 있습니다.

Stepper stepper(STEPS, 8, 10, 9, 11);
stepper.setSpeed(12);
stepper.step(STEPS);

Stepper 객체변수 선언하고 Stepper 객체변수의 setSpeed()함수의 회전속도와 step()회전 스템수로 각도f를 회전 하게 됩니다. 이 세개의 함수만 이해하시면 쉽게 제어 할 수 있게 됩니다. 그런데 사실 쉽지는 않습니다. STEPS의 수를 계산하기가 약간 어려울 수 있습니다. Stepper Motor의 부품 사양을 보고 계산해서 구해야 하기 때문에 이부분이 어려울 수 있지만 이부분만 제대로 지정해 주시면 그 다음 부분들은 어렵지 않을 꺼에요.

참고로, 다른 Stepper Motor를 사용하실 때에는 해당 Stepper Motor 라이브러리를 검색하시셔 라이블러리에 추가해서 사용하시면 됩니다. 혹시 라이브러리가 없는데 그냥 따라서 include만 시킨다고 되는게 아니니깐요. 자신이 사용하는 부품명으로 우선 검색하시고 있으면 그걸 사용하세요. 없다면 공용 Stepper Motor 라이브러리를 사용하시고요.

마지막으로, 여러분들은 360도 회전을 할 수 있다면 어떤 것을 해보고 싶은지 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] Stepper Motor 제어

IOT/아두이노|2019. 4. 30. 09:00

[아두이노] Stepper Motor 제어



오늘은 Stepper Motor를 다뤄보려고 합니다. Motor편은 Servo Motor 이후 오랫만이죠. Servo Motor은 아두이노에서 바로 제어가 가능하기 때문에 따로 준비사항이 없었지만 Stepper Motor나 DC Motor는 모터 쉴드가 필요합니다. 아두이노에서 직접 Motor에 연결해서는 안됩니다. 직접 연결한다고 해서 작동을 안하는 것은 아니지만 아두이노에 문제가 생길 수 있기 때문에 직접 제어를 피해야 합니다. 그 이유는 Motor는 많은 전류를 필요하기 때문에 아두이노에 전류를 공급할 때 아두이노에서 일시적으로 전류가 불안정한 상태에 빠져 숏다운도 될 수 있습니다. 그리고 Motor의 역전류 현상에서 아두이노에 역전류가 흐르게 되면 아두이노는 치명적 손상을 입히게 됩니다. 그래서, Motor의 경우는 아두이노에 직접 Motor와 연결하지 않습니다. 그 사이에 모터 쉴드가 필요하고 모터 쉴드를 통해서 Motor를 제어하게 됩니다.

오늘 배우게 될 Stepper Motor는 참 재있는 부품입니다. Servo Motor와 유사하게 각도를 제어한 부품입니다. Servo Motor는 일반적으로 360도 회전이 안됩니다. 변형시키거나 특수 Motor의 경우는 회전이 가능하지만 일반적으로 일정 사이각 180도로 회전이 제한이 됩니다. 하지만 Stepper Motor는 360도 회전 뿐 아니라 각도를 정교하게 회전 시킬 수 있어 큰 장점을 지닌 Motor입니다.

이제 본격적으로 Stepper Motor에 대해서 살펴보도록 하겠습니다.


1. 5V 스템모터 + ULN2003 모터 드라이버



[ Fritzing 부품 그림 ]


Stepper Motor는 모터 내부의 고정자의 극 수에 따라서 1상, 2상, 3상, 4상, 5상, 6상 등 여러 종류로 나뉩니다.

그리고, 회로방식에서 Unipolar Steppers, Bipolar Steppers으로 나뉩니다. 아래 아두이노 공식 홈페이지의 튜토리얼에 자세히 나와 있으니깐 읽어주세요.



제 실험에서 Stepper Motor를 ULN 2003 모터 드라이브로 제어를 하게 됩니다.

Stepper Motor의 자세한 설명은 사실 포스트에 전체적인 내용을 쓰기에는 그림작업이 너무 힘들고 글로써 설명하자니 그림 없이 의미 전달하기에는 가독성이 너무 떨어지고 해서 괜찮은 글들을 소개하는 수준으로 Stepper Motor의 기본 개념 설명을 대신하고자 합니다. 사실 이태훈님의 블로그보다 더 쉽게 설명을 할 자신이 없어서 아래 참고할만한 사이트들을 링크 걸어놓습니다.


[출처]


참고로, pdf 파일로 만들어 놓은 세번째 링크 주소에서 유니플러 "1 상 여자 동작" 이 부분을 특히 잘 봐주시기 바랍니다. 이부분을 사용하여 Stepper Motor를 제어를 실험할 예정입니다. 1 step에 하나의 상만 전류가 공급되는 방식인데 이걸로 스템모터를 라이브러리 없이 모터를 제어할 수 있습니다.

1 step => A상(5V), B상(0V), C상(0V), D상(0)
2 step => A상(0V), B상(5V), C상(0V), D상(0)
3 step => A상(0V), B상(0V), C상(5V), D상(0)
4 step => A상(0V), B상(0V), C상(0V), D상(5V)

예) 1 step 일 경우 :

  digitalWrite(stepPin[0],HIGH);
  digitalWrite(stepPin[1],LOW);
  digitalWrite(stepPin[2],LOW);
  digitalWrite(stepPin[3],LOW);
  delay(10);

이렇게 계속 반복을 하고 각도를 회전하게 됩니다 위 step 순서로 전류가 공급되었을 때 이 방향을 정방향이라고 하면 역방향은 위 과정을 역순으로 진행하면 역방향이 되겠죠. 링크된 pdf 파일에 있는 곳에서 그림으로 보시면 대충 어떤식으로 진행되는지만 파악해 두세요. 이걸 코딩으로 표현한다면 어떻게 할지도 여러분들이 공곰히 생각해보세요.

힘든 분들은 이부분을 그냥 잊어주세요. 편하게 라이브러리를 처음에 사용하시는 것이 좋겠죠.

스템 모터 쉴드


위 그림을 보시면 대충 위치는 파악 하실 수 있겠죠. 참고로 Stepper Motor선 연결은 연결부분이 구별되어 있어서 개별핀의 위치는 신경 안쓰셔도 됩니다. 회로도를 그릴 때 만 연결 핀 위치를 명확하게 표현 해야하지만요. 참고로 아두이노에서 IN1에 전류가 공급되면 입력 핀에 신호가 온 것을 해당 LED에 불이 들어오게 하여 입력 신호 상태를 알 수 있는 모터 드라이브네요. 실제로 구매하시면 전원 담당 부분은 +, -가 표기가 제대로 안되어 있으니깐 +, -위치는 암기해 주세요. 나머지 선은 순서대로 연결만 하면 됩니다.

자세히 세부적인 핀 연결은 아래 그림을 잘 확인하시면 됩니다. 연결 선은 Color Name으로 표시 되어 있기 때문에 맞춰서 연결하시면 됩니다. 이 동작은 위에 링크 걸린 곳에 가시면 다른분이 자세한 설명이 있으니깐 깊게 공부하실 분들은 가셔서 필독하시면 됩니다.


제가 생각하기에는 처음에는 우선 스템 모터를 회전시키는 동작에만 초점을 두었으면 합니다. 뭔가 실제로 돌아가는 걸 보고 나서 스템 모터의 내부 원리에 대해서 이해하시는게 더 좋을 듯 싶네요. 나중에 이걸로 다양한 표현을 하다가 좀 더 깊게 공부하고 싶은 욕구가 생길 때 정석으로 공부하시고 지금은 그냥 모터를 회전시킬 수 있는 것에만 관심을 가지시면 됩니다. 스템 모터 라이브러리를 직접 여러분들이 만들 필요가 없으니깐요. 우리는 단지 이 부품을 가지고 회전만 시킬 수 있는 능력을 갖추시면 됩니다. 그리고 회전을 시켰을때 이것을 어디에 써먹을지에 좀 더 초점을 두었으면 합니다.

2. Stepper Motor 회로도


  • 준비물 : 5V 스템모터, ULN2003 모터드라이버, 아두이노우노
  • 내용 : 8,9,10,11핀을 순서대로 IN1, IN2, IN3, IN4에 연결하고 스템모터와 UNL2003 모터쉴드에 연결하시오.

여기서 전원부분은 아두이노에서 빼냈습니다. 참고로 전원은 아두이노보다는 따로 외부 전원을 사용하시는 것이 좋습니다. 귀찮아서 실제로도 아두이노에 연결해서 사용했지만 Stepper Motor를 여러개 연결 할 경우 아두이노에 문제가 생길 수도 있습니다.


3. 코딩


  • 내용 : stepper 라이브러리 없이 1상 여자 방식으로 회전 시켜보자.

함수

pinMode(사용핀,사용모드) : 사용핀을 INPUT/OUTPUT 모드를 설정
digitalWrite(사용핀,출력상태) : 사용핀을 HIGH(5V) or LOW(0V) 설정
delay(시간값) : 시간값만큼 대기




1상 여자 방식은 전류의 흐름을 아래 그래프와 같습니다.


이걸 표로 살펴보면,


위 표처럼 4개의 핀에 전류를 순서대로 공급하면 됩니다.

회로도에서 핀 8(A), 9(B), 10(C), 11(D)의 핀을 전류 공급 순서를 step 순서대로 순차적으로 공급하면 Stepper Motor가 회전을 하게 됩니다.

1 step => A상(5V), B상(0V), C상(0V), D상(0)
2 step => A상(0V), B상(5V), C상(0V), D상(0)
3 step => A상(0V), B상(0V), C상(5V), D상(0)
4 step => A상(0V), B상(0V), C상(0V), D상(5V)

1 Step : 1,0,0,0 으로 사용자 정의 함수를 하나 phasesate()함수를 하나 만들어 봅시다.

void phasesate(int a, int b, int c, int d, int delayval){
  digitalWrite(stepPin[0],a);
  digitalWrite(stepPin[1],b);
  digitalWrite(stepPin[2],c);
  digitalWrite(stepPin[3],d);
  delay(delayval);
}

이렇게 함수를 하나 정의해 놓으면 이 함수를 호출함으로서 아래와 같이 간단히 표현할 수 있습니다.

phasesate(1,0,0,0,10);

phasesate(1번핀,2번핀,3번핀,4번핀,속도)로 이렇게 표현이 됩니다.

패턴을 하나의 함수로 묶으면,

void stepmovea(){
  phasesate(1,0,0,0,10);
  phasesate(0,1,0,0,10);
  phasesate(0,0,1,0,10);
  phasesate(0,0,0,1,10);
}

이제 회전을 시켜볼까요.

void loop() {
  for(int i=0;i<500;i++){
    stepmovea();
  }
}

약 500정도에 360도 근처정도의 회전을 시키게 됩니다. 정확히 360도 회전을 하지 않습니다.

역회전을 한다면, stepmovea() 패턴 함수를 역순으로 진행하면 됩니다.

void stepmoveb(){
  phasesate(0,0,0,1,10);
  phasesate(0,0,1,0,10);
  phasesate(0,1,0,0,10);
  phasesate(1,0,0,0,10);
}

이렇게 하면 역순으로 회전이 되겠죠.

코드를 종합해 보면.

const int stepPin[4] = {8,9,10,11};

void setup() {

  for(int i=0;i<4;i++){
    pinMode(stepPin[i],OUTPUT);
  }
}

void loop() {
 
  for(int i=0;i<500;i++){
    stepmovea();
  }
  for(int i=0;i<500;i++){
    stepmoveb();
  }
}
void phasesate(int a, int b, int c, int d, int delayval){
  digitalWrite(stepPin[0],a);
  digitalWrite(stepPin[1],b);
  digitalWrite(stepPin[2],c);
  digitalWrite(stepPin[3],d);
  delay(delayval);
}

void stepmovea(){
  phasesate(1,0,0,0,10);
  phasesate(0,1,0,0,10);
  phasesate(0,0,1,0,10);
  phasesate(0,0,0,1,10);
}

void stepmoveb(){
  phasesate(0,0,0,1,10);
  phasesate(0,0,1,0,10);
  phasesate(0,1,0,0,10);
  phasesate(1,0,0,0,10);
}

4. 결과


회전 속도를 10으로 해서 그런지 좀 느리게 회전을 하네요. 그리고 500번의 루프를 돌았는데 약 360도 정방향으로 회전 했다가 역방향으로 회전하고 이렇게 반복적으로 회전을 수행한 결과입니다.

마무리


오늘은 Stepper 라이브러리 없이 회전을 직접 시도 했습니다. 회전시 문제점은 정교한 회전을 하기 위해서는 Stepper Motor의 사양과 delay(시간)값을 수학적으로 계산해야 합니다. 이런 복잡한 부분을 직접 코딩하는 것은 귀찮은 일이죠. Stepper Motor의 종류에 따라서 접근 방식과 계산이 달라지기 때문에 시간적으로 낭비입니다. 그렇기 때문에 우리는 편하게 Stepper 라이브러리를 사용해야 합니다. Stepper Motor로 뭘 만들지 상상해야지 Stepper Motor를 직접 라이브러리를 만드는데 시간을 낭비하면 안되겠죠.

Stepper Motor가 어떻게 회전을 했는지만 의미만 이해하시고 이론적인 부분은 위에 링크로 소개한 게시물들로 가셔서 이론을 배우세요.


댓글()

[아두이노] 스위치버턴+Servo Motor 제어

IOT/아두이노|2019. 4. 21. 09:00

[아두이노] 스위치버턴+Servo Motor 제어



어제 조이스틱으로 Servo Motor를 실험을 했는데 포스트를 본신 분들이 그냥 글을 보는거로만 끝내는 것이 좀 그런 것 같아서 조이스틱의 느낌을 스위치 버턴으로 최대한 살려서 비슷하게 가상시뮬레이터에서 테스트를 해 볼 수 있도록 변경을 해 봤습니다. 원리는 동일하고 스위치 버턴 2개로 조이스틱의 좌/우측의 회전 값으로 표현을 했고 스위치 버턴 1개로 조이스틱의 스위치 버턴으로 표현을 해서 총 3개의 스위치 버턴으로 조이스틱의 느낌을 담아 회로도와 코딩을 수정했네요. 위에 공개회로도를 클릭하시면 바로 가상시뮬레이터를 실행 할 수 있는 창이 나오니깐 가상시뮬레이터로 직접 스위치를 눌러 Servo Motor를 회전 시켜보세요. 대충 어떤 느낌으로 회전이 이루어지는지 체험을 하실 수 있습니다. 이제 본격으로 어떻게 표현 했는지 살펴보도록 하죠.

1. 회로도 구성


  • 준비물 : 스위치 버턴 3개, Red LED 1개, 저항 220옴 1개, Servo Motor 1개, 아두이노우노
  • 내용 : Servo Motor핀은 7번에 연결, 스위치 버턴은 3,4,5 번핀에 연결, Red LED은 12번 핀으로 연결


어제 포스팅한 조이스틱 + Servo Motor 회로에서 조이스틱 부분만 스위치 3개로 대신 표현한 회로도 입니다. 나머지는 동일하고 스위치 버턴 3개만 변경 했네요.

3. 코딩


  • 사용함수 : pinMode(), digitalRead(), delay(), servo.attach(), servo.write(), map()
  • 내용 : 스위치 버턴 2개로 Servo Motor를 회전 시키고 나머지 한개로 Red LED를 제어 깜박이게 해보자.

함수

  • pinMode(사용핀, 입/출력모드) : 해당 사용핀을 어떤 모드로 사용할지 결정한다. INPUT_PULLUP모드로 설정(내부저항을 사용할 예정)
  • digitalRead(디지털핀) : 디지털 신호를 읽음(0 or 1)
  • digitalWrite(출력핀, HIGH/LOW) : 출력핀에 HIGH(5V) or LOW(0)를 출력
  • delay(시간값) : 시간값만큼 대기한다.

Servo Motor 함수

#include <Servo.h> 
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큰 서보모터를 회전 시킴

[ 조이스틱 소스 ]

#include <Servo.h> 

Servo servo;

const int AXIS_X = A0;
const int AXIS_Y = A1;
const int SW_P = 3; 

const int servoPin = 7;
const int redLed = 12;

int m_Angle = 0;
 
void setup() {
  servo.attach(servoPin);
  pinMode(redLed, OUTPUT);
  pinMode(SW_P,INPUT_PULLUP);
}
 
void loop() {
  m_Angle = map(analogRead(AXIS_X),0,1023,0,180);
  digitalWrite(redLed,digitalRead(SW_P));
  servo.write(m_Angle); 
  delay(100); 
  
}

변경 후, 어떻게 코딩이 되었는지 볼까요.

[스위치 버턴 소스]

#include <Servo.h> 

Servo servo;

const int AXIS_X1 = 5;
const int AXIS_X2 = 4;
const int SW_P = 3; 

const int servoPin = 7;
const int redLed = 12;

int m_Angle = 0;

void setup()
{
  servo.attach(servoPin);
  pinMode(redLed, OUTPUT);
  pinMode(AXIS_X1, INPUT_PULLUP);
  pinMode(AXIS_X2, INPUT_PULLUP);
  pinMode(SW_P, INPUT_PULLUP);
}

void loop()
{    
  if(digitalRead(AXIS_X1) == 0){  
    if(m_Angle<180){
      m_Angle=m_Angle+10; 
    }          
  }
  else if(digitalRead(AXIS_X2) == 0){ 
    if(m_Angle>0){
      m_Angle=m_Angle-10; 
    }      
  }
  else{
    digitalWrite(redLed,digitalRead(SW_P));
  }
  servo.write(m_Angle);
  delay(100);
}

pinMode()함수로 두개의 스위치 버턴의 모드를 풀업저항모드로 설정을 했고 loop()함수 내부의 코드가 좀 바뀌었네요.

조이스틱보다 코딩이 좀 길어졌죠. if~else if 문을 사용했습니다. AXIS_X1과 AXIS_X2 로 두개의 변수를 설정해서 방향 스위치를 만들었습니다. AXIS_X1 방향이 정방향이면 AXIS_X2은 역방향이 됩니다.

이제 회전 시킬려면 스위치를 눌렀을 때 상황을 만들어 줘야 합니다. 정방향 스위치를 눌렀는지 역방향 스위치를 눌렀는지를 IF문으로 표현 하면 됩니다. 참고로 내부풀업저항을 이용 했기 때문에 초기 상태는 '1'의 상태입니다. 여기서 스위치를 누르면 '0'의 상태가 됨으로 스위치가 눌렀을 때 '0'인가 라고 IF문에서 체크하면 됩니다.

다음과 같은 코딩으로 표현이 되겠죠.

if(digitalRead(AXIS_X1) == 0) {
    정방향 10도 회전;
}
else if(digitalRead(AXIS_X2) == 0){  
    역방향 10도 회전;
}

이제 두 개의 방향스위치로 해당 스위치를 누르면 10도씩 회전하게 만들려면 m_Angle(각) 변수를 하나 만들어 놓고 이 변수값을 10씩 변화를 주면 됩니다.

  • 정방향 => m_Angle=m_Angle+10;
  • 역방향 => m_Angle=m_Angle-10;

간단하게 이 표현으로 스위치를 눌렀을 때 정방향은 +10이 되고 역방향은 -10이 되도록 위 문장을 코딩을 하면 간단하게 회전 시킬 각을 만들 수 있습니다.

그런데 Servo Motor은 0~180도 회전만 할 수 있습니다. 아무리 스위치 버턴을 눌러도 180도 이상 증가하면 안되고 0도이하로 감소해도 안됩니다.

if(m_Angle<180)  m_Angle=m_Angle+10; 

if(m_Angle>0)  m_Angle=m_Angle-10; 

이렇게 if문으로 각에 대해 제한을 두면 아무리 스위치를 눌러도 180이상 0이하로 m_Angle 값은 더이상 변하지 않게 됩니다.

종합해보면,

  if(digitalRead(AXIS_X1) == 0){  
    if(m_Angle<180) m_Angle=m_Angle+10; 
  }
  else if(digitalRead(AXIS_X2) == 0){ 
    if(m_Angle>0) m_Angle=m_Angle-10;     
  }
    else{
    digitalWrite(redLed,digitalRead(SW_P));
  }

이렇게 방향 스위치를 누르지 않았다면 else 문을 수행하는데 SW_P핀의 스위치가 상태값에 따른 redLed핀의 출력을 만들어 내면 마무리가 됩니다. 그냥 else에서 SW_P핀의 스위치가 눌러졌는지 체크하고 그냥 넘어가는 표현이라고 생각하시면 될 듯 싶네요.
이 부분을 else if()문으로 눌러졌는지 물어 보는 코딩을 해도 되지만 그냥 스위치 상태값으로 LED를 상태를 결정하기 때문에 묻는 조건식은 생략 했네요.

4. 결과


가상시뮬레이터 결과 영상입니다.


이것도 실제로 표현을 해서 어떻게 돌아가는지 돌려봤네요.


마무리


어제는 조이스틱으로 Servo Motor를 제어 했고 오늘은 스위치 버턴으로 같은 느낌으로 Servo Motor를 제어를 해 보았습니다. 뭔가를 표현학 그게 동작하는 모습을 봐야 어떤 느낌인지 이해가 빠를 꺼에요. 원래 Servo Motor 회전 제어를 예전 포스트에서 리모콘으로 조정했던 포스트였습니다. 아마도 Bluetooth 통신 포스트를 할 때도 LED나 Servo Motor를 가지고 제어를 하지 않을까 싶네요. 가장 시각적으로 표현하기 쉬운 부품이니깐요.

아무튼 어제 배운 조이스틱의 내용이 중요하니깐 꼭 조이스틱의 동작 원리를 이해하셨으면 합니다. 그리고 오늘 다룬 내용은 같은 의미로 스위치 버턴이 대신한 응용편으로 복습이라고 생각하시면 될 듯 싶네요.

여러분들도 다른 것을 이용하여 비슷한 동작을 수행하시고 싶으시다면 어떤 부품으로 비슷한 느낌을 표현할 수 있을지 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] 조이스틱 + Servo Motor 제어

IOT/아두이노|2019. 4. 20. 09:00

[아두이노] 조이스틱 + Servo Motor 제어



조이스틱으로 뭔가를 조정하는 응용 예제로 소개할 만한게 없나 고민하다가 지금까지 포스트 한 것 중에서 찾다가 조정 느낌이 느낄 수 있는 LED로 실험 했다가 그냥 포스트에 올리는 걸 포기했네요. 다른 부품으로 찾다가 Servo Motor를 제어를 해보는게 재밌 소재가 될 것 같고 따로 코딩하는 부분도 별로 없을 것 같아 실험 주제로 결정 했네요. 코딩도 아주 초 간단하게 원리만 표현 했고 지난 시간에 조이스틱 조정기 값을 읽는 것을 배웠으니깐 이제는 그 값을 기준으로 Servo Motor의 Angle을 정하면 되니깐 별로 어렵지 않고 재밌는 포스팅이 될 것 같네요.

자! 그러면 Servo Motor를 조이스틱으로 제어를 해 봅시다.

1. 회로도 구성


  • 준비물 : 조이스틱 1개, Servo Motor 1개, Red LED 1개, 저항 220옴 1개, 아두이노우노
  • 내용 : A0, A1 핀은 x,y 방향 아날로그 신호를 받고 5번핀은 스위치 신호를 받도록 선은 연결하고 7번핀에 Servo Motor 출력핀으로 연결하고 12번핀은 Red LED의 출력으로 사용한다.

[ Fritzing 디자인 ]


회로도를 보시면 좀 복잡해 보일 수 있는데, 조이스틱에서 방향 신호(VRX, VRY)와 스위치 신호(SW) 선만 아날로그 핀과 디지털 핀에 연결 하고, Servo Motor핀과 LED 핀들은 사용하고자 하는 디지털 핀을 선택해서 연결하시면 됩니다. 나머지는 다 전원에 관련된 핀이라 회로도 그림만 좀 복잡해 보이고 선 연결은 어렵지 않습니다.

2. 코딩



함수

  • pinMode(사용핀, 입/출력모드) : 해당 사용핀을 어떤 모드로 사용할지 결정한다. INPUT_PULLUP모드로 설정(내부저항을 사용할 예정)
  • analogRead(아날로그핀) : 아날로그 신호를 읽음(0~1023)
  • digitalRead(디지털핀) : 디지털 신호를 읽음(0 or 1)
  • digitalWrite(출력핀, HIGH/LOW) : 출력핀에 HIGH(5V) or LOW(0)를 출력
  • delay(시간값) : 시간값만큼 대기한다.
  • map(입력값,입력최소,입력최대,출력최소,출력최대) : 입력값이 입력 최소~최대범위가 출력 최소~최대에 매칭되어 출력

Servo Motor 함수

#include <Servo.h> 
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큼 서보모터를 회전 시킴

설계

조이스틱의 어떤 값으로 Servo Motor를 제어 해야 할까요. X 방향, Y 방향, 스위치 버턴 이 세가지 신호 중에 어떤 것을 사용할까요. 여기서, X, Y 값 중에 하나를 선택하면 됩니다. Servo Motor 한개가 하나의 축 방향이라고 생각하면서 접근하시면 됩니다.

간단히, 저는 X 방향 아날로그 신호로 Servo Motor를 제어 해볼까 합니다.

그러면 조정값을 어떻게 Servo Motor랑 매칭해야 할까요. 한번 생각을 해보세요.

조이스틱이 처음 어떤 상태로 어떤 값을 초기값으로 되어 있나요. 조이스틱의 현재 상태와 현재 신호를 곰곰히 생각 해보시면 그 안에 답이 있습니다. 바로, 조이스틱은 방향 아날로그 신호값이 중앙값으로 처음 시작 합니다. 즉, 0~1023의 아날로그 신호에서 512이라는 중심값이 초기 상태로 아날로그 신호로 시작 합니다.

이때 왼쪽은 0~512 사이가 되고 오른쪽은 512~1023 사이의 값으로 좌우 방향을 나타낼 수 있습니다. 이 원리를 이용하시면 쉽게 해결 됩니다. 512를 기점으로 좌우 신호 값으로 Angle를 표현하면 되니깐요. 여기서 0~1023의 아날로그 신호값에서 0도가 0의 값이 되고 180도는 1023이 되게 하면 중앙값 512은 90도가 되고 이 개념을 가지고 코딩을 하면 됩니다.

그렇게 하면, 처음 시작은 중앙값 512로 Servo Motor은 90도에서 시작하게 되고 아날로그 신호가 0에 가까울 수록 0도에 가까워지고 아날로그 신호가 1023에 가까울 수록 180도에 가까워지겠죠.

여기서, 문제점은 0을 0도로 1023을 180도로 어떻게 표현 할까요. map()함수를 이용하면 됩니다. 은근히 자주 사용하는 함수인데 여기에서도 사용 하네요.

m_Angle = map(analogRead(AXIS_X),0,1023,0,180);

AXIS_X핀의 아날로그 신호값이 0~1023 범위에서 출력 0~180범위에 어느 정도의 위치가 되는지 알아서 해당 값을 찾아주는 함수입니다. 만약, 512값이 입력신호로 들어오면 출력 0~180 범위에서 90이라는 값의 위치하니깐 90이 반환되어 나옵니다. 즉, 입력신호값을 0~180사이의 값으로 자동으로 변환시켜주는 함수인 셈이죠.

이 한줄로 조이스틱의 X방향의 아날로그 신호를 각도로 만들어 낼 수 있겠죠.

 servo.write(m_Angle); 
 delay(100); 

아날로그 신호 512일 때, Servo.write(90)함수로 90도를 Servo Motor가 회전하게 됩니다. delay()함수는 Servo Motor가 충분이 회전할 시간값을 줘야 하기때문에 같이 코딩 해야 합니다. 100은 0.1초인데 더 짧게 하시면 좋겠죠. 그냥 이전 포스트 소스를 가져다가 표현한거라 딜레이 시간은 그냥 뒀네요. 원래 저 값은 특정한 각도로 회전 한뒤에 다시 다른 특정한 각도로 회전할 때까지의 충분한 시간 값인데 조이스틱 조정기에서는 이렇게 큰 시간값은 필요 없습니다. 각도 값 범위가 변화율은 크지 않기 때문에 짧게 시간을 주면 됩니다. 100은 아주 큰 값이지만 조정기로 해본 결과 그냥 사용해도 제 실험에서는 상관 없어서 그냥 뒀네요.

결론은 조이스틱의 X방향 신호값을 읽어와서 map()함수로 각도를 만들어 내고 servo.write()로 각도를 출력하는 코딩으로 딱 두줄이면 조이스틱으로 Servo Motor를 움직이게 할 수 있게 됩니다.

그러면, 전체 소스를 살펴봅시다.

[ 소스 ]

#include <Servo.h> 

Servo servo;

const int AXIS_X = A0;
const int AXIS_Y = A1;
const int SW_P = 3; 

const int servoPin = 7;
const int redLed = 12;

int m_Angle = 0;
 
void setup() {
  servo.attach(servoPin);
  pinMode(redLed, OUTPUT);
  pinMode(SW_P,INPUT_PULLUP);
}
 
void loop() {
  m_Angle = map(analogRead(AXIS_X),0,1023,0,180);
  digitalWrite(redLed,digitalRead(SW_P));
  servo.write(m_Angle); 
  delay(100);   
}

변수가 코딩의 절반을 차지 하네요. 정작 로직 코딩은 몇 줄 안되는 데 말이죠.

  digitalWrite(redLed,digitalRead(SW_P));

서비스 코딩으로 스위치 버턴을 누르면 12번에 연결된 Red LED가 꺼지도록 했네요. 조이스틱 스위치 버턴은 내부풀업저항을 이용하기 때문에 초기값은 '1'이 됩니다. 그래서 전원이 공급되면 Red LED에 불이 들어오게 됩니다. 그리고 스위치를 누르면 '0'의 상태가 되어서 Red LED는 불이 꺼지게 됩니다.

4. 결과




5. 추가 내용


위 실험 코딩은 그냥 조정기의 0~1023값을 단적으로 0~180도로 나눠서 조정한 거라서 실제로 뭔가 조정하거나 정밀 제어 할 때는 사용하지 않는 코딩입니다. 단순히 조정기를 움직일 때 그 움직임 값으로 Servo Motor를 움직이게 한 것 뿐이니깐요.

원래는 조정기에 대한 코딩을 제대로 하실려면은 조정기를 움직일 때에 채터링 문제 또는 조정값의 범위라던가 조정기의 수치에 따른 회전 속도라든가 고려해야할 것이 산더미로 많습니다. 거기에 조종기의 중심값의 보정 작업도 추가 해야 합니다.

이런것들을 다 코딩하면 회전 하나의 동작을 하기 위해서 수십 줄의 코딩으로 늘어나게 됩니다. 배보다 배꼽이 더 커지겠죠. 여기서 배워야할 것은 Servo Motor를 조이스틱으로 회전할 수 있는 기초 원리를 배우는게 목적이기 때문에 간단하게 원리만 전달하는 코딩만 한 것이니깐 여러분들이 실제로 뭔가를 제어할려면 많은 부분을 생각하고 코딩 하셔야 합니다.

그리고, 여러분들이 이 기초를 이해 하셨다면 조정기에 필요한 부분이 뭐가 있을지 생각하고 여러가지 상황들을 제시하고 그 문제를 해결하면서 코딩을 늘려가셨으면 합니다.

마무리


조이스틱으로 Servo Motor를 제어할 수 있게 되었네요. 한개의 Servo Motor를 제어 했지만 여기서 Y방향 값도 같이 적용을 하여 Motor 두개를 사용한다면 어떻게 될까요. x와 y방향으로 평면의 좌표로 이동할 수 있게 됩니다. 3D 프린터나 평면 그림을 그리는 프린트기 같은 걸 만들 수 있겠죠. 여기에 사용되는 Motor는 Stepper Motor로 360도를 회전하는 것을 사용합니다. 어찌 되었든 조정기로 뭔가를 움직이는 물체를 제작 가능해 지겠죠.

마지막으로, 뭔가 로봇 같은 관절 제어 쪽으로 상상력을 발휘해 보세요. 재밌는 상상력들을 많이 해 보셨으면 합니다.

예를들어, 로봇 손가락을 상상해 보세요. 자신의 손을 쫙 펴보세요. 손가락 하나에는 3마디로 구성 되어 있잖아요. 이 각 마디와 마디 사이에는 관절이 있습니다. 이 3개의 관절을 Servo Motor가 대신 한다고 상상을 해보세요. 손을 다시 주먹을 쥐어보세요. 한마디가 구부러질 때 몇도의 각도가 되나요. 각 관절이 90도에 가깝게 꺽기게 됩니다. 그러면 Servo모터로 3개의 관절을 동일하게 angle을 0도에서 서서히 90도에 가깝게 일정한 속도로 회전 시키면 어떻게 될까요. 주먹을 쥐는 형태로 자연스럽게 구브러 지겠죠. 다시 펼 때는 90도에서 서서히 0도로 3개의 Servo모터를 회전 시키면 어떻게 될까요 서서히 손가락을 펴는 모습이 되겠죠.
직접 자신의 손을 주먹을 쥐었다 폈다를 반복하면서 머리속에서 손가락 관절의 각도를 그려 보세요. 그 원리를 깨우치게 되면 로봇 손가락 관절 제어를 할 수 있게 됩니다.

일상의 모습을 보고 우리는 그걸 코딩화 할 수 있습니다. 일상의 모습을 코딩화 하기 위해서는 끊임없이 상상력을 끌어 올려야 하고요. 계속 자신에게 어떤 것을 배우고 다룰 수 있게 되면 다음에 뭘 배우지 보다는 이것을 이용하여 뭘 할 수 있지 하고 2개 이상의 응용할 수 있는 상상력 훈련을 하셨으면 합니다.


댓글()

[아두이노] 릴레이 모듈+온도센서+DC모터 응용

IOT/아두이노|2019. 3. 28. 09:00

[아두이노] 릴레이 모듈+온도센서+DC모터 응용 



다른 주제로 포스팅을 끝냈는데 @ellenalee 님이 릴레이 모듈로 선풍기를 제어 해보셨다고 하셔서 이걸 한번 비슷하게 구현해보면 릴레이 모듈의 응용으로 괜찮을 것 같아서 가상시뮬레이터에서 느낌만 비슷하게 한번 구현해 보도록 하겠습니다.

1. 회로도 구성


  • 준비물 : relay DPDT 1개, DC Motor 1개, Temperature Sensor[TmP36] 1개, Power Supply, 아두이노우노
  • 내용 : 온도센서의 값에 의해서 모터가 회전할 수 있게 회로도를 구성해 보자.


대충 DC Motor가 선풍기라고 생각해 봅시다. 느낌은 그런데로 비슷해가 표현 했네요.

3. 코딩


  • 사용함수 : analogRead(), map()
  • 내용 : LED 깜박이는 소스를 이용해서 그 값을 제어값으로 해서 Light bulb 깜박이게 해보자.
  • 참고소스 : [아두이노] 온도센서(TMP36) 제어

복습

  • analogRead(아날로그핀) : 아날로그신호값을 읽음
  • map(입력값,입력최소값,입력최대값,출력최소값,출력최대값) : 입력값이 입력범위에 기준에 맞게 출력범위의 해당값을 출력.

변형함수(만든함수)

  • fmap(입력값,입력최소값,입력최대값,출력최소값,출력최대값) : map()함수와 동일하지만 자료형 타입을 float형으로만 변경했습니다. 계산값이 실수형 값이라서 map()함수를 사용하기가 애매해서 인자들을 전부 float형으로 변경했네요.

[ 온도센서 측정 소스 ]

[아두이노] 릴레이 모듈+온도센서+DC모터 응용
void loop(){
  float V =fmap(analogRead(A0),0,1023,0,5); //map함수 원리를 이용한 다이렉트 Voltage계산
   //공식
  //float V = analogRead(A0)*5.0/1023;
 
  float C = (V-0.5)*100;  //섭씨 C = (F-32)*1.8;
  float F = C*9.0/5.0+32; //화씨 F = C*1.8+32;
 
}

float fmap(long x, long in_min, long in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) /(float) (in_max - in_min) + out_min;
}

여기서 대충 온도 C변수 값에 따라서 릴레이모듈를 제어하면 되겠죠

[아두이노] 릴레이 모듈+온도센서+DC모터 응용
   if(C>35){    //35도 이상이면 참
     digitalWrite(2, HIGH); //스위치 켜라
  }
  else{
     digitalWrite(2, LOW);  //스위치 꺼라
  }  

끝! 간단하게 느낌만 살렸습니다.

전체적인 코드를 살펴보면,

[아두이노] 릴레이 모듈+온도센서+DC모터 응용
void setup()
{
  Serial.begin(9600);
  pinMode(7, INPUT_PULLUP);
  pinMode(2, OUTPUT);
}
void loop()
{
  
  float V =fmap(analogRead(A0),0,1023,0,5); //map함수 원리를 이용한 다이렉트 Voltage계산
  float C = (V-0.5)*100;  //섭씨 C = (F-32)*1.8;
  
  if(C>35){    
     digitalWrite(2, HIGH); 
  }
  else{
     digitalWrite(2, LOW);   
  }   
}

float fmap(long x, long in_min, long in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) /(float) (in_max - in_min) + out_min;
}

4. 결과


[ 35도 이하일때 ] : 그림에서는 25도 위치



[ 35도 이상일때 ] : 그림에서는 57도 위치


동영상을 찍어야 했지만 그냥 이미지로 간단히 차이점을 화살표로 표시 해 놨습니다. 25도일때는 0.00A로 전류가 흐르지 않는 상태이고 57도 위치에서는 79.9mA로 전류가 흐르는 상태입니다. 그 차이점으로 확인해주시기 바랍니다.

코딩에서는 35도 이상이면 모터가 회전되게 되어 있습니다. 실제 시뮬레이터로 돌아가는 장면을 보고 싶으면 공개되 회로도에서 바로 시뮬레이터를 실행 시켜서 그 결과를 확인 할 수 있습니다.


링크된 곳으로 가면 위 그림과 같은 창이 뜹니다. 거기서 1번 눌러주셔서 2번의 시뮬레이터 실행버턴의 창이 뜹니다. 그리고 2번을 누르시면 실행을 시킬 수 있습니다. 한번 온도조절을 해보시고 결과를 확인해 보세요.

마무리


상상이 어렵지만 표현은 그렇게 어렵지 않습니다. 매번 상상의 나래를 펼치라는 멘트를 날리는 이유가 바로 그 이유입니다. 온도에 따라서 선풍기를 자동을 켜볼까라는 상상력이 뭔가를 만들어 냅니다.

여러분들도 어떤 원리를 배우게 되면 그 원리에서만 멈추지 말고 상상력을 더해서 새로운 뭔가를 창조할 수 있는 능력을 키우시면 엄청난 결과를 얻을 수 있을 거에요.

언제나 그럴듯이 상상의 나래를 펼쳐 보세요.

댓글()

[아두이노] RC카 장애물 감지시 방향 전환 기초

IOT/아두이노|2019. 2. 22. 11:52

[아두이노] RC카 장애물 감지시 방향 전환 기초



초음파레이더의 원리는 지난 시간에 기초 실험으로 살펴 보았습니다. 하다보니깐 RC카 자율주행할 때 초음파 센서로 장애물 감지을 할 경우가 문득 떠오르더군요. 그래서 초음파레이더 만들기 소스를 그대로 이용하여 몇부분만 추가하여 코딩을 완성했습니다. 그냥 갑자기 떠오른 생각이라서 코딩은 깔끔하지 않네요. 막 떠오르는데로 코딩한거니 그 점을 감안해서 코딩을 보시기 바랍니다.

1. 회로도 구성


  • 준비물 : 서보모터 2개, 초음파센서 1개, 아두이노우노
  • 내용 :45~ 125도 서보모터를 회전시키면서 초음파센서로 거리를 측정부분은 그대로 유지한 상태에서 RC카 앞바퀴 방향 제어용 서보모터를 추가하자.


머리속에서 각 부품을 실제 RC카에 배치를 상상해 보세요. 첫번째 초음파센서가 서보모터에 부착되어 있고 두번째 서보모터는 RC카 앞바퀴에 연결되었다고 상상 해보세요.

3. 코딩


  • 사용함수 : attach(사용핀), write(각도), pulseIn(입력핀, HIGH), Serial.begin(9600), Serial.print(출력값),Serial.println(출력값)
  • 내용 : 간단히 서보모터가 회전은 45~125도 범위를 잡고 전방의 장애물 탐지를 초음파센서가 담당하게 코딩하고 앞바퀴담당 서보모터는 거리 50이하 일때 전방 90도 기준으로 좌측에 장애물 발견시 우측으로 우측 장애물 발견시 좌측으로 회전시키는 코딩을 한다.

설계

앞바퀴 방향 제어를 하기 위해서 서보모터를 제어해야겠죠.

그래서 서보모터 객체를 하나 더 선언합니다.

Servo servo2;

그리고 핀을 10번을 사용하기 위해서 연결합니다.

int servoPin2 = 10;
servo2.attach(servoPin2); //angle (0~180)

앞바퀴 장애물 방향전환 조건문을 만듭니다.(하나의 패턴으로 자율주행 첫단추로 접근하는 시작 코딩)

  if(m_distance<=50 && m_angle>90) {    
    servo2.write(45);
    delay(50);   
  }
  else if (m_distance<=50 && m_angle<90){
    servo2.write(125);
    delay(50);   
  }
  else {
    servo2.write(90);
    delay(50);   
  }
  if(m_distance<=50 && m_angle>90) {    

if조건문

  • 첫번째 조건식 : "장애물(벽) 거리가 50cm 이하이고 각도가 90도 보다 큰가?" (왼쪽장애물확인)
    그러면 왼쪽 장애물이 너무 가까우니깐 오른쪽으로 핸들을 꺽어야 겠죠.
  • 두번째 조건식 : "그게 아니면 장애물(벽) 거리가 50cm 이하이고 각도가 90도보다 작은가?"(오른쪽장애물확인)
    그러면 오른쪽 장애물이 너무 가까우니깐 왼쪽으로 핸들을 꺽어야 겠죠.
  • 거짓이면 : 그냥 앞바퀴 방향각 90도로 정중앙 정면을 향하게 한다.

즉, 조건식으로 50cm이하일때 현재 방향에 따른 핸들의 방향을 결정하게 됩니다. 50cm이하에 장애물이 없으면 그냥 정면으로 계속 진행하도록 로직을 짜봤습니다.
여기서 핸들 각도를 변수로 빼면 중복되는 코딩이 3번 반복되는데 이걸 하나로 줄일 수 있겠지만 우선은 동작을 좀 더 직관적으로 이해할 수 있도록 하기 위해서 수정은 안했습니다.

초음파레이더 소스와 위에 방향전환 로직을 합쳐서 전체코딩을 을 살펴보면

#include <Servo.h>

Servo servo1;
Servo servo2;
int servoPin1 = 9;
int servoPin2 = 10;
int m_distance=0;
int m_angle=45;
int m_chk=0;

void setup() {
  Serial.begin(9600);
  servo1.attach(servoPin1); //angle (0~180)
  servo2.attach(servoPin2); //angle (0~180)
}

void loop() {
  
  //초음파센서 몸체 회전
  servo1.write(m_angle);
  delay(50);
  
  m_distance=CalDistance(7);  //초음파센서로 거리계산함수   
  Serial.print("d : ");
  Serial.println(m_distance);
  
  //앞바퀴 장애물 방향전환
  if(m_distance<=50 && m_angle>90) {    
    servo2.write(45);
    delay(50);   
  }
  else if (m_distance<=50 && m_angle<90){
    servo2.write(125);
    delay(50);   
  }
  else {
    servo2.write(90);
    delay(50);   
  }
  
  //초음파센서 회전각
  if(m_chk==0){
    if(m_angle<125)m_angle+=1;
    else m_chk=1;
  }
  else{
    if(m_angle>45)m_angle-=1;
    else m_chk=0;
  }
  
}

int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW); 
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);
  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.

  return distance; //거리값 리턴
}

5. 결과


설계과정은 이번에 생략하고 결과만 시뮬레이터로 돌려 보는 장면만 보여 드립니다. 지난시간에 회로도 만드는 과정을 해봤으니 구지 중복해서 다시 똑같은 것을 찍을 필요는 없고 서보모터만 하나 추가했기 때문에 결과만으로 충분해서 회로도 배치과정은 생략했네요.

마무리


오늘은 아두이노 RC카에 주행에 관한 첫 단추인 방향전환에서 하나의 패턴을 기초 패턴으로 로직을 만들었습니다. 이걸로 주행이 가능하지 않습니다. 단지 이렇게 코딩해 놓고 조금씩 패턴을 늘리고 로직을 늘려가야 합니다.

여기서는 단적으로 왼쪽 장애물 감지시 레드존에 들어가고 바로 오른쪽으로 45도 회전을 시켜서 그린존으로 탈출하는 코딩입니다. 어떤 문제가 있을까요 바로 현재 RC카의 주행 속력에 대한 장애물과의 거리별로 방향전환 각도를 가변적으로 해야합니다. 그래야 매끄렇게 회전이 됩니다. 그리고 또 문제는 가령 전방 직선도로로 달릴때 통과 할 수 공간이 충분하지만 가력 왼쪽이나 오른쪽 방향에 장애물이나 벽과의 거리가 50 이하면 그냥 방향핸들을 반대 방향으로 꺽어버리는 문제가 방생합니다. 즉, 전방에 안전거리 확보가 확인되면 그상태로 주행하고 안전거리가 미확인시 좌우측에 위험거리가 감지되면 방향을 전환하더록 로직을 수정해야겠죠.

이렇게 주행에서의 문제들을 하나씩 찾아내고 그걸 해결하는 패턴들을 하나씩 만들어 내야합니다. 그래야 자율주행이 가능해지는 것이죠. 다른 방식으로 장애물 감지되면 그상태에서 주행이 정지되고 안전거리 확보 패턴로직 주행을 하고 다시 안전거리가 확보되면 주행을 다시 시작하는 방식으로도 변경할 수 있겠죠.

코딩을 쉽게 하기 위해서는 실제 장난감 자동차를 방에서 손으로 밀면서 주행을 시켜보세요 그리고 관찰하면 이런 상황일때 이걸 어떻게 어떻게 빠져나올지 손으로 자동차를 움직이시고 그 움직을 잘 기억했다가 그 움직이는 동작 패턴을 코딩화 하시면 됩니다.

암튼 집에 굴러다니는 장단감을 가지고 한번 손으로 밀면서 띠따! 빵빵! 하면서 가지고 노세요. 그리고 나서 그 움직임을 기역했다가 코딩을 상상해보세요.


댓글()

[아두이노] 초음파레이더 만들기(기본동작제어)

IOT/아두이노|2019. 2. 21. 10:16

[아두이노] 초음파레이더 만들기(기본동작제어)



이번시간에 초음파레이더를 기본동작 원리를 실험하는 시간을 갖도록 하겠습니다. 가장 많이 검색되는 주제이기도 하죠. 현재 초음파센서가 없는 관계로 실제 구동 장면은 추가하고 싶었는데 아쉽게 못 보여 드리네요. 그래도 가상시물레이터에서 충분히 표현이 되니깐 실제 만드는 것은 그리 어렵지 않을꺼에요. 물론 거리계산에 따른 오차율 보정 작업이 필요하지만요. 오늘은 서보모터를 회전시키면서 초음파센서로 거리를 측정하고 그 값을 아두이노 IDE 시리얼모니터로 거리 값이 동시에 출력되게 표현 함으로 초음파레이더의 기본 원리를 배워보도록 하죠.

1. 초음파센서 복습


제 블로그에서 정리할 때 함수로 표현했었습니다. 초음파센서로 거리를 측정하는 로직을 따로 외부함수로 만든 것을 그대로 인용해서 적용하겠습니다.int distance = duration / 57.5; 이것은 가상시뮬레이터에 대충 거리를 보정한 값이고 실제로는 공식을 해보고 거리값의 오차가 있으면 약간씩 값을 바꾸면서 거리값을 보정하시면 됩니다.

  • 공식 : ((float)(340 * duration) / 10000) / 2; (340은 초음기종에 따라 달라질 수 있는점 참고)
int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW); 
  
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);
  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.

  return distance; //거리값 리턴
}

이렇게 함수로 만들어 놓으면 나중에 초음파센서를 사용할 때 이 함수를 복사하시면 되겠죠. 이건 3핀 초음파센서여서 사실 4핀 초음파센서에서는 TRIG, ECHO핀이 따로 존재하기 때문에 setup()함수에서 각 핀을 선언해주시면 되기 때문에 CallDistance()함수에서 핀의 출력/입력모드를 선언할 필요가 없습니다. 가상시뮬레이터에서는 3핀이여서 어쩔 수 없이 추가된 것이기 때문에 실제로 1200원 짜리 4핀 초음파센서를 사용하시면 핀모드 선언을 지워주셔서 함수를 사용하시면 됩니다.
쏘는것은 TRIG, 받는 것은 ECHO 핀인 것만 기억하시면 쉽게 변경되시겠지요. 즉, 3핀 초음파센서에서 출력모드일 때 TRIG이고 입력모드일 때 ECHO라고 생각하시면 됩니다.

2. 서보모터 복습


#include <Servo.h>
 
 Servo servo;
 int servoPin = 9;
 
 servo.attach(servoPin); //angle (0~180)
 servo.write(m_angle);
 delay(50);
 
 m_angle+=1; or m_angle= m_angle+1;

9번핀을 servo모터의 출력핀으로 연결하고 servo.write()로 각도(angle)만큼 이동하는데 마지막 라인의 m_angle은 +1씩 증가합니다. 즉 0도에서 1도씩 계속 servo모터를 회전하게 되는 것이죠. 딜레이시간은 1도여서 아주 짧게 0.05초를 줬습니다.

3. 회로도 구성


  • 준비물 : 서보모터 1개, 초음파센서 1개, 아두이노우노
  • 내용 : 0~180도 서보모터를 회전시키면서 초음파센서로 거리를 측정해보자.


가장 간단한 회로도 입니다. 대충 회로도를 보시고 해당 위치의 핀에 선을 연결하시면 됩니다.

4. 코딩


  • 사용함수 : attach(사용핀), write(각도), pulseIn(입력핀, HIGH), Serial.begin(9600), Serial.print(출력값),Serial.println(출력값)
  • 내용 : 간단히 0~180의 범위를 1도씩 회전하면서 초음파로 거리를 측정하여 아두이노시리얼모니터에 거리값을 출력한다.

설계는 위의 1, 2에서 복습 차원의 코딩을 그대로 사용했습니다. 중요한 것은 0에서 180도 까지 1도씩 회전시키고 다시 180에서 0도까지 1도씩 회전하는 것이죠.

설계

(1) 제어 변수를 만든다.

Servo servo; 
int servoPin = 9;

int m_distance=0;
int m_angle=0;
int m_chk=0;
  • servo : 서보클래스 객체 선언
  • servoPin : 서보모터 제어하는 핀번호
  • m_distance : 거리 계산값(초음파센서로 거리측정동작을 수행하는 CalDistance()함수로 구한 값이 저장)
  • m_angle : 서보모터의 회전각(write()함수로 서보모터를 실제 회전)
  • m_chk : 회전 방향 상태값(초기값 0은 0에서 180도로 회전을 뜻하고 1은 180도에서 0도로 회전을 뜻한다.)

(2) m_chk 변수가 회전 방향 상태를 나타내고 제어하는 방법

if(m_chk==0){
    if(m_angle<180)m_angle+=1;
    else m_chk=1;
  }
  else{
    if(m_angle>0)m_angle-=1;
    else m_chk=0;
  }

if 조건문으로 m_chk변수의 초기값이 0이다 m_angle(각)도 0도이다. 조건식이 m_chk==0 으로 m_chk가 0과 같은가 아닌가로 물어본다. 두가지 패턴으로 분리되는 순간이다. 0가 같다면 참인문장이 1이면 거짓문장이 수행 되겠죠. m_chk변수가 초기값으로 0임으로 참이 된다. 그 안에 다시 if문으로 m_angle(각)이 180도보다 작은가가 조건식이 걸려있다. 180도보다 작으면 m_angle+=1;로 m_angle=m_angle=+1과 같은 문장이다. 즉, m_angle(각)은 1도ㅀ 증가하게 된다. 그런데 만약에 m_angle(각)가 180도 이상이 되면 else 이하 문장이 수행됩니다. m_chk=1로 회전 방향 상태값이 바뀌게 됩니다. 그러면 다음 루프때 if 조건문 m_chk가 0이 아니기 때문에 else 이하 문장이 수행되고 반대로 m_angle 변수가 0보다 크냐고 붇고 크면은 m_angle-=1로 -1도로 감소하게 된다. 위 코딩하고는 반대 동작을 수행하는 것이죠.

다시 종합해서 설명하자면 m_chk변수 기준으로 0이면 m_angle 값이 180도가 될때까지 1씩 증가하고 180도가 되면은 m_chk변수를 1로 변경하고 m_chk변수가 1이면 m_angle 값이 0도가 될때까지 -1씩 감소하게 된다. 그리고 0도가가 되면 m_chk변수를 0으로 변경한다. 이렇게 무한 반복하게 되는 로직입니다.

(3) 결과는 아두이노 IDE 시리얼모니터로 출력

Serial.begin(9600);

Serial.print("d : ");
Serial.println(m_distance);

begin(9600)은 setup()함수에 선언하고 나머지 모니터 출력은 print(출력값)와 printIn(출력값)함수가 있는데 print()은 출력값을 출력하고 새로운 라인으로 넘어가지 않고 현재 라인에 머문다. printIn()은 출력값을 출력한뒤에 새로운 라인으로 넘어가라는 의미 입니다. 문서 장성할 때 키보드 Enter 명령으로 생각하시면 됩니다. C언어에서는 '\n'로 생각하시면 됩니다.

Serial.print("d : ");
Serial.println(110);

결과 => d : 110

대충 의미를 이해하셨겠죠.

코딩을 하면

#include <Servo.h>

Servo servo; 
int servoPin = 9;
int m_distance=0;
int m_angle=0;
int m_chk=0;

void setup() {
  Serial.begin(9600);
  servo.attach(servoPin); //angle (0~180)
}

void loop() {
  servo.write(m_angle);
  delay(50);
  
  m_distance=CalDistance(7);  //초음파센서로 거리계산함수   
  Serial.print("d : ");
  Serial.println(m_distance);
    
  if(m_chk==0){
    if(m_angle<180)m_angle+=1;
    else m_chk=1;
  }
  else{
    if(m_angle>0)m_angle-=1;
    else m_chk=0;
  }
}

int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW); 
  
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);
  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.

  return distance; //거리값 리턴
}

5. 결과


가상시뮬레터에서 실험 결과는 깔끔하게 나왔는데 실제 표현에서는 보정 작업이 꼭 필요 합니다.

그리고, 원래 실제로 초음파레이더를 만든다면 그래픽작업으로 레이더를 만들어 레이더에 검색된 물체를 현재회전각과 거리값으로 (x,y)의 좌표를 구할 수 있고 그걸 그래픽작업한 모니터에 원하는 형태로 그래픽모양을 출력하면 좀 더 그럴싸해 지겠죠.

여기에서는 단지 아두이노 IDE 시리얼모니터에 거리값만 출력했습니다. 그게 기본 초음파레이더의 정보입니다. 서보모터의 각도와 거리만 있으면 측정된 물체의 (x,y) 좌표를 구할 수 있고 그것을 프로세싱이나 스크래치로 이미지화 하면 보기가 더 좋겠지만 핵심은 그게 아니로 회전과 거리 측정을 동시에 수행한다는 것에 있습니다. 이미지 시각화를 단지 보여주는 부수적인 효과일뿐이죠. 동영상을 보시고 서보모터가 회전하면서 시리얼모니터에 초음파 센서의 거리가 측정되어 출력되는 걸 잘 보시고 상상을 해보세요. 서보모터에 초음파센서가 부착되어 회전되는데 그 회전하는 각도에 정면의 물체와의 거리를 측정하는 모습을요. 그러면 아마 대충은 이해가 되실 꺼에요.

마무리


간단히 초음파레이더의 기본 로직만 설명했습니다. 그리고, 오늘 포스팅한 코딩은 사실 정교한 제어라고 할 수 없습니다. 왜냐면 서보모터 종류에 따라 좀 다르게 회전이 되는 경우가 있습니다. 참고로 코딩에서 servo.attach(servoPin, Min, Max)로 범위를 설정하고 servo2.writeMicroseconds(각도시간값)으로 제어하시면 더 정교하게 제어가 가능할꺼에요. 실제로 테스트 할때는 위 방식대로 해보다가 안되면 이 방식으로 바꿔서 서보모터의 각도를 제어하시면 됩니다. 4핀이였으면은 현실 4핀 서보모터랑 동일해서 더 편했을텐데 3핀 초음파센서여서 약간 코딩이 아쉽습니다.

추가로 스크래치나 프로세싱 프로그램을 다룰 수 있는 분이라면 그걸 통해서 초음파레이더에 그래픽처리를 통해 실제 레이더처럼 표현이 가능할 꺼에요. 프로세싱을 제대로 공부한적이 없고 잠깐 맛보기로 함수만 몇개 써본게 전부라 프로세싱 프로그램을 사용하여 레이더 그래픽효과를 다음편에 추가해서 보여주고 싶긴 하는데 할까 말까 고민되네요. 잠깐 프로세싱에서 레이더 표현 그래픽 함수 몇개만 공부해서 로직을 짜면 되기는 한데 프로세싱을 새로 설치하고 공부하기가 좀 귀찮아서 생략할지 아니면 표현을 할지 고민을 해 봐야 겠네요.

오늘의 포스팅의 내용은 서모모터회전을 하면서 초음파센서로 거리를 측정한다 이것만 머리속에 넣으시면 됩니다.


댓글()

[아두이노] Servo.h 없이 직접 서보모터를 회전

IOT/아두이노|2019. 2. 20. 13:38

[아두이노] Servo.h 없이 직접 서보모터를 회전



이번에 서보모터의 단순히 전원 공급으로만로 회전을 시켜보는 걸 해보겠습니다. 이것도 오래전 Servo.h 없이 제어를 할 수 있는 방법이 없을까 하고 고민을 좀 했었습니다.
https://github.com/esp8266/Arduino/blob/master/libraries/Servo/src/Servo.h 에서 Servo 클래스에 대해 분석도 하고 했지만 너무 복잡하더군요. 그냥 단순 제어를 해볼려고 조사하다가 전기 신호 파형에 따라 각도를 제어할 수 있다고 해서 digitalWrite()함수 하나로 제어하는 실험을 하게 되었습니다. 혹시 서보모터의 원리를 보다 자세히 알고 싶다면 구글링 서보모터원리라는 단어로 하시면 쉽게 찾을 수 있을거에요. 이번 실험은 전류를 서보모터에 보내고 펄스 간격을 시간값으로 해서 쪼개서 전류공급과 차단을 하면 서보모터의 회전을 시키는 실험으로 진행됩니다. 그리고 이전에 포스팅한 Ardunio min = 544, max=2400 값을 기준으로 이번에 가상시뮬테이의 회전 실험을 해보도록 하죠. 제 블로그의 있던 소스를 그대로 가져와서 실험 포스팅을 하도록 하겠습니다. 고치자니 귀찮니즘이 밀려와서 구지 고칠 필요가 없다고 생각되어 그냥 소개합니다.
그리고 가상시뮬레이터에서 실험하던 당시 서보모터가 다른 기종이여서 min=1000, max=2000을 기준으로 회전시켰지만 이번 실험은 544~2400 값으로 해서 회전이 되는 서보모터를 사용하여 기준은 544~2400으로 실험했습니다.

1. 서보모터의 회전 이해


구글링 검색하시면 서보모터 원리에 대해서 자세히 소개되어 있는데 그냥 아래의 그림처럼 단순하게 생각하시고 넘어가시면 됩니다. 전기 신호는 일정한 간격으로 해서 아래와 같은 파형을 만들어 냅니다. 전류가 공급되고 차단하는 그 시간차 값으로 서보모터의 각도를 만들어 낸다고 가단히 생각하세요. 전류 공급(5V)가 짧은 간격으로 공급된다면 회전하는 각이 그 시간만큼만 회전한다고 생각하면 됩니다.


파형을 아두이노에서 만든다면( Ardunio min = 544, max=2400 값을 기준)

   digitalWrite(servoPin, HIGH);  
   delayMicroseconds(1500);     
   digitalWrite(servoPin, LOW);   
   delayMicroseconds(펄스간격남은값);   

즉, 서보모터 핀에 HIGH(5V)의 전류를 1500마이크로초 동안 전류를 공급하고 LOW(0)로 전류를 차단을 펄스간격남은값(max-회전시간값)만큼이란 표현입니다. 현실 구동은 스피드하게 회전하지 않겠지만 가상시뮬레이터에서 저리 코딩하면 순간 90도정도의 각도로 회전해버립니다. 가상시뮬레이터에서 서보모터라이브러리에서 제공되는 함수와 비슷하게 회전을 보여줄려면은 대충 delay()함수로 시간을 좀 더 크게 대기시켜서 보면은 최대한 비슷하게 회전합니다.

대충,

void setup() {
  pinMode(9, OUTPUT);
}
void loop() {
   digitalWrite(9, HIGH);  
   delayMicroseconds(1500);     
   digitalWrite(9, LOW);   
   delayMicroseconds(2400-1500);   
   delay(100);
}

이정도만 코딩해서 아래 회로도 디자인한 곳에다 코딩을 넣어보세요. 1500시간값만큼의 각도까지 움직이다가 그 각도에서 덜덜덜 떨고 있을꺼에요. 정상적으로 동작되는걸 보실꺼에요

여기서 펄스간격남은값 2400-1500으로 해서 펄스 간격을 일정하게 만들었습니다. 그냥 아두이노의 최대값 2400을 펄스간격 기준값으로 설정했네요. 펄스간격 기준값 2400을 기준으로 얼마만큼의 시간이 5V와 0V가 되었는지의 시간입니다. 그래서 1500만큼의 전류가 공급되었으니 나머지 시간은 LOW에 할당해야겠죠.

쉽게말해서, 회전 파형을 만든다고 생각하시면 됩니다. 구지 자세히 생각하지 마시고요. 이런게 있다는 정도만 이해하시고 오늘 포스팅을 깊게 이해하실 필요 없습니다. 그냥 Servo 라이브러리 파일이 오픈소스로 제공되는데 구지 힘들게 만들 필요는 없겠죠. 이렇게 만들었다고 해서 정교한 컨트롤은 되지 않으며 단지 이런식으로 회전이 이뤄지는 거구나 정도용이니 깊게 생각하지 마세요.

2. 회로도 구성


  • 준비물 : 서보모터 1개, 아두이노우노
  • 내용 : 서보모터 회전 시키자


서보모터는 Vcc(5V), Gnd, Signal(입력신호)로 3핀이로 구성되어 있고 아두이노에서 Signal핀에 신호를 보냄으로써 각도를 제어는 지난시간과 동일합니다.

3. 코딩


  • 사용함수 : pinMode(사용핀, 모드), digitalWrite(사용핀, 상태), delayMicroseconds(시간값), delay(시간값)
  • 내용 : 간단히 180도 회전만 시키자.

설계

우선 delayMicroseconds() 함수를 사용한 목적은 서보모터를 회전시킬 시간값이고 지연시간도 이 함수로 사용했습니다. 그리고 0~180도까지 회전이 끝나면 delay()로 1초동안 잠시 대기는 목적으로 사용합니다.

그리고 digitalWrite()함수를 사용하기 위해서는 pinMode()함수로 핀모드를 설정해야 합니다. 그리고 digitalWrite()함수로 서보모터에 신호값으로 전류 공급과 차단으로 전기신호파형을 만들어 특정 각으로 회전시킵니다.

코딩을 하면

int servoPin = 9;
int m_max=2400;

void setup() {
  pinMode(servoPin, OUTPUT);
}

void loop() {
  for(int i=544;i<2400;i++){
   digitalWrite(servoPin, HIGH);  
   delayMicroseconds(i);     
   digitalWrite(servoPin, LOW);   
   delayMicroseconds(m_max-i);    
  }
  delay(1000);

  for(int i=2400;i>544;i--){
   digitalWrite(servoPin, HIGH);  
   delayMicroseconds(i);     
   digitalWrite(servoPin, LOW);   
   delayMicroseconds(m_max-i);    
  }
  delay(1000);
}
}

for문으로 사용해서 min(544)값에서 max(2400)값이 될때까지 delayMicroseconds(i)로 전류의 공급 간격입니다. 이게 바로 각도이고요. delayMicroseconds()함수을 이용하여 전기 파형을 만들어 냅니다. 전류 공급시간이 작을 경우 펄스간격 시간단위로 짧은 파형이 작게 형성됩니다. 그럴때 서보모터의 움직이는 회전이 짧아서 그 상태를 유지하는거고요 i값이 클경우 그 파형이 크게 형성됩니다. 서보모터의 움직이는 회전각 커집니다. 그리고 그 회전에 도달하면 더이상 회전을 하지 않으며 그 회전각을 계속 유지하게 됩니다. 544에서 2400될때까지 전원 공급 간격이 점차적으로 커져가니깐 결과적으로 0~180도로 회전을 시키게 되는 것이죠. 두번째 for문은 2400에서 544가 될때까지 i의 전원 공급 간격이 적어지니깐 180~0도로 회전을 시키게 됩니다.

이해가 안된다고 생각하시면 쉽게 delayMicroseconds(i) 각도로 생각하시고 전원 공급과차단의 간격을 펄스간격 기준값(m_max)값이라고 생각하시면 됩니다. 펄스간격동안 delayMicroseconds()함수로 전류의 파형을 만들고 그 파형만큼의 각도로 회전을 시킨다고만 이해하시면 됩니다.

4. 결과


따로 제작과정없이 그냥 코드만 삽입해서 그 결과를 보여드립니다. 한번 서보모터 회전를 이해하기 위해서 동영상에서 과연 1500마이크로초 동안 공급될때 어느정도의 회전이 되고 그 상태를 유지할때 서보모터의 움직임을 유심히 살펴보시면서 회전을 어떤 느낌으로 회전되는지 이해하시면 되겠습니다. 그리고 본론으로 들어간 실험 코딩은 0~180도까지 회전 시키고 1초 대기했다가 180~0도까지 회전 시키는 과정을 반복합니다. 그리고 여기서는 아두이노우노의 2400의 값을 펄스파형을 이 일정하게 증가하다가 일정하게 감소하는 과정에서 전류를 시간값으로 해서 파형을 만들어내고 그 파형에 따라서 각도가 발생하는 점을 생각하시고 동영상의 서보모터의 회전을 보시면 되겠습니다.

마무리


쉬울 것 같아서 소개했는데 하고나니깐 이거 전기 신호의 파형을 봐야하고 서보모터의 동작원리를 자세히 알아야하는데 그렇게 자세히 설명을 하자니 삼천포로 빠질 가능성이 있어서 기본만 설명하다보니 좀 애매하게 됐네요.
오늘 포스팅 내용은 그냥 이런게 있구나 정도로만 하고 그냥 넘어가시고 구지 깊게 생각할 필요 없고요 편하게 Servo.h 의 Servo 클래스를 이용하시면 됩니다.
그냥 예전에 호기심에서 시작해서 그냥 제어만 해본거라 오늘 포스팅은 가볍게 읽고 이해가 안되더라도 무시하고 넘어가시면 됩니다.


댓글()

[아두이노] 서보모터 제어

IOT/아두이노|2019. 2. 19. 09:18

[아두이노] 서보모터 제어



이전 시간까지는 칩과 관련해서 제어를 하다보니 복잡한 주제를 포스팅 했었습니다. 이번에는 좀 쉬운 주제를 다루고자 합니다. 기본 서보모터 동작 제어만 다루기 때문에 별거 아니라고 생각 할 수 있지만 서보모터 제어는 나중에 아두이노로 작품을 만들때 가장 많이 사용하는 부품입니다. 그리고 사용 목적에 따라서 코딩 능력을 필요로 하는 부분이기도 하기 때문에 사용목적에 따라 좀 어려울 질 수 있는 부품이기도 합니다. 하지만 오늘 포스팅은 단순한 제어를 통해 서보모터를 체엄하도록 하죠.

1. 서보모터



서보모터는 Vcc(5V), Gnd, Signal(입력신호)로 3핀으로 구성되어 있습니다. 아두이노에서 Signal핀에 신호를 보냄으로써 각도를 제어하게 됩니다.

2. 회로도 구성


  • 준비물 : 서보모터 1개, 아두이노우노
  • 내용 : 서보모터 회전 시키자


가장 간단한 회로도 입니다. Vcc은 5V에 Gnd은 Gnd에 Signal은 9핀에 연결하면 완료입니다. 원래 모터제어를 할때는 모터쉴드보드가 필요합니다. 하지만 서보모터의 경우는 아두이노에서 직접 제어해도 됩니다.

3. 코딩


  • 사용함수 : attach(사용핀), write(각도), writeMicroseconds(각도시간값)
  • 내용 : 간단히 180도 회전만 시키자.

설계

(1) 서보모터객체 선언

서보모터를 사용하기 위해서는 제어함수들이 있는 Servo클래스를 객체로 선언합니다. 이말은 예전에 변수 선언과 같은 의미로 생각하시면 됩니다. Servo 클래스를 하나의 변수에 저장되었다고 생각하시면 됩니다.

#include <Servo.h> 
Servo servo1; 

Servo.h 파일에 있는 Servo 클래스를 servo1이라는 객체변수로 선언합니다. 그래서 servo1 객체를 통해서 Servo 클래스 안에 함수들을 사용하게 됩니다. 쉽게말해서 servo1.함수() 이렇게 Servo클래스의 함수를 사용할 수 있게 됩니다.

(2) 서보모터객체 연결

servo1.attach(사용핀);
servo1.attach(사용핀,min,max); //Ardunio min = 544, max=2400

attach()함수는 해당핀을 서보모터를 제어하는 핀으로 사용하겠다는 의미로 받아 들이시면 됩니다. 여기서, 두가지 방식이 있는데 첫번째, 사용핀만 인자로 넘겨주면 기본값으로 자동으로 세팅됩니다. 두번째, min, max로 범위를 따로 지정해 줄 수 있습니다. 두번째 방식이 좀 더 서보모터를 정교하게 제어할 수 있습니다.

과거 tinkercad.com 사이트로 옮기전 사이트에서는 서보모터가 다른 종류가 한개 더 있었는데 그 모터에서는 min, max값을 1000, 2000으로 줘서 1500일때 90도 회전이였습니다. 가상시뮬에터에서는 그냥 544, 2400으로 맞춰서 실험했네요. 서보모터마다 제어하는 각도가 차이가 있기 때문에 조정 작업이 필요합니다. 그래서 범위를 지정해서 하실때는 사용하시는 서보모터의 종류에 따라 다르니깐 꼭 보정을 하셔야 합니다.

(3) 서보모터객체 제어

servo1.write(180); 
servo1.writeMicroseconds(2400);

여기에서 write(각도)로 제어하시면 서보모터 기종에 따라서 약간 차이를 보이게 됩니다. 정확하게 제어가 안될 수 있습니다. 하지만 writeMicroseconds(2400)로 제어하시면 오차율을 줄일 수 있습니다. 하지만 보정을 해야하기 때문에 좀 귀찮은 점이 있지요.

(3) delay()함수 사용

delay(2000);

180도를 회전 시킬려고 하는데 일정 시간이 필요합니다.

만약에,

servo1.write(180); 
delay(500);
servo1.write(0); 
delay(500);

delay(500)으로 할 경우 0.5초동안 180도로 갈려고 회전하는 도중에 딜레이 시간이 끝나면 0도로 가는 명령라인이 실행됩니다. 그러면 180도 회전이 안된 상태에서 다시 0도로 회전되어 버리는 현상이 발생합니다. 그래서 각도별 delay()함수로 시간 조정이 필요합니다.

코딩을 하면

첫번째,

#include <Servo.h> 

Servo servo1; 
int servoPin1 = 9;

void setup() 
{ 
    servo1.attach(servoPin1); 
}

void loop() 
{
    servo1.write(180);     
    delay(2000);
    servo1.write(0);     
        delay(2000);
}

두번째,

#include <Servo.h> 
 
Servo servo1; 
int servoPin1 = 9;

void setup() 
{ 
    servo1.attach(servoPin2,544,2400); //Ardunio min = 544, max=2400
}


void loop() 
{
    servo1.writeMicroseconds(2400);
    delay(2000);
    servo1.writeMicroseconds(544);
    delay(2000);   
}

5. 결과




가장 빠르게 작업했네요.

마무리


그러면 서보모터로 제어할 수 있는게 뭐가 있을까요. 지금까지 다룬 부품중에 이거랑 같이 쓸 수 있는게 뭐가 있을지 곰곰히 생각해 보세요.

가령 초음파센서를 여기에다 붙이여 어떻게 될까요. 서보모터로 각도가 제어된다면 거기에 초음파센서가 부착된다면 초음파 센서를 회전 시킬 수 있다는 의미가 됩니다. 회전 각도에 따른 거리측정이 이루어지면 어떤 일이 할 수 있을가요. 가장 많이 알려진게 레이더 입니다. 즉, 각도별 거리를 측정해서 모니터에 초음파센서에 감지된 장애물을 표시 할 수 있습니다. 또 하나는 자율주행차 입니다. 주변의 장애물을 측정해서 미로 같은 곳을 주행하면서 혼자서 피해다니면서 주행을 할 수 있게 되겠죠. 또 한가지는 180도 각도의 거리를 측정하여 그 거리들을 일정한 간격으로 게속 누적 측정한다면 그 데이터로 3D Rendering도 수행이 가능합니다. 그외로 서보모터와 초음파센서가 결합하면 다양한 표현이 가능합니다.

그러면 서보모터만으로 제어할 수 있는 다른 것들은 뭐가 있을까요. 이 글을 읽으시는 당신의 가장 가까운 곳에 정답이 있습니다. 바로 당신이 하는 행동을 비슷하게 제어할 수 있습니다. "뭘까요!" 그것은 바로 관절입니다. 관절 꺽기가 된다는 것이죠. 당신의 손을 바라보세요. 그리고 둘째(집게) 손가락을 한번 구브렸다 펴보세요. 둘째 손가락은 3마디이고 그 중간 관절을 중심으로 각 마디가 구브러졌다 펴질 꺼에요. 바로 그게 회전입니다. 그 관절 부위를 서보모터로 제어한다고 생각하시면 됩니다. 손가락 하나를 제어하게 되면 손을 제어할 수 있게 됩니다. 그러면 한쪽 팔을 제어할 수 있게 되고 그러면 로보팔을 만드는데 이용하면 되겠죠. 보다 정교하게 제어하고 싶을때 360도 회전하면서 하고 싶다면 스템모터로 제어하면 됩니다.

기본 제어로 180도 회전이였지만 좀 더 전문적으로 제어하기 위해서는 현실에서 곤충의 보행을 관찰하면서 관절의 각도를 계산해서 4,6,8축 로못으로 보행을 시킬 수 있고 사람의 손 동작의 움직일때 각 마디의 각도를 관찰하여 각도를 계산하시면 로봇팔을 만들 수 있습니다.

마지막으로 본인의 움직임의 각도를 측정해서 그걸 아두이노로 서보모터로 회전시켜 비슷하게 표현 하는 것을 상상해 보세요. 참고로 여러개의 서보모터를 아두이노에서 제어가 가능하지만 서보모터의 전류를 아두이노에서 전부 공급하면 안됩니다. 가상시뮬레이터에서는 가능하지만 현실에서는 모터에게 전류를 순간 많이 공급하면 아두이노에 불안정 전류 공급이 이루어질 수 있기 때문에 안좋습니다. 외부전류 공급을 추천 드려며 많은 모터를 제어하고자 할때는 따로 여러모터를 한번에 제어할 수 있는 보드가 있는데 오래전에 본거라 보드 이름이 생각 안나네요. 한번 구글링 하셔서 여러 모터를 제어할 수 있는 보드를 찾아보세요.


댓글()