Data Analysis : Prologue - EMQ + Kafka + openTSDB + Grafana


사실 이 포스팅의 제목을 어떻게 정해야 할지 고민이 많았다. 일단 클러스터 설치는 마쳤으니 당연히 이전 포스팅의
카테고리에서는 벗어나야 할 것이고, 실제 데이터를 다루기 시작하였으니 데이터 분석의 시작은 시작인데…
수집되는 데이터 자체가 딱히 분석할만한 데이터는 아니고…암튼 조금은 모호한 지점에 있는 작업이 되어버렸다.
더군다나 본격적인 데이터 분석은 아직 한참 더 공부한 이후 시작될 터인데…


하지만 역시나 데이터를 다루기 시작했으니 데이터 관련 제목을 붙여야 하겠다고 결정을 했다. Prologue라는 부제와
함께. 이후 The Beginning을 시작하게 되겠지만 아마도 시간이 조금 걸리지 않을까 싶다. 오늘은 가볍게나마
이전에 클러스터 구성 내용을 정리할 때 누락된 openTSDB와 Grafana 설치에 대한 내용과 EMQ -> Kafka ->
openTSDB -> Grafana로 이어지는 데이터 수집/저장/시각화 과정을 정리해보도록 하겠다.


시나리오


직전 포스팅(아두이노 온습도계 제작)에서도 언급했듯이 좀 더 그럴싸한 데이터를 모아 분석을 해보고 싶었지만
막상 작업을 시작하고 보니 마땅히 모을 수 있는 데이터가 없었다. 그렇다고 데이터를 찾아 다닐수만은 없기에
우선 쉽게 접근할 수 있는 것으로부터 시작해보기로 했다. 시나리오는 다음과 같다.


  1. 아두이노로 제작된 온습도 및 먼지 센서를 통해 온도,습도,먼지 농도,시간 데이터 수집
  2. MQTT 프로토콜을 통해 EMQ 서버로 데이터 전송
  3. EMQ 서버로 publishing 된 데이터를 Kafka에서 subscribing
  4. Kafka에 들어온 데이터를 openTSDB에 저장
  5. openTSDB에 저장된 데이터를 Grafana로 시각화
  6. Spark를 이용하여 데이터 분석 (이상 감지)


EMQ를 통해 M2M 통신을 하는 상황이거나 데이터 누락을 어느 정도 허용하는 작업이라면 굳이 Kafka가 필요하진
않을 것이다. 하지만 일단 데이터를 수집하고 분석하는 작업을 전제로 하였기에 누락되는 데이터를 Kafka에서 1차로
완충하도록 구성하였다. 최종 분석단계에 해당하는 6번 Spark를 이용한 데이터 분석은 아직 좀 더 학습이 진행되어야
할 부분이다.


openTSDB 설치 및 실행


최근 시계열 데이터를 처리하는 NoSQL로는 influxDB가 대세인듯하다. 다만 openTSDB가 HBase를 기반으로
작동한다는 한 가지 이유만으로 나는 openTSDB를 선택하였다. 설치 및 연동 과정에서 후회를 많이 했지만 결국
불굴의 의지로 제대로 연동 시키고야 말았다…ㅠ.ㅠ influxDB가 대세라는 것은 구글링만 해봐도 충분하다. 이놈에
openTSDB는 검색을 해도 설치 외에는 쓸만한 정보가 없더라…-.-


간단하게 openTSDB의 개요를 말하자면 TSD라는 데몬을 통해 외부의 시계열 데이터를 HBase에 생성된 테이블에
저장하는 구조이다.




설치 시 유의사항

현재 최신 릴리즈는 2.3.0 이며 pre-release가 2.4.0 RC2까지 나와있다. 아래 설명할 kafka와의 연동을
위한 opentsdb-rpc-kafka라는 plugin을 사용하기 위해서는 반드시 2.4.0 RC2 버전을 설치해야 한다.
나는 이 사실을 모르고 2일간 삽질을 했다…ㅠ.ㅠ 


설치는 역시 간단하다. 압축 파일을 다운로드 받아 적당한 디렉토리(나는 당근 /opt)에서 압축을 풀고 압축을 푼
디렉토리 내에 있는 build.sh를 실행시키는 것만으로 설치는 끝이다. 


path


다만 앞서 말한 것처럼 openTSDB는 HBase 기반으로 작동을 하기 때문에 HBase가 이미 설치 되어있다는 것을 
전제로 한다. 나는 이미 이전에 클러스터를 구성하였기에 바로 설치에 들어갔다.


설치가 끝나면 build.sh 파일이 있는 그 경로에 build라는 경로가 추가되고 그 안에 openTSDB 실행 파일 등이
위치하게 된다. 설치가 끝나면 가장 먼저 테이블을 생성해주어야 한다. 테이블 생성 스크립트는 build/src 아래에
있다. 아래와 같이 실행해준다.


$ cd /opt/opentsdb/build
$ env COMPRESSION=NONE HBASE_HOME=/opt/hbase ./src/create_table.sh


이렇게 실행하면 ‘tsdb’, ‘tsdb-uid’, ‘tsdb-tree’, ‘tsdb-meta’ 이렇게 4개의 테이블이 HBase 상에 생성된다.


HBase

그리고 마지막으로 설정을 한다. 설정파일은 최초에 압축을 푼 디렉토리 밑에 있는 src 디렉토리에 있다.
나의 경우에는 /opt/opentsdb/src가 그 경로이며 파일 이름은 opentsdb.conf이다. 이 파일을 build로
복사한다. 즉, /opt/opentsdb/build로 복사를 하는 것이다. 수정해야 할 설정 내용은 간단하다.


# The TCP port TSD should use for communications
# *** REQUIRED ***
tsd.network.port = 4242
...
# The location of static files for the HTTP GUI interface.
# *** REQUIRED ***
tsd.http.staticroot = /opt/opentsdb/build/staticroot

# Where TSD should write it's cache files to
# *** REQUIRED ***
tsd.http.cachedir = /tmp/opentsdb
...
# Whether or not to automatically create UIDs for new metric types, default
# is False
tsd.core.auto_create_metrics = true


여기까지 모두 마쳤으면 /opt/opentsdb/build아래 있는 tsdb를 다음과 같이 실행해준다.


$ /opt/opentsdb/build/tsdb tsd


실행한 콘솔창에 로그가 올라가는 것이 보일 것이고 localhost:4242로 접속을 하면 아래 이미지와 같은 콘솔
화면을 볼 수 있다.


openTSDB


센서 데이터의 포맷 변경


기존에 아두이노 온습도계에서 EMQ 서버로 전송하던 데이터는 포맷을 좀 바꾸어야 한다. openTSDB에 저장하기
위한 데이터 포맷은 JSON 형식으로 모두 3가지 유형이 있다.


  • Metric
  • Aggregate
  • Histogram


자세한 설명은 생략하고…-.- 나는 가장 기본적인 Metric 형태로 데이터를 저장하기로 했다. Metric 형태로 저장할
경우 JSON 포맷은 다음과 같다(여러건을 한번에 보내려면 아래 형식을 배열 안에 넣으면 된다).


{
    "metric": "sys.cpu.nice",
    "timestamp": 1346846400,
    "value": 18,
    "tags": {
       "host": "web01",
       "dc": "lga"
    }
}


metric은 전체 측정 단위를, timestamp는 이름 그대로 long 타입의 타임스탬프를 value는 측정 값을, tags는
세부 분류를 위한 키-값 쌍을 추가하면 된다. 나의 예로 좀더 자세하게 살펴보자.


[
   {
      "type": "Metric",
      "metric": "mqtt.home.pcroom",
      "timestamp": 1346846400,
      "value": 22.5,
      "tags": {
         "type": "temperature",
         "loc": "pcroom"
      }
   },
   {
      "type": "Metric",
      "metric": "mqtt.home.pcroom",
      "timestamp": 1346846400,
      "value": 18.2,
      "tags": {
         "type": "humidity",
         "loc": "pcroom"
      }
   },
   {
      "type": "Metric",
      "metric": "mqtt.home.pcroom",
      "timestamp": 1346846400,
      "value": 22.3,
      "tags": {
         "type": "dust",
         "loc": "pcroom"
      }
  }
]


위 내용을 살펴보면 우선 같은 시점에 측정된 온도/습도/먼지의 측정값을 배열로 넘기고 있다. 이 데이터를
만드는 것은 아두이노에서 처리하며 관련 내용은 아래 링크의 소스코드 부분을 참조하면 된다.


아두이노를 이용한 온도/습도/먼지 측정기


최상위에 있는 type 값은 openTSDB 자체를 위한 것이 아니라 opentsdb-rpc-kafka plugin에서 필요한
것이다. 3가지 유형 중 하나를 지정하기 위한 속성이다.



metric의 값은 임으로 정하면 되는 것으로 나의 경우 그 의미는 mqtt를 이용하여 home에 있는 컴퓨터방
pcroom의 측정값이라는 뜻이다. 


timestamp와 value는 달리 설명할 부분이 없고 tags에 보면 type에 각각 temperaturehumiditydust
라는 값이 들어있는데 의미 그대로 온도와 습도와 먼지를 뜻한다.


이렇게 3개의 값을 하나의 배열에 포함시켜 보내는 것이다. tags에서 loc는 큰 의미는 없다. 필요에 따라 추가적인
값을 지정하면 된다.


Kafka와 openTSDB 연동


openTSDB 자체에 대한 자료 혹은 openTSDB와 Grafana 연동에 대한 자료는 그럭저럭 찾을 수 있었는데
Kafka와 openTSDB와의 연동에 대한 자료는 거의 찾을 수가 없었다. 구글링을 하면 가장 상위에 검색되는
내용이 2개인데 하나는 opentsdb-rpc-kafka라는 openTSDB의 plugin이고 다른 하나는 
kafka-connect-opentsdb라는 Kafka connector이다. 뭔가 사용하기에는 connector쪽이 쉬워보였으나
openTSDB의 HTTP API를 이용한다는 점이 맘에 들지 않았다. 그래서 opentsdb-rpc-kafka를 선택하게
되었고 그렇게 고난의 길은 시작되었다…ㅠ.ㅠ


일단 opentsdb-rpc-kafka는 Github에서 소스를 내려받은 후 빌드를 하여 jar 파일을 만들어야 한다.
소스는 Maven 프로젝트로 되어있어 이클립스에서 빌드하거나 또는 프로젝트 디렉토리 아래에서 man package
명령으로 빌드하면 된다.


플러그인 소스 경로는 다음과 같다.

https://github.com/OpenTSDB/opentsdb-rpc-kafka


이렇게 빌드된 jar 라이브러리를 /opt/opentsdb/build 아래에 plugins 디렉토리를 만들어 그 안에 넣는다.
plugin 라이브러리의 최종 위치는 다음과 같다. 그리고 이 plugin의 경로는 반드시 classpath에 추가시켜주어야 한다.


$ /opt/opentsdb/build/plugins/opentsdb-rpc-kafka-2.3.2-SNAPSHOT.jar


그리고 opentsdb.conf 설정 파일에 plugin과 관련된 내용을 추가해주어야 하는데 아래와 같은 내용을 설정 파일의
제일 하단에 추가해주면 된다. 물론 각 설정 값은 각자의 환경에 맞게 입력해야 한다. 고정이라고 주석을 달아놓은
설정 외에는 각자 환경에 맞춰 수정을 하자.


# --------- PLUGINS ---------------------------------
tsd.core.plugin_path = /opt/opentsdb/build/plugins
## 고정
tsd.core.storage_exception_handler.enable = true
## 고정
tsd.core.storage_exception_handler.plugin = net.opentsdb.tsd.KafkaStorageExceptionHandler
## 고정
tsd.rpc.plugins = net.opentsdb.tsd.KafkaRpcPlugin
KafkaRpcPlugin.kafka.zookeeper.connect = rpi1:2181,rpi2:2181,rpi3:2181
KafkaRpcPlugin.kafka.metadata.broker.list = rpi1:9092,rpi2:9092,rpi3:9092
KafkaRpcPlugin.groups = mqtt
KafkaRpcPlugin.mqtt.topics = mqtt-kafka
## 고정
KafkaRpcPlugin.mqtt.consumerType = raw
## 고정
KafkaRpcPlugin.mqtt.deserializer = net.opentsdb.data.deserializers.JSONDeserializer
#KafkaRpcPlugin.mqtt.rate = 5
KafkaRpcPlugin.mqtt.threads = 3
## 고정
KafkaRpcPlugin.seh.topic.default = seh


앞서 말한 바와 같이 이 플러그인을 사용하기 위해서는 openTSDB 2.4.0 RC2 버전을 설치해야 한다.
한동안 자바 개발을 등한시 했더니 아주 사소한 것을 파악하지 못해 삽질을 따따블로 했다. 분명 빌드는 잘 되는데
plugin 설치하고 openTSDB를 기동하면 쌩뚱맞게 속성에 접근하지 못한다느니, 메소드가 존재하지 않는다느니
하는 메시지가 나오면서 plugin이 제대로 작동하지 않는 것이다.


나중에 확인해보니 소스에 포함된 openTSDB 라이브러리는 2.4.0 버전이고 내가 설치한 openTSDB는 2.3.0
이었다. 전체적인 패키지 구조는 같지만 클래스 내에 변화가 있었기 때문에 속성이나 메소드 사용에 문제가
있었던 것이다. 확인 후 부랴부랴 2.4.0 RC2 버전으로 다시 설치를 하였다.


이제 다시 한 번 openTSDB를 실행해보자(이미 실행중이라면 실행 중인 콘솔에서 ctrl+c를 눌러 종료시키자).
드디어 Kafka에서 데이터를 쭈~욱 쭉 뽑아오는 모습을 볼 수 있을 것이다.

 

data


이 데이터는 이전에 클러스터 관련 포스팅 중 EMQ와 Kafka의 연동을 통해 가져온 데이터들이다. 관련 내용은 아래 
링크에서 확인할 수 있다.


Cluster : The Beginning - Apache Kafka와 EMQ 연동

주의 사항

EMQ에서 Kafka로 전송된 데이터의 포맷은 형태가 다르다. 실제로 openTSDB에 저장하기 위한 내용은
payload 부분에 들어있는데 이 payload의 값은 Base64 인코딩이 되어 저장된다. kafka와 EMQ 연동을
위해 사용한 connector에서 그렇게 처리를 하고 있다. 따라서 opentsdb-rpc-kafka plugin에서 데이터를
제대로 처리하기 위해서는 Kafka에서 받아온 값에서 payload의 값만을 추출한 뒤 이 값을 Base64 디코딩
하여 전달해야 한다. 


이 부분을 처리해야 하는 소스는 net.opentsdb.tsd.KafkaRpcPluginThread.java의 run 함수이다.


이렇게 모든 과정이 끝나면 앞서 localhost:4242로 접근했던 웹 콘솔을 통해 쿼리가 가능하다. 그런데 어쩐 일인지
나의 경우 오류가 발생하면서 데이터 조회가 되지 않았다. 콘솔창에 찍히는 URL이 조금 이상해 보이긴 한데 그것이
원인인지는 잘 모르겠다. 결국 openTSDB 자체 웹 화면에서 데이터를 조회하는 것은 성공하지 못했다.


openTSDB


Grafana 설치 및 설정


적어도 설치에 있어서는 지금껏 설치한 모든 시스템 중 Grafana가 가장 친절하였다.
운영체제별로 다운로드 버튼이 있고 이 버튼을 누르면 해당 운영체제에 설치하는 방법이 나온다.
나는 현재 Mac mini에 설치를 하고 있기 때문에 Mac에 대한 설치 방법을 제공해 주는 것이 얼마나
반가웠는지 모른다…ㅠ.ㅠ 설치는 매우 간단하다.


brew update 
brew install grafana


이렇게 설치를 하고나면 다음과 같은 내용이 콘솔 화면에 표시된다.


To have launchd start grafana now and restart at login:
  brew services start grafana
Or, if you don't want/need a background service you can just run:
  grafana-server —config=/usr/local/etc/grafana/grafana.ini —homepath /usr/local/share/grafana cfg:default.paths.logs=/usr/local/var/log/grafana cfg:default.paths.data=/usr/local/var/lib/grafana cfg:default.paths.plugins=/usr/local/var/lib/grafana/plugins


친절하게 Grafana의 실행 방법을 알려주는 것이다. 나는 그냥 간단하게 brew를 통해 실행을 하였다.
설정파일은 상당히 긴 편인데 따로 변경해주어야 할 것은 아무것도 없다. 웹 화면의 포트를 바꾸는 정도?


openTSDB와의 연동


설정을 바꾸지 않았다면 localhost:3000으로 Grafana 웹 콘솔에 접근할 수 있다. 처음 접속하면 계정을 물어보는데
기본 계정은 admin / admin이다. 접속을 해서 가장 먼저 수행해야 하는 작업은 Datasource를 연결하는 것이다.
Grafana는 다양한 DB와 연동 가능한데 type 항목에서 select box를 클릭하면 다음과 같이 목록이 표시된다.



나머지 설정들은 자료도 많고 하니 참고해서 입력하면 되는데 나같은 경우 HTTP settings에서 Access를 Direct로
설정하니 데이터 소스에 연결이 되지 않았다. Access를 proxy로 설정하고 HTTP Auth를 With Credentials로
설정하니 비로소 데이터소스 연결에 성공했다는 메시지가 표시되었다. 이 과정에서도 왜 연결이 안되는지 Grafana의
소스까지 까뒤집고 난리를 치면서 상당한 시간을 보냈다…ㅠ.ㅠ 전체적인 설정은 다음과 같다(OpenTSDB settings
의 Version은 현재 2.3까지만 선택 가능한데 2.4.0 RC2를 설치한 경우에도 2.3을 선택하면 된다).


Grafana


Datasource를 성공적으로 연결했다면 이제 대시보드를 만들면 된다. 좌측 메인 메뉴에서 Dashbord에 마우스를
올리면 나타나는 서브 메뉴에서 + New를 선택하여 새로운 대시보드를 만드는데 처음 나타나는 화면은 대시보드에
표현할 유형을 선택하는 화면이다. 나머지는 차차 알아보고 나는 우선 맨 앞의 Graph를 선택했다.


Grafana


Graph를 선택하면 아래 이미지와 같이 비어있는 Graph panel이 덩그러니 하나 나온다. 여기서 상단의 Panel Title을
클릭하면 몇가지 메뉴가 나오는데 Edit를 선택하자


Grafana


Edit를 선택하면 Graph panel 하단으로 Graph 조건을 입력하는 화면이 나온다. 역시 다른 항목들은 차차
알아보기로 하고 중요한 몇가지만 살펴보자.


먼저 General 항목으로 가서 Graph 이름을 지정해준다. 그러면 Graph 화면 상단에 있던 Panel Title이라는
문구가 지정해준 이름으로 바뀐다. 일단 TEST라고 입력해 보았다.


Grafana


다음 가장 중요한 Metric 설정이다. 역시 많은 항목이 있지만 꼭 필요한 2가지만 설정해보자.
먼저 Metric 값을 입력한다. 기억하시겠지만 openTSDB로 넘기는 JSON 문자열에 metric이라는 키가 있는데
바로 그 값을 넣어주면 된다. 나는 mqtt.home.pcroom이었다. Metric 값만 입력해도 벌써 챠트가 그려지는데
나의 경우 하나의 Metric에 온도/습도/먼지 3가지 데이터가 들어있었다. 


이 시점에 나타나는 그래프는 설정화면의 Metric 옆에 보이는 Aggregator가 sum으로 되어있기 때문에 
온도 + 습도 + 먼지의 값으로 그려진 그래프다. 한마디로 쓰레기다…-.- 물론 이런 값이 필요한 경우도 분명 있을 
것이다. 각각의 데이터로 그래프를 그리기 위해서는 tags에 지정한 구분값을 추가로 입력해주어야 한다. 


아래 그림과 같이 내가 tags에 지정한 type키와 그에 대한 값인 temperature를 입력해주었다. 입력 후 우측에 
있는 add tag 버튼을 눌러야 반영된다.


Grafana

Grafana


이제 온도에 대한 데이터만 Graph에 표시된다.
그렇다면 하나의 Graph에 여러 데이터를 표시하려면 어떻게 하면 될까?
Edit 영역 하단에 보면 Add Query라는 파란색 버튼이 있다. 이 버튼을 클릭하면 동일한 입력 폼이 하나 더 생기며
이렇게 새로 생긴 입력 폼에 tags를 구분해서 입력해주면 된다.


Grafana


최종 화면은 요렇게 보인다.


Grafana


정리


이거 은근히 내용이 길어져버렸다. 달리 생각하면 이 작업에 그만큼 많은 시행착오와 노력이 들어갔다는 말이 되겠지만
최근에는 시계열 데이터도 Elasticsearch와 Kibana를 이용하여 처리하는 것이 대세인 것 같아 내가 이러려고
openTSDB를 선택했던가 하는 자괴감이 든다는 철지난 개그가 절로 나오게 되었다…ㅠ.ㅠ 아무래도 본격적으로
데이터 처리를 하게 되면 바로 이 Elasticsearch + Kibana 조합으로 가지 않을까 싶다.


그래도 클러스터 구성에서부터, 작은 데이터를 가지고 클러스터의 일부분만 사용하는 작업이긴 하지만 실제 데이터를
다루어보았다는 점에서 상당한 만족감을 느낀다.


원래 최종 목표는 이렇게 수집한 데이터를 인공지능으로 분석하는 것이었다. 그리고 그 내용에는 트위터의 데이터를
수집하여 RNN 분석을 하는 것도 있었는데 앞으로의 계획이 너무 거창해져서 일단 그 부분은 보류를 해야 할 것 같다.


앞으로의 계획은 이렇다.

  1. 아두이노와 메카넘휠을 이용한 차량형 로봇 제작, 수집 대상은 모터 회전수, 초음파 센서를 이용한 장애물과의 거리 데이터, 충돌 센서를 이용하여 장애물과의 충돌이 발생했을 때의 false 정보, 바퀴의 둘레와 모터 회전수를 조합한 이동 거리 정보, 기타 부가 정보로 카메라를 이용한 영상 정보 등이다.
  2. 정보의 활용은 2가지이다. DQN을 이용한 강화학습을 통해 낮은 수준의 자율주행 구현이 그 하나이고 gazebo simulator를 이용한 Digital Twin을 구현하는 것이 다른 한가지다.


무식하면 용감하다고…그래도 그간 조금씩 습득해온 지식으로는 뭔가 어렵지 않게 될 것 같기도 한데 현실은 
어떨지…-.-


이제 긴 여정을 위해 잠시 학습하는 시간을 갖고 이후 블로그 포스팅은 주로 학습 내용을 정리하는 수준이 될 것 같다.
그럼 다음에…

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^




아두이노를 이용한 온도/습도/먼지 측정기


일단 EMQ + Kafka + Hadoop + HBase + Spark의 구성으로 클러스터 구성 작업은 마무리가 되었다.
그리고 현재 시점에 추후 정리하겠지만 센서 데이터 처리를 위한 openTSDB와 시각화를 위한 Grafana까지
추가로 설치를 완료한 상태이다.


구슬이 서말이라도 꿰어야 보배라고 아무리 클러스터를 구성하고 각종 시스템을 그럴듯하게 설치를 해놔봐야
전기 먹는 하마 밖에 더 되겠는가? 각 시스템의 용도에 맞는 처리를 해보고 그 흐름을 알아야 진정한 목표를 이루는
것일 터다. 게다가 개발자의 역할을 갖고 있는 나로서는 아마 백날가도 운영을 위한 클러스터에 하둡을 설치할
기회는 없을 것이다. 이렇게 보면 진정한 목표는 이러한 빅데이터 생태계에서 개발자로서의 역할을 활용할 지점을
찾는 것이라 보아야 한다.


어쨌든 기본적으로 필요한 것은 데이터이고 그래서 우선 간단하게 아두이노와 온습도 센서, 먼지 센서 등을 이용하여 
센서데이터를 수집해보기로 했다. 하지만 전혀 간단하지 않았다…ㅠ.ㅠ 오늘은 그 힘든 여정을 살펴보자


흔하디 흔한…하지만 유니크한…


사실 내가 하는 작업의 대다수는 거의 4~ 5년 전에 이미 누군가 시도했던 일들이다. 앞서 진행한 클러스터 구성도
그렇지만 아두이노를 이용하여 온도/습도/먼지를 측정하고 이를 데이터로 저장한 후 챠트를 이용하여 시각화 해서
보여주는 작업은 웬만큼 아두이노를 아는 사람들이라면 이미 오래 전에 해보았던 작업들이다.


그런데…남들 보다 센서 하나 더 붙인 것이 이렇게 특이한 상황을 만들어낼 줄은 꿈에도 몰랐다…ㅠ.ㅠ


일단 구성품을 보자.


아두이노 MEGA 2560

아두이노 메가 2560


ESP8266-01 Wi-Fi 모듈

ESP8266-01


DHT22 온습도 센서

DHT22


GP2Y1010 미세먼지 센서

GP2Y1010


DS3231 RTC 모듈

DS3231


네오픽셀 LED 모듈

네오픽셀 LED


LCD 모듈 (Nokia 5110)

Nokia 5110 LCD


몇가지 부품들은 설명이 좀 필요할 것 같은데…우선 RTC 모듈은 시간의 흐름에 따른 데이터를 저장해야 하기에
시간 정보가 필요하였고 부가적으로 화면에 시간과 날짜를 표시하기 위해 추가하였다. 다음으로 네오픽셀 LED
모듈의 경우 먼지 센서의 측정값에 따라 직관적으로 농도를 표시하기 위해 LED가 필요했는데 일반 LED로 4가지
색상을 표시하거나 RGB LED를 사용하기에는 아두이노 나노에 남는 입출력 핀이 부족하여 조금 비싸지만 네오픽셀
LED를 사용하게 되었다.


주의사항


혹시라도 그럴리는 없겠지만… 이 글을 보면서 바로 작업을 진행하는 분들이 계실지 몰라 주의해야 할 사항을 먼저 
언급해 두어야겠다.


처음 작업을 시작할 때에는 완성품의 부피를 줄이고자 아두이노 나노로 시작을 하였다. 핀수가 적긴 하지만 그래도
위에 언급한 부품들을 꼭 맞게 연결할 수 있을 정도는 되었다. 그러나 다른 곳에서 문제가 생겼다. 많은 센서들을
연결하다보니 사용하는 라이브러리 수가 늘어났고 또 Wi-Fi 모듈을 통해 MQTT 통신을 해야 하다보니 문자열
코딩이 많아졌다.


사실 아두이노로 그래도 적지 않은 작업을 했지만 역시 문돌이는 문돌이라 여전히 모르는 것 천지다보니 처음에는
완성된 온습도계가 자꾸 리셋되는 이유를 몰랐다. 이곳 저곳을 검색한 후 아두이노의 메모리 문제라는 것으로 판단하고 
그 해결책으로 pgmspace를 include하여 문자열에 대해 F함수 처리를 하였지만(F(“string”)) 그것으로는 역부족이었다. 


결국 아두이노를 나노에서 메가로 교체를 하였다(사실 아두이노의 스펙을 잘 몰라 중간에 우노도 한 번 사용을 했다).
이렇게 교체되는 과정에서 아두이노 사이즈가 커지는 바람에 나노나 우노에 맞춰 만든 외장 케이스가 못쓰게 되어버렸다.
열심히 드릴질까지 해가면서 야심차게 만들었는데…ㅠ.ㅠ


참고로 아두이노에는 3가지 영역의 메모리가 있으며 그 중에 개발자들이 활용할 수 있는 영역은 SRAM이다.
아두이노 종류에 따른 메모리 용량은 아래 표와 같다(나노는 우노와 동일하다).



소프트웨어적으로는 MQTT 통신을 위한 PubSubClient 라이브러리에서 주의를 좀 해야 한다.
이 라이브러리를 초기화 하기 위해서는 Wi-Fi 객체가 Client 타입이어야 하며 이를 지원하는 라이브러리가
몇가지 있는데 나는 그 중에 WiFiEsp를 사용하였으며 이러한 라이브러리를 사용하기 위해서는 ESP8266 모듈의
펌웨어도 AT25-SDK112 firmware라는 펌웨어를 사용해야 했다.


결론은…많은 센서를 사용하게 된다면 입출력 핀 문제 뿐만 아니라 메모리 문제 때문에라도 아두이노 메가를 사용하는
것이 안전하다는 점이다.


아두이노와 센서들의 연결


워낙에 많은 센서들을 부착하다보니 전체 배선도를 표시하는 것은 그리기도 또 읽기도 쉽지 않을 것 같아
각각의 센서 연결도를 따로따로 보여주도록 하겠다.


ESP8266-01


DHT22


GP2Y1010


DS3231 RTC


네오픽셀 LED


Nokia 5110 LCD


코드 작성


전체 코드가 조금 길기는 하지만 우선 주석과 함께 모두 올려본다.


//  Wi-Fi 모듈을 PubSubClient와 함께 사용하기 위해 선택한 ESP8266용 라이브러리
#include <WiFiEsp.h>
#include <WiFiEspClient.h>

// MQTT 통신을 위한 라이브러리
#include <PubSubClient.h>

// DHT22 온습도 센서 라이브러리
#include <DHT.h>

// DS3231 RTC 모듈 라이브러리
#include <DS3231.h>

// Nokia5110 LCD 사용을 위한 라이브러리
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>

// 네오픽셀 LED 사용을 위한 라이브러리
#include <Adafruit_NeoPixel.h>

// 메모리 관리를 위한 F함수 또는 PROGMEM을 사용하기 위한 헤더파일. 아두이노 메가를 사용하는
// 시점에서 큰 의미는 없어졌음
#include <avr/pgmspace.h>

// MQTT 서버 주소
IPAddress server(123, 123, 123, 123);
// 공유기 SSID와 비밀번호
char ssid[] = "ABCDEFG";
char pass[] = "password";

// Wi-Fi 모듈 상태를 표시하기 위한 변수
 int status = WL_IDLE_STATUS;

// WI-Fi 모듈 객체 선언
WiFiEspClient espClient;
// Wi-Fi 객체를 파라미터로 MQTT 통신을 위한 clietn 생성
PubSubClient client(espClient);

// DHT22 사용을 위한 데이터 핀 설정과 모델 타입 매크로 선언
#define DHTPIN    5
#define DHTTYPE   DHT22

// DHT22 사용을 위한 객체 생성
DHT dht(DHTPIN, DHTTYPE);

// GP2Y1010 측정 핀과 측정을 위한 내부 LED 연결 핀 설정
int measurePin = A0;
int ledPower = 6;
 
// 먼지 측정을 위해 필요한 각종 변수 선언 (자세한 내용은 타 사이트 참조)
int samplingTime = 280;
int deltaTime = 40;
int sleepTime = 9680;
 
float voMeasured = 0;
float calcVoltage = 0;
float dustDensity = 0;

float totSum = 0;
float totCnt = 1.0;
float prevD = 0.0;

// DS3231 RTC 모듈 객체 생성
DS3231  rtc(SDA, SCL);
// 날짜와 시간 표시를 위한 변수
char *dateStr;
char *prevDateStr = "";

// Nokia 5110 LCD 사용을 위한 객체 생성
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 8, 9, 10, 11);

// 네오픽셀 LED  사용을 위한 핀 번호 및 LED의 index 매크로 선언
#define PIN 4   
#define LEDNUM 1  
// 네오픽셀 LED 사용을 위한 객체 생성
Adafruit_NeoPixel strip = Adafruit_NeoPixel(LEDNUM, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  // 시리얼 초기화
  Serial.begin(9600);
  // Wi-Fi 모듈 사용을 위한 Serial1 초기화 
  Serial1.begin(9600);
  WiFi.init(&Serial1);

  // Wi-Fi 모듈 연결 여부 확인
  if (WiFi.status() == WL_NO_SHIELD) {
    while (true);
  }
  
  // AP에 접속
  while ( status != WL_CONNECTED) {
    status = WiFi.begin(ssid, pass);
  }

  // MQTT를 위한 서버 정보 설정 및 콜백함수 설정. 나는 MQTT publishing만 할 것이기에
  // 콜백 함수는 빈 함수로 남겨둠
  client.setServer(server, 1883);
  client.setCallback(callback);
  
  // 기타 설정들은 다름 함수로 뺌
  otherSetting();
  delay(2000);
}

void otherSetting() {
  // DS3231 RTC 모듈 초기화
  rtc.begin();

  // 시작 날짜와 시간을 설정하는 코드로 최초 사용시에만 필요함
//  rtc.setDate(25, 1, 2018);
//  rtc.setTime(23, 21, 45);
//  rtc.setDOW(4);
  
  // 네오픽셀 LED 초기화
  strip.begin();
  pinMode(ledPower,OUTPUT);

  // Nokia 5110 LCD 초기화
  display.begin();
  display.setContrast(65);
  display.setTextSize(1);
  display.setTextColor(BLACK);

  // DHT22 초기화
   dht.begin();
}

void callback(char* topic, byte* payload, unsigned int length) {
  //MQTT 통신에서 subscrib시 필요한 내용이나 나의 경우 publish만 하므로 그냥 비워둠
}

void loop() {
  // 메인 루프에서는 MQTT 통신이 끊어져있으면 연결을 하고 연결되어있으면 데이터를 모아서
  // MQTT 서버로 전송하는 작업을 진행함
  if (!client.connected()) {
    // 연결이 끊어져있으면 다시 연결 시도	
    reconnect();
  } else {
    // MQTT 서버에 연결되어있으면 데이터를 수집하여 MQTT 서버로 보냄
    getValueAndSendData();
    // 위 작업을 5초마다 한 번씩 수행
    delay(5000);
  }
}

void getValueAndSendData() {
  // 온도, 습도측정 값을 담을 변수를 선언하고 DHT22 센서로부터 측정값을 받아옴
  float temperature = dht.readTemperature(); 
  float humidity = dht.readHumidity();
  // 먼지 센서의 경우 조금 복잡한 계산이 필요하여 따로 함수 처리를 함
  float d = getdust();

    // 간혹 먼지 센서에서 노이즈값이 나오므로 이에 대한 처리를 해줌(값자기 100 가까운 값이
    // 나오기도 하나 음수에 대해서만 처리했음
    if (d < 0.0) {
      d = prevD;
    }

    prevD = d;

    // DS3231로부터 요일값을 받아옴
    dateStr = rtc.getDOWStr(FORMAT_SHORT);

    // 현재 먼지 센서의 측정 값을 순간값이 아닌 그 시점까지의 그날 평균으로 보여주고 있으며(1일 평균)
    // 날짜가 바뀌면 다시 처음부터 계산하도록 처리함
    if (prevDateStr == dateStr) {
      totSum += d;
      totCnt += 1.0;
    } else {
      totSum = d;
      totCnt = 1.0;
    }

    prevDateStr = dateStr;

    float avgD = totSum / totCnt;

    // 먼지 측정 값에 따라 네오픽셀 LED의 색상을 변경함
    if (avgD <= 30) {
      strip.setPixelColor(0, 0, 0, 255); 
    } else if (avgD <= 80) {
      strip.setPixelColor(0, 0, 255, 0); 
    } else if (avgD <= 150) {
      strip.setPixelColor(0, 255, 255, 0); 
    } else if (avgD >= 151) {
      strip.setPixelColor(0, 255, 0, 0); 
    }
    strip.show();

    // 센서 데이터들을 JSON 포맷으로 만들어 MQTT 서버로 전송함
    // 이 JSON 포맷은 시계열 데이터용 NoSQL인 openTSDB에 넣기 위한 포맷임
    // 주의할 것은 PubSubClient 라이브러리는 전송 가능한 패킷 사이즈가 128byte로 
    // 기본 설정되어 더 큰 크기의 패킷 전송을 위해서는 MQTT_MAX_PACKET_SIZE 값을
    // 변경해주어야 한다. 나는 넉넉하게 1024로 설정했다.
    char attributes[393];
    String dtStr = String(rtc.getUnixTime(rtc.getTime()));
    String payload = F("[");
          payload += F("{");
          payload += F("\"type\":\"Metric\"");
          payload += F(",");
          payload += F("\"metric\":\"mqtt.home.pcroom\"");
          payload += F(",");
          payload += F("\"timestamp\":"); payload += dtStr; 
          payload += F(",");
          payload += F("\"value\":"); payload += String(temperature); 
          payload += F(",");
          payload += F("\"tags\": {"); 
          payload += F("\"type\":\"temperature\",");
          payload += F("\"loc\":\"pcroom\"");
          payload += F("}},");
          
//    payload.toCharArray( attributes, 131 );
//    client.publish("/mqtt", attributes);
    //Serial.println(attributes);
    
          payload += F("{");
          payload += F("\"type\":\"Metric\"");
          payload += F(",");
          payload += F("\"metric\":\"mqtt.home.pcroom\"");
          payload += F(",");
          payload += F("\"timestamp\":"); payload += dtStr; 
          payload += F(",");
          payload += F("\"value\":"); payload += String(humidity); 
          payload += F(",");
          payload += F("\"tags\": {"); 
          payload += F("\"type\":\"humidity\",");
          payload += F("\"loc\":\"pcroom\"");
          payload += F("}},");

//    payload.toCharArray( attributes, 131 );
//    client.publish("/mqtt", attributes);
    //Serial.println(attributes);
    
          payload += F("{");
          payload += F("\"type\":\"Metric\"");
          payload += F(",");
          payload += F("\"metric\":\"mqtt.home.pcroom\"");
          payload += F(",");
          payload += F("\"timestamp\":"); payload += dtStr; 
          payload += F(",");
          payload += F("\"value\":"); payload += String(avgD); 
          payload += F(",");
          payload += F("\"tags\": {"); 
          payload += F("\"type\":\"dust\",");
          payload += F("\"loc\":\"pcroom\"");
          payload += F("}}");
          payload += F("]");
    
    // String 타입으로 만든 JSON 문자열을 byte 배열로 변경한 후 서버로 publishing
    // "/mqtt"는 토픽 이름
    payload.toCharArray( attributes, 393 );
    client.publish("/mqtt", attributes);

    //Serial.println(attributes);
    
    // 날짜, 시간, 요일, 온도, 습도, 먼지 등의 값을 LCD에 출력
    display.clearDisplay();   
    display.setCursor(0,0);
    display.print(rtc.getTimeStr(FORMAT_SHORT));
    display.print(F(" ["));
    display.print(dateStr);
    display.print(F("]\n"));
    display.println(rtc.getDateStr(FORMAT_LONG, FORMAT_BIGENDIAN, '-'));
    display.print(F("\nT: "));
    display.println(temperature, 1);
    display.print(F("H: "));
    display.println(humidity, 1);
    display.print(F("D: "));
    display.print(avgD, 1);
    display.display();
}

// MQTT 서버에 연결이 안되었을 시 재연결하는 함수
void reconnect() {
  while (!client.connected()) {
    // 아래 connect 함수를 통해 전달하는 3개의 문자열은 그냥 임의로 정해도 됨
    if (client.connect("ARDUINO", "mazdah", "abcdefg")) {
      Serial.println(F("connected"));
    } else {
      Serial.print(F("failed"));
      delay(5000);
    }
  }
}

// 먼지 측정을 위한 함수
// 고수들은 한번 측정된 값을 사용하지 않고 짧은 시간에 여러번 측정을 하여 그 평균을 사용함
float getdust(){
  digitalWrite(ledPower,LOW);
  delayMicroseconds(samplingTime);
  voMeasured = analogRead(measurePin);
  
  delayMicroseconds(deltaTime);
  digitalWrite(ledPower,HIGH);
  delayMicroseconds(sleepTime);
  
  calcVoltage = voMeasured * (5.0 / 1024);
  dustDensity = (0.17 * calcVoltage - 0.1) * 1000.0;
  return(dustDensity);
}


코드가 좀 길기는 하지만 어려운 코드는 없다. 다만 나의 경우 앞서 말한 것 처럼 메모리 용량이 적은 아두이노를 
사용했다가 원인불명(?)의 리셋으로 한참을 시간 낭비 했고 MQTT 라이브러리의 특성을 잘 몰라 또 한참 시간을 
낭비하게 되었다. MQTT 관련한 시간 낭비 중 하나는 코드에 주석처리한 전송 패킷 사이즈 문제였고 다른 한가지는
지속적으로 데이터를 송신하지 않으면 네트워크가 끊어지는 것이 당연한데 데이터를 보내지도 않으면서 자꾸
MQTT 네트워크가 끊어지는 것이 코드나 센서 이상이라고 생각해서 꽤나 고민했다. 결국은 내가 데이터를 보내기
시작하면서 이해를 하게 되었다.


결과물


처음 아두이노 나노와 우노를 이용해서 만들었을 때 그에 준하여 케이스를 만들었다. 예전에 샀던 블루투스
스피커의 아크릴 포장 케이스가 마침 딱 알맞은 크기였기에 거기에 공기를 통하게 하기 위한 구멍을 열심히
뚫어서 나름 예쁜 모양을 만들었는데…


메모리 문제로 아두이노 메가로 바꾼 후 더이상 아두이노와 센서들이 깔끔하게 케이스 안으로 들어가질 않는다.
결국 내장을 뽑아낸 채 이렇게 볼쌍 사나운 모습으로 열심히 데이터를 모으고 있다…ㅠ.ㅠ


사실 모듈 배치를 고려하지 않은 부분도 있었다. 아무래도 연결된 센서(특히 ESP8266)나 아두이노 자체에서도
발열이 있기에 아무리 구멍을 뚫었다 할지라도 좁은 공간에 함께 넣게 되면 온도 측정에 심각한 영향을 미치게
된다. 먼지 센서도 가급적이면 통풍이 잘되도록 신경을 써주어야 한다.


정리


시작할 때 생각했던 것보다 많이 어려웠고 또 시간도 많이 잡아먹었다.
하지만 그만큼 새로운 사실들도 많이 알게되어 결코 낭비만은 아니었다고 생각한다.


사실 애초에 온/습도 또는 먼지를 측정하여 처리하는 내용은 워낙 흔해서 수집할만한 다른 데이터가 없을지
많이 고민을 해보았다. 그런데 은근히 만만치가 않았다. 생각 외로 아두이노 센서 중에 데이터를 수집할만한
센서가 그리 많지 않았다. 어떤 데이터를 모을까 생각하며 시간을 축내기보다는 흔한 작업이지만 일단 데이터를
모아보자는 생각에 이와 같은 작업을 진행하게 되었다. 사실 중요한 것은 앞서 구성한 클러스터를 통해
정상적으로 데이터를 처리할 수 있는지 확인하는 것이 우선이었으니까…


다음 포스팅에서 정리하겠지만 우선 이렇게 만든 온습도계를 통해 데이터를 모아 MQTT 서버를 거치고 Apache
Kafka를 통해 openTSDB에 저장을 하고 Grafana를 통해 시각화 하는 부분까지는 정상적으로 된 것 같다
(하지만 세부적으로는 Kafka쪽에 더 살펴봐야 할 것이 남아있다). 맛보기로 현재 들어오고 있는 데이터에 대한
Grafana 챠트를 올린다.

grafana


일단 기본적인 작업을 성공하였기에 다음에는 조금 더 큰 작업을 해보려고 한다. 내 수준에서 가능할지는 모르겠지만
메카넘 휠을 사용한 차량형 로봇으로 모터의 회전 수와 회전 방향, 거리 센서를 통한 데이터, 충돌 센서 및 충돌 스위치 
센서를 통해 충돌시 실패 감지 등의 데이터를 수집하고 이를 인공지능으로 분석하여 낮은 수준의 자율주행차를 만들어
볼 계획이다. 내 지식 수준에서 바로 될 수는 없는 장기 목표라서 올해 말쯤에나 결과를 볼 수 있겠지만…^^;;;


이 작업이 성공하면 다음은 자율주행 드론에 도전!!!


암튼 하고 싶은 것이 널려서 참 행복한 한 해가 될 것 같다.
그래도 우선 다음 포스팅에서는 센서데이터 -> MQTT -> Kafka -> openTSDB -> Grafana로 이어지는 데이터
처리에 대해 알아보도록 하겠다.

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


아두이노 드론 만들기 #6 - MDF 프레임의 마지막…


사실 이번 글은 또다른 실패담이라서 쓰지 않으려고 했는데 손이 심심해서 쓴다…-.-
더이상 MDF로는 시도를 하지 않을 생각이었는데 혹시나 하는 생각에 한 번 더 도전을 했다.


하지만 아무래도 MDF 프레임을 이용한 시도는 여기서 마쳐야 할 것 같다.
물론 프레임만의 문제는 아니다. 그러나 프레임 외의 문제들이 MDF로 해결 할 수 없는 문제라는 것이
MDF의 비극적인 운명이다…ㅠ.ㅠ


다운사이징


일단 지난 번 실패담에 대해 많은 분들이 의견을 주셨다. 그중 가장 신빙성 있고 개선하기 쉬운 내용은 바로 기체에
비해 프로펠러가 너무 작다는 것이었다. 사실 지난 번 잠깐이라도 뜬 기체는 10인치 프로펠러였는데 이번 테스트에
사용하고 있는 프로펠러는 5인치 사이즈이고 실제 직경은 5인치 보다 작다(5인치면 대략 12.7cm인데 실제로는 
대략 11cm정도이다).


결국 기체의 사이즈를 줄이기로 했다. 우선 기체의 폭을 줄이기로 하였고 폭이 줄어들면 소재가 동일하니 당연히 무게도
줄어들게 될 것이다. 다시 한번 모눈종이에 열심히 도안을 하고 MDF 레이저 커팅 주문을 했다.



그렇게 만들어진 기체는 아래 이미지와 같다. 이전 기체와의 비교를 위해 두 기체를 겹쳐놓고 사진을 찍었다.
우선 기체의 폭은 직경을 기준으로 약 10cm정도 줄어들었다.



1차 테스트 결과


변경된 기체는 사이즈가 줄어든 것과 각종 기판과 배터리가 외벽 안쪽에 있던 것이 부피가 줄면서 외벽 밖으로 배치
되었다는 점, 외벽간의 공간을 테이프로 틀어막았다는 점 등이 달라진 부분이다.


하지만…


여전히 반응이 없었다. 역토크(프로펠러의 반작용)를 상쇄하기 위한 방향타가 지나치게 공기의 흐름에 저항을 일으키는
것 같아서 방향타를 모두 제거하니 겨우 기체가 제자리에서 뒤집히는 정도의 반응을 보였다.


그냥 끝내기는 뭔가 아쉬워 한 번 더 다운사이징을 감행했다.


2차 다운사이징


8각형 구조로 8군데로 배치한 외벽 중 4개를 제거했다. 아무래도 몸통과 외벽을 연결하는 지지대쪽에서도 공기 흐름에
저항이 발생하는 것 같았다. 결국 뭔가 모양은 비맞은 생쥐 마냥 뭔가 빈티가 나보이긴 하지만 무게는 대폭 줄어서 최초의
기체에 비해 120g 정도가 줄어들었다. 그리고 외벽쪽에 배치했던 배터리는 모터 위쪽으로 올렸다.



2차 테스트 결과


일단 작은 프로펠러에 대해 방향타가 주는 영향이 너무 큰 덕에 일단 방향타는 미리 제거하고 테스트를 하기로 했다.
물론 이럴 경우 기체는 프로펠러 반대 방향으로 신나게 돌 것이다….-.- 그래도 일단 떠주기만 하면 좋으련만…


처음 시험 비행을 위해 나갔더니 어디선가 접촉이 불량했는지 모터가 돌아가지 않았다. 다시 들어와 점검후 나갔다.
아두이노와 점퍼선을 이용해 기판들을 연결하다보니 이런 문제도 종종 생긴다.


그리고 드디어 테스트…



발레 로봇을 만들어야 하나…예상대로 몸통이 신나게 돈다. 그리고 안타깝게도 뜨지는 않는다…ㅠ.ㅠ


정리


애초에 예견했던 결과이긴 하지만 아쉬움은 남는다.
일단 지난 번 실패에서부터 오늘의 실패까지에서 몇가지 개선해야 할 부분을 정리해보자면 다음과 같다.


  1. 아두이노과 관련 기판의 부피를 최소화 할 것. 이를 위해 간단한 회로 설계와 많은 납땜 작업이 필요할 듯싶다.
  2. 유션형의 기체 설계. 프로펠러가 작은 경우 바람의 저항을 받는 요소에 매우 민감하다. 따라서 공기가 흐르는 곳은 최대한 유선형으로 설계가 되어야 한다.

*** 추가로 좀 어처구니 없는 착각을 한 것이 배터리 무게를 줄이고자 적은용량의 배터리 2개를 직렬로 연결해서 사용하였다.
그래서 배터리 무게에서는 약 10g 정도 이득을 봤는데...오늘 확인해보니 직렬 연결을 위해 만든 커넥터 무게가 무려 20g이었다...
뭐하자고 이 짓을 한 것인지...ㅠ.ㅠ




결국 이러한 문제 해결을 위해서는 3D 모델링도 익혀야 하고 간단하게나마 회로 설계도 공부를 좀 해야 할 것 같다.
우선 다음 포스팅은 3D 모델링을 통해 새로운 기체를 설계하는 내용이 될 것이다. 과연 쓸만한 기체가 만들어질지…






블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


아두이노 드론 만들기 #5 - 처절한 실패의 내멋대로 분석


연휴 10일…모든 연휴가 그렇듯 시작은 여유로웠으나 마지막에 남는 것은 연휴의 기분을 연장하기 위한 처절한 발악이다.
그래도 무리한 계획은 잡지 않았기에 - 오히려 너무 여유를 부렸다고 해야 할까 - 일단 목표한 바 까지는 진행을 했다.
2일에 한 번 꼴로 아이들과 함께 외출도 하고 작은아이 두발 자전거도 마스터해주고 그리고 나서도 드론을 조립하고
아두이노 스케치 코드도 수정하여 비행 테스트까지 진행을 하였다.


하지만 이 ‘비행’ 드론이 비행을 하지 않았다…-.-
또 한 번의 실패…
무식해서 용감한 것도 어느 정도지…점점 용기도 사그라드는 것 같다…ㅠ.ㅠ


오늘은 이 실패에 대한 간략한 자체분석이므로 특별한 내용은 없으니
남 뻘짓하는 것에 관심이 있지 않은 분들은 그냥 가던 길 가시는 것이 좋으리라…ㅠ.ㅠ


새로운 프레임으로 조립하기


일단 새로운 프레임의 컨셉은 이러했다.
드론 전용 Flight Controller(이하 FC)를 사용하지 않고 아두이노와 같은 범용 기판을 사용하는 경우의 핸디캡이라면 
일단 직접 프로그래밍을 해야 한다는 것 외에 FC의 역할을 하기 위해 별도의 기능들을 위한 추가 부품으로 인해 부피와
무게가 증가한다는 점이다. 더구나 프로펠러 하나짜리를 만들다보니 추가되는 부품들이 더 많아졌다.


하나 하나 따져보면, 메인 컨트롤러를 위한 아두이노 나노, 방향타를 컨트롤하기 위한 서보모터, 서보모터를 제어하기 
위한 서보모터 컨트롤러, 아두이노와 서보모터에 전원을 공급하기 위한 별도의 배터리 (ESC에 BEC 기능이 있더라도
서보모터 구동을 위해서는 별도의 배터리가 있어야 안정적일 것이다), 그리고 감압을 위한 별도의 기판까지…


이렇게 되면 사실 무게보다는 부피가 더 부담이 된다. 즉 이 부품들을 아무리 똘똘 뭉쳐 부피를 줄인다 해도 모터 밑으로
배치할 경우 프로펠러가 만들어내는 바람에 저항을 줄 수밖에 없다. 더군다나 이번에 새로 사용한 2205 2300kv급 
모터는 5인치 프로펠러를 사용하기 때문에 더더욱 부피에 민감할 수 밖에 없다.


새로운 프레임은 2205 2300kv급 모터를 염두에 두고 설계한 것이다보니 위와 같은 제약을 고려해서 각종 부품과 
배터리는 외부에 장착하는 형태가 되었다. 바로 아래 사진과 같이.



그리고 완전체의 무게는 다행히도 예상했던 것보다 많이 나가지는 않았다. 내가 사용한 SunnySky 2205 2300kv 
모터의 경우 데이터 시트 상 14.8V (4셀) 배터리 사용 시 최대 추력이 1kg에 달하는데 조립된 드론의 무게는 대략 
560g 정도였다. 무게 측면에서는 일단 안정권이라 볼 수 있었다.



무게의 균형을 위해서 배터리로 4셀 배터리 1개를 사용하지 않고 동일한 모델의 2셀 배터리 2개를 직렬로 연결해서
사용을 했다.


왜 날지를 못하니…ㅠ.ㅠ


사실 실패를 목격하기 전에는 기대 만빵이었다. 특별히 날지 못할 조건이 없었기에 지난 번 테스트 때처럼 삐딱하게라도
날아 오를 것으로 기대했었다.


그러나…


이틀간의 실험에서 이 무늬뿐인 드론은 단 1mm도 뜨지 않았고 2일째에는 최대 출력에서의 진동 때문인지 드디어 모터
지지대가 자리를 이탈하면서 프로펠러가 프레임 일부를 잘라먹고 3엽 프로펠러 날개도 한짝이 부러져버렸다. 
아래 이미지는 그 처참한 결과물이다.



여전히 무작정 내 멋대로 만들기나 할 뿐이지 지식적인 진전은 없다보니 도무지 이 드론이 왜 뜨질 않는지 납득이 가질 
않는다. 납득이…


다만 예전에 로봇을 만들 때 경험했던 torque의 개념을 응용해보자면 같은 무게의 물체를 들어올릴 경우 모터의 중심
축으로부터 들어올릴 물체의 거리가 멀어질수록 모터의 힘이 더 세야 한다는 논리, 역으로 말하자면 모터의 힘이 동일
하다면 들어올릴 물체가 모터의 중심 축에 가까울 수록 더 무거운 물체를 들어올릴 수 있다는 논리에 의해 대부분의
무게를 차지하는 배터리와 기판들을 드론의 바깥쪽에 배치한 것이 치명적인 실수가 아니었을까 추측을 해본다.


하지만 앞서 언급했듯이 5인치 프로펠러를 사용하는데 모터 밑으로 각종 부품들을 배치하다보면 그 부피가 커져 분명
바람의 저항이 커질 것이고 역시나 날지 못하는 슬픈 드론이 되고 말 딜레마에 빠져있는 것이다. 게다가 내가 프로펠러
하나짜리 드론을 만들고자 했던 이유 중 하나가 비교적 소형화된 기체를 만들고자 함이었는데 위와 같은 문제를 해결
하고자 프로펠러 사이즈를 키운다는 것(물론 모터도 그에 맞게 바꾸어야 할 것이고) 또한 또다른 딜레마가 되고 만다.


이래저래 새로운 방법을 찾아야 할 처지가 된 것이다.


니들이 Coanda 효과를 알아!


아…제목은 그저 ‘니들이 게 맛을 알어!’에 대한 아재스런 패러디일 뿐이다…-.-
유튜브를 검색해보면 이미 10년전에 이 coanda 효과를 이용한 드론을 날린 사람들이 수두룩하다.
그러니 지금 내가 이 이론을 거들먹 거리면서 말할 게제는 아니어도 한참 아니다.
바로 그 10년전 영상을 먼저 감상좀 해보자.

https://youtu.be/sdGVI7kJld0


Coanda 효과라는 것은 이런 것이다.

http://terms.naver.com/entry.nhn?docId=703245&cid=50320&categoryId=50320


분류가 건축용어사전이네…-.-
더 자세한 내용을 알고 싶으면 영어의 장벽을 넘으시라…ㅠ.ㅠ

https://en.wikipedia.org/wiki/Coandă_effect


동영상을 보면 coanda 효과를 이용한 드론은 프로펠러 하단에 마치 치마와 같은 곡면의 구조물을 갖추고 있다.
이는 프로펠러의 바람에 저항을 일으키는 것이 아니라 바람이 그 곡면을 따라 흐르면서 아래쪽으로 ‘다운워시’라고
하는 공기의 흐름을 만들어내고 이 힘으로 뜨게 되는 것이다. 이 치마 형태의 구조물에 부품을 넣게 되면 부피의 부담
없이 드론을 만들 수 있게 되는 것이다.


다만 프로펠러 사이즈와 치마 형태의 구조물이 어느정도의 크기 비율을 가져야 하는지, 즉 치마 형태의 구조물을 얼마나
작게 만들 수 있는지가 관건인데 이런 자료는 아무리 찾아봐도 보이지가 않는다…ㅠ.ㅠ 역시나 뻘짓과 삽질의 전도가
매우 양양하다…


정리


사실 내가 이번 실패의 원인을 제대로 분석하고 있는지도 잘 모르겠다. 적절한 프로펠러를 쓴 것인지, 배터리는 적절한
용량을 사용한 것인지, 프레임 설계상의 다른 문제는 없었는지…


어쨌든 큰돈 들여 설계하고 제작한 이 MDF 프레임은 띄우기가 쉽지 않아보인다…ㅠ.ㅠ


위에서는 Coanda 효과를 언급했지만 사실 Coanda 효과를 이용한 드론도 프레임 형태를 만들기가 쉽지 않은 터라
아마도 그에 앞서 지난 포스팅에 만들었던 형태의 드론을 먼저 만들게 될 것 같다. 물론 사이즈는 더 커질 것이고…
올해 안에 띄우는 게 목표인데 벌써 10월 중순에 접어들고 있으니 이거 계획대로 띄울 수 있을지 모르겠다…


암튼 도전은 계속 될 것이고 언젠가는 띄우고 말 것이다!
이상 눈물 없인 볼 수 없는 애련의 실패기를 마친다…ㅠ.ㅠ







블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


아두이노 드론 만들기 #4 - 프레임을 만들어보자


참으로 오랜만에 글을 쓴다.
마지막 포스팅이 8월 15일이었으니 한달을 넘긴 것이다.
사실 휴가를 다녀오면 부지런히 이것저것 하려고 마음 먹었는데 뭔가 마음도 싱숭생숭하고 이래저래 일도 많고…
그러다보니 드론에 손을 댈 겨를이 없었다.


드론과 관련된 마지막 작업은 드론 프레임을 만들기 위한 작업이었다.
아무리 내가 세심한 주의를 기울인다 하더라도 수작업으로 만드는 것에는 한계가 있었다.
정확한 치수를 재는 것은 물론 그 치수대로 자르는 것이 그냥 ‘불가능’이었다.


그리고 그 대안으로 생각한 것은 3D 프린터였는데 그 와중에 또 다른 방법이 있다는 것을 알았다.
바로 레이저 커팅이라는 방법이다. 물론 레이저 커팅도 제약은 있다. 재료가 주로 목재, 아크릴, 종이류라는 점과
곡면 가공은 포기를 해야 한다는 것, 그리고 절단면을 수직으로밖에 할 수 없어 나처럼 8각형으로 만들 경우 면과 면이
딱 맞아떨어지지 않는다는 점이다. 그래도 우선 동료에게 부탁한 3D 모델링이 완성되기 전에
시도는 한 번 해보자 하고 MDF라는 목재를 이용하여 레이저 커팅을 이용한 프레임을 만들어보기로 하였다.


도안


우선 무작정 디자인 툴로 작업을 하기 보다는 모눈종이에 대략적인 도안을 해보기로 하였다.
모눈 종이를 마지막으로 만져 본 것이 100만년은 된 것 같다. 나 학교 다닐 때는 기술/공업이라는 과목이 있었는데…
뜻밖의 아재 인증이려나…^^;;;


아뭏든 모눈종이를 주문하고 택배를 받아보니 뭔가 감회가 새로웠다.


그러나…


모눈 종이를 만져본 것이 100만년 전이라는 것은 곧 내 나이가 100만살(그냥 갑시다…-.-)이라는 것을 의미함을 깜빡했다.
눈이…눈이…ㅠ.ㅠ 어느날 다가온 ‘노안’의 시련은 결코 만만한 것이 아니었다. 1mm단위는 물론이거니와 5mm 단위도
헷갈릴 때가 많았다. 일단 대략적인 치수만 기억하기로 하고 닥치는대로 그려보았다.


그리고…


아래와 같은 한 장의 도면(한 장은 도면이라고 하기도 뭐해서 뺐다)을 그려냈다.



디자인 툴로 그리기


레이저 커팅을 하기 위해서는 보통 Adobe Illustrator나 AutoCad로 작업을 한다고 한다.
AutoCad는 구할 길이 없고 어찌어찌 Illustrator를 구해 작업을 하기로 했다.


사실 개발 일을 하면서 개인 프로젝트를 위해 포토샵은 간혹 만져보았으나 일러스트레이터는 거의 사용을 해보 적이
없다. 전체적인 UI는 포토샵과 닮았으나 세세한 부분에서 포토샵과 많은 차이가 있었다. 하지만 해야 할 작업이란 단지
잘라낼 범위를 선으로 표시하는 것 정도이기에 한 번 도전을 해보기로 하였다.


모눈종이에 그린 도면을 바탕으로 부품으로 잘라낼 영역들을 펜툴을 이용하여 적절한 치수로 그려나갔다.
어려웠던 점은 단순히 드래그하는 것만으로는 정확한 수치를 맞추기가 어려웠다. 하나의 선을 그린다고 할 때
단순히 드래그 하는 경우 y좌표가 시작 점은 100mm인데 끝 점은 100.195mm가 되는 등 미묘하게 수치가 어긋났다.
결국 일일이 상단의 옵션 창에 정확한 수치를 입력해 줄 수밖에 없었다. 처음에는 매우 번거로웠으나 익숙해지니 이 짓도
할만했다. 새삼 디자이너들의 노고에 숙연해지는 순간이었다…-.-


그렇게 그려낸 부품들의 도안은 아래 그림과 같다(혹시나 필요한 분이 있을까 모르겠지만 원본 ai 파일은 글 말미에
첨부하도록 하겠다).



혹시나 프레임 제작 중의 실수를 대비해 여분의 부품을 많이 그렸고 또 약간 형태를 달리한 도안도 추가하여 양이 좀
많아졌다.


레이저 커팅


일단 가장 궁금해 하실 비용부분은 첨부한 도안의 경우 총 5만원이 조금 넘는 비용이 들었다…ㅠ.ㅠ
일반적으로 레이저 커팅은 시간단위로 비용을 받으며 기본 비용을 10분에 1만원을 책정하는 업체가 있고
5분에 5천원을 책정하는 업체도 있는데 기본 금액 이후에는 1분에 1000원을 받기 때문에 그냥 속편하게 1분에 
1000원이라고 생각하면 된다.


처음 하는 일러스트레이터라 혹시나 업체에서 안내한 도안 가이드에 벗어나지는 않았을까 걱정했는데 다행히도 도안
자체는 한 방에 통과했다. 다만 앞서 적은 비용 문제로 잠시 고민은 했지만 일단 드론을 만들어야겠다는 열망…보다는
도안 한다고 개고생한 것이 아까워서 덥썩 결제를 하였다…ㅠ.ㅠ


그리고 드디어 결과물이 도착했다.



처음 해보는 레이저 커팅이라 사실 조금 신기했다. 재질은 강도와 무게를 고려하여 MDF 2T 두께로 신청을 하였는데
딱 적절했던 것 같다. 이렇게 배송된 것을 하나씩 하나씩 장인의 정신으로 뜯어내어(사실 다 잘라진거 흐트러지지 않게
뒷면에 테이프로 고정이 되어있을 뿐이라서 그냥 잡아 떼면 된다…-.-) 박스에 담으니 아이들 영영제 박스 2개에 꼭 알맞게
채워졌다(내가 블로그를 하면서 참 PPL 많이 들어가는데…나중에 유명해지면 광고비 청구해야겠다…).



조립하기


전체적으로 한방에 작업한 것 치고는 정말 잘 진행되었는데 2곳 정도 치수를 잘못 잰 곳이 있었다.
다행히 MDF가 여러모로 가공이 쉬운 재질이라 한 곳(길이가 길어진 곳)은 어찌 해결을 했는데 문제는 원했던 것보다
길이가 짧아진 부분이다. 게다가 이 부분은 전체 프레임을 흔들림 없이 고정하는데 큰 역할을 해야 하는 부분이라 조금
심각하다.


우선 프레임만 가조립을 한 모습은 아래와 같다.



무게는 대략 106g인데…예상보다 많이 나와 조금 걱정스럽다.



아래 이미지는 측면 보호판을 추가로 붙였을 때의 모습니다. 이 측면 보호 판의 길이가 짧게 되어 공간이 떠버린다.
이 부분이 서로 맞닿아 미는 힘으로 고정을 시키려고 했는데…망했다…ㅠ.ㅠ 이 부분에서 힘을 받지 못하면 중심으로
연결되는 8개의 판이 몸통과 연결되는 부위가 약해서 자칫 공중분해 될 위험이 크다.



게다가 이 측면 판을 추가하니 무게가 엄청 치솟는다. 무려 178g…어떻게 하든 아두이노나 배터리쪽에서 무게를 줄여야
하는데…가능할지 모르겠다. 측면판에 구멍을 내어 무게를 줄이는 방법도 고려를 하고 있다.



정리


근 한달간 작업으로 일단 프레임다운 프레임은 만들었다. 모양만… 과연 이 무게로 띄울 수 있을지가 의문이다.
다음 주까지 연구를 좀 해보고 죽이 되든 밥이 되든 다음 주 주말에는 새 프레임으로 한 번 띄워봐야겠다.
그래도 오랜 시간 고생해서 만들어진 것인데…성과가 있었으면 싶다. 


우선은 수직으로 뜨고 내리는 것을 목표로 시험 비행을 할 예정이고 결과가 성공적일 경우 역토크 방지와 방향전환 등
세부적인 기능을 추가할 예정이다. 


그리고 하나의 실험이 더 남았다.


이번 제작한 프레임이 ‘면’ 위주로 설계된 것이라면 회사 동료에게 모델링을 부탁한 3D 프린터용은 모터가 장착될
부분만 육면체 형태이고 나머지 프레임은 핫바스틱을 이용하여 만들 예정이다. 말하자면 ‘선’ 위주로 만들게 될 것이다.
아무래도 무게 측면에서는 많은 이점이 있으리라 예상해본다. 


최종적으로 어느 프레임이 선택될 것인지는 모르겠지만 당장에는 이 MDF 프레임이 잘 날아줬으면 좋겠다.

마지막으로 도안한 ai 파일을 첨부한다.

SingleCopter_frame_시안.ai









블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


아두이노 드론 만들기 - 쉬어가기


오늘 포스팅은 드론 만들기와는 직접적인 관계가 없으니 오로지 드론에만 관심이 있으신 분들은 굳이 읽지 않으셔도
좋겠다. ^^


지난 주말에 좀 더 심혈을 기울여서 수작업으로 할 수 있는 한 정밀하게 드론 프레임을 만들어보고자 하였으나…
똥손은 금박을 입혀도 똥손인 것이었다…ㅠ.ㅠ 토요일 일요일 합쳐서 거의 12시간 이상을 작업한 것 같은데 그 결과물은
너무나 형편 없는 것이었다.


여전히 선은 맞지 않고 수평은 삐딱하며 면은 울퉁불퉁한 상태이다. 
다만 파이프를 T자로 연결할 방법이 묘연했으나 그에 대한 해결책을 찾은 것이 소득이라면 소득일까?
오늘 포스팅의 주요 내용은 바로 ‘빨대’, 유식하게 스트로우(straw)를 이용하여 파이프를 연결하는 팁을 소개하는 것이다.


방망이 깎던 노인의 마음으로…


좀 더 아구가 맞는 프레임(사실 프로펠러 하나짜리라 프레임이랄 것도 없다)을 만들어보고자 주말 내내 꽤 많은 시간을
할애했다. 중점을 둔 것은 최대한 수평을 맞출 것, 주요 지지대로 사용하는 핫바 스틱을 좀더 손쉽게 결합하거나 뺄 수 
있을 것, 이 두 가지였다.


일단 내가 가진 재료 중 가장 평평한 재료는 3T짜리 하드보드와 3T, 5T 우드락이었는데 우드락의 경우 너무 약해서 주 
재료로 사용하기에는 무리가 있었다. 결국 로봇 만든 이후 쳐다도 안보던 하드보드에 다시 칼질을 시작했다.


직전에 사용한 하드스틱은 평평하지도 않고 또 각각의 두께가 서로 달라 나중에 모터를 고정시키려다보니 반듯하게 고정
되지 않았다. 그래서 새로운 재료를 찾을 수 밖에 없었다.


하드보드는 평평하고 강도도 비교적 높고 다양한 접착제로 접착이 가능한 이점이 있으나 문제는 무게가 많이 나간다는 
것이다. 어쨌든 머리 속에 대충 상상하던 모양을 아래 사진과 같이 만들었다.






하드보드 자르는 것도 일이지만 저 시커먼 카본 파이프 자르는 것도 엄청난 일이다…저 사이즈가 외경 7mm, 내경 5mm
짜리인데 카본 파이프가 섬유질로 만들어져있다보니 실톱으로 자르다보면 톱이 걸려서 잘 잘리지가 않는다. 그래서
파이프를 돌려가면서 자르다보니 항상 단면이 비뚤어진다…ㅠ.ㅠ


어쨌든 저 카본파이프의 역할은 핫바스틱을 연결하는 조인트의 역할이다. 다 만들고나니 무게는 약 50g 남짓이다.


결론적으로 이렇게 열심히 만들었지만 딱딱 맞아 떨어지지 않는 것도 그렇고, 무게도 그렇고 썩 마음에 들지 않아서
직장 동료에서 저 모양대로 솔리드 웍스로 모델링을 부탁했다. 역시 3D 프린터의 도움을 받지 않고는 해결되지 않을 듯
싶다.


빨대로 파이프 연결하기


작업을 하면서 새삼 표준이라거나 규격이란 것이 참 중요하구나 하는 것을 느꼇다.
앞서 말한 것처럼 내가 사용한 카본 파이프가 외경 7mm, 내경 5mm(물론 다양한 규격이 있다)였는데 핫바스틱이 또
대체로 직경 5mm이다. 즉 카본 파이프에다 핫바 스틱을 끼우면 잘 들어간다. 물론 두 물건 모두 정밀하게 제작된 물건이
아니다보니 어떨 땐 헐겁고 또 어떨 땐 조금 깎아내야 들어가기도 하지만…


여기에 또 빨대 역시 내경 5mm짜리가 있다. 이렇게 사이즈를 맞춰놓으면 드론 프레임같은 것은 무리겠지만 아이들 공작
에는 꽤 많은 활용도가 있을 것 같다.


드론 프레임을 만들면서 가장 고민이 되었던 것이 파이프를 T자로 연결할 일이 있었는데 도무지 방법이 생각나지 않는
것이었다. 기성품으로는 내가 가진 재료들을 연결할 만한 것을 찾을 수가 없었다. 그러던 중 회사에서 커피를 마시다가
빨대를 보고 불현듯 아이디어가 떠올랐다.


이제부터 하나씩 알아보자.


우선 빨대를 적당한 길이로 자른다. 나는 파이프가 들어갈 부분을 대략 2Cm로 그리고 파이프를 감쌀 부분을 직경 5mm
기준으로 원둘레가 대략 14mm가 나오기 때문에 총 길이를 좀 여유를 두어 55mm로 잘랐다. 그리고 아래 그림과 같이 양쪽 끝에서 
2Cm 되는 부분에 표시를 하고 그 곳까지 빨대와 평행한 방향으로 칼집을 내준다.



다음으로 표시한 가운데 부분을 T의 수평이 될 파이프에 아래 사진과 같이 둘러준다.



그리고 둘러준 파이프에서 수직방향으로 내려온 양 끝 중 하나에 T의 수직이 될 파이프를 끼워준다. 그리고 다른 한쪽은
칼집을 냈으니 잘 펼쳐서 파이프를 끼운쪽에 덮어준다. 사실 여기서 수직 방향으로도 칼집을 내면 좀 더 수월하게 감쌀 수
있지만 빨대가 잘 찢어지는 재질이다보니 너무 칼집을 많이 내는 것은 좋지 않겠다는 생각에 추가 작업은 하지 않았다.
그래서 덮어 씌우는 것이 딱 맞아 떨어지진 않는다.



그 상태에서 수직 파이프를 둘러싼 빨대를 테이프로 고정을 시켜준다. 바로 사용할 파이프들을 연결했다면 그 상태에서
바로 접착을 해버리면 되고 그렇지 않다면 파이프들을 모두 뽑아준다. 그러면 아래 사진과 같이 간이 조인트가 만들어
진다.^^



이런식으로 만든 조인트와 핫바 스틱을 이용하여 아래와 같은 모양을 만들 수 있다.



굳이 설명이 필요 없겠지만 1자로 연결하는 것이야 뭐 그냥 빨대를 잘라서 하면 된다.
응용 범위를 좀 넓히면 +자나 Y자 또는 +에 수직방향으로 추가 연결부위가 있는 것 등 다양한 조인트를 만들 수있을 것이다.


정리


서두에서도 언급했다시피 오늘은 드론에 대한 이야기라기 보다는 일반적인 공작에서의 팁이 될만한 내용을 다뤄봤다.


물론 독특한 드론을 만드는 것이긴 하지만 드론을 만든다는 것이 생각보다 시간도 많이 걸리고 별다른 진척이 없어서
꽤나 지루하기도 하고 힘들기도 하다. 하지만 그 뒤에 이어질 개인 프로젝트를 위해 좀 더 노력을 해봐야겠다.


16일부터 21일까지 휴가를 얻었으나 작업을 할 수 없는 환경이 될 것 같다. 휴가를 갔다오면 회사 동료가 모델링한
드론 바디가 나올 것이고 그 것으로 3D 프린팅을 해서 다시 도전을 해봐야겠다. 모터도 좀 더 힘이 센 놈으로 주문을
해놨다^^


그럼 다음을 기약하며…







블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


아두이노 드론 만들기 #3


확실히 로봇을 만들 때보다 많은 부분에서 어렵다. 이번 포스팅도 지난 포스팅을 올린지 근 3주만에 작성하게 되었다. 
물론 회사에 급한 일이 있어 주말에도 일을 좀 하게 된 이유도 있지만 근본적으로 드론의 프레임을 만드는 것이 결코
쉬운 작업이 아닌 탓이다.


일단 이번 목표는 지난 포스팅에서 확인한 프로펠러 하나를 썼을 때 발생하는 역토크(counter torque, anti-torque)를
어떻게 해결하는가이다. 그리고 여러 곳에서 얻은 정보를 보았을 때 또다른 동력원을 사용하지 않는 방법은 오직 동체가
프로펠러가 도는 방향으로 같이 회전하도록 별도의 날개(vane)를 사용하는 것이었다. 우선 이 부분에 주안점을 두어
프레임을 만들어보았다.


재료 사모으기


지난 포스팅에도 얼핏 언급을 했지만 프레임을 만드는데 있어 가장 큰 장벽은 바로 ‘쓸만한 도구’가 없다는 것이다.
따로 작업실이 있는 것도 아니고 그냥 온 식구가 함께 사는 집에서 거실 또는 방 한구석에 앉아 칼이며 가위며 니퍼,
자, 접착제, 글루건 등등 가장 기본적인 도구만을 사용하다보니 여간 손이 가는 것이 아니다. 게다가 오차도 엄청 발생을
하여 만들고 나면 균형도 안맞고…ㅠ.ㅠ 그래서 가급적이면 이미 만들어져 있는 것들 중에 용도에 가장 맞는 것을 고르는
것이 큰 일 중에 한다. 하지만 그게 어디 그리 쉽게 구해지는가…그나마 이번에는 프로펠러 가드용으로 사용할 수 있는
꽤 괜찮은 물건을 구했는데 바로 크리스마스 장식에 쓰이는 ‘스티로폼 원리스’라는 물건이다.



가장 큰 사이즈가 외경 35cm로 내경은 10inch 프로펠러를 장착했을 때 아슬아슬하게 맞는 수준이다. 아슬아슬한 사이즈
이다보니 하나를 사용할 수는 없고 2개를 틈을 주어 위 아래로 겹쳐 그 사이에서 프로펠러가 돌도록 만들었다. 바로 아래
이미지와 같이.



그리고 그 것보다 작은 사이즈를 같이 구입해서 랜딩기어로 사용하기로 했다.
하지만 이 것도 완벽한 재료가 되지 못한 것이 큰사이즈의 무게가 약 25g 정도로 부담이 될만한 무게라는 점이다.


그리고 다른 용도로 banggood에서 몇가지 물건을 사면서 같이 산 벨크로로 된 배터리 타이가 있는데 이 배터리 타이는
배터리 뿐만 아니라 다른 부분들을 고정시키는데도 매우 유용하였다. 가격은 10개 3500원 정도이다.



그리고 지난 포스팅에 언급했던 우드락 3T, 5T짜리가 도착해서 잔뜩 쌓여있는 상태이다^^


새로운 프레임을 만들자.


일단 테스트를 할 때 가장 불한안 것이 바로 프로펠러다. 프로펠러로 인해 사람이 다칠 수도 있고 또 프로펠러가 상하게 
되면 당연히 테스트도 못하게 되기 때문에… 그래서 가장 고심했던 부분이 프로펠러 가드 역할을 하는 부분이다. 다행히
앞서 말한 원리스라는 물건을 찾아 해결을 하게 되었다.


가장 기본적인 뼈대는 핫바 스틱으로 연결하였다. 목공 본드로는 불안하여 연결 부위는 모두 글루건으로 한 번 더 고정을
시켰다. 그정도 하니 웬만큼 버텨 주었다(글루건 떡칠로 무게가 몇 십 그램은 늘어났을 것 같다는 점은 함정…-.-). 
사실 강도를 생각해서 카본 파이프를 주로 쓰려고 했지만 가격도 가격이거니와 이거 자르는 것이 겨우 실톱 하나로 작업
하는 나에게는 여간 힘든 것이 아니라서…ㅠ.ㅠ 그래도 원리스와 연결되는 부분 등 몇군데는 카본 파이프를 조인트 용도로
사용을 했다.


일단 이렇게 해서 완성된 모습이 아래와 같다.



가장 중요한 부분을 설명하지 않았는데 바로 역토크를 막기 위한 장치이다. 
사실 내가 뭔가 설계를 꼼꼼히 하고 작업을 하는 것이 아니라 그냥 머리 속에 있는 이미지를 토대로 막 만드는 것이다 보니
만들고 나면 뭔가 잘못되었다고 판단하는 경우가 허다하다. 그런데 이번에는 어찌어찌 하다보니 제법 잘 활용할 수 있는
구조가 되었다. 역토크를 방지할 날개를 어디에 붙일까 고민을 하다가 마침 적당한 위치가 보여 딱 알맞게 붙였다.
바로 아래 사진과 같이… 빨갛게 둘러친 부분이 바로 역토크를 막기 위한 날개이다.



그리고 일단 전체 프레임은 ‘프로펠러 가드’, 아두이노 및 기타 보드를 감싸는 ‘메인 바디’ 그리고 ‘랜딩 기어’가 분리되도록
구성을 하였다.


1차 테스트


우선 동영상 감상~

이렇게 만들어진 새로운 프레임으로 첫 번째 테스트를 진행하기로 했다.
날도 더운데 밖에 나가기는 귀찮고 해서 동영상에서 보는 것과 같이 책상을 뒤집어 놓고 드론을 책상 네 다리에 붙들어 
매고 테스트를 하였다…^^;;


일단 뜨는 상태는 지난 포스팅 때 완전히 옆으로 누워서 어뢰처럼 날아가던 것에 비하면 상당히 양호해졌다.
그런데 완전히 풀쓰로틀 상태에서 겨우 저만큼이 뜬 것이다. 이번에는 영락없이 무게를 의심할 수 밖에 없다. 그리고
위에 프레임 사진에서 보다시피 랜딩기어로 사용한 원리스의 면적이 너무 넓다고 판단되었다.


당장에 무게를 줄이는 것은 쉽지 않아보였고 그래서 우선 랜딩기어 역할을 하는 원리스에서 필요없는 부분을 모두 잘라
내었다.


2차 테스트


역시 동영상 먼저~





랜딩기어의 일부를 잘라낸 덕분인지 일단 뜨는 것은 훨씬 수월해졌다.
하지만 여전히 거의 풀쓰로틀 상태에서만 뜨는데다가 역토크의 영향도 아직은 완전히 해결되지 않은 모습이다.


또한 자꾸 뒤집어지는 상태로 보아 전체 균형이 안맞고 프로펠러 가드로 사용한 원리스의 무게가 생각보다 무거운 것으로
판단된다. 사실 이미 저정도 만든 상황에서 그 것도 글루건으로 단단히 고정시켜버린 상황에서 다시 무게 균형을 맞춘다는
것은 거의 불가능에 가깝다. 가능한한 현재 상태에서 개조를 해보겠지만 아무래도 새로 프레임을 짜야 할 것 같다…ㅠ.ㅠ



그래도 나에겐 희망을 갖게 하기에 충분한 이륙이었다.


정리


우선 역토크를 막는 부분에서 많이 어려울 것이라 생각되었는데 이런 완구 수준의 드론에서는 적절한 각도의 날개만
있으면 심각한 역토크의 영향은 받지 않는 것으로 판단된다.


그렇다면 이제부터 해결해야 할 문제는 무게를 줄이는 것과 균형을 잡는 것인데…


앞서 말한 것과 같이 현재 프레임을 조금은 더 개조를 해보겠지만 만일 문제 해결이 안된다면 아예 새로운 프레임으로
제작해볼 생각이다. 아래와 이미지와 같은 형태로…
사실 별로 멋이 없어보여서 이런 프레임은 쓰지 않을 생각이었지만…-.-



그리고 앞에서는 언급을 하지 않았지만 지난 포스팅에서의 테스트 때 프로펠러를 바닥에 갈아먹어서 프로펠러 길이가
약 3 cm 정도 짧아져 있는 상태이다. 1인치면 2.54cm… 결국 10인치 프로펠러가 9인치보다 작아져있는 것이다.


이런 저런 이유로 동체의 무게를 줄이는 작업과 더불어 새 프로펠러와 더 큰 추력을 갖는 모터의 구입 등도 고려를
해봐야 할 것 같다.


다음 작업은 언제가 될지…갈 길이 멀고도 멀다…ㅠ.ㅠ







블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


아두이노 드론 만들기 #2


아두이노로 드론 만들기 #1을 포스팅하고 벌써 2주가 흘렀다.
사실은 #1을 쓴 그 주 주말에 이미 실내에서 프로펠러 달고 테스트하다가 한번 식겁한 상황을 겪고 어찌어찌 드론
프레임을 만드느라 많은 시간을 보냈으며 지난 주말(7얼 15일)에 1차 야외 테스트를 진행하여 처참하게 실패를
하였다.


따라서 오늘의 포스팅은 전적으로 실패담이 될 것이다. 즐겁게 보아주시길…ㅠ.ㅠ


겁없이 뎀볐다가…


모터를 돌리는데 성공한 바로 그날, 욕심이 생겼다. 기왕 모터가 돌아간 김에 프로펠러를 붙여보고 싶은 유혹을
뿌리칠 수 없었다. 그래도 안전장치는 해야겠기에 가능한한 날아오르면서 궤도를 이탈하지 않도록 한다고 한 것이
영상에 보이는 상태이다.


몸통에서 4방향으로 평행하도록 나무젓가락을 꽂고(4족보행 로봇 때나 이번에 진행하는 드론 때나 이놈에 나무젓가락
없었으면 어떻게 작업을 했을까? 나무 젓가락으로 기술 진보를 이루는 나는 Homo Chopstickus?) 그 사이에 고정된
막대(이건 빨래 건조대에서 뽑았다…-.-)를 꽂아 틀어지지 않게 하려고 했는데…빨래 건조대에서 빠지는 파이프가 2개
밖에 없어서 4방향 모두 설치하지를 못했다…ㅠ.ㅠ


결국 좌우 2방향만 파이프를 걸고 내쪽으로 뻗은 나무젓가락을 잡아서 어떻게 제어를 해보려고 했지만 영상에서 보는 
것과 같이 이게 이정도로 제어될 수준이 아니었다. 괜히 고수들이 프로펠러 조심하라는 게 아니었다. 만일 저 아직 모양도
제대로 갖추지 못한 드론 같지도 않은 드론이 내 얼굴로 튀어왔다면…생각만해도 아찔하다…ㅠ.ㅠ

 


가장 어려운 것? 프레임 만들기


아두이노로 뭔가를 만들 때 가장 어려운 부분이 무엇일까? 사람마다 생각이 다르겠지만 나의 경우에는 바로 물리적인
‘프레임’을 만드는 것이었다. 이미 이전에 4족보행 로봇을 만들때 3T 규격의 하드보드지를 잘라 로봇의 프레임을
만들었을 때도 커터로 그 두꺼운 종이를 자르는게 어찌나 힘이 들었는지…그마저도 그렇게 손목에 관절염 걸릴 듯이
열심히 잘라대도 자르고 난 결과물은 왜 그리 비뚤어지는지…ㅠ.ㅠ


머리가 나쁘면 손발이 고생한다는 말이 있는데 사실 그 역도 성립한다. 손이 똥손이면 머리가 고통에 몸부림친다…ㅠ.ㅠ
생각대로만 만들어지면 참 좋으련만 자르거나 붙이거나 뚫거나 할 것 없이 똥손이 하는 일은 무조건 비뚤어진다.
그렇게 만들어진 결과물은 항상 기우뚱 할수밖에 없다. 피사의 사탑은 역사적 가치라도 있겠으나 내가 만든 이 비뚤어진
몹쓸 것은 그냥 쓰레기일 뿐이다…ㅠ.ㅠ


그래서 가급적이면 내가 별도로 손을 대지 않아도 그 자체로 사용 가능한 재료들을 주로 찾게 된다. 그마저도 로봇을
만들 때는 워낙 서보모터 토크가 좋기에 무게에 대해 크게 신경을 안썼는데 드론은 공중을 나는 장치이다보니 무게가
무척이나 신경이 쓰인다.


물론 일반적인 드론을 만든다면 기존 프레임을 구입해서 할 수도 있지만 앞서 말했듯이 나는 프로펠러 하나로 움직이는
드론을 만들려고 하는 것이다보니 기성품을 구매할 수가 없다. 오롯이 내가 스스로 만들어야 하는 것이다. 역시나
3D 프린터를 질러야 한다는 내 마음 깊은 곳의 외침이 힘을 갖는 순간이 아닐 수 없다. 그러나 그저 꿈일뿐…ㅠ.ㅠ


아무튼 3D 프린터가 인류의 기술 발전을 무지막지하게 단축시킬 것이라는데 몰표를 줄 수 밖에 없는 상황이다.


간단한 제작 과정


먼저 양해를 구할 것은 일단 제작이 시작되면 귀차니즘에 의해 일일히 사진을 찍어두지 않는다. 어차피 실패작에 대한
이야기이므로 그냥 그런가보다 하고 넘어가면 되시겠다.


앞서 말한 바와 같이 가능하면 노가다를 줄이기 위해 내가 별도로 손을 대지 않고 바로 사용 가능한 재로를 구하게 된다.
처음 눈독을 들인 것은 알루미늄 캔이었는데 그대로 사용하기는 어려워 포기를 했다. 그러다 눈에 들어온 것이 있었으니
바로 아래 사잔과 같은 쁘띠첼 껍데기였다(이거 PPL 아닌가? 광고비 청구해야 하나?).


억지로 구겨넣으니 아두이노 나노도 들어가고 옆을 조금 자르니 ESC도 가로질러 끼울 수 있고 카본 파이프를 대충 십자
형태로 가로지르니 얼추 모양새가 괜찮았다. 그렇게 만들어진 것의 원형이 글 맨 앞의 영상에 보여지는 모습이다.


이후 방향타를 움직이기 위한 서보모터와 서보모터 구동을 위해 사용한 서보 컨트롤러 보드, 그리고 서보 모터에 전원을
공급해줄 9V 추가 배터리와 추가 배터리의 전압을 5V로 떨어뜨려줄 강압기(빵판용 전원 보드) 등등 상당히 많은 부품
들을 추가 장착하게 되었는데. 최종 모습은 대략 다음과 같다.



쁘띠첼 껍데기가 모양도 그렇고 꽤 괜찮았는데 아쉬운 점은 칼집을 잘못내면 잘 찢어진다는 점과 워낙 약해서 쉽게 
찌그러진다는 것이었다. 예를 들어 모터를 나사로 고정하는데 조금 세게 돌리면 이놈에 껍데기가 사정없이 찌그러져
모터의 수평이 안맞게 되는 것이다.


어쨌든 처음 테스트의 영상만을 기준으로 생각할 때 이정도만 되어도 일단 뜨지 않을까 하는 막연한 기대로 테스트를
시작했다.


1차 시도 : 움직이는 척이라도 해볼래…-.-?


우선 영상을 보자…-.-



어처구니 없게도 꼼짝을 안한다…ㅠ.ㅠ 원래는 8인치 프로펠러를 썼다가 꼼짝을 안하길래 추력이 부족한가 싶어
10인치 프로펠러로 바꿨다. 그런데 프로펠러 보호 가드라고 붙여놓은 저 검은색 폼보드 직경이 아슬아슬해서 결국
프로펠러가 폼보드를 갈아먹고 말았다…ㅠ.ㅠ


처음에는 아주 단순하게 무게 탓이라고 생각했다. 정말 꼼짝을 안했으니…
하지만 사용한 모터에 대한 data sheet는 아니지만 동급의 A2212 13T 1000kv 모터에 대한 data sheet를 보면
내가 구성한 환경에서 최대 802g은 띄울 수 있어야 한다. 물론 프로펠러 사이즈가 약간 틀리긴 하지만 실제 드론의
무게도 400g이 채 못되는 무게이니 꼼짝도 못한다는 것은 말이 안된다.




그래서 다른 쪽으로 고민을 해보다가 떠오른 생각이 몸통이 너무 넓은 것이 아닌가 하는 것이었다. 게다가 현재 몸통의
형태가 나팔꽃을 뒤집어놓은 형상이라 프로펠러가 돌면서 발생시킨 바람이 옆으로 분산되면서 양력을 약하게 만든 것이
아닌가 하는 생각을 하게 되었다.


그래서 몸통 주변으로 덕지덕지 올려놓았던 면적이 넓은 보드들을 모두 모터 축과 수평행한 위치로 옮겼다. 그렇게 해서
가능한한 바람이 저항을 받지 않도록 슬림하게 만들었는데 그 형태가 바로 아래에 보이는 이미지 형태이다(사실 아래 
이미지는 시험 비행에 실패한 후의 모습이다).



2차 시도 : 그렇다고 움직이는 척만 하냐…ㅠ.ㅠ


역시 우선 영상부터 보자…



1차 시도 실패 후 2차 시도를 하기까지 1주일이 걸렸다. 누누히 이야기 하지만 일단 프레임에 손이가기 시작하면 노다가도
그런 노가다가 없다. 사정이 이렇다보니 일단 프레임을 고쳐야 하는 경우 쉽게 손이 가지 않는다. 아 3D 프린터여…ㅠ.ㅠ


그래도 프레임(이라기 보다는 부품의 배치)을 고친 후 뭔가 반응이 있었다. 자빠져서 구르고 엉망이 되었지만 일단 움직인
것이다. 하지만 역시 걸음마 하려고 한 발 띄었다가 바로 비틀거리며 넘어지는 갓난 아기 같은 모습은 영 불만이다.
이번에는 무엇이 잘못되었을까?


우선 생각난 것은 처음 자세가 균형이 맞지 않았다는 것이다. 기울어진 자세에서 출발을 하려니 제대로 떠오르지 못하고
기울어지면서 넘어지고 말았다는 생각…틀린 생각은 아니었지만 이게 다가 아니라는 점을 곧 깨닫게 되었다.


3차 시도 : 어뢰 만드는 줄…-.-


오늘의 마지막이자 하일라이트 영상이다. 얼른 확인하고 싶어 퇴근 후 오밤중에 동네 공원가서 테스트했다…-.-
그렇다보니 영상이 상당히 시커멓다. 화면 밝기 최대로 하고 보시길…



아래 이미지는 실패 후 처참한 모습이다.



지난 번 2차 시도에서의 실패 원인을 수평 문제로 생각했기에 프레임을 다시 만들었다. 이번에는 애들 공작용 하드스틱을
이용해서 최대한 수평이 맞도록…하고 싶었으나 역시나 손이 똥손이라…ㅠ.ㅠ 그래도 최대한 노력했다. 그렇게 나온 것이
아래 사진과 같은 모습니다. 일단 아직 서보모터는 달기 전으로 무게는 대략 348g이다.



그러나 테스트 결과는 앞의 동영상과 같았다(그래도 1, 2차에 비하면 이게 어딘가…ㅠ.ㅠ).
그리고 이 동영상을 보고 근본적인 문제를 찾아냈다. 아니 찾아냈다고 추측하고 있다.


애초에 프로펠러 하나짜리 드론을 만든다고 했을 때 물리학과를 나온 회사 동료 한명이 그게 가능하냐고 물었다.
바로 회전체가 있어 회전을 하는 경우 그 힘을 상쇄하기 위해 몸통은 반대쪽으로 회전하게 된다는 것, 쉽게 말해
작용과 반작용, 역 토크라고 하는 것 때문이다. 이미 아는 사람들은 다 알지만 헬리콥터가 정상적으로 움직일 수 있는 것은 
테일로터라든지 같은 축에 서로 반대로 회전하는 2개의 프로펠러를 쓴다든지 하는 방식으로 이 힘을 상쇄시키기 때문이다. 


그런데 그런 장치 없이 드론을 만들겠다고 하니 의문을 가질 법 하다. 그런데 이상한 것은 내가 유튜브에서 구한 자료
동영상에 나오는 프로펠러 하나짜리 드론들은 어째서인지 그런 장치가 보이질 않는 것이다. 그래서 아예 그런 부분을
고려하지 않았다(나중에 네이버 카페와 페북의 아두이노 사용자 모임을 통해 그 비밀을 알게 되었지만…).


나의 결론은 이렇다.
프로펠러가 회전하면서 몸통에는 역회전이 걸리고 드론이 제대로 뜨기도 전에 몸통이 반대로 회전을 하면서 드론을
지지하고 있던 다리가 지면에 걸려 자세가 기울어지면서 결국 중심을 잃고 넘어졌다는 것이다. 그렇다면 나는 이
역 토크(반작용)을 상쇄시킬 방법을 찾아야 하는 것이다.


앞서 언급한 것처럼 몇몇 커뮤니티의 도움과 구글링을 통해 최초에 방향타의 초기 위치를 지면과 수직으로 놓지 않고
몸통이 프로펠러와 같은 방향으로 돌도록 세팅하는 것으로 진행을 해보려 한다. 다음 포스팅은 이 작업의 결과가 될
것이다.


정리

애초에 문돌이로서 이런 작업을 한다는 것 자체가 어려운 일이기는 하지만 그 가운데서도 역시 어려운 것은 하드웨어적인
부분, 물리적인 힘이 작용하는 부분을 해결하는 것이다. 로봇을 만들 때고 드론을 만들 때고 무게와 힘이 적절한 위치에
적절한 강도로 작용하게 한다는 것이 결코 쉽지 않은 작업이다. 사실 소프트웨어적인 부분은 프로펠러 하나짜리라 그런지
별로 할 것이 없다. 조종기 코드 180라인 정도 드론쪽 코드 130라인 정도로 꽤 단순한 코드다.


아직까지 변변한 지식도 특별한 손재주도 없는 상태에서 오로지 직관과 시행착오만을 가지고 작업을 진행하다보니 그
결과 역시 보잘것 없는 상태다. 


일단 재도전을 위해 3T, 5T짜리 스티로폼 보드를 잔뜩 주문을 해놓았다. 또 얼마나 시간을 잡아먹을지는 모르겠지만
우선 이 재료들로 작업을 진행해보고 만일 영 안되겠다 싶으면 없는 재주이지만 간단하게나마 모델링을 해서 무료로
3D 프린팅이 가능한 곳(광화문쪽에 있는 국립현대미술관에서 가능하다고 한다)에서 출력해서 도전을 해봐야겠다.


확실히 걷는 것(로봇)보다 나는 것(드론)이 상당히 어렵다…ㅠ.ㅠ 제대로 띄울 수 있을지는 모르겠지만…
아무튼 다음 포스팅은 스티로폼 보드 도착 후 또 얼마간의 시간이 흘러야 할 것 같다.
그 때까지 무더위 잘 견뎌내시길…^^;





블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


아두이노 드론 만들기 #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 라이브러리의 함수들을 좀 더 제대로
알고 썼더라면 조금 더 일찍 성공에 다다르지 않았을까 하는 생각이 든다.

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






블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


아두이노 드론 만들기 : Prologue


뭔가 촉이 왔다!
그래서 혹시나 하고 지난 블로그를 되돌아 봤더니…역시나…
아두이노 4족보행 로봇 만들기를 처음 포스팅 한 것이 2016년 6월 20일이었다.
이건 뭐…지킬박사와 하이드씨도 아니고, 늑대인간도 아니고…여름만 되면 공돌이로 변신하고자 하는 욕구가 넘치는
돌연변이 문돌이라고나 할까…-.-


그 때 첫 문장이 이러했다.


이제 막 걸음마를 뗐는데…
벌써 수퍼맨이 되어 날아가려고 한다고나 할까… 

[아두이노] 아두이노 4족보행 로봇 만들기 ~ 1


그랬다. 그 때는 정말 아무 것도 모른 채로 하룻강이지 범 무서운 줄 모르고 무작정 덤볐던 것 같다^^
그러면 지금은? 일단 걷는 것은 해봤으니 진짜로 날아볼 수 있을까?


솔직히 말하면 그리 자신은 없다. 아두이노라는 공통점이 있을 뿐 사용하는 모터도 다르고 모터가 다르니 구동하는
방식도 다르고, 게다가…내가 만들려는 것이 보통 볼 수 있는 프로펠러가 4개나 6개 달린 그런 드론이 아니라 단지
프로펠러 1개로 날아가는 흔히 싱글콥터라고 불리는 형태의 것이라 더더욱 쉽지 않을 것 같다.


상상은 자유~


사실 처음에는 기왕 해보는 거 남이 하지 않은 걸 해보자 하고 프로펠러 하나짜리 드론을 생각했는데 역시나 내가
생각한 것은 이미 지구인의 절반은 생각한 것이라…벌써 그런 시도들을 잔뜩 하고 있었다. 하지만 그게 끝이 아니므로
그냥 도전하기로 했다.


이미 사람들이 만든 싱글콥터들

http://www.techholic.co.kr/news/articleView.html?idxno=6677

https://youtu.be/pF0uLnMoQZA?list=PLbfJADDuNxBd8rud9H4eLfhcK113_XTJB


굳이 싱글콥터를 만들기로 한 이유는? 모터 하나 살 돈밖에 없어서…는 아니고…^^;

최초의 발상은 영화 ‘프로메테우스’로부터 왔다. 프로메테우스에 보면 외계 행성에 도착한 지구인들이 엔지니어의
기지를 탐사할 때 한 과학자가 2대의 공모양 드론을 날린다. 그 드론들은 레이저를 쏘면서 동굴(기지) 내부를 스캔하고
스캔한 데이터는 모선에 입체 영상으로 재구성된다. 바로 그 드론을 만들어보고 싶었다.




하지만 현시창이라던가…
내가 뭔수로 그런걸 만드나…ㅠ.ㅠ
그래도 흉내나 내보려고 했으나 처음부터 막힌 것이 3D 프린터가 없는 상태에서 공 모양의 껍데기를 만들 재간이
없는 것이다. 결국 애초의 목표는 포기를 하고 대신에 프로펠러 1개로 움직이는 녀석으로 바람의 영향을 받지 않는
좁은 공간을 탐색할 수 있는 드론을 한 번 만들어보자고 생각했다.


되든 말든 상상은 할 수 있는거니까.


벋뜨 그러나...내 머리 속에는 왼쪽의 아이언맨이 있으나 내가 손에 쥘 아이언맨은 오른쪽이 아닐까 싶은 불안감이…-.-



준비 작업


까~이꺼! 드론 뭐 별거 있나? 모터 좀 사다가 배터리 연결하고 아두이노 연결해서 프로펠러 막 돌리면 붕붕 거리면서
날아다니…기는 개뿔…모터 돌리는데만 일주일이 걸렸다…ㅠ.ㅠ 일단 모터를 돌리는 과정은 다음 포스팅에서 자세히
다루기로 하고 오늘은 무엇무엇을 준비했나 보자.


이제부터 나오는 용어들은 드론으로 검색하면 모두 자세히 알 수 있는 내용이므로 별다른 설명 없이 지나가겠다.


우선은 무조건 소형화를 목표로 하여 가장 작은 재료들을 모았다. 아무래도 드론 하면 핵심 부품이 모터이므로
모터를 검색했다. 그리고는 주로 미니 드론에 쓰이는 코어리스(coreless) 모터를 발견하고는 아무생각없이 모터와
짝이 맞는 프로펠러와 함께 덜컥 구입을 했다.


하지만 이 모터는 한개로 드론을 만들 수 있는 추력(쉽게 말해 들어올릴 수 있는 힘)이 나오질 않았다.
기본적으로 모터 외에도 배터리와 플라잉 컨트롤러(비행을 제어하는 보드. 나는 아두이노를 사용하기로 한 것이다) 등이
더 붙어야 하는데 거기까지는 어떻게 될까 했더니 아두이노를 구동하기 위해 따로 배터리를 달거나 혹은 전원을 분배
할 수 있는 장치가 더 필요했던 것이다. 결국 이 모터는 아래와 같은 요상한 물건을 하나 만들어놓고는 일단 고이 
보관하기로 했다.



그리고 곧 브러쉬리스(Brushless) 모터 + ESC + 프로펠러 세트인 제품을 하나 샀는데 이놈은 프로펠러 길이가
10인치(약 26Cm)인 놈이라서 다시 레이싱 드론용으로 모터와 프로펠러 5인치(약 12Cm)짜리를 따로 구입했다. 
이번에는 혹시 잘 안되면 일반적인 드론을 만들어보고자 4세트를 샀다. 


더이상의 자세한 내용은 생략하고 기본적으로 필요한 구성품을 보면 다음과 같다.


  1. 아두이노 나노2개 : 하나는 조종기용, 하나는 드론용
  2. nRF24L01 트랜시버 2개 : 역시 하나는 조종기용, 하나는 드론용
  3. 브러쉬리스 모터 1개
  4. 전자 변속기(ESC) 1개
  5. 프로펠러 2개 (정방향 1개, 역방향 1개) : 모터와 변속기 프로펠러는 세트 상품
  6. 조종기용 조이스틱 한 개 (조이스틱 2개가 붙어있는 모델)
  7. 리튬 폴리머 배터리 11.1v 550mAh 30C 2개 (한 개는 2호기 만들면 쓸 것)
  8. 리튬 폴리머 충전기 1 개


일단 여기까지가 기본적으로 모터를 구동시키는데 필요한 부품들이다. 최종 완성을 위해서는 방향타에 쓰일
서보모터와 몇가지 센서들이 더 필요하게 될 것이다. 물론 외장도 만들어야 하고…


정리


사실 아직 만들던 4족보행 로봇도 완성시키지 못한 상태다.
언제나 느끼는 바이지만 나는 프로토타입형 인간이다. 실제 회사 업무를 할 때도 초기 작업은 빠르고 정확하게
잘 하는 편인데 프로젝트가 궤도에 오른 후에는 엄청 게을러진다…-.-


아마도 이 드론 프로젝트도 일단 날고나면 뒷방으로 밀려날지도…^^;


하지만 나의 큰 그림상에서는 이 드론 만들기도 로봇 만들기에 연장선 상에 있으니 조만간 통합 작업이 이루어질
것이다. 그 과정에서 꼭 필요한 것이 3D 프린터인데…언제나 살 수 있을까…ㅠ.ㅠ 서울 시내에 무료로 사용할 수
있는 곳도 있다니 우선은 그쪽을 이용해보는 것도 방법이긴 하겠다.


암튼 이번 포스팅은 이만 줄이고 다음 포스팅에서는 브러쉬리스 모터 구동과 ESC 설정에 대해 알아보도록 하겠다.






블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

티스토리 툴바