Embedded System/ARDUINO

[아두이노] 가속도센서를 이용한 서보모터 제어

임베지수 2017. 4. 2. 14:51

교생실습 중에 해본것인데 가속도센서를 이용하여 서보모터를 제어해 보았다. 3축 가속도센서로 사용한 모듈은 마이센이라는 회사에서 만든 [P0-AXA-12-01] 3축 아날로그 가속도 센서모듈이다.

 

 

 

서보모터는 국민서보라 불리는 HS-311 두 개를 사용하였다.

 

 

 

사용한 보드는 아두이노 UNO보드를 사용하여 만들어보았다~

나는 서보모터 2개를 사용하기 때문에 X, Y, Z축 중에서 X, Y축만 사용하였다. 기본적인 회로는 다음과 같다.

 

 

회로까지는 문제가 없는데 A0, A1 핀에서 받아온 ADC값을 이용하여 서보모터의 각도 값으로 변환하고 넣어줄 때 별다른 필터없이 바로 서보모터에 입력하면 문제가 발생한다. 노이즈때문에 값이 고정되어 있지 않고 계속해서 변하기 때문이다. 가끔씩 튈 때도 있다... 그래프로 나타내자면 왼쪽 아래 그림처럼 깔끔하게 나오지 않고 왔다갔다 난리부르스를 친다. 나는 이를 해결하고자 이동평균필터를 사용하였다. 오른쪽 아래 그림의 빨간 선처럼 스무스하게 인식하게 하는것이 목적이다.

 

     

 

 

일단 아무런 필터 없이 ADC에서 받아오는 값을 서보모터 각도 값으로 바로 입력했을 때의 결과를 보자!

 

 

떨림이 매우 심하고 제대로 동작하지 않는다....그래서 이동평균필터를 구현해보도록 한다! 이동평균필터의 구현은 아래 그림을 먼저 보자.

 

 

이동평균이란 말 그대로 전체 데이터 값의 평균을 구하는 것이 아니라 지정된 개수의 최근 데이터 값만으로 평균을 구하는 방식이다. 구간 1에서의 평균을 구하고, 구간 2에서의 평균을 구하고 하는 식으로 맨 처음 데이터는 버리고 새로운 데이터를 추가하여 평균을 구한다. 데이터 수는 임의로 정하면 된다. 그림에서는 5개가 되겠다.

 

자~ 이제 가속도 센서값을 각도값으로 바꿔보자. 먼저 아두이노에 analogRead함수를 이용하여 0~90도에서의 센서 값을 읽어보자. x축의 경우 0도에서 270, 180도에서 420의 값이 출력되었고 y축도 측정해본 결과 265~410의 값이 출력되었다. 그럼 이제 식이 나온다. x축을 먼저 보면, 270~420의 값을 가지므로 차이는 150이다. 이것을 180도와 매칭해야되므로

 

각도 = (기울기가 최대일때 ADC값 - 현재 측정된 ADC값) x {180도/(최대ADC와 최소 ADC의 차이)}

 

x축 각도 = (420 - 측정된 ADC값) X (180도/150)

 

라는 식이 나오고 y축에 대해서도 해보면 265~410이니까 차이는 145, 이것을 180도와 매칭시키면 아래와 같이 나온다.

 

y축 각도 =  (410 - 측정된 ADC값) X (180도/145)

 

 

아두이노로 구현한 코드는 다음과 같다~

#include <Servo.h>

#define SIZE 150

Servo mx;
Servo my;

int xx_arr[SIZE];
int yy_arr[SIZE];
int xx_angle;
int yy_angle;
float x_sum;
float y_sum;

void setup(){
 mx.attach(9);
 my.attach(10);
 
 x_sum=0;
 for(int i=0; i<SIZE; i++)
 {
   xx_arr[i] = (420-analogRead(A1))*1.2;
   if(xx_arr[i] > 180)
    xx_arr[i] = 180;
   else if(xx_arr[i] < 0)
    xx_arr[i] = 0;
    x_sum+=xx_arr[i];
 }
 
 y_sum=0;
 for(int i=0; i<SIZE; i++)
 {
   yy_arr[i] = (410-analogRead(A0))*1.24;
   if(yy_arr[i] > 180)
    yy_arr[i] = 180;
   else if(yy_arr[i] < 0)
    yy_arr[i] = 0;
    y_sum+=yy_arr[i];
 }
 
}

void loop() {
////////////////////////////////////////////////
  x_sum-=xx_arr[0];  

  for(int i=0; i<SIZE-1; i++)
    xx_arr[i] = xx_arr[i+1];
    
  xx_arr[SIZE-1] = (420-analogRead(A1))*1.2;
  x_sum+=xx_arr[SIZE-1];
  
  xx_angle = x_sum/SIZE;
  if(xx_angle > 180)
  xx_angle = 180;
  else if(xx_angle < 0)
   xx_angle = 0;
////////////////////////////////////////////////

////////////////////////////////////////////////
  y_sum-=yy_arr[0];
  for(int i=0; i<SIZE-1; i++)
    yy_arr[i] = yy_arr[i+1];
    
  yy_arr[SIZE-1] = (410-analogRead(A0))*1.24;
  y_sum+=yy_arr[SIZE-1];
  
  yy_angle = y_sum/SIZE;
  if(yy_angle > 180)
  yy_angle = 180;
  else if(yy_angle < 0)
   yy_angle = 0;
////////////////////////////////////////////////
  mx.write(xx_angle);
  my.write(yy_angle);
  
  delay(2);

}

 

아래는 이동평균필터를 적용했을 때의 영상이다.

 

 

고등학교 수준에선 이정도만 하면 되겠지??

 

'Embedded System > ARDUINO' 카테고리의 다른 글

[아두이노] 3x3x3 LED 큐브 만들기  (19) 2017.04.02
[아두이노] 스타트! 아두이노!  (2) 2017.04.02