아두 이노 실험 - adu ino silheom

이 책에서는 고등학교 교과과정에 포함된 주요 이론을 증명하는 실험을 설계하고, 복잡하고 반복적인 실험 과정에 아두이노를 적용함으로써 좀 더 체계적이고 편리한 실험 환경을 만드는 방법을 다룹니다.


또한, 이 책에 포함된 아두이노 프로그래밍은 쉽고 기초적인 내용들로 구성되어 있으므로 누구나 따라 할 수 있습니다. 이를 통해 아두이노 프로그래밍 기초를 학습하고, 더불어 아두이노를 활용하는 더 나은 프로젝트 실현에 디딤돌이 되어줄 것입니다.


이 책에서 다루는 실험들은 다음과 같습니다.


■ 중력가속도 측정하기(물리)

■ 접촉면적과 마찰력의 관계(물리)

■ 역학적에너지 보존 법칙(물리)

■ 소리의 속력 측정하기(물리)

■ 굴절률 측정하기(물리)

■ 솔레노이드 내부 자기장 측정하기(물리)

■ 빛의 세기에 따른 증산 작용 비교하기(생명과학)

■ 빛의 세기에 따른 광합성 속도의 변화(생명과학)

■ 보일-샤를의 법칙(화학)

■ 산-염기 적정 그래프 그리기(화학)

■ 끓는점 오름 측정하기(화학)

■ 위도에 따른 태양복사에너지의 입사량 측정하기(지구과학)

■ 기상관측과 일기예보(지구과학)

■ 구름 생성과 단열변화(지구과학)


지은이 소개

SADA 지음

SADA는 임베디드 플랫폼을 기반으로 세상과 소통할 수 있는 피지컬 컴퓨팅을 구현하는 경기북과학고등학교의 과학동아리입니다. SADA는 다양한 프로그래밍 언어를 이용하여 창의적인 생각을 실체화할 수 있는 시도를 하며, 궁극적으로 소프트웨어와 하드웨어의 진정한 융합을 꿈꿉니다. 이 책을 시작으로 SADA에서 얻은 폭넓은 경험을 많은 사람들과 함께 나누고 싶습니다.

아두이노(Arduino)는 중·고등학생들도 손쉽게 다양한 실험 장치를 직접 제작할 수 있도록 해주는 유용한 도구이다. 우리는 아두이노를 활용하여 초음파 스피커로부터 방출되는 음파를 스캔하여 공간에 형성된 음압을 정량적으로 측정할 수 있는 1차원 초음파 스캐너를 만들었다. 이 실험 장치를 활용하여 스피커로부터의 거리 및 방향에 따라 음압이 어떻게 변화하는지를 확인하고, 하나의 스피커가 형성하는 음파의 모양과 두 개의 스피커를 동시에 작동시켰을 때 나타나는 초음파 간섭무늬를 정량적으로 측정하고 물리 이론으로 설명하였다. 또한, 두 음원의 위상차에 의한 효과 및 반사에 의한 영향도 알아보았다. 이 초음파 간섭 실험 장치는 중등교육현장에서 음파의 물리적 특성 및 파동의 간섭 현상의 교육에 활용이 기대된다.


With the ample resources provided by Arduino, it is easy to implement a variety of experimental apparatus even for secondary school students. Using Arduino, we made an 1D ultrasonic wave scanner, which can quantitatively measure sound pressure in space by scanning sound waves from speakers. With this scanner, we examined how sound pressure changes according to the distance from and the direction of a speaker, and therefore could probe the acoustic fields. Likewise, we also measured the interference patterns from two speakers and explained the result with theory. Even more, we checked the effects of the phase differences and the reflection of sound waves. This experimental setup, along with the aforementioned activities, is expected to be very helpful in teaching physical properties of sound waves and interference in secondary schools.

오늘은 몇일동안 포스트한 장애물을 피하는 자율주행 테스트 해보는 시간으로 내용을 채우고자 합니다. 좀 더 정교한 자율주행 코딩을 할까도 생각 했지만 처음은 단순한 장애물을 피하는 자율주행을 보여드리는 것이 좋을 것 같아서 종합 코딩은 간단하게 표현하여 자율주행의 의미를 전달하고자 합니다.

아두 이노 실험 - adu ino silheom


우선 초음파 아두이노 RC카를 사진으로만 보면 정확히 구조를 이해할 수 없으니 간단히 fritzing으로 회로도를 보여드리고 간단히 장애물피하기 자율주행 코딩을 보여드리겠습니다.

1. 초음파센서 아두이노 RC카 회로도


  • 준비물 : L293D Motor Shield, DC Motor 2개, Servo Motor 1개, 초음파센서 1개, 외부전원 2개, 아두이노우노
  • 내용 : A4(Trig), A5(Echo)로 초음파센서에 연결하고 Servo Motor는 왼쪽 상당애 Servo Pin(-,+,Sig)에 연결한다. DC Motor 2개는 M3, M4에 연결합니다.

아두 이노 실험 - adu ino silheom



위 사진의 아두이노 RC카의 회로도는 아래 그림과 같이 구성되어 있습니다.

아두 이노 실험 - adu ino silheom


위 그림에서 외부 전원을 두개로 분리해서 공급합니다. 참고포 점퍼핀 덮개를 빼주면 아두이노와 Motor Shield의 전원을 나눌 수 있습니다. 그림이 좀 복잡해 보일 수 있지만 내용에 자세히 핀 연결 설명이 되어 있으니깐 해당 위치에 부품의 선을 연결해주시면 됩니다.

2. 코딩


  • 참고 :
    초음파센서 아두이노 RC카 장애물 감지 방법(아두이노)
    아두이노 RC카 초음파센서로 장애물 피하기 패턴(아두이노)

이전 post에서 장애물을 감지하는 방법과 장애물을 피하는 패턴을 살펴 보았습니다. 여러개를 설명했는데 그중에 각각 한개씩 간단한 표현으로 코딩을 하겠습니다.

[기본소스]

#include <AFMotor.h>

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);

void setup() {
  motor1.setSpeed(200);
  motor2.setSpeed(200);
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

void loop() {  

  초음파센서 장애물 감지;

  motor1.run(FORWARD); //전진
  motor2.run(FORWARD);
  delay(1000);
}

1) 서보모터+초음파센서 장애물 감지


[코딩 순서]

  • 40~140도 사시를 10도씩 회전하면서 초음파센서로 장애물를 측정한다.
  • 장애물 감지 거리를 15cm 미만일 때 감지로 간주한다.
void loop() {
  //초음파센서 회전
  servo.write(angle);
  delay(50);
  
  int distance = sonar.ping_cm();

  //장애물 감지
  if(distance>0 && distance<15){
     movePattern(); 
     //Serial.println(distance);
     motor1.run(FORWARD);
     motor2.run(FORWARD);  
  }   
  //회전 각도
  if (angle == 140) state = -10;    
  else if (angle == 40) state = 10; 
  angle += state;
}

2) 장애물 피하기 패턴


[코딩 순서]

  • 장애물 감지시 0.5초 동안 후진한다.
  • 전방 90도를 기준으로 90이상이면 우회전 90미만 좌회전 시킨다.
  • 장애물 피하기 회전이 끝나면 다시 전진 주행한다.
  • 초음파센서는 방향은 90도로 위치시킨다. (90도 기준으로 다시 10도씩 변화를 시키기 위해서)
void movePattern(){
     motor1.run(BACKWARD);
     motor2.run(BACKWARD);
     delay(500);   
    if(angle>=90){
      motor1.run(FORWARD);
      motor2.run(BACKWARD);
      delay(500);   
    }
    else{
      motor1.run(BACKWARD);
      motor2.run(FORWARD);
      delay(500);   
    }
    angle=90;
    servo.write(angle);
    delay(100);
}

3) 기본소스+장애물감지소스+장애물피하기 패턴소스


위 세가지 소스를 합치면 다음과 같습니다. 참고로 몇가지 다듬어서 코딩을 했습니다.

#include <AFMotor.h>
#include <Servo.h> 
#include <NewPing.h>

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);

Servo servo;
const int servoPin = 10;
const int TRIG = A4;
const int ECHO = A5;
const int MAX_DISTANCE = 100;
NewPing sonar(TRIG, ECHO, MAX_DISTANCE);

int speed=200;
int state = 10;
int angle = 90;

void setup() {
//  Serial.begin(9600);
  motor1.setSpeed(speed);
  motor2.setSpeed(speed);
  motor2.run(RELEASE);
  motor2.run(RELEASE);

  servo.attach(servoPin);
  servo.write(angle);
  delay(1000);

  //Start
  motor1.run(FORWARD);
  motor2.run(FORWARD);  
}

void loop() {
  //초음파센서 회전
  servo.write(angle);
  delay(50);
  
  int distance = sonar.ping_cm();

  //장애물 감지
  if(distance>0 && distance<15){
     movePattern(); 
     //Serial.println(distance);
     motor1.run(FORWARD);
     motor2.run(FORWARD);  
  }   
  //회전 각도
  if (angle == 140) state = -10;    
  else if (angle == 40) state = 10; 
  angle += state;
}
void movePattern(){
     motor1.run(BACKWARD);
     motor2.run(BACKWARD);
     delay(500);   
    if(angle>=90){
      motor1.run(FORWARD);
      motor2.run(BACKWARD);
      delay(500);   
    }
    else{
      motor1.run(BACKWARD);
      motor2.run(FORWARD);
      delay(500);   
    }
    angle=90;
    servo.write(angle);
    delay(100);
}

setup()함수에서 초기화 작업을 하는데 젤 먼저 DC기어모터의 속도를 200으로 세팅하고 각 Motor를 RELEASE(해제 or 정지)상태로 둔다. 그리고 나서 주행전 Servo Motor의 초기 위치는 90도 회전 시켜 초음파센서가 전방90도를 바라보게 한다. 그 다음 1초동안 준비 상태로 있다가 FORWARD(전진)명령으로 아두이노 RC카가 출발하게 됩니다.

loop()함수는 angle(각도)에 위치로 회전을 0.05초 간격으로 합니다. 회전이 될 때 초음파센서(sonar)의 거리를 측정하여 distance에 저장합니다. 이 값이 0보다 크거나 15cm보다 작은 값일 때 장애물을 감지한 걸로 감주합니다. 0의 값은 MAX_DISTANCE의 제한최대거리 값을 넘게 되면 0으로 반환되기 때문에 0의 값이 주행 중에 일정 간격으로 발생하게 됩니다. 그렇기 때문에 0보다 크거나 15cm보다 작은 값으로 if문으로 체크하는 조건식을 만든 것이죠. 이렇게 15cm 미만일 때 장애물 감지 판정을 내리게 되면 if문 이하 문장을 수행 하게 됩니다. 이때 장애물 피하기 패턴을 코딩하면 됩니다 movePattern()함수로 장애물을 피하는 동작을 코딩해 놨는데 이 방식으로 피하고 나면 다시 motor1, motor2은 FORWARD(전진)으로 게속 자율주행을 하게 됩니다.

여기서, movePattern()함수를 살펴보면 먼저 0.5초 동안 후진을 하고 후진 한 후에 Servo Motor가 회전한 현재 각(angle)의 값이 90보다 큰거 아니면 작은 가로 나누게 되는데 이때 90도보다 크면 좌측에 장애물을 감지했기에 우회전 명령문을 0.5초 동안 90도 우회전을 하게 되고 90도보다 작으면 우측에 장애물을 감지했기에 좌회전 명령문으로 0.5초 동안 90도 좌회전을 하게 됩니다. 그리고 다시 전진 주행을 하기 전에 초음파센서의 방향 위치를 초기화 상태로 전방 90도 위치로 회전시켜놓습니다.

대충 전체의 소스에 대해 설명을 했는데 이해하셨는지 모르겠네요.

만약, 다른 방식으로 접근하고 싶다면 장애물 감지 코딩 부분을 다른 감지 주행패턴을 선택해서 붙여 넣으시면 되고요. 장애물 피하기 패턴도 movePattern()함수 내부의 코딩을 원하는 패턴으로 만들어서 넣으시면 됩니다.

3. 결과


그냥 전방의 장애물을 손으로 표현해서 손으로 막을 때 이 손을 아두이노 RC카가 장애물로 감지하고 초음파센서가 감지한 각도에 따라 좌/우회전을 하는 짧게 촬영한 영상입니다.


이렇게 간단하게 장애물 피하기로 자율주행을 출발합니다.

위 코딩을 좀 더 정교하게 하려면 다음과 같이 해야 합니다.

void loop(){
  //장애물 감지
  if(distance>0 && distance<15){
     movePattern(); 
  }  
}   
void movePattern(){
     motor1.run(BACKWARD);
     motor2.run(BACKWARD);
     delay(500);   
    if(angle>=90){
      motor1.run(FORWARD);
      motor2.run(BACKWARD);
      delay(회전각도시간값);   
    }
    else{
      motor1.run(BACKWARD);
      motor2.run(FORWARD);
      delay(회전각도시간값);   
    }
    motor1.run(FORWARD);
    motor2.run(FORWARD);    
        
    angle=90;
    servo.write(angle);
    delay(100);
}

여기서, 회전각도시간값은 여러분들이 가지고 있는 아두이노 RC카의 바퀴힐과 속도를 기준으로 1초(1000)에서 delay값 100을 기준으로 몇도 회전되는지 체크했다가 정확하게 회전각도시간값을 넣어주세요. 그리고 회전을 시킨 후 바로 전진 주행으로 바꿔 주시면 좀 더 정확하게 회전 후 전진 주행이 됩니다. 사실 촬영할 때는 loop()함수에다가 넣어서 간단히 movePattern()함수에서는 회전 주행만 표현했는데 마지막 초음파센서의 90 회전 초기화에서 0.1초 동안의 회전이 더 일어나는 단점을 가지게 되더군요. 그러면, 회전각도시간값으로 정확히 회전을 시키더라도 초기화 0.1초동안 회전이 더 발생하는 문제가 생길 수 있습니다. 단순하게 장애물 피하기 코딩에서는 0.1초 차이는 티도 안나기 때문에 무시하고 촬영을 했지만 post을 작성하면서 이 코딩부분을 수정해서 올릴까도 했지만 촬영은 이전 소스를 기반으로 주행 시켜서 그냥 약간 부족한 상태로 수정 없이 올리게 되었네요. 하지만 이렇게 추가로 이야기 하고 넘어가야 할 것 같아서 좀 더 내용을 채우게 되었네요.

motor1.run(FORWARD);
motor2.run(FORWARD);   

아무튼 이 명령이 어느 위치에 있느냐에 따라 결과는 달라지게 됩니다. 단순한 코딩은 무시 할 정도로 표현해도 되지만 정교한 코딩에서는 명령문의 위치에 따라 딜레이 0.1초 차이도 회전 오류각을 발생할 수 있기 때문에 코딩을 완료되더라도 완료된 코딩의 흐름을 머리속에서 상상하면서 문제가 없는지 체크를 꼭 하셔야 합니다.

마무리


위 코딩을 보면 그렇게 어려운 코딩이 아닙니다. 재밌는 것은 이 원리를 이해하시면 또다른 다양한 표현을 할 수 있습니다. 장애물을 피한다면 아두이노 RC카가 도망다니는 RC카로 표현 할 수 있습니다. 가령, 전방의 특정 각도의 장애물이 감지되면 그 장애물의 각도을 기준으로 180도 회전해서 뒤로 도망가는 RC카를 만들 수 있습니다. 인간과 RC카가 이 원리로 접근하게되면 인간이 아두이노 RC카를 추적하고 아두이노 RC카는 도망자가 됩니다.

아니면, 주행을 규칙적으로 지그재그로 표현을 한다면 지그재그 주행을 할 때 전방의 초음파센서가 장애물을 감지하면 진행 방향에 있는 장애물을 피하기 위해서 진행 라인의 주행을 포기하고 다음 라인의 주행을 이여간다면 로봇청소기와 같은 주행을 할 수 있게 됩니다.

또 다른 상상을 하면 아두이노 RC카를 랜덤 주행을 일정 범위안에서 수행되게 해놓고 있다가 초음파센서의 장애물 감지 범위에 인간이 다가갔을 경우 일정 거리까지 아두이노 RC카가 따라오도록 주행패턴을 만들게 되면 어떤 느낌의 아두니오 RC카가 될까요. 바로 애완동물 아두이노 RC카로 표현이 가능해 집니다.

간단히 장애물 감지와 장애물 피하는 동작의 원리를 곰곰히 생각해보니깐 방금 이야기 했던 상상들이 떠오르더군요. 위에서 설명한 주제들의 원리는 전부 동일한 원리 입니다. 장애물 감지와 장애물 피하기에서 장애물 피하기를 역발상으로 장애물 따라오기로 생각의 관점을 바꾸면 이렇게 또 다른 결과가 나올 수 있습니다. 재밌는 아두이노 RC카를 만들 수 있겠죠.