로봇카 기초 배우기

아두이노 로봇카

4. 더듬이센서로 주행하기

Contents

아두이노 로봇카 실습 세부목차


1. 프라이비 에듀 마이컴 보드 시작하기 

2. LED동작과 서보모터 제어 

3. 서보모터 조립과 동작 그리고 주행하기 

4. 더듬이센서로 주행하기 

5. 포토트랜지스터로 주행하기 

6. 적외선센서로 주행하기 

7. 주행거리 제어하기


※ 본 페이지의 내용은 패럴렉스(Learn.Parallax.com)사의 공식 홈페이지에서 제공되는 자료를 참고하여 작성되었습니다. 또한, 본 내용은 아두이노 neo-로봇카v2 또는 아두이노 로봇카N 키트실습에 사용할 수 있습니다.

더듬이 센서로 로봇카 주행하기


본 자료의 원문을 참조하려면 패럴렉스 사의 아두이노를 위한 로보틱스 홈페이지를 방문할 수 있습니다. 이 자료의 로봇카 모든 제어명령 및 센서신호 수집작용은 프라이비 보드 또는 아두이노를 통하여 수행됩니다. 프라이비 보드는 아두이노 우노와 동일한 ATmega328P 마이컴이 사용된 호환보드이며, 아두이노 우노와 동일한 사용법이 적용됩니다.


지금부터 로봇카 주행 기초단계를 시작합니다. 더듬이 센서는 기계적 접촉으로 디지털 감지 신호를 생성하기 때문에 복잡한 원리의 센서를 사용하는 것보다 쉽고 간단합니다. 광센서와 같은 비접촉센서 주행동작을 시작하기 이전에 아래 실습을 먼저 시도하기를 추천합니다.


아래 세부 내용들을 살펴볼 수 있습니다.

1) 더듬이를 로봇카에 부착하고, 촉각신호 점검하기

2) 더듬이로 로봇카 주행하기

3) 모서리 탈출을 위한 인공지능


로봇카의 동작을 제어하기 위한 방법중 가장 간단한 수단이 터치스위치 또는 촉각스위치 센서를 사용하는 것입니다. 직접 물리적인 힘을 전달을 통하여 접촉을 감지하고 로봇카의 동작을 제어할 수 있습니다. 이런 방식의 동작제어는 로봇카 제어 이외에 다른 산업분야에서도 다양하게 적용될 수 있습니다.


지금부터 더듬이로 불리는 촉각 스위치를 로봇카에 장착하고 로봇카의 다양한 주행동작들을 유도할 것입니다. 만약 로봇카의 전방에 어떤 장애물 또는 장벽이 가로막고 있다면 더듬이 센서가 이를 감지하고 진행방향을 바꾸거나, 심지어 후진으로 탈출하게 할 수 있습니다. 우리는 이러한 프로그래밍 동작들을 더듬이 센서의 측정값과 연결하여 프로그래밍할 것입니다. 우리들의 최종 목표는 미로속에 로봇카가 놓이더라도, 로봇카 스스로 판단하고 주행하여 미로를 탈출하는 자율주행 로봇카를 구현하는 것입니다.


1) 더듬이를 로봇카에 부착하고, 촉각신호 점검하기


로봇카 부품중 더듬이 와이어 두 개와 3-핀 헤드, 1/2인치 스탠드오프 및 220Ω 저항(빨강-빨강-갈색) 10KΩ 저항(갈색-흑색-주황색)을 먼저 준비한다. 로봇카를 조립한 후 아래 그림과 같이 더듬이 두 개를 밀착시키고 조입니다.


그림입니다. 원본 그림의 이름: CLP00001b580027.bmp 원본 그림의 크기: 가로 343pixel, 세로 225pixel

그림입니다. 원본 그림의 이름: CLP00001b580028.bmp 원본 그림의 크기: 가로 358pixel, 세로 269pixel


더듬이 센서 동작원리는 아두이노 푸시버튼 동작원리와 매우 유사합니다. 만약 아두이노 푸시버튼 동작실습을 해보았다면 더듬이의 동작원리를 쉽게 이해할 수 있습니다. 아래 풀다운 풀업 방식의 푸시버튼 회로에서 푸시버튼을 더듬이 촉각센서로 교환하면 됩니다. 220Ω 저항이 더듬이회로에서 더 사용된 이유는 로봇카 섀시에 예상하지 못한 전기적 충전상태로 인한 과전류로 인해 아두이노 보드가 피해를 입지 않도록 하기 위함입니다.


그림입니다. 원본 그림의 이름: CLP00001b580029.bmp 원본 그림의 크기: 가로 319pixel, 세로 195pixel


더듬이 촉각회로의 구성은 5V의 전압방향에 10KΩ 저항과 220Ω 저항을 직렬로 연결하고 아두이노 디지털핀 5번과 7번으로 각각 연결합니다. 그리고 10KΩ 저항과 220Ω 저항 사이에 3핀 헤드와 가까운 거리에 더듬이가 위치하고 있습니다. 더듬이는 스크루 나사를 통하여 로봇카 섀시와 연결되어 있기 때문에 디지털 신호의 접지 역할을 수행합니다. 더듬이의 역할은 로봇카가 진행하는 방향으로 어떤 장애물이 존재하면 더듬이가 물리적 힘을 받아서 휘게 되고, 따라서 더듬이가 3핀 헤드와 접촉하게 됩니다. 이제 우리는 아두이노 디지털핀 입력모드에서 더듬이가 3핀헤드와 접촉했는지 접촉하지 않았는지를 구분할 수 있으며, 이 신호로 로봇카 전방에 어떤 장애물이 존재하는지를 판단할 수 있습니다.


위 사진에서 더듬이가 3핀헤더와 접촉하지 않았을 때는 디지털핀과 연결된 회로는 220Ω 저항 10KΩ 저항 그리고 5V 전압 순서로 연결되어 5V의 전원전압이 직접 공급되는 것을 확인할 수 있습니다. 그렇지만, 더듬이가 3핀헤더와 접촉할 때는 디지털핀과 연결된 회로는 220Ω 저항과 섀시 그리고 220Ω 저항과 10KΩ 저항 5V 전압 순서로 전기가 흐르는 경로가 더 생깁니다. 이때 5V 전원에서 공급되는 전류는 대부분 로봇카 섀시로 흐르기 때문에 아두이노 디지털핀에서 감지되는 전압신호는 0V 를 나타냅니다. 이런 원리로 더듬이가 3핀헤드와 닿지 않으면 아두이노 디지털핀 입력전압은 5V (HIGH 상태)로 감지되고, 더듬이가 3핀헤드와 닿으면 아두이노 디지털핀 입력전압은 0V (LOW 상태)로 감지됩니다.


이제 스케치로 이런 입력신호를 직접 살펴볼 차례입니다. 아두이노에서 입력전압의 크기를 디지털신호로 읽을 때는 digitalRead() 함수를 사용합니다. 그리고 pinMode() 설정에서는 INPUT 모드로 설정하여야 합니다. 아래 스케치를 업로드하여 출력 상태를 시리얼모니터로 관찰해보세요.


void setup()

{

  pinMode(7, INPUT);

  pinMode(5, INPUT);

  Serial.begin(9600);

void loop()

  byte wLeft = digitalRead(5);

  byte wRight = digitalRead(7);

  Serial.print(wLeft);

  Serial.println(wRight);

  delay(50);


만약 정상적으로 스케치가 업로드되었다면 아래 그림과 같은 동작화면을 얻을 수 있을 것입니다. 더듬이가 3핀헤드와 닿는 순간 해당되는 방향의 디지털핀 입력신호가 아래 사진과 같이 0으로 표시되어야 합니다. 양쪽 더듬이가 모두 3핀헤드에 닿는다면 당연히 시리얼 모니터에 표시되는 값은 00이 되어야 합니다.


그림입니다. 원본 그림의 이름: CLP00001b58002a.bmp 원본 그림의 크기: 가로 405pixel, 세로 147pixel


그리고 loop() 함수내의 스케치를 아래와 같이 수정해서 사용해도 결과는 동일합니다. 별도의 변수를 사용하지 않고 동작시킨 스케치 형태입니다.


   void loop() // Main loop auto-repeats

   {

      Serial.print(digitalRead(5));     // Display wLeft

      Serial.println(digitalRead(7));   // Display wRight

      delay(50);                    // Pause for 50 ms

   }


더듬이가 3핀헤드와 닿는지 닿지 않는지에 대한 상태를 눈으로 확인하는 것은 더듬이의 물리적인 접촉을 전기적인 접촉으로 변환하는 현상에 대한 것이기 때문에 매우 어려울 수 있습니다. 이런 문제를 해결하기 위해 우리는 아래 회로와 같이 별도의 LED 회로를 추가할 것입니다.


그림입니다. 원본 그림의 이름: CLP00001b58002b.bmp 원본 그림의 크기: 가로 293pixel, 세로 138pixel

아두이노 디지털핀 2번과 8번으로 각각 LED 회로를 추가했다면, if 조건문을 사용하여 각각의 더듬이 상태를 LED 불빛 상태로 연계하여 표시하는 스케치를 추가할 수 있습니다.


   pinMode(8, OUTPUT); // Left LED indicator -> output

   pinMode(2, OUTPUT); // Right LED indicator -> output


   if(wLeft == 0) {

      digitalWrite(8, HIGH);

   }

   else {

      digitalWrite(8, LOW);

   }


   if(wRight == 0) {

      digitalWrite(2, HIGH);

   }

   else {

      digitalWrite(2, LOW);

   }


위 스케치를 추가하면 좌/우 더듬이가 3핀헤드와 접촉했는지 접촉하지 않았는지에 대한 상태를 LED 불빛만으로 쉽게 판단할 수 있습니다. 이런 추가 기능은 로봇카의 순간적인 동작 상태를 쉽게 이해하고 수정할 수 있게 도와줍니다. 더듬이 신호에 따라 LED가 구분되어 잘 동작하는지 확인하는 스케치를 완성해보세요! 지금까지 소개한 내용들을 단순히 서로 합치기만 하면 됩니다.


2) 더듬이로 로봇카 주행하기


앞서 배운 로봇카 주행동작과 더듬이 촉각센서 동작 실습을 이제 합치려고 합니다. 이제 로봇카에 어떤 일들이 일어날지 궁금하지 않은가? 스케치 명령만으로 로봇카가 수동적인 동작을 하던 것과 다르게, 이제 능동적인 동작을 하게 될 것입니다. 로봇카 스스로 판단하고 동작하는 형태이기 때문에 우리는 이런 형태를 자율주행이라고 부르기도 합니다.


무인자동자의 가장 낮은 단계 자율주행에 대한 모형 실습을 시작합니다. 로봇카 주행과 더듬이센서 신호를 어떻게 서로 연결짓는가는 모두 프로그래머의 자유로운 선택에 달려있습니다. 당신이라면 어떤 선택으로 로봇카와 더듬이 센서신호를 관계짓고 싶은가?


여기서는 왼쪽 더듬이에 장애물이 감지되면 왼쪽으로의 로봇카 진행이 어려울 것으로 예상하여 오른쪽으로 회전하는 동작을 연결할 것입니다. 반대로 오른쪽 더듬이에 장애물이 감지되면 오른쪽으로의 로봇카 진행이 어려울 것으로 예상하여 왼쪽으로 회전하는 동작을 연결할 것입니다. 당연히 두 개의 더듬이가 동시에 장애물을 감지하면 로봇카를 후진하는 동작으로 연결할 것입니다. 여러분이 실습을 진행해보면 두 개의 더듬이가 동시에 장애물을 감지하는 경우는 거의 발생하지 않을 것입니다. 이제 이런 문제들을 우리는 이장에서 공학적으로 해결하려고 합니다.


아래 스케치를 여러분의 로봇카에 업로드하고 동작을 점검해봅시다. 스케치 내용은 지금까지 다뤄온 내용을 하나로 합친 것으로 이해하기 어렵지 않을 것입니다. 물론 아래 스케치에는 부저 동작에 대한 스케치도 포함되어 있기 때문에 여러분의 로봇카 디지털 핀 4번에 부저 회로를 구성하는 것을 잊지 마세요.


#include <Servo.h>

Servo servoLeft; 

Servo servoRight;

void setup() {

  pinMode(7, INPUT); 

  pinMode(5, INPUT);  

  tone(4, 3000, 1000); 

  delay(1000); 

  servoLeft.attach(13); 

  servoRight.attach(12); 

void loop() {

  byte wLeft = digitalRead(5);  

  byte wRight = digitalRead(7); 

  if((wLeft == 0) && (wRight == 0)) {

     backward(1000); 

     turnLeft(800); 

  }

  else if(wLeft == 0) {

     backward(1000); 

     turnRight(400); 

  }

  else if(wRight == 0) {

     backward(1000); 

     turnLeft(400); 

  }

  else {

     forward(20); 

  }

}

void forward(int time) {

  servoLeft.writeMicroseconds(1700); 

  servoRight.writeMicroseconds(1300); 

  delay(time); }

void turnLeft(int time) {

  servoLeft.writeMicroseconds(1300); 

  servoRight.writeMicroseconds(1300); 

  delay(time); }

void turnRight(int time) {

  servoLeft.writeMicroseconds(1700); 

  servoRight.writeMicroseconds(1700); 

  delay(time); }

void backward(int time) {

  servoLeft.writeMicroseconds(1300); 

  servoRight.writeMicroseconds(1700); 

  delay(time); }


위 스케치를 사용하여 로봇카의 동작을 자세히 관찰해보면 벽면 정중앙에서도 양쪽 더듬이가 동시에 감지하는 것이 아니라 미세하게 두 개의 더듬이중 아느 한 개가 먼저 감지한다는 것을 느낄 것입니다. 이런 종류의 로봇동작은 생소한 것이 아니다. 오히려 로봇 동작과 관련하여 매우 자주 목격할 수도 있습니다.


두 개의 더듬이가 동시에 감지해야 하는 특수한 조건이 모서리에 로봇카가 위치할 때입니다. 모서리의 특성상 로봇카가 좌측으로 회전하거나 우측으로 회전하는 동작으로는 충분하지 않고, 후진으로 탈출하는 것이 최선인 경우입니다. 그렇지만, 두 개의 더듬이가 동시에 접촉을 감지하지 않으면 우리는 로봇카를 후진하도록 제어하는 것이 간단하지 않습니다. 이제 이런 어려움을 해결하기 위하여 조금의 지능을 스케치에 더 추가해겠습니다.


3) 모서리 탈출을 위한 인공지능


모서리에서 로봇카를 간단히 탈출시키려면 어떤 방법이 있을까? 더듬이 촉각센서의 위치를 교정하는 방법은 간단하지 않고, 항상 교정해야 하는 어려움도 있습니다. 무엇보다 해결책이 지속적이지 않다는 것입니다.


이제 모서리에서의 로봇카 동작을 한번 자세히 관찰해봅시다. 여러분의 회전동작이 90도 보다 더 큰 각도로 회전하도록 설계하지 않았다면 다음 설명과 같을 것입니다. 모서리에서 왼쪽 더듬이가 접촉을 하면 로봇카는 오른쪽으로 90도 정도 회전한 후 다시 전진하는데, 이때는 다시 오른쪽 더듬이가 접촉을 하게 됩니다. 그리고 오른쪽 더듬이가 접촉을 하면 로봇카는 왼쪽으로 90도 정도 회전한 후 다시 전진하는 상황을 반복할 것입니다. 이런 어려움을 탈출할 수 있는 한가지 해결책은 양쪽 더듬이를 번갈아 가면서 접촉하는 횟수를 세는 것입니다.


조금 전 설명한 모서리 탈출 알고리즘을 적용해보자. 더듬이의 이전 접촉상태 값을 기억하고 현재의 더듬이 접촉상태 값을 이전과 비교하여 반대 값을 갖는다면 카운터 변수에 1을 더합니다. 카운터 변수의 값이 얼마인지에 따라 여러분은 로봇카를 후진시키거나 U턴 동작하도록 제어할 수 있으며, 따라서 로봇카는 모서리를 성공적으로 탈출할 수 있을 것입니다. 그리고 로봇카 탈출 후에는 카운터 변수의 값을 초기상태값 0 으로 되돌리는 것을 잊지마세요. 


이상의 로봇카 동작에 부합하는 스케치를 아래에서 소개합니다.


#include <Servo.h> 

Servo servoLeft; 

Servo servoRight;

byte wLeftOld; 

byte wRightOld;

byte counter; 

void setup() {

  pinMode(7, INPUT); 

  pinMode(5, INPUT);  

  pinMode(8, OUTPUT); 

  pinMode(2, OUTPUT);  

  tone(4, 3000, 1000); 

  delay(1000); 

  servoLeft.attach(13); 

  servoRight.attach(12); 

  wLeftOld = 0; 

  wRightOld = 1;

  counter = 0; 

void loop() {

  byte wLeft = digitalRead(5);  

  byte wRight = digitalRead(7); 

  if(wLeft != wRight) { 

     if ((wLeft != wLeftOld) &&   (wRight != wRightOld))

     {

         counter++; 

         wLeftOld = wLeft; 

         wRightOld = wRight;

         if(counter == 4) 

         {

             wLeft = 0; 

             wRight = 0;

             counter = 0; 

         }

     }

     else {

         counter = 0; 

     }

  }

  if((wLeft == 0) && (wRight == 0)) {

     backward(1000); 

     turnLeft(800); 

  }

  else if(wLeft == 0) {

     backward(1000); 

     turnRight(400); 

  }

  else if(wRight == 0) {

     backward(1000); 

     turnLeft(400); 

  }

  else {

     forward(20); 

  }

}

void forward(int time) {

  servoLeft.writeMicroseconds(1700); 

  servoRight.writeMicroseconds(1300); 

  delay(time); 

}

void turnLeft(int time) {

  servoLeft.writeMicroseconds(1300); 

  servoRight.writeMicroseconds(1300); 

  delay(time); 

}

void turnRight(int time) {

  servoLeft.writeMicroseconds(1700); 

  servoRight.writeMicroseconds(1700); 

  delay(time); 

}

void backward(int time) {

  servoLeft.writeMicroseconds(1300); 

  servoRight.writeMicroseconds(1700); 

  delay(time);

}



이전 페이지로       다음 페이지로