아두이노 드론 만들기 #1


지난 포스팅에서 잠시 언급했지만 정말 BLDC 모터 돌리는데만 꼬박 1주일이 걸렸다. 중간에 잠깐 되는 듯하다가
변속기(이하 ESC) 2개만 태워먹고 결국은 실패했다. 괜한 도전을 시작했나 싶었으나 그래도 인생 짬밥이 있는데
하다보면 수가 나겠거니 하고 계속 시도하다가 결국은 모터를 구동하는데 성공했다.


오늘 포스팅은 간단하게 BLDC 모터를 구동하는 과정을 정리해보도록 하겠다. 
사실상의 주된 내용은 아두이노의 Servo 라이브러리에 대한 설명이니 이 부분이 필요없는 분들은 그냥
맨 마지막 동영상이나 보시고 넘어가시라~


간단한 기초 지식 설명


이미 아는 사람은 다 알지만 나는 문돌이의 입장에서 글을 쓰는만큼 간단하게 기초 지식부터 짚고 넘어가겠다.
하지만 나보다 더 잘 설명하는 글들이 많으니 그냥 링크로 대신한다(결코 귀차니즘의 발로가 아니라고는 말 못하겠다…). 
개인적으로 가장 설명이 잘 되어있다고 생각되는 블로그들을 위주로 링크한다.


BLDC 모터 : http://bit.ly/2uCa078
ESC : http://bit.ly/2sABx7F (혹은 위의 BLDC 설명 블로그에서도 확인 가능)
LiPO 배터리 : http://bit.ly/2tAxSrJ
프로펠러 : http://bit.ly/2uW2eV5


일단 가장 기본적으로 필요한 지식들은 위의 4가지라고 보면 될 것 같다. 문제는 이미 조립이 되어있는 완제품이나
아니면 모든 구성품들이 다 갖추어진 조립 Kit를 사는 경우가 아니라면 이 4가지 부품의 궁합을 잘 맞춰서 구매를
해야 하는데 이 과정이 꽤나 성가시고 어렵다. 


특히나 각각의 부품을 검색하다보면 설명한 글마다 부품 제조업체에서 제공하는 data sheet를 보면 사용법을 알 수 
있다고 하는데 웬만한 제품이 중국산이다보니 제대로된 data sheet 찾기가 결코 만만치 않다.


내가 지금 사놓은 부품 중 일부도 모터와 프로펠러는 짝이 맞는데 ESC와 배터리가 맞지 않아 추가로 구매해야 하는
상황이다. 무려 4세트나 되는데…ㅠ.ㅠ


아무튼 직접 제작을 목표로 하시는 분들은 위 내용들을 잘 숙지하셔서 불필요한 낭비를 줄이도록 하시길 빈다.


아두이노 RF 통신하기


사실 조종기도 따로 사고 싶었으나 조종기와 수신기 가격이 결코 만만치가 않았다. 게다가 그런 것을 사다 집에 들여
놓으면 분명 집사람의 반발이 있을 것 같아 도무지 살 엄두가 나질 않았다.


그래서 대안으로 선택한 것이 아두이노의 조이스틱 모듈과 nRF24L01 트랜시버의 무선 통신을 이용하여 
조종기를 만들기로 했다. 이 부분도 불필요하게 글의 길이가 길어지는 것을 막기 위해 링크로 대신한다.


아두이노 조이스틱 모듈 사용 : http://bit.ly/2uVzwn2
nRF24L01을 이용한 무선 통신 : http://bit.ly/2sRbmhh


이 과정에서는 주의할 점이 조이스틱 모듈이 원점이 제대로 안잡힌다는 문제다. 낱개로 파는 조이스틱 모듈과 두 개가
하나의 기판에 붙은 것 2종류의 조이스틱 모듈을 사용해보았는데 두 종류 모두 센터값 512에서 멈춰있지 않고 계속
1~2 정도의 오차가 생기면서 값이 지속적으로 변하였다. 내가 불량품을 산 것인지 대체로 이정도는 허용 범위인 것인지
모르겠다.


관련 스케치 코드는 다음 포스팅에서 공개하도록 하겠다.


ESC 캘리브레이션하기


바로 이 작업에서 1주일의 시간과 2개의 ESC가 날아갔다…ㅠ.ㅠ
ESC에 대해 설명한 글들에서도 자세한 정보들은 data sheet를 참조하라는데 data sheet 찾는 것도 만만치 않고
또 찾아도 내용 파악이 쉽지 않다…-.-


ESC 캘리브레이션이란 간단히 말하면 조종기의 최솟값/최댓값과 ESC의 최솟값/최댓값을 mapping 시켜주는
과정이다. 다음 블로그에서 조금 자세히 알아볼 수 있을 것이다.


ESC 캘리브레이션이란? : http://ivivaldi.blog.me/220917752795


하지만 처음 실패의 이유는 곰곰히 생각해보면 개발자로서 참 부끄러운 일이기도 하다.
흔히 뭔가 준비가 안되어있는 사람들에게 하는 말이 ‘군인이 전쟁터에 총도 안들고 나간다’는 것이다.
군인에게 가장 필요한 도구가 ‘총’인 것과 같이 개발자에게 가장 필요한 도구는 API다.
군인이 전쟁터에서 훌륭히 싸우기 위해 평소에도 총에 기름칠을 하고 정비를 하듯이 개발자도 자유자재로 원하는 
기능을 만들어내기 위해서는 적재적소에 올바른 API를 적용할 줄 알아야 한다. 비록 평소에 달달 외우지는 못할지언정
어떤 API를 쓸 상황이 되면 그 API에 어떤 속성들이나 함수들이 있으며 그 사용법은 어떻게 되는지를 한 번쯤은
살펴보고 사용을 해야 할 것이다.


나의 실패는 바로 이 과정이 없었기 때문이다.
아래 코드는 최초로 찾은 아두이노를 통해 ESC를 캘리브레이션 하는 코드이다.

#include <Servo.h>

Servo esc_b; 
Servo esc_r; 

int black = 180; //모터로 들어가는 초기값은 180. 즉 최대값.
int red = 180;

void setup() {

  Serial.begin(9600);
  esc_b.attach(6, 1000, 2000); //내가 실수한 부분!!!
  esc_r.attach(7, 1000, 2000); 

  Serial.setTimeout(50); 
  esc_b.write(180);
  esc_r.write(180);
}

void loop() {

  esc_b.write(black); 
  esc_r.write(red);

  Serial.print(black);
  Serial.print('\t');

  Serial.print(red);
  Serial.println('\t');
}

void serialEvent()
{
  while (Serial.available()) {
    black = Serial.parseInt();
    red = Serial.parseInt();
  }
}



물론 이 코드를 그대로 쓰지는 않았다. 우선 다행스러워보이는 것은 BLDC 모터를 제어하는데도 Servo 라이브러리를
사용한다는 것이다. Servo라이브러리라면 작년에 4족보행 로봇 만들 때 지겹게 써오던 라이브러리가 아닌가?


하지만…난 Servo 라이브러리를 반쪽만 알고 있었다…


Servo 라이브러리 살펴보기


Servo 라이브러리는 PWM을 통해 모터를 제어하기 위한 라이브러리이다.



이 Servo 라이브러리의 출력함수에는 2가지가 있다. 바로 write()와 writeMicroseconds()이다.
두 함수 모두 int 값을 파라미터로 받지만 그 범위가 틀리다. 다음은 각 함수의 특징이다.


  1. write(int value) : value의 범위는 0 ~180이다. value는 각도를 의미한다. 90을 중심으로 90보다 작은 값과 90보다 큰 값은 서로 반대 방향으로 움직인다. 내부적으로 writeMicroseconds함수를 호출한다.
  2. writeMicroseconds(int value) : value의 일반적인 범위는 1000 ~ 2000이나 모터 제조사에 따라 700 ~ 2300까지도 설정 가능하다. 모터가 움직이지 않을 때까지 최댓값을 늘릴 수 있으나 모터가 움직이지 않음에도 지나치게 값을 올릴 경우 높은 전류가 흘러 위험하다.


볼드 처리한 문구에 주목을 해야 한다. 우선 write함수는 내부적으로 writeMicroseconds를 호출하게 되어있다.
하지만 두 함수는 각각의 범위가 있고 그 범위가 서로 다르다. 때문에 write함수에서 writeMicroseconds함수를
호출할 때는 map함수를 통해 write에서 사용하는 범위인 0 ~ 180의 값을 writeMicroseconds에서 사용하는
범위의 값으로 변환하여 호출을 한다. 그런데 이 때 단순히 0 ~ 180을 1000 ~ 2000으로 변환하지 않는다.
즉, map(value, 0, 180, 1000, 2000)으로 호출하지 않는다는 말이다. 바로 다음과 같이 호출한다.

value = map(value, 0, 180, SERVO_MIN(),  SERVO_MAX());


이것이 어떤 의미일까? 차근 차근 알아보자.
우선 Servo 라이브러리에는 MIN_PULSE_WIDTH와 MAX_PULSE_WIDTH라는 2개의 상수가 정의되어있다.
MIN_PULSE_WIDTH는 544라는 값을 가지고 있고 MAX_PULSE_WIDTH는 2400이라는 값을 가지고 있다.


또한 Servo 라이브러리에는 SERVO_MIN()와 SERVO_MAX()라는 매크로 함수가 정의되어 있는데 각각 다음과 
같다.

#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4)  // minimum value in uS for this servo
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4)  // maximum value in uS for this servo


마지막으로 우리가 Servo 라이브러리를 사용하기 위해서는 attach() 함수를 통해 모터가 연결된 PIN을 이용하게 
되는데 이 attach 함수는 2개의 함수가 오버로딩 되어있다. 다음과 같다.

uint8_t Servo::attach(int pin)
uint8_t Servo::attach(int pin, int min, int max)



그런데 이 처음의 파라미터 하나짜리 attach 함수도 내부적으로는 파라미터 3개짜리 attach 함수를 호출하도록 되어있다.
즉, uint8_t Servo::attach(int pin)함수의 전체 모습은 다음과 같다.

uint8_t Servo::attach(int pin)
{
  return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
}


그리고 파라미터 3개짜리 attach 함수에서는 두 번째와 세 번째 파라미터를 다음과 같이 this->min과 this->max에
할당한다.

this->min  = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
this->max  = (MAX_PULSE_WIDTH - max)/4;


위 내용을 참고할 수 있도록 Servo 라이브러리 소스 코드와 아두이노 공식 사이트의 레퍼런스 페이지를 링크한다.



Servo 라이브러리 소스코드 : http://bit.ly/2tZ4kGh
아두이노 레퍼런스 페이지 : http://bit.ly/1NjpZI0


나의 실수


앞서도 말했듯이 나는 검색을 통해 아두이노로 ESC 캘리브레이션을 하는 소스를 찾았고 그 것을 그대로 쓰지 않고
수정을 했다. 수정한 코드는 다음과 같다. 몰론 이 코드는 잘못된 코드다…-.-

#include <Servo.h>

Servo esc; 

int max = 180; //모터로 들어가는 초기값은 180. 즉 최대값.

void setup() {

  Serial.begin(9600);
  esc_b.attach(9); //내가 실수한 부분!!! attach 함수의 두 번째와 세 번째 파라미터의 의미를 몰랐다.

  Serial.setTimeout(50); 
  esc.write(180);
}

void loop() {

  esc.write(max); 

  Serial.print(max);
  Serial.print('\t');
}

void serialEvent()
{
  // 이 시점에서 시리얼 모니터를 통해 0을 입력한다.
  while (Serial.available()) {
    max = Serial.parseInt();
  }
}


만약 이 코드대로라면 한 번 계산을 해보자. attach(int pin) 함수를 사용한 경우 write함수에서 writeMicroseconds를 호출할 때 값이 어떻게 매핑될까? 순서대로 살펴보자.


  1. attach(9) 호출
  2. attach(9, 544, 2400) 호출
  3. this->min과 this->max에는 모두 0이 할당됨
  4. esc.write(180) 호출
  5. 내부적으로 val = map(180, 0, 180, 544, 2400)을 통해 값을 매핑한 후 writeMicroseconds(val) 호출


이렇게 되면 결국 최댓값은 2400이 설정되고 만일 모터가 최댓값 2000까지 설정된 모터라면 앞서 본 것과 같이
높은 전류가 흐르게 되어 위험한 상황이 될 수도 있는 것이다. 이후로도 유사한 맥락의 실수를 몇처례 더 한 후 결국
ESC 2개를 태워먹고 말았다.


드디어 모터를 돌리다!


결국 진행 단계에서는 확실하게 원인을 찾지 못하였고 2300kv급의 레이싱 드론용 모터로 테스트 하던 것을 1000kv
모터로 시도하여 성공하게 되었다. 아마도 1000kv 모터는 2400 이상의 신호에도 견디도록 제작된 것 같다.
하지만 역시 안전한 구동을 위해서는 파라미터 3개짜리 attach 함수를 이용하여 최솟값과 최댓값을 명시적으로 지정해
주는 것이 바람지할 것으로 생각된다.


아래 동영상은 모터 구동 성공 동영상이다. 




모터가 어찌나 세게 도는지 혼자서 날아가는 줄 알았다…-.-
프로펠러 붙이면 어찌될지 자못 기대가 된다.


정리


결정적으로는 모터의 허용 값을 몰랐던 것이 원인이겠으나 역시나 Servo 라이브러리의 함수들을 좀 더 제대로
알고 썼더라면 조금 더 일찍 성공에 다다르지 않았을까 하는 생각이 든다.

시작이 반이라고…이제 모터 돌렸으니 프로펠러만 달면 붕붕 날아다닐까?
다음 포스팅에서는 진짜 그런지 한 번 확인해보도록 하겠다^^






저작자 표시
신고

+ Recent posts

티스토리 툴바