<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>NTREXGO - 디바이스마트, 엔티렉스 컨텐츠 통합 사이트 &#187; 입선작</title>
	<atom:link href="http://www.ntrexgo.com/archives/tag/%ec%9e%85%ec%84%a0%ec%9e%91/feed" rel="self" type="application/rss+xml" />
	<link>http://www.ntrexgo.com</link>
	<description>엔티렉스, 디바이스마트 컨텐츠 통합 사이트</description>
	<lastBuildDate>Thu, 03 Mar 2022 06:47:11 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>[45호]색종이로 작곡하는 춤추고 노래하는 자동차로봇</title>
		<link>http://www.ntrexgo.com/archives/34454</link>
		<comments>http://www.ntrexgo.com/archives/34454#comments</comments>
		<pubDate>Sat, 25 Nov 2017 00:00:51 +0000</pubDate>
		<dc:creator>디바이스마트 매거진</dc:creator>
				<category><![CDATA[blog-posts]]></category>
		<category><![CDATA[디바이스마트 매거진]]></category>
		<category><![CDATA[특집]]></category>
		<category><![CDATA[45호]]></category>
		<category><![CDATA[Feature]]></category>
		<category><![CDATA[디바이스마트]]></category>
		<category><![CDATA[매거진]]></category>
		<category><![CDATA[융합]]></category>
		<category><![CDATA[입선작]]></category>
		<category><![CDATA[프로젝트 공모전]]></category>

		<guid isPermaLink="false">http://www.ntrexgo.com/?p=34454</guid>
		<description><![CDATA[디바이스마트매거진 45호 &#124; 색상 인식으로 춤추고 노래하는 자동차 로봇을 개발하였다.]]></description>
				<content:encoded><![CDATA[<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-27-40-142.png" rel="lightbox[34454]"><img alt="Cap 2018-02-05 09-27-40-142" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-27-40-142-620x213.png" width="620" height="213" /></a></p>
<p><strong>2017  ICT 융합 프로젝트 공모전 입선작</strong></p>
<p><span style="font-size: x-large;"><strong>색종이로 작곡하는 춤추고 노래하는 자동차로봇</strong></span></p>
<p style="text-align: right;"><strong>글 | 숭실대학교 김세현</strong></p>
<p style="text-align: left;">
<p><span style="color: #0000ff;"><strong>1. 심사평</strong></span><br />
<strong>칩센</strong> 작품명을 보았을 때 작곡을 어떻게 하는지, 그 작곡에 대해 어떻게 로봇이 동작하는지가 매우 궁금하였습니다. 첨부한 동영상 및 보고서 내용을 보았을때, 작곡이라기 보다는 어떠한 형태이든지 간에 입력이 이루어 지는 수동적인 (흔히 보는 형태의) 입력과, 입력된 악보에 대해 어떤 방식과 패턴으로 로봇이 움직이는 등에 대한 내용이 매우 부족한 것으로 보입니다. 보고서에서 말한 영유아 및 어린이를 대상으로 한다고 보더라도 음계와 색상을 매칭하는 것 자체가 쉽지 않은 과정으로 보입니다. 작품의 이름 대비 많은 부분이 아쉬운 결과물로 느껴집니다.</p>
<p><strong>뉴티씨</strong> 색상 인식이 인상적인 작품입니다. 실제로 기술을 적용하려면 새로운 암호인증 시스템으로 만들거나, 공장의 불량감지 같은 분야에서 사용이 가능해 보입니다.</p>
<p><strong>위드로봇</strong> 컬러 센서를 음악과 연결한 부분이 참신합니다. 전체적으로 완성도가 높은 작품으로 평가합니다.</p>
<p><span style="color: #0000ff;"><strong>2. 작품 개요</strong></span><br />
개발자가 생각하는 주요 고객 타겟 층은 다음과 같은 세 부류이다.<br />
첫째, 한국의 현대사회는 고령화 시대와 1인 가족 시대로 점점 가게 되면서, 사회적, 경제적, 개인적인 여러 방면에서 다양한 문제들이 함께 발생하고 있다. 개인적인 정서적 문제 중 하나가 바로 ‘외로움’이다. 이에 따라 독거노인들이나 1인 가족들의 적막한 삶에 활력소가 되어주고 외로움을 채워줄 존재가 점점 필요해지고 있다. 그 빈자리를 채우기 위해 주인의 명령에 재롱을 떨며 춤추고 노래하는 손자, 손녀 같은 또는 친구 같은 로봇을 개발하였다.</p>
<p>두 번째 주요 고객층은 영유아 및 어린이들이다. 어린이들에게 색감으로 계이름을 시각적으로 받아들이게 해주고, 로봇이 아이가 직접 만든 색종이 계이름판으로 음악을 만들어 그에 맞춰 춤까지 추는 모습을 보며, 자연스럽게 음악과 로봇에 친숙해질 수 있다. 이는 결과적으로 아이들의 정서 발달 및 로봇과 관련된 가치관 발달에도 큰 도움을 줄 수 있다.</p>
<p>마지막으로, 아직 전자공학이나 로봇공학과 친숙하지 않은 일반인들에게도 그들의 가치관에 대한 긍정적인 효과를 기대할 수 있다. 현실적으로 로봇공학의 발전에 대해 다소 거부감이 있는 일반인들이 많다. 이들에게 로봇이 ‘재롱’을 부리는 모습은 그들의 감성을 자극하여 로봇과 더욱 친숙해질 수 있고 로봇에 대한 호감 이미지를 만드는 데에 기여한다.</p>
<p>이와 같은 세 가지 효과를 기대하며 색상 인식으로 춤추고 노래하는 자동차로봇을 개발하였다.</p>
<p><span style="color: #0000ff;"><strong>3. 작품 설명</strong></span><br />
<span style="background-color: #ffffff; color: #33cccc;"><strong>3.1. 주요 동작 및 특징</strong></span><br />
<span style="color: #00ccff;"><strong> 3.1.1. 주요 동작</strong></span><br />
<strong> Ⅰ. 악보 생성 기능</strong><br />
(1) 컬러센서를 이용한 색상 감별로 악보 생성</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-27-59-998.jpg" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34693" alt="Cap 2018-02-05 09-27-59-998" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-27-59-998.jpg" width="620" height="434" /></a></p>
<p>TYPE 29의 컬러센서를 컬러 인식 모드인 MODE 2로 설정하여 컬러센서 자체에서 인식 가능한 검정, 파랑, 초록, 노랑, 빨강, 흰색, 갈색을 인식하여 각 색에 음계를 매치하여 라즈베리파이 내의 buffer에 저장하였다가, 사용자가 색 악보에 따라 작곡한 음악을 듣고자 하면 한 음계 당 500ms 동안 스피커로 각 음계를 출력하며 ‘노래’를 하도록 하였다. 아래 표는 각 색상마다 매치한 계이름과 주파수를 정리한 표이다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-07-480.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34694" alt="Cap 2018-02-05 09-28-07-480" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-07-480.png" width="620" height="295" /></a></p>
<p>1-2) 계이름과 1:1 매치되는 숫자로 악보 생성<br />
컬러센서로 인식할 수 있는 색상의 수는 위의 표에서 볼 수 있듯이 7가지로 한정되어있기 때문에, 다양한 음을 사용하는 악보를 만들기에는 부적합하다. 따라서 사용자가 각 계이름에 맞게 미리 매치시켜놓은 숫자들의 나열을 buffer에 입력함으로서 악보를 생성할 수도 있다. Linux의 C 언어에서는 getchar()이나 scan() 종류의 함수와 같은 문자를 입력받는 함수가 따로 없다. 따라서 사용자가 직접 이 기능을 하는 함수를 만들어야 한다. 아래 소스는 getchar() 함수를 구현한 getch 함수 소스코드이다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-21-898.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34695" alt="Cap 2018-02-05 09-28-21-898" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-21-898.png" width="620" height="430" /></a><br />
악보 생성은 위와 같이 두 가지 방법으로 가능하지만, 앞으로는 주로 색상 악보를 만들어서 작곡하는 모드를 주로 다룰 것이다.</p>
<p><strong>Ⅱ. 저장된 악보에 맞춰 춤추는 기능</strong><br />
EV3 자동차로봇에 달려있는 4개의 포트에는 자동차 좌측 앞바퀴, 우측 앞바퀴 2개를 연결하는 Large motor 2개와 Medium motor 1개를 연결시켜 직진, 후진, 좌회전 직진, 우회전 직진, 좌회전 후진, 우회전 후진, 팔 올리기, 팔 내리기 등 EV3와 연결된 모터가 할 수 있는 8가지 기능은 각 음계와 매치되어 로봇이 춤을 추게 된다. 각 음계와 연결된 motor 모션은 다음 표와 같다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-30-230.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34696" alt="Cap 2018-02-05 09-28-30-230" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-30-230.png" width="620" height="269" /></a></p>
<p><strong>Ⅲ. 터치 센서를 이용한 간단한 on-off 기능 탑재</strong><br />
터치 센서란, 단순하게 누르면 1의 값을, 누르지 않으면 0의 값을 유지하는 스위치와 같은 원리의 센서이다. 이 센서를 이용해, 처음 프로그램을 실행하고 한 번 센서를 눌렀을 때 자동차가 직진을 하면서 바닥에 깔린 색종이의 조합인 악보를 컬러센서로 센싱을 하도록 했고, 센싱해야 하는 색종이의 모임이 끝나면 다시 한 번 터치 센서를 눌러야만 저장한 악보대로 노래를 하고 춤을 추도록 제어하였다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-39-932.jpg" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34670" alt="Cap 2018-02-05 09-28-39-932" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-39-932.jpg" width="620" height="429" /></a></p>
<p><span style="color: #33cccc;"><strong>3.1.2. 특징</strong></span><br />
<strong> Ⅰ. 마인드스톰 EV3</strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-45-914.jpg" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34671" alt="Cap 2018-02-05 09-28-45-914" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-28-45-914.jpg" width="620" height="428" /></a></p>
<p>마인드스톰 EV3란, LEGO사와 MIT 미디어랩의 합작품으로, 로봇을 만들고 프로그래밍을 할 수 있는 블록이다. labVIEW의 블록 코딩을 사용하면 아주 쉽게 코딩이 가능하다. 하지만, 이번 프로젝트에서는 당연히 블록코딩을 사용하지 않았다. EV3의 VM(Virtual machine)에서 실행되는 bytecode라는 것이 있는데, USB, Bluetooth, WiFi를 통해 VM으로 전송이 가능하다. 바이트코드의 목록과 형식은 LEGO MINDSTORMS EV3 Firmware Develop kit를 참고하였다. 바이트 코드는 OP code, parameter, return valude로 구성된다. 각 값들을 정의에 따라 바꾸며 섬세한 EV3 제어가 가능하다.</p>
<p><strong>Ⅱ. 라즈베리파이3로 EV3를 WiFi로 제어</strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-30-30-181.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34672" alt="Cap 2018-02-05 09-30-30-181" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-30-30-181.png" width="620" height="346" /></a></p>
<p>EV3를 제어하는 MCU로는 라즈베리파이3를 채택하였다. 라즈베리 파이2와는 다르게 3에서는 와이파이 기능이 자체적으로 들어가 있기 때문에 근거리에서 무선으로 EV3 자동차로봇을 제어하는 데에 적합하였다. 이 때, 라즈베리파이라는 외부의 MCU로부터 EV3를 제어하므로 Direct Command 형식으로 명령코드를 작성하였다. Direct Command는 외부에서 EV3로 byte code를 전송할 때 사용하는 형식으로, USB, Bluetooth, WiFi로 전송 가능하며 이 또한 LEGO MINDSTORMS EV3 Communication Developer kit를 참고하였다. 응답으로 Direct reply를 받을 수 있다.</p>
<p>&nbsp;</p>
<p><strong>Ⅲ. 컬러 센서 사용하는 악보 제작</strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-30-35-966.jpg" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34673" alt="Cap 2018-02-05 09-30-35-966" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-30-35-966.jpg" width="620" height="429" /></a></p>
<p>컬러 센서로 색종이의 색깔을 구분하여 악보를 제작할 수 있다. 이 때 프로젝트 환경이었던 실내 장판은 갈색이 아닌 무색(색 없음, value 값 0)으로 컬러 센서가 인식을 하였다. 실제로, 컬러 센서로 색을 인식시킬 때 가장 까다로웠던 색이 바로 갈색인데, 조금 밝은 갈색이면 노란색으로, 조금 어두운 갈색은 검은색으로, 또 약간 붉은 기가 있는 갈색은 빨간색으로 인식하여 컬러센서를 위한 갈색을 찾는 데에 조금 애를 먹었다. 하지만 시중에서 판매되는 색종이로 갈색 값을 어렵지 않게 인식시킬 수 있으므로, 사용자가 사용하는 데에는 큰 무리가 없을 것으로 예상된다. 컬러센서로 인식시킬 색상 악보의 예시는 다음 사진과 같다. 아래 ‘악보’는 조지 윈스턴의 ‘캐논 변주곡’의 앞부분이다. 화살표 방향으로 자동차로봇이 전진하면서 컬러센서로 색상을 인식하고 각 색에 1:1 매칭이 되어있는 숫자로 변환시켜 색종이의 순서대로 저장하게 된다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-30-43-930.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34674" alt="Cap 2018-02-05 09-30-43-930" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-30-43-930.png" width="620" height="203" /></a></p>
<p><strong>Ⅳ. 소프트웨어적 발전 : Direct Command를 자동으로 전송하는 함수 및 Direct Reply를 자동으로 수신하는 함수의 알고리즘 개발</strong><br />
Direct Command는 위에서 언급하였듯이 EV3로 bytecode를 전송할 때 사용하는 형식으로, Little Endian을 사용하며 리턴 값으로 사용하는 Global variable의 offset은 align되어야 하며, 커맨드를 보내는 형식 또한 다소 복잡하다. 아래는 Direct Command의 형식을 정리한 그림이다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-30-50-380.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34675" alt="Cap 2018-02-05 09-30-50-380" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-30-50-380.png" width="620" height="296" /></a></p>
<p>bytecode를 보낼 때마다 이 형식을 지키며 일일이 보내게 되면 굉장히 시간이 많이 걸리고, 복잡하다. 따라서 이번 프로젝트에서 Direct Command를 구조체로 미리 만들어서 함수의 매개변수로 보내면 byte code로 해석하여 EV3로 전송해주는 send_command() 함수를 개발하였다. 또한, Direct Reply 또한 해석하여 shell에 print해주는 read_reply() 함수 또한 개발하였다. 함수의 작성 코드는 다음과 같다.</p>
<p>① send_command 함수</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-02-634.png" rel="lightbox[34454]"><img class="alignnone size-large wp-image-34676" alt="Cap 2018-02-05 09-31-02-634" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-02-634-513x620.png" width="513" height="620" /></a></p>
<p>② read_reply 함수</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-11-014.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34677" alt="Cap 2018-02-05 09-31-11-014" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-11-014.png" width="620" height="351" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-21-164.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34678" alt="Cap 2018-02-05 09-31-21-164" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-21-164.png" width="620" height="447" /></a></p>
<p>이 두 함수는 이 프로젝트뿐만 아니라 byte code를 작성하여 마인드 스톰 EV3를 제어하는 모든 프로젝트에서 아주 유용하게 쓰이게 될 것이다.</p>
<p><span style="color: #33cccc;"><strong>3.2. 전체 시스템 구성</strong></span><br />
<strong>Ⅰ. 소프트웨어적 구성</strong><br />
라즈베리파이와 EV3를 연결하여 명령을 송수신한다. 라즈베리파이와 EV3는 USB 포트로 연결되며, 라즈베리파이의 전원 공급은 EV3의 USB 단자로부터 공급받게 된다. 리눅스 환경에서 와이파이로 라즈베리파이와 ssh로 통신하여 C 프로그래밍으로 춤추고 노래하는 자동차로봇의 알고리즘을 짜고 실행시키게 되면, USB 드라이버는 USB 컨트롤러를 통해 프로그래밍 명령을 전달하게 된다.</p>
<p>이 때 EV3의 특성상, 외부에서 오는 명령은 EV3 전용 Byte code로 작성이 되어있어야 하며, 이 byte code는 EV3의 USB controller를 통해 USB 드라이버로 인식되어 EV3 내부의 가상 머신에 전달된다. USB로 연결된 라즈베리파이와 EV3는 일단 라즈베리파이에서 USB 장치 디바이스 파일을 찾는데, 다른 USB 장치가 없이 EV3만 연결되어있을 때는 절대경로로 /dev/hidraw0 파일을 찾으면 된다. 라즈베리파이에서 해당 디바이스 파일을 읽고 쓰기 위해 open을 할 때도 이 위치의 디바이스 파일을 이용하면 된다. 이 가상머신 위에서 EV3는 라즈베리파이 알고리즘의 명령대로 각 Output 드라이버들에게 명령을 전달하고, 모터 컨트롤러와 스피커에 전기적 신호가 전달되어 명령을 수행하게 된다. EV3와 라즈베리파이를 연동하는 일련의 과정을 도표로 요약하여 작성하면 다음과 같다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-43-615.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34679" alt="Cap 2018-02-05 09-31-43-615" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-43-615.png" width="620" height="413" /></a></p>
<p><strong>Ⅱ. 하드웨어적 구성</strong><br />
절차적인 하드웨어 구성 도표는 다음과 같다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-52-696.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34680" alt="Cap 2018-02-05 09-31-52-696" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-31-52-696.png" width="620" height="290" /></a></p>
<p>조립 및 연결이 완료된 라즈베리파이와 EV3의 전체 조감도는 다음과 같다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-32-02-697.jpg" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34681" alt="Cap 2018-02-05 09-32-02-697" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-32-02-697.jpg" width="620" height="433" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-32-05-981.jpg" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34682" alt="Cap 2018-02-05 09-32-05-981" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-32-05-981.jpg" width="620" height="433" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-32-14-430.jpg" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34683" alt="Cap 2018-02-05 09-32-14-430" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-32-14-430.jpg" width="620" height="433" /></a></p>
<p><span style="color: #33cccc;">3.3. 개발 환경</span><br />
<strong>Ⅰ. 개발 언어</strong><br />
라즈베리파이는 기본적으로 Linux 환경에서 동작한다. 리눅스 환경에서 주로 쓰이는 언어는 C언어와 Python인데, 본 프로젝트에서는 C언어를 사용하였다.</p>
<p><strong>Ⅱ. 사용 시스템</strong><br />
MCU로 라즈베리파이 3를 채택하였다. ssh 통신으로 근거리에 있는 자동차 로봇과 호스트 컴퓨터 사이의 무선 통신을 하는데에 WiFi 기능이 기본적으로 탑재되어있는 라즈베리파이 3가 적합하다고 판단했다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-34-40-081.jpg" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34684" alt="Cap 2018-02-05 09-34-40-081" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-34-40-081.jpg" width="620" height="433" /></a></p>
<p><strong>Ⅲ. 사용 툴</strong><br />
Linux Ubuntu OS 기반에서 vim 편집기를 사용하여 개발하였다.</p>
<p><span style="color: #0000ff;"><strong>4. 단계별 제작 과정</strong></span><br />
<strong>Ⅰ. 전체적인 제작 과정</strong><br />
위 프로젝트를 진행하는데 소요된 전체 제작 기간은 2017년 1월 29일부터 2017년 2월 26일까지이다. 1월 말 며칠 동안은 리눅스 및 라즈베리파이에 대해 이론적으로 공부하는 시간을 가졌으며, 실질적으로 춤추고 노래하는 자동차 로봇 프로젝트를 진행하는 데에 소요된 기간은 2월 한 달이다. 따라서 단계별 제작 과정을 2월 한 달 간의 시간의 흐름을 따라 도식적으로 도표로 정리하면 다음과 같다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-34-47-463.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34685" alt="Cap 2018-02-05 09-34-47-463" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-34-47-463.png" width="620" height="202" /></a></p>
<p><strong>Ⅱ. EV3 로봇 조립</strong><br />
마인드스톰 EV3 키트에 포함되어있는 설명서대로 조립을 진행한다. 그 후 전원을 키고, 데모 프로그램을 실행해보며 제대로 조립을 했는지 확인한다. EV3는 사용자의 목적에 따라 다양한 형태로 조립이 가능한데, 나는 4개의 바퀴가 달린 포크레일 형태로 조립하였다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-34-56-081.jpg" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34686" alt="Cap 2018-02-05 09-34-56-081" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-34-56-081.jpg" width="620" height="435" /></a></p>
<p>그 결과 4개의 바퀴를 위한 large motor 2개, 포크레일의 팔을 제어하기 위한 medium motor 한 개가 조립 시 필요하였다. 특히, 라즈베리파이를 EV3 로봇에 고정시켜야했는데, 이 때 LEGO의 장점이 십분 발휘되었다. 위의 그림 4-1.1에서 확인할 수 있듯이, 라즈베리파이를 EV3 본체의 우측에 고정시키기 위해 여분의 레고 조각들을 이용해 개발자가 라즈베리파이가 고정될 수 있도록 라즈베리파이 둥지를 만들었다.</p>
<p><strong>Ⅲ. 라즈베리파이와의 연동</strong><br />
본 프로젝트에서 사용한 MCU는 라즈베리파이3로, 호스트 컴퓨터와 라즈베리파이 사이의 WiFi를 이용한 ssh 통신이 기본적으로 가능하다. 아래 그림 4-2.1은 호스트 컴퓨터에서 라즈베리파이로 ssh를 이용해 접속한 모습이다. 개발자가 사용한 라즈베리파이의 ip 주소가 192.168.0.10이며 성공적으로 라즈베리파이와 통신이 연결된 것을 확인할 수 있다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-02-196.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34687" alt="Cap 2018-02-05 09-35-02-196" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-02-196.png" width="620" height="317" /></a></p>
<p><strong>Ⅳ. 핵심 알고리즘 개발</strong><br />
알고리즘의 흐름도는 아래 그림 4-3.1과 같다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-08-697.png" rel="lightbox[34454]"><img class="alignnone size-full wp-image-34688" alt="Cap 2018-02-05 09-35-08-697" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-08-697.png" width="620" height="439" /></a></p>
<p>&nbsp;</p>
<p><strong>Ⅴ. 테스트 및 디버깅</strong><br />
컬러 센서가 감지하는 색상은 정확한 편이었지만, 다양한 색상 인식이 필요하고 고정적인 크기의 색상 인식 물체가 필요한 이유로 색종이를 색상 인식 물체로 선정했다. 그런데, 색종이의 밝은 분홍색은 컬러센서가 하얀색으로 인식하고, 색종이의 주황색은 컬러센서가 노란색으로 인식하는 등 조금의 오차가 있었다. 따라서 이러한 부분들을 컬러 센서를 테스트하는 과정에서 컬러 센서가 인식하는 대로 다시 분류하는 작업이 필요했다. 특히 색상 인식이 까다로웠던 색은 갈색이었는데, 밝은 갈색은 노란색, 붉은 갈색은 빨간색, 어두운 갈색은 검정색으로 인식하였다. 다행스럽게도 색종이의 갈색은 한 번의 오류 없이 항상 갈색으로 인식하여 갈색 색종이를 악보로 쓸 수 있었다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-21-917.png" rel="lightbox[34454]"><img class="alignnone size-large wp-image-34689" alt="Cap 2018-02-05 09-35-21-917" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-21-917-491x620.png" width="491" height="620" /></a></p>
<p>라즈베리파이와 EV3를 연결시켜 고정시키는 과정에서 여러 종류의 커넥팅 라인들 또한 많을 수 밖에 없었는데, 이 선들의 무게중심이 EV3 자동차 로봇의 한 쪽으로 극단적으로 쏠리게 되면 자동차가 직진할 때 조금 회전하는 경향이 보였다. 따라서 선들을 묶어서 EV3 본체 중간에 움직이지 않도록 고정시켰다. 그 뒤로는 직진을 할 때 문제가 일어나지 않았다.<br />
색상을 인식한 후 buffer에 저장하는 과정에서 더 빠르고 단순한 방법으로 알고리즘을 개발하기 위해 여러 방법으로 알고리즘을 변형하고 테스트해보았다. 다음 그림 4-1.1은 알고리즘 테스트 과정 중인 모습이다.</p>
<p>가장 많은 방법으로 테스트를 해본 것 중 하나는 바로 무음 처리이다. 프로젝트를 진행하면서 한 가지 해결하지 못한 것은, 첨부 2 또는 첨부 3의 동작 동영상에서 볼 수 있듯이 박자가 쉬는 부분에서는 스피커의 출력 주파수를 0Hz로 설정하고 스피커의 출력 시간 또한 0초로 설정하였음에도 그 타이밍에 ‘툭’하는 소리가 나는 현상이 일어난다. 스피커를 출력하는 동안 무음으로 처리되어야 할 부분에 마치 잡음처럼 들리기도 하여, 이 부분을 해결하지 못한 점이 아쉽다.</p>
<p><span style="color: #0000ff;"><strong>5. 작동 이미지</strong></span></p>
<p><strong><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-32-150.jpg" rel="lightbox[34454]"><img class="alignnone  wp-image-34690" alt="Cap 2018-02-05 09-35-32-150" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-32-150-525x620.jpg" width="600" /></a></strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-40-364.jpg" rel="lightbox[34454]"><img class="alignnone  wp-image-34691" alt="Cap 2018-02-05 09-35-40-364" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/Cap-2018-02-05-09-35-40-364.jpg" width="600" /></a></p>
<p><span style="color: #0000ff;"><strong>6. 참고문헌</strong></span><br />
· NCS(임베디드SW구현)기반의 스마트로봇EV3, 남상엽, 2014, 이지테크<br />
· 산딸기 http://www.rasplay.org</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ntrexgo.com/archives/34454/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[45호]영상인식을 통한 불법중국어선 탐지 도우미</title>
		<link>http://www.ntrexgo.com/archives/34457</link>
		<comments>http://www.ntrexgo.com/archives/34457#comments</comments>
		<pubDate>Sat, 25 Nov 2017 00:00:14 +0000</pubDate>
		<dc:creator>디바이스마트 매거진</dc:creator>
				<category><![CDATA[디바이스마트 매거진]]></category>
		<category><![CDATA[특집]]></category>
		<category><![CDATA[45호]]></category>
		<category><![CDATA[Feature]]></category>
		<category><![CDATA[ict]]></category>
		<category><![CDATA[공모전]]></category>
		<category><![CDATA[디바이스마트]]></category>
		<category><![CDATA[매거진]]></category>
		<category><![CDATA[융합]]></category>
		<category><![CDATA[입선작]]></category>
		<category><![CDATA[프로젝트]]></category>

		<guid isPermaLink="false">http://www.ntrexgo.com/?p=34457</guid>
		<description><![CDATA[디바이스마트 매거진 45호 &#124; 우리나라 배타 수역을 지킬 무인선을 개발하기로 하였다. 이를 개발하기에 앞서 자율주행자동차를 구현하여 자율 주행 알고리즘을 개발한 후 무인선으로 발전시켜 나가도록 한다.]]></description>
				<content:encoded><![CDATA[<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-1.png" rel="lightbox[34457]"><img class="alignnone size-large wp-image-34702" alt="45 ict 탐지도우미 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-1-620x214.png" width="620" height="214" /></a></p>
<p><strong>2017 ICT 융합 프로젝트 공모전 입선작</strong></p>
<p><span style="font-size: x-large"><strong>영상인식을 통한 불법중국어선 탐지 도우미</strong></span></p>
<p style="text-align: right">글 | 부산대학교 손유선, 오은주, 제세은</p>
<p><span style="color: #0000ff"><strong>1. 심사평</strong></span><br />
<strong>칩센</strong> 실제 현실에 적용이 필요할 수도 있을 듯한 주제로 보여 아이디어 부분에 높은 점수를 주었습니다. 기본적으로 기존 라인트레이서을 주로 다룬 것으로 보이고, 보고서를 통해 구현된 내용 또한 라인트레이서에 가깝습니다. 영상을 인지하여 특정한 경로의 주행을 반복하게 하자면 기본적으로 라인트레이서의 기능을 보유하여야 하겠으나, 보고서에 언급하였듯이 부표를 통해 경로를 탐색, 이동하게 하자면 바닥면 선을 따라가는 라인트레이서 보다는 부표와 같은 특정 점을 인지하여 주행하여야 하는 부분이 추가되어야 할 듯한데, 이 부분에 대한 고려가 부족해 보입니다. 또한 불법조업어선에 해당하는 영상 처리나 전달의 부분이 제대로 언급되지 않은듯 한 점이 아쉬움이 듭니다.</p>
<p><strong>뉴티씨</strong> 전체적으로 완성도가 높지만, 작품의 개념을 설명하는 듯한 장비 구성입니다. 시간이 된다면 물에 띄워보는 것도 나쁘지 않을 것이라 생각됩니다.</p>
<p><strong>위드로봇</strong> template matching 방식은 대상체의 크기, 방향이 달라지면 인식률이 크게 떨어지는 단점이 있습니다. 이 부분을 극복할 수 있는 추가 연구가 있으면 좋겠습니다. 그리고 바다에서 위치 추정은 기존 GPS 또는 DGPS만으로도 충분하기에 이 분야의 활용을 검토하면 완성도를 높일 수 있을 것으로 보입니다.</p>
<p><span style="color: #0000ff"><strong>2. 작품 개요</strong></span><br />
<span style="color: #33cccc"><strong>2.1. 프로젝트의 기획 및 내용</strong></span><br />
<span style="color: #00ccff"><strong> 2.1.1. 프로젝트의 기획</strong></span><br />
<strong> Ⅰ. 프로젝트 도출배경</strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-1.jpg" rel="lightbox[34457]"><img class="alignnone  wp-image-34701" alt="45 ict 탐지도우미 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-1.jpg" width="496" height="298" /></a></p>
<p>지난 10월, 중국 불법조업에 관한 문제가 연일 뉴스에 오르내렸다. 중국의 불법조업 어선문제는 어제, 오늘만의 일이 아니라 계속해서 있어왔던 문제이고, 우리나라뿐만 아니라 전 세계적인 문제이다. 중국 어선으로부터 영해를 지키기 위해 인명피해마저 발생하고 있다. 중국 어선들의 불법조업 장소와 수위가 민감해질수록 국민들의 감정도 악화되고 있다. 다른 나라의 폭침, 포격 등이 뉴스까지 소개되며 물리적 대응을 더 강하게 하라는 여론이 생기는 상황이다. 하지만 과연 이것이 더 나은 해결책이 될까?</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-2.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34704" alt="45 ict 탐지도우미 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-2.png" width="496" height="330" /></a></p>
<p>현재 자율주행은 모든 분야에서 주목하는 제어 시스템이다. 자율주행 자동차만 생각하기 쉽지만 사실 무인 선박에도 많은 관심이 쏠리고 있다. 자연환경이 척박한 극지대나 해적들의 위험을 피할 수 있는 무인 상선, 군사용으로 활용 가능한 정찰선 등 활용도는 무궁무진하다. 날씨, 파도 등을 고려한 최적의 운항 항로 제시 기능을 통해 연료비 등을 아낄 수도 있다. 선박 내에 있는 자동화 장비를 실시간 모니터링 할 수 있는 시스템으로 선박의 고장 여부를 진단하고 필요한 조치를 취할 수 있다. 이처럼 무인선의 개발은 해양 기술의 발전에 크게 기여할 것이라는 것에 아이디어를 얻어 개발 방향을 잡게 되었다.</p>
<p><strong>Ⅱ. 프로젝트 아이디어의 기획</strong><br />
여러 목적을 가진 무인선 중에서도 우리는 정찰에 초점을 맞췄다. 우리나라 영해를 제집 드나들 듯 하는 중국 불법 조업 어선들이 커다란 문제점으로 사회 이슈가 된 지 오래이다. 해경의 감시에도 불구하고 수십 척에서 2백 여척이 넘는 중국 어선들이 배타 수역을 넘어 수자원을 고갈시키고 있다. 러시아와 일본의 경우 불법 어선이 발견된 즉시 해군을 출동시켜 불법 어선을 압수하거나 진압한다고 한다. 여기에서 해경의 감시에는 부족함이 있다고 판단하여 우리나라 배타 수역을 지킬 무인선을 개발하기로 하였다. 이를 개발하기에 앞서 자율주행자동차를 구현하여 자율 주행 알고리즘을 개발한 후 무인선으로 발전시켜 나가도록 한다.<br />
자율 주행을 구현하는 여러 방법들 중 라인트레이서를 선택하였는데 이는 우리가 정찰을 목표로 하고 있기에 일정한 경로를 주기적으로 돌아다니는 것이 중요하다고 생각했기 때문이다. 만약 무인선으로 발전시킨다면 적외선을 배의 좌측에 위치 시켜 일정 거리 마다 부표를 띄운 후 그 부표를 줄로 연결시켜 따라가도록 할 것이다.</p>
<p><span style="color: #00ccff"><strong>2.1.2. 프로젝트 구현 내용</strong></span><br />
<strong> Ⅰ. 자율주행 &#8211; 라인트레이서</strong><br />
시중에 판매되고 있는 RC카를 분해하여 우리가 원하는 구동 부분만 남기도록 한다. 이 차체에 아두이노, 적외선 센서, 모터드라이버를 연결하여 라인트레이서를 구현하도록 한다. 아두이노는 전체의 연산을 담당하는 제어부이다. 아두이노는 적외선 센서의 출력 값이 입력으로 들어가고 이 값을 연산하여 아두이노는 그에 따른 출력 값을 모터드라이버로 내보내 준다. 모터드라이버는 차체의 모터 부분과 직접 납땜하여 자동차의 움직임을 제어한다.</p>
<p><strong>Ⅱ. 영상 인식</strong><br />
차체에 부착된 라즈베리파이에 파이 카메라와 서보모터를 연결한다. 파이 카메라로 영상을 라즈베리파이로 보내주면 라즈베리파이에서 서버로 영상을 송출한다. 라즈베리파이에 연결한 서보모터는 카메라를 회전시켜주는 역할을 한다. 불법 중국어선을 탐지하는 것이 목적이므로 주위를 넓게 살피는 것이 중요하기 때문에 카메라를 회전시켜 시야를 확보하도록 한다. 영상을 라즈베리파이에, 연결된 IP 주소의 한 포트로 실시간 전송시키고, 그 주소를 소스 코드에 입력하여 노트북에서 영상을 분석한다. 우리가 구현하는 것은 정찰기이기 때문에 사람이 모니터로 불법어선의 침입을 감지해야한다고 생각하여 노트북에서 영상분석을 하도록 하고 영상 분석 결과를 노트북 화면에서 확인할 수 있도록 한다. 미리 중국어선의 이미지를 입력 값으로 두고 임계값을 넘을 경우 불법어선으로 간주, 화면에 빨간 네모로 표시한다.</p>
<p><span style="color: #0000ff"><strong>3. 작품 설명</strong></span><br />
<span style="color: #33cccc"><strong> 3.1. 주요 동작 및 특징</strong></span><br />
<span style="color: #00ccff"><strong> 3.1.1. 주요 동작</strong></span><br />
<strong> Ⅰ. 자율 주행</strong><br />
차의 앞머리에 부착된 적외선 센서 모듈을 이용하여 바닥에 깔린 라인을 인식한다. 이 모듈은 각각 4개의 발광부와 수광부로 이루어진 디지털 4ch이다. 좌측 2개(Sensor1, Sensor2), 우측 2개(Sensor3, Sensor4)의 센서로 이루어져 방향 뿐만 아니라 회전정도를 더 섬세하게 알 수 있다. 각 센서마다 달린 LED로 센서값의 변화를 육안으로 쉽게 값을 확인할 수 있다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-3.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34706" alt="45 ict 탐지도우미 (3)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-3.png" width="496" height="266" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-4.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34708" alt="45 ict 탐지도우미 (4)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-4.png" width="496" height="258" /></a></p>
<p>아두이노에서는 인식한 라인을 바탕으로 조향을 결정하고 모터의 동작을 제어한다. 직선 라인 부분에서는 속도와 방향을 일정하게 유지하고, 곡선부분에서는 라인에 맞춰, 속도와 방향을 바꾸어 좌회전 또는 우회전을 수행한다. RC카는 속도와 직진 또는 후진을 제어하는 뒷바퀴와 방향과 회전률을 제어하는 앞바퀴로 이루어져 있어, 이를 각각의 모터드라이버 채널에 연결하여 동시에 제어하도록 한다.</p>
<p><strong>Ⅱ. 영상 인식</strong><br />
차에 부착된 라즈베리파이에서 서버로 영상 정보를 계속 전송한다. 파이카메라를 라즈베리에 연결하여 외부의 영상정보를 파이카메라가 실시간으로 받도록 한다. 파이카메라에는 서보모터를 부착하여 카메라의 시야를 넓히도록 한다. 이때, 라즈베리파이에 연결한 파이카메라는 라즈베리파이용으로 나온 카메라 모듈이다. 파이카메라는 고화질 비디오뿐만 아니라 스틸 사진을 찍는데 사용할 수 있으며, 초보자가 사용하기 쉽게 만들어 놓은 카메라 모듈이다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-5.png" rel="lightbox[34457]"><img class="alignnone size-full wp-image-34710" alt="45 ict 탐지도우미 (5)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-5.png" width="620" height="239" /></a></p>
<p>라즈베리파이 카메라 모듈은 사용자 정의 설계된 추가 기능 라즈베리 파이이다. 카메라에 연결되어 있는 케이블에 따라 데이터가 전송이 되고 높은 데이터 전송률을 가진다.<br />
라즈베리파이 내에서는 외부의 영상정보를 받아와 전송시키는 역할을 한다. 이후 전송된 영상은 노트북에 visual studio에서 c++로 opencv 헤더를 참조하는 코드를 구성하여 처리한다. 영상을 처리하는 것을 보드 내부에서 할 수도 있겠으나, 보드의 RAM이 1GB인 점을 생각할 때 영상 처리 속도가 매우 느릴 것으로 생각되어 PC에서 처리하는 것으로 결정한다.</p>
<p><span style="color: #00ccff"><strong>3.1.2. 기능별 상세 기술</strong></span><br />
<strong> Ⅰ. 영상전송</strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-6.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34711" alt="45 ict 탐지도우미 (6)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-6.png" width="250" height="263" /></a></p>
<p>라즈베리파이에 파이카메라를 연결하여 배의 전방을 촬영한다. 이 영상을 웹으로 전송하면 디바이스에 관계없이 웹 브라우저에만 접속할 수 있으면 볼 수 있다는 이점이 있어 촬영한 영상을 웹으로 전송하도록 한다. 파이카메라에서 웹으로 영상을 전송하는 데에 사용한 기술은 mjpg-streamer이다. mjpg-streamer는 jpg 촬영을 연속으로 수행해서 마치 영상처럼 보이도록 해준다. 설치한 후 특정 port로 접근할 수 있는 HTTP Server 역할을 해준다. 이 페이지를 통해 MJPG 영상을 볼 수 있기 때문에 브라우저로 접속해서 볼 수 있고 소스 코드에서 URL을 불러 영상처리 작업을 진행 할 수도 있다.</p>
<p><strong>Ⅱ. 적외선 센서 AM-IRS4D</strong></p>
<p><strong><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-7.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34712" alt="45 ict 탐지도우미 (7)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-7.png" width="496" height="186" /></a></strong></p>
<p>라인을 따라 주행하는 자동차를 만들기 위해 적외선 센서 모듈을 사용하였다. 이 모듈은 4개의 센서로 구성되어 있고, 각 센서에는 발광부와 수광부, 가변저항, LED로 구성되어 있다. 발광부는 적외선을 내보내고, 바닥에 반사된 적외선은 수광부에서 감지된다. 흰색은 빛을 반사하고 검은색은 빛을 흡수하는 성질을 이용하여, 바닥에 반사된 적외선의 양으로 라인을 감지한다. LED는 적외선 센서 값의 변화를 나타낸다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-8.png" rel="lightbox[34457]"><img class="alignnone size-full wp-image-34713" alt="45 ict 탐지도우미 (8)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-8.png" width="620" height="233" /></a></p>
<p>적외선 센서의 출력은 그림9의 상단 그림과 같이 아날로그 신호이다. 이것을 비교기에서 적당한 값으로 잘라 0, 1로 구분 할 수 있다. 0, 1신호는 디지털 신호이기 때문에 AD 변환기없이 마이크로컨트롤러에서 입력 받아 사용할 수 있다. 0, 1로 자르는 기준은 가변저항으로 변경할 수 있다. 우리는 흰 바탕의 검은색 선을 트래킹하도록 설정해주었다. 라인을 감지해보면 검정색 라인 위에서는 LED가 꺼지고 흰색 바탕 위에서는 LED가 켜진다. (라인 감지 시 0 입력)</p>
<p><strong>Ⅲ. OpenCV</strong></p>
<p><strong><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-9.png" rel="lightbox[34457]"><img class="alignnone size-full wp-image-34714" alt="45 ict 탐지도우미 (9)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-9.png" width="296" height="337" /></a></strong></p>
<p>OpenCV(Open Source Computer Vision)는 오픈 소스 컴퓨터 비전 C 라이브러리이다. 원래는 인텔이 개발했고, 윈도, 리눅스 등의 여러 플랫폼에서 사용할 수 있다. 실시간 이미지 프로세싱에 중점을 둔 라이브러리이다. 인텔 CPU에서 사용되는 경우 속도의 향상을 볼 수 있는 Intel Performance Primitives (IPP)를 지원한다.<br />
우리는 이 기술을 이용하여 중국어선을 탐지하도록 한다. OpenCV에서 제공하는 Template matching을 이용하여 입력으로 넣은 이미지 파일과 유사한 부분이 영상에 포착될 경우 영상에 빨간색 Rectangle로 표시하도록 한다.</p>
<p><strong>Ⅳ. 모터동작제어</strong></p>
<p><strong><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-2.jpg" rel="lightbox[34457]"><img class="alignnone  wp-image-34703" alt="45 ict 탐지도우미 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-2.jpg" width="496" height="425" /></a></strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-10.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34715" alt="45 ict 탐지도우미 (10)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-10.png" width="496" height="352" /></a></p>
<p>모터의 동작 제어는 모터드라이버 L298N을 사용하여 제어하였다. 모터드라이버란 DC 모터의 방향, 속도를 제어하기 위해 사용하는 모듈이다. 아두이노 회로만으로는 DC 모터에 충분한 전류의 양을 공급할 수 없고, 전류의 제어가 어렵고 복잡하다. 모터드라이버를 사용하면 DC모터를 쉽게 제어할 수 있다. 하나의 모터 드라이버는 2개의 모터를 제어할 수 있다. 따라서 우리의 경우, 모터 A의 위치에는 조향 제어(앞바퀴)를, 모터 B에는 속도 제어(뒷바퀴)를 연결하였다. 모터 드라이버에는 모터를 제어하기 위한 H-bridge라는 칩이 있다. 이 칩은 발열이 심하여 검은색 방열판을 달아 전류가 많이 흘러도 화재가 발생하지 않도록 설계되어 있다. 3개의 단자를 통해 외부전원의 (+)(-)극, 라즈베리파이의 (+)(-)극을 연결한다. 아두이노가 줄 수 있는 전류는 40mA 정도지만 DC모터를 제어하기 위해서는 적어도 150mA 정도의 전류가 필요하다. 특히나 우리 자율주행 자동차의 경우 차체와 각종 보드 및 모듈의 무게가 함께 실려 속도를 내기 위해서 더 많은 전류를 필요로 했다. 이는 배터리 여러 개를 병렬로 연결함으로서 해결하였다. 대용량 모터용으로는 2A까지 제어가 가능하다.<br />
모터드라이버(L298N)를 사용하는 데에는 9V 배터리 하나를 연결하여 구동하였지만 모터드라이버가 제공하는 힘에 비해 차체가 너무 무거워 굴러가지 않았다. 결국 문제는 전압이 아니라 전류였고, 9V 550mAh 3개와, 7.2V 3000mAh를 병렬로 연결하여 모터드라이버에 연결하였다. 모터드라이버를 통해 전압은 5V로 감압한 뒤 인가된다. 앞바퀴와 뒷바퀴 모두 원활하게 전원 공급이 가능하다. 속도 또한 모터드라이버와 아두이노를 사용해 속도를 제어할 수 있다.</p>
<p><strong>Ⅴ. 자율주행 &#8211; 조향제어</strong></p>
<p><strong><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-11.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34716" alt="45 ict 탐지도우미 (11)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-11.png" width="496" height="322" /></a></strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-12.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34717" alt="45 ict 탐지도우미 (12)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-12.png" width="496" height="290" /></a></p>
<p>위의 L298N 모터 드라이버에서 IN1, IN2에 아두이노의 디지털 핀에 연결하여 신호를 받아 ENA 부분에 핀을 연결하여 PWM 제어를 해주어 아래의 기판에 모터 부분 +, &#8211; 단자에 출력하여 속도를 조절한다. 그리고 모터 드라이버의 IN3, IN4를 아두이노의 디지털 핀에 연결하고 ENB 부분에 디지털 핀을 연결하여 ENB 부분을 조절하여 각도를 조절하고 아래의 기판에 조향 제어 부분의 +, &#8211; 단자로 출력을 하여 IN3에 HIGH 신호를 IN4에 LOW 신호를 주면 좌측으로 회전하고 반대로 LOW, HIGH 신호를 주면 우측으로 회전하게 된다.</p>
<p><strong>Ⅵ. 카메라 서보모터 제어</strong></p>
<p><strong><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-13.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34718" alt="45 ict 탐지도우미 (13)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-13.png" width="248" height="207" /></a></strong></p>
<p>비용절감을 위해 회전식 카메라를 사용하기보다 서보모터에 파이카메라를 부착하는 방법을 선택하였다. 서보모터(SG-90)는 0~180도 회전 가능하며 아두이노 5v 전압, GND, 출력 핀을 필요로 한다.</p>
<p><span style="color: #33cccc"><strong>3.2. 전체 시스템 구성</strong></span><br />
<span style="color: #00ccff"><strong> 3.2.1. 전체 구성도</strong></span></p>
<p><span style="color: #00ccff"><strong><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-14.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34719" alt="45 ict 탐지도우미 (14)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-14.png" width="496" height="285" /></a></strong></span></p>
<p>정찰기의 전체 구성을 위와 같이 크게 3개로 나눌 수 있다. 조향부의 경우, 모터를 제어하는 부분이다. 제어부 중 모터에 관여하는 것은 아두이노이다. 아두이노와 조향부에 속해있는 모터드라이버, 적외선 센서를 연결하여 아두이노에서 제어 명령을 내린다. 영상부에는 서보모터와 파이카메라가 있다. 파이카메라는 제어부의 라즈베리파이에, 서보모터는 아두이노에 연결한다. 아두이노는 서보모터를 돌리면서 조향부를 계속 제어해야하므로 인터럽트를 사용하여 처리하도록 한다. 라즈베리파이는 파이카메라에서 받아온 영상을 서버로 전송하는 역할을 한다. 라즈베리파이3는 와이파이가 내장되어 있어 따로 와이파이 모듈을 연결할 필요 없이 서버로 영상을 전송할 수 있다.</p>
<p><span style="color: #00ccff"><strong>3.2.2. H/W</strong></span><br />
<strong>Ⅰ. 센서부(경로 감지)</strong><br />
주행방향의 전면을 향하는 위치에 센서를 배치하여 앞으로 가야하는 경로를 판단하며 주행할 수 있도록 하였다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-3.jpg" rel="lightbox[34457]"><img class="alignnone  wp-image-34705" alt="45 ict 탐지도우미 (3)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-3.jpg" width="496" height="258" /></a></p>
<p><strong>Ⅱ. 구동부</strong></p>
<p><strong><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-15.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34720" alt="45 ict 탐지도우미 (15)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-15.png" width="496" height="246" /></a></strong></p>
<p>모터보드는 시중의 RC카의 차체를 사용하여, 내장되어있는 모터를 이용해 구동하였다. 총 사용한 모터는 2개로써, 전면(앞바퀴)과 후면(뒷바퀴)에 배치하였다. 전면(앞바퀴)에 사용한 모터는 조향을 제어하고, 후면(뒷바퀴)에 있는 모터는 직진과 전진을 제어한다. 이 모터를 제어하는 데에는 L298N의 모터 드라이버를 사용하였다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-16.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34721" alt="45 ict 탐지도우미 (16)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-16.png" width="496" height="257" /></a></p>
<p>파이캠을 회전시키기 위해 서보모터 SG90을 사용하였다. 서보모터와 아두이노와 연결하여 0 ~ 180도까지 움직이도록 한다. 서보모터는 타이머 인터럽트 MStimer2 라이브러리를 이용하여 여러 가지 일을 동시에 할 수 있도록 하였다.</p>
<p><span style="color: #00ccff"><strong>3.2.3. S/W</strong></span></p>
<p><span style="color: #00ccff"><strong><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-17.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34722" alt="45 ict 탐지도우미 (17)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-17.png" width="496" height="280" /></a></strong></span></p>
<p>소프트웨어의 경우 위와 같은 기능이 필요하다. 라즈베리파이에서는 nano 명령어를 통해 shell script로 mjpg-streamer를 구동시켜 라즈베리파이에서 받아온 영상 정보를 서버로 보낸다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-18.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34723" alt="45 ict 탐지도우미 (18)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-18.png" width="496" height="260" /></a></p>
<p>위는 라즈베리파이에서 받아온 영상을 처리하는 코드 중 일부이다. 이 코드에서는 실시간으로 받아온 영상 정보를 계속 처리하는 과정이다. 서버 URL을 입력하여 받아온 영상을 cap에 넣고 그 영상을 origin에 넣는다. origin은 원본 영상을 의미한다. 불법 중국어선 사진을 mat 타입의 boat라는 변수에 저장해두고 template matching을 사용하여 일치 여부를 알아낸다. 이때 일치할 경우 그 주위에 사각형을 그려준다. 원본 영상에서 불법 어선이라 생각되는 곳에 사각형을 그린 영상을 img_display라는 mat 타입의 변수에 저장한다. 이 영상을 imshow로 user에게 보여주면 사용자는 실시간으로 불법 중국 어선이 탐지될 경우 사각형으로 표시되는 영상을 볼 수 있다. 위의 코드에서는 원본 영상과 사각형으로 표시한 후의 영상 둘 다 보여주도록 하였다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-19.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34724" alt="45 ict 탐지도우미 (19)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-19.png" width="496" height="163" /></a></p>
<p>위의 라즈베리파이에서 실행시킬 파일 내용이다. mjpg-streamer라는 기술을 이용하여 라즈베리파이가 파이카메라로부터 받아온 영상을 라즈베리파이가 연결된 wifi의 8222 port를 열어 전송시키는 것이다. 이 기술을 사용하기 위해서는 라즈베리파이에 mjpg-streamer를 설치하고 설치한 곳의 link를 위와 같이 library path로 설정해 주어야한다. 만약 이후에 보드 내부에서 영상을 처리해야하는 순간이 온다면 파이카메라로부터 받아온 영상을 라즈베리파이 내부에 사진파일로 저장하고 이 사진파일을 opencv에서 받아와 처리한 다음 처리한 사진을 아까와 같은 이름, 경로로 저장시킨다. 이렇게 할 경우 받아온 즉시 opencv로 처리하므로 실시간 처리라 할 수 있다. 실시간 처리가 된 사진을 mjpg-streamer 기술을 이용하여 위와 같이 일정 포트로 전송시키면 우리는 영상처리가 완료된 영상을 서버로 받아볼 수 있을 것이다. 아래부터는 아두이노에서 실행시킬 코드이다.</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>#include&lt;Servo.h&gt;<br />
#include &lt;MsTimer2.h&gt;<br />
Servo servo;</p>
<p>int left_1 = 4; //라인트레이서 센서1<br />
int left_2 = 3; //라인트레이서 센서2<br />
int right_3 = 6; //라인트레이서 센서3<br />
int right_4 = 7; //라인트레이서 센서4</p>
<p>int servoPin = 5; //카메라 서보모터<br />
int angle = 0 ; //카메라 서보모터 각도</p>
<p>int IN1 = 8; //DC모터1<br />
int IN2 = 9; //DC모터2<br />
int ENA1 = 10; //DC모터 속도<br />
int ENA2 = 11; //서브모터 속도<br />
int IN3 = 12; //서브모터1<br />
int IN4 = 13; //서브모터2</p>
<p><em>그림 24 아두이노 핀 설정</em></p>
</div>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void setup(){<br />
Serial.begin(9600);<br />
pinMode(left_1,INPUT);<br />
pinMode(left_2,INPUT);<br />
pinMode(right_3,INPUT);<br />
pinMode(right_4,INPUT);<br />
MsTimer2::set(500, curl);<br />
MsTimer2::start();<br />
servo.attach(servoPin);<br />
pinMode(IN1,OUTPUT);<br />
pinMode(IN2,OUTPUT);<br />
pinMode(ENA1,OUTPUT);<br />
pinMode(ENA2,OUTPUT);<br />
pinMode(IN3,OUTPUT);<br />
pinMode(IN4,OUTPUT);<br />
}</p>
<p><em>그림 25 아두이노 핀 모드 설정</em></p>
</div>
<p>아두이노의 핀 중 3, 4, 6, 7번은 적외선센서 모듈에 연결하고 5번 핀은 파이카메라와 연결할 서보모터에 연결한다. 핀 8~13번은 모터 드라이버의 ENA, ENB, IN1,2,3,4에 연결한다. 적외선센서와 연결된 핀은 INPUT으로 설정하고 모터드라이버에 연결된 핀은 OUTPUT으로 설정한다. 서보모터에 연결된 핀도 설정해준다.<br />
카메라를 부착시킬 서보모터는 메인 루프문과 병렬적으로 실행되어야 한다. 그렇지 않으면 서보모터가 동작하는 동안 차체는 아무 동작도 할 수 없기 때문이다. 따라서 MsTimer2라는 라이브러리를 추가하여 병렬 실행을 가능하게 한다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-20.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34725" alt="45 ict 탐지도우미 (20)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-20.png" width="496" height="184" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-21.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34726" alt="45 ict 탐지도우미 (21)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-21.png" width="496" height="218" /></a></p>
<p>&nbsp;</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void curl(){<br />
int servoPin = 5;<br />
int angle = 0;<br />
for(angle = 0; angle &lt;180; angle++)<br />
{<br />
servo.write(angle);<br />
delay(20);<br />
}<br />
for(angle = 180; angle &gt; 0 ; angle &#8211;)<br />
{<br />
servo.write(angle);<br />
delay(20);<br />
}<br />
}</p>
<p><em>그림 28 파이카메라 서보모터 동작 코드</em></p>
</div>
<p>파이카메라를 부착할 서보모터는 주기적으로 180도를 회전하며 카메라가 다양한 각도를 촬영하도록 한다. curl() 함수는 메인 루프문과 별개로 500ms마다 타이머 인터럽트로 인해 호출되기 때문에 어떤 delay로 없이 서보모터를 동작시킬 수 있다.</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void forward(){<br />
digitalWrite(IN1,HIGH);<br />
digitalWrite(IN2,LOW);<br />
analogWrite(ENA1,150);<br />
digitalWrite(IN3,LOW);<br />
digitalWrite(IN4,LOW);<br />
analogWrite(ENA2,255);<br />
}<br />
void Stop(){<br />
digitalWrite(IN1,LOW);<br />
digitalWrite(IN2,LOW);<br />
analogWrite(ENA1,0);<br />
digitalWrite(IN3,LOW);<br />
digitalWrite(IN4,LOW);<br />
}</p>
<p><em>그림 29 아두이노 조향 제어1</em></p>
</div>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void turn_left(){<br />
digitalWrite(IN1,HIGH);<br />
digitalWrite(IN2,LOW);<br />
analogWrite(ENA1,180);<br />
digitalWrite(IN3,HIGH);<br />
digitalWrite(IN4,LOW);<br />
analogWrite(ENA2,255);<br />
}<br />
void turn_right(){<br />
digitalWrite(IN1,HIGH);<br />
digitalWrite(IN2,LOW);<br />
analogWrite(ENA1,180);<br />
digitalWrite(IN3,LOW);<br />
digitalWrite(IN4,HIGH);<br />
analogWrite(ENA2,255);<br />
}</p>
<p><em>그림 30 아두이노 조향 제어2</em></p>
</div>
<p>그림 33, 그림 34는 자동차의 모터 제어를 위한 부분이다. IN1이 HIGH, IN2가 LOW이면 정방향으로 주행을 하고 0~255까지 PWM 제어를 통해 속도를 조절할 수 있다. 그리고 IN3, IN4에 HIGH, LOW를 주게 되면 좌회전, LOW,HIGH를 주게 되면 우회전을 하게 되고 이 때 ENA2에 255 값을 주게 되면 45도 회전하게 된다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-22.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34727" alt="45 ict 탐지도우미 (22)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-22.png" width="496" height="234" /></a></p>
<p>적외선 센서의 값은 검은색 라인을 인식하면 0이고, 흰색 바탕을 인식하면 1이다. 흰색 바탕을 인식하거나 센서 값이 적절하지 않은 경우(예를 들면 1-0-0-1) 라인을 인식하지 못했으므로 정지하도록 한다. 센서 값에 따른 방향을 정리하면 위의 표와 같이 나타난다. 이를 이용하여 방향을 판단하는 함수를 작성하면 다음과 같다.</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>char DIRECTION(){<br />
char Direction=&#8217;S';<br />
if(digitalRead(left_1)==0 &amp;&amp; digitalRead(left_2)==0 &amp;&amp; digitalRead(right_3)==0 &amp;&amp; digitalRead(right_4)==0) Direction = &#8216;G&#8217;;<br />
else if(digitalRead(left_1)==1 &amp;&amp; digitalRead(left_2)==0 &amp;&amp; digitalRead(right_3)==0 &amp;&amp; digitalRead(right_4)==0) Direction = &#8216;R&#8217;;<br />
else if(digitalRead(left_1)==1 &amp;&amp; digitalRead(left_2)==1 &amp;&amp; digitalRead(right_3)==0 &amp;&amp; digitalRead(right_4)==0) Direction = &#8216;R&#8217;;<br />
else if(digitalRead(left_1)==1 &amp;&amp; digitalRead(left_2)==1 &amp;&amp; digitalRead(right_3)==1 &amp;&amp; digitalRead(right_4)==0) Direction = &#8216;R&#8217;;<br />
else if(digitalRead(left_1)==0 &amp;&amp; digitalRead(left_2)==0 &amp;&amp; digitalRead(right_3)==0 &amp;&amp; digitalRead(right_4)==1) Direction = &#8216;L&#8217;;<br />
else if(digitalRead(left_1)==0 &amp;&amp; digitalRead(left_2)==0 &amp;&amp; digitalRead(right_3)==1 &amp;&amp; digitalRead(right_4)==1) Direction = &#8216;L&#8217;;<br />
else if(digitalRead(left_1)==0 &amp;&amp; digitalRead(left_2)==1 &amp;&amp; digitalRead(right_3)==1 &amp;&amp; digitalRead(right_4)==1) Direction = &#8216;L&#8217;;<br />
else Direction = &#8216;S&#8217;;<br />
return Direction;<br />
}</p>
<p><em>그림 31 센서 값으로 방향을 판단하는 함수</em></p>
</div>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>char Direction = DIRECTION();<br />
switch(Direction){<br />
case &#8216;S&#8217;:<br />
Stop();<br />
Serial.println(Direction);<br />
break;<br />
case &#8216;L&#8217;:<br />
turn_left();<br />
Serial.println(Direction);<br />
break;<br />
case &#8216;R&#8217;:<br />
turn_right();<br />
Serial.println(Direction);<br />
break;<br />
case &#8216;G&#8217;:<br />
forward();<br />
Serial.println(Direction);<br />
break;<br />
defalt:<br />
break;<br />
}<br />
delay(50);</p>
<p><em>그림 32 방향에 따른 모터 제어</em></p>
</div>
<p>방향을 판단하는 함수 DIRECTION() 값에 따라 모터를 제어하는 메인 코드이다. 직진으로 판단한 경우 변수 Direction은 ‘G&#8217;이므로 forward() 함수가 실행된다. Direction은 좌회전으로 판단한 경우 ’L&#8217;로 우회전으로 판단한 경우는 ‘R로 나타나며 각각 turn_left() 함수와 turn _right() 함수를 실행한다.</p>
<p><span style="color: #33cccc"><strong>3.3. 개발 환경</strong></span><br />
<span style="color: #00ccff"><strong> 3.3.1. 하드웨어</strong></span><br />
라즈베리파이3, L298N 모터 드라이버, SG-90 서보 모터, 적외선 센서 AM-IRS4D, 아두이노(UNO) 등</p>
<p><span style="color: #00ccff"><strong>3.3.2. 운영체제</strong></span><br />
Window 10, raspbian-jessie</p>
<p><span style="color: #00ccff"><strong>3.3.3. 개발 언어</strong></span><br />
C, C++, 쉘 스크립트(Shell Script)</p>
<p><span style="color: #0000ff"><strong>4. 단계별 제작 과정</strong></span><br />
<span style="color: #33cccc"><strong> 4.1. 아두이노</strong></span></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-23.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34728" alt="45 ict 탐지도우미 (23)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-23.png" width="496" height="319" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-24.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34729" alt="45 ict 탐지도우미 (24)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-24.png" width="496" height="319" /></a><br />
<span style="color: #33cccc"><strong> 4.2. 라즈베리파이</strong></span></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-25.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34730" alt="45 ict 탐지도우미 (25)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-25.png" width="496" height="286" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-26.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34731" alt="45 ict 탐지도우미 (26)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-26.png" width="496" height="256" /></a></p>
<p><span style="color: #33cccc"><strong>4.3. 적외선센서</strong></span></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-27.png" rel="lightbox[34457]"><img class="alignnone  wp-image-34732" alt="45 ict 탐지도우미 (27)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-27.png" width="496" height="256" /></a></p>
<p><span style="color: #33cccc"><strong>4.4. 주행</strong></span></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-4.jpg" rel="lightbox[34457]"><img class="alignnone  wp-image-34707" alt="45 ict 탐지도우미 (4)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-4.jpg" width="496" height="318" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-5.jpg" rel="lightbox[34457]"><img class="alignnone  wp-image-34709" alt="45 ict 탐지도우미 (5)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-ict-탐지도우미-5.jpg" width="496" height="319" /></a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ntrexgo.com/archives/34457/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[45호]은정-Go(인공지능 체스로봇)</title>
		<link>http://www.ntrexgo.com/archives/34452</link>
		<comments>http://www.ntrexgo.com/archives/34452#comments</comments>
		<pubDate>Sat, 25 Nov 2017 00:00:05 +0000</pubDate>
		<dc:creator>디바이스마트 매거진</dc:creator>
				<category><![CDATA[디바이스마트 매거진]]></category>
		<category><![CDATA[특집]]></category>
		<category><![CDATA[45호]]></category>
		<category><![CDATA[ict]]></category>
		<category><![CDATA[공모전]]></category>
		<category><![CDATA[디바이스마트]]></category>
		<category><![CDATA[매거진]]></category>
		<category><![CDATA[융합]]></category>
		<category><![CDATA[입선작]]></category>
		<category><![CDATA[프로젝트]]></category>

		<guid isPermaLink="false">http://www.ntrexgo.com/?p=34452</guid>
		<description><![CDATA[디바이스마트 매거진 45호 &#124; 은정-Go는 인공지능 알고리즘을 이용한 지능형 체스 로봇으로 대국 게임이라는 것이 꼭 컴퓨터 상의 소프트웨어만으로 동작 되는 것이 아닌, 하드웨어를 포함한즉, 로봇과 소프트웨어를 접목시킴을 제시하고 있습니다. ]]></description>
				<content:encoded><![CDATA[<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-1.png" rel="lightbox[34452]"><img class="alignnone size-large wp-image-34640" alt="45 feature 은정고 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-1-620x233.png" width="620" height="233" /></a></p>
<p><span style="font-size: large"><strong>2017 ICT 융합 프로젝트 공모전 입선작</strong></span></p>
<p><span style="font-size: x-large"><strong>은정-Go(인공지능 체스로봇)</strong></span></p>
<p style="text-align: right">글 | 단국대학교 김은정, 김건희, 곽영우</p>
<p>&nbsp;</p>
<p><span style="color: #0000ff"><strong>1. 심사평</strong></span><br />
<strong>칩센</strong> 얼핏 보기에 구성 방법이 쉬워 보이지만, 기본 회로부 하드웨어의 구성, 모터 및 전자석을 이용한 말의 이동 등을 고려하면 machanic 부분뿐만 아니라 일부 머신 러닝까지 고려가 된 복잡한 작품으로 판단됩니다. 나름대로 고민하여 구성했을 알고리즘을 문서로 표현하기 위해 어려운 부분은 충분히 이해하지만, 알고리즘에 대한 설명이 너무 단편적이고 부족해 보이는 것도 사실입니다. 하지만 다시 말하지만 기술적으로 많은 부분에 대해 충분한 고려를 하고, 이를 융합하고자 시도한 기술 구성적 난이도 부분은 충분히 잘 했다고 생각됩니다.</p>
<p><strong>뉴티씨</strong> 체스 인공지능을 실제 눈으로 보이게 해서 꽤나 흥미로웠습니다. 이 기술을 이용하면, 알파고 같은 인공지능 체스 기사가 나올 수도 있을 것 같습니다. 파이팅입니다.</p>
<p><strong>위드로봇</strong> 재미있는 아이디어로 완성도 있는 결과까지 잘 도달한 것 같습니다. 전시회에서 많은 인기를 끌 것 같습니다.</p>
<p><span style="color: #0000ff"><strong>2. 작품 개요</strong></span><br />
세계가 주목하고 있는 제4차 산업혁명의 주요 의제 중 하나일 만큼 인공지능 로봇은 이슈가 되고 있습니다. 실제로도 인공지능을 이용한 자율 주행 자동차도 도로에 나오고, 인공지능 바둑 알고리즘 알파고와 사람의 대결도 많은 이슈를 받았습니다. 은정-고는 인공지능 체스 알고리즘을 이용한 체스 로봇입니다. 현재 알까기 로봇이나, 장기 로봇 등 여러 대국 게임 작품들을 살펴보면, MFC만을 사용하여 하드웨어적인 요소가 없거나 영상 처리 등을 이용하여 프레임이나 집게 손이 장기 말을 이동하는 방식이 대부분을 차지합니다.</p>
<p>은정-고는 거기에 보다 사람들의 흥미를 유발할 수 있게 업그레이드하여 프레임을 감췄습니다. 겉으로 보기에는 체스 판위에 웹캠도 없이, 프레임도 없이 체스 말만 올라와 있지만 Computer와 User의 대국이 시작되면 User가 말을 움직이면, 그에 따라 Computer의 말이 저절로 움직여 대응을 하기 시작합니다.</p>
<p><span style="color: #0000ff"><strong>3. 작품 설명</strong></span><br />
<span style="color: #33cccc"><strong>3.1. 주요 동작 및 특징</strong></span><br />
은정-Go는 인공지능 알고리즘을 이용한 지능형 체스 로봇으로 대국 게임이라는 것이 꼭 컴퓨터 상의 소프트웨어만으로 동작 되는 것이 아닌, 하드웨어를 포함한, 즉 로봇과 소프트웨어를 접목시킴을 제시하고 있습니다.</p>
<p>대부분의 인공지능 알고리즘과 하드웨어를 접목시킨 대국 로봇을 보자면, 대부분 영상처리를 이용하여 말을 파악하고, 집게 팔 같은 외관상 보이는 프레임으로 말을 움직이는 형태를 갖추고 있습니다. 그러나 은정-Go는 웹캠이 필요한 영상처리 대신 마그네틱 센서와 자석을 이용하여 말의 위치를 파악했습니다. 또한 외관상 보이는 집게 팔 대신 전자석과 스텝 모터 제어를 통해 프레임을 체스판 밑으로 감췄습니다. 따라서 오락적인 요소를 더욱 높였습니다. 또한 몇 수 앞을 내다볼 수 있는 Min-Max Algorithm을 개선한 Alpha-Beta Algorithm을 사용하여 Computer의 말들에게 이점이 되는 node만 선택하여 효율적으로 탐색하는 알고리즘을 사용하였습니다. 추가적으로 평가 함수를 사용하여 보다 지능적인 판단을 하여 더욱 유리한 대국을 이끌 수 있게 하였습니다.</p>
<p>전체적인 과정은 다음과 같습니다. User가 말을 이동하였으면, 그 좌표 값을 CPU의 gpio 핀에 연결된 마그네틱 센서가 인식을 한 후 말의 위치를 파악합니다. 센서로 파악된 말의 위치를 판별한 후 유리한 대국인지 먼저 평가를 합니다. 그 후 몇 수 앞을 내다봐서 가장 유리한 node를 찾는 알고리즘을 돌린 후 Computer의 말이 움직여야할 좌표 값을 찾습니다. 좌표 값을 찾은 후 진동이 적고 저속에서 토크가 쎈 바이폴라 스텝 모터 2개를 사용하여 x축, y축으로 이동합니다. 이때 User의 말을 먹게 되는 경우 User의 말은 밖으로 먼저 나온 뒤 모터가 움직이게 됩니다.</p>
<p><span style="color: #00ccff"><strong>3.1.1. H / W</strong></span><br />
<span style="color: #000000"><strong>Ⅰ. 센서부</strong></span><br />
센서는 자석의 성질을 이용한 magnetic reed switch를 사용하였습니다. reed switch는 접점 부분이 비활성 가스를 충전한 유리관 속에 봉인되어 있는 스위치로, 자성이 가해지면 양쪽의 reed에 N극과 S극이 유도되어 자기 흡인력에 의해 reed가 붙게 됩니다. 따라서 전류가 흐르게 되고 자계가 소거되면 reed의 탄성에 의해 접점이 복귀됩니다. 따라서 각각의 말에 자석을 부착한 후 그 자성에 의해 좌표 값을 알게 됩니다. 그림 3에서 볼 수 있듯이 자석이 reed switch 중앙에 위치할 경우는 OFF가 되고 그 외에는 ON이 되므로 ON의 범위를 더 높이기 위해 체스 칸의 사이드에 센서를 부착하였습니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-2.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34641" alt="45 feature 은정고 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-2.png" width="496" height="328" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-3.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34642" alt="45 feature 은정고 (3)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-3.png" width="496" height="322" /></a></p>
<p>8*8로 이루어진 나무판 위에 센서를 64개 부착한 뒤 가로 세로를 각각 한 줄에 이어 CPU의 GPIO pin에 연결하였습니다. 이 때 reed switch만 연결 할 경우 다른 GPIO out pin으로 역류하는 ghost activations이 발생하므로 그것을 막기 위해 다이오드 1N4148을 부착하였습니다. 또한 구조상으로 센서를 빨간색 원안에 놓으면 ON이 되기 때문에 체스 칸의 길이인 55mm와 reed switch의 길이인 12mm를 고려하여 OFF되지 않는 범위 내에 말이 존재하도록 자석은 6mm인 네오디움 자석을 부착하였습니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-4.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34643" alt="45 feature 은정고 (4)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-4.png" width="496" height="299" /></a></p>
<p><strong><span style="color: #000000">Ⅱ. 모터부</span></strong><br />
스텝 모터에는 크게 유니폴라 스텝 모터와 바이폴라 스텝 모터가 있습니다. 유니폴라 스텝 모터는 각각의 코일에 1개의 권선만 지나기 때문에 1방향 밖에 전류가 흐르지 않습니다. 바이폴라 스텝 모터는 각각의 코일에 2개의 권선이 지나기 때문에 방향성이 존재하게 됩니다. 유니폴라 스텝 모터는 회로가 간단하며 고속 회전시 토크가 높습니다. 반대로 바이폴라 스텝 모터는 회로가 복잡하나 저속 회전시 토크가 높습니다. 따라서 저속 구동시 토크가 높고, 진동이 덜한 바이폴라 스텝 모터를 사용하기로 하였습니다. 모터 드라이버는 L298N을 사용하여 제어를 하였고, 2상 4선식 42BYGH2673A-C 바이폴라 스텝 모터 두 개를 사용하여 각각 x축과 y축을 이동하였습니다. 스텝 모터의 제어는 1상 여자 방식, 2상 여자 방식, 1-2상 여자 방식이 있는데, 선택에 따라 입력 pulse와 step의 특성이 달라집니다. 1상은 출력토크가 낮다는 단점이 있었고, 2상은 1상에 비해 배의 전원 용량이 필요하나 토크가 높아 난조가 일어날 확률이 낮습니다. 따라서 1상과 2상을 반씩 합쳐놓은 부드러우면서 토크가 적당히 쎈 1-2상 방식을 사용했습니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-5.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34644" alt="45 feature 은정고 (5)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-5.png" width="496" height="458" /></a></p>
<p><span style="color: #000000"><strong>Ⅲ. 하드웨어 설계</strong></span><br />
하드웨어를 체스 판 아래에 감추기 위해서 보통의 대국 로봇과 다르게 하드웨어 설계를 했습니다. 우선, 모터 제어에 정확도를 높이기 위해 톱니를 내었고, 그에 맞는 레일을 깔았습니다. 그 후 모터가 바닥과 수평이 되게끔 철봉으로 수평을 맞췄습니다. 모터에 철봉을 관통하는 지지대를 고정시킨 후 모터를 작동시키면 수평으로 나아가게 하였습니다. Solid Works로 작업한 사진 속 그림 10번은 모터가 움직이는 한 세트이고, 그 세트가 총 두 개가 존재합니다. 밑에 있는 y축 모터 위에는 x축 모터를 부착하였고, x축 모터 위에는 전자석을 장착하여 알고리즘을 돌리고 난 후 나온 좌표 값에 의해 밑에 자석이 붙어 있는 말이 움직이게 됩니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-6.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34645" alt="45 feature 은정고 (6)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-6.png" width="496" height="234" /></a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="color: #00ccff"><strong>3.1.2. Algorithm</strong></span><br />
<strong>Ⅰ. 센서부</strong><br />
우선, 자석이 붙은 말의 위치를 reed switch 센서로 알아내야 합니다. 첫째로 switch-case 문을 사용하여 LOW0번부터 LOW7번까지 순서대로 ON 시킨 후 OFF 시키는 것을 반복하였습니다. LOW0가 ON이 되어 있을 때 COL0번부터 COL7번까지 탐색한 후 OFF 시키고, 다음에 LOW1이 ON이 되어 있을 때 또 COL0번부터 COL7번까지 탐색한 후 OFF 시키고 그 다음을 반복하였습니다. 자석이 체스 칸 위에 있을 경우 reed switch 센서의 접점이 물려 전류가 흐르게 되고, LOW는 0부터 7까지 ON, OFF를 반복하므로 COL의 GPIO pin에 신호가 오게 되면, x축의 좌표 값은 LOW의 GPIO pin, y축의 좌표 값은 COL의 GPIO pin임을 알 수 있습니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-2.jpg" rel="lightbox[34452]"><img class="alignnone  wp-image-34647" alt="45 feature 은정고 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-2.jpg" width="496" height="298" /></a></p>
<p>둘째로, 밑에 자석이 붙은 말이 움직임에 따라 0과 1로만 나타내는 것이 아니라 User가 말을 들어 올린 말이 User의 말인지 Computer의 말인지의 정보도 있어야 합니다. 또한 어떠한 말인지의 정보도 있어야 합니다. 우선 말이 들어 올린 경우를 구분하기 위해 position을 pre와 next로 나눴습니다. 말이 들어 올려진 경우는 위치 정보가 1이었다가 0이 됩니다. 또한 말이 내려진 경우는 위치 정보가 0이었다가 1이 됩니다. pre_position은 말이 움직이기 전 상태를 말합니다. 만약 pre_position이 1인 말의 next_position이 0이 될 경우 들어올려진 경우로 파악합니다. 또한 pre_position이 0인 말의 next_position이 1이 될 경우는 들어 올려진 말이 내려진 경우라고 파악합니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-7.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34648" alt="45 feature 은정고 (7)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-7.png" width="496" height="278" /></a></p>
<p>셋째로, 들어 올려진 말이 User의 말인지 Computer의 말인지 구분하기 위해 체스의 처음 정보, 즉 처음 세팅 값은 룰로써 정해져 있기 때문에 User은 -의 점수 Computer은 +의 점수로 구분하였습니다. 또한 말들마다 점수의 차이를 주어 말마다의 가치를 나눴습니다. 그 정보들을 User가 말을 들어 올릴 때 다른 변수에 저장을 해놓고 User가 말을 내려놓을 때 그 정보들을 입력 받는 알고리즘을 짰습니다. 이 때 말을 들어 올리는 행위는 User만 하는 행위이므로 만약 검은색 말, 즉 +의 점수가 들어 올려 졌을 경우는 User가 Computer의 말을 먹는 상황이라고 파악합니다.</p>
<p><strong>Ⅱ. 모터부</strong><br />
우선, 스텝 모터의 기본 원리는 상을 순서대로 ON 시키면서 회전을 하는 것입니다. 사용한 모터는 2상 4선식 바이폴라 모터이므로 A, B, A&#8217;, B&#8217;의 순서로 GPIO pin에 연결하였고, CPU의 Timer Interrupt를 사용하여 차례대로 GPIO pin을 ON시켜 모터를 구동 하였습니다. Timer Interrupt 주기는 10로 설정하였고 모터의 속도와 가속도를 설정하기 위해 가속도 공식을 사용하여 수식을 세워 함수로 만들었습니다.</p>
<p>말을 움직이기 위해서는 x축의 모터가 -x방향과 +x방향 둘다 자유자재로 움직일 수 있어야 합니다. 이때 스텝 모터는, 구동을 하던 방향을 바꾸기 위해서는 ON 시켜주는 GPIO pin의 순서를 반대로 해주면 되므로, Timer Interrupt를 멈춘 뒤 순서를 반대로 해주었습니다. y축 모터도 마찬가지 이므로 모터를 움직이는 함수는 총 4개로 모터의 마지막 위치에서 가야할 위치에 따라 +x+y, -x+y, +x-y, -x-y 를 판별한 후 모터를 구동하였습니다.</p>
<p><strong>Ⅲ. AI Algorithm</strong><br />
체스 알고리즘은 몇 수 앞을 내다볼 수 있는 Min-Max 알고리즘을 바탕으로 구현하였습니다. Min-Max 알고리즘이란 Computer가 유리한 값은 크고, User가 유리한 값은 작게 가치를 두어 노드를 뻗어나가는 알고리즘입니다.</p>
<p>즉, Max는 Computer, Min은 User입니다. 노드의 깊이를 3이라고 두면, 마지막 노드에서부터 위로 올라가면서 Max, Min을 판별하여 가장 가치 있는 노드를 선택합니다. 어미가 Min인 경우 뻗어나간 자식들 중 제일 작은 가치를 고릅니다.</p>
<p>이것은 User가 경우의 수 들 중 가장 User에게 유리한 경우를 선택한다고 가정한 선택입니다. 그리고 그 위에 올라가면 어미가 Max가 되는데, Max는 Computer가 둘 수 있는 가장 좋은 경우이기 때문에 똑같이 Max는 Computer가 둘 수 있는 수 중 가장 가치가 높은 노드를 선택할 것입니다. 이렇게 다른 경우의 수, 다른 노드까지 탐색을 하면 Computer가 선택할 수 있는 수 중 가장 가치 있는 노드가 나옵니다. 이것만으로 해서도 게임이 이루어질 수 있지만 깊이가 깊어질수록, 엄청난 노드의 수로 게임이 처질 수 있습니다. 따라서 Alpha-Beta 가지치기를 이용하여 Min-Max 알고리즘에서 탐색시간과 탐색 효율을 높여주는 알고리즘을 이용하였습니다. Alpha-Beta 가지치기란, 예를들면 Min에서는 가장 작은 가치를 선택하지만, Max에서는 가장 큰 가치를 선택하기 때문에 만약 Min의 가치가 300이면 다음 Max는 300보다 작은 200을 선택하지 않을 것이고, 또한 다른 자식노드의 자식노드가 200보다 작은 값이 나와도 Max로는 선택을 하지 않을것이 때문입니다. 따라서 효율이 떨어지는 가지는 Cut off되어 더 이상 탐색하지 않게 됩니다.</p>
<p>알고리즘 구현에 필요한 것들은 여러 가지가 있습니다. 알고리즘의 효율을 높여주는 Cut off함수(노드를 뻗어나가다가 가지 않아도 되는 경우에 가지를 쳐서 더 이상 뻗어 나가지 않도록 하는 함수)와 각 말들이 움직일 수 있는 경로를 만든 함수, 게임의 종료 여부를 결정하는 종단함수 등이 있습니다. 그러나 이 모든 것을 훌륭히 구현했다고 해도 지능 수준이 낮다면 게임이 이루어지지 않습니다. 따라서 이 문제를 해결하기 위해 정석 함수와 평가 함수에 대한 고찰이 이루어 졌습니다. 정석 함수란 정석적으로 기능을 부여하기 위한 함수이며 평가 함수에서는 우선적인 상대적 우열을 가린 후 동일한 가치를 가지는 국면들을 정석 함수를 이용하여 2차적 우열을 가리는 방식을 사용하여 지능적인 요소를 더욱 높였습니다.</p>
<p>우선, 알고리즘을 돌리기에 앞서 지금 대국이 Computer에게 유리한 대국인지를 평가 함수를 이용하여 판단합니다. 만약 User에게 먹힐 위험이 있는 말이 있는 경우의 가치를 더 크게 판단합니다. 그 다음 정석 함수를 이용하여 노드를 뻗어나가고, 그 가치를 밑에서부터 평가하면서 올라오게 됩니다. 또한 Computer의 말이 먹히더라도 만약 가치를 교환할만한 국면이면 그 노드에 가치를 더하는 함수도 추가하여 지능을 높였습니다.<br />
다음은 예시입니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-8.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34649" alt="45 feature 은정고 (8)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-8-525x620.png" width="420" height="496" /></a></p>
<p>&nbsp;</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-9.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34650" alt="45 feature 은정고 (9)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-9.png" width="446" height="329" /></a></p>
<p>Computer의 각각 말이 갈 수 있는 좌표 값과 그 좌표 값에서 인공지능으로 갈 수 있는 모든 경우의 수를 우선 저장시킵니다. 그리고 Cut off 함수와 Alpha-Beta알고리즘을 적용시켜 가장 유리한 움직임을 저장시킵니다. 그리고 앞서 처음에 했던 평가 함수와 적용시킵니다.</p>
<p>다음은 평가 함수 내에서도 평가를 하는 경우입니다. 그림21의 초록색 화살표는 처음의 먹힐 가능성이 있는 Computer의 말이 먹을 수 있는, 또는 피할 수 있는 경우가 나타납니다. 파란색 화살표는 AI알고리즘을 통해 말이 이동한 경우 먹힐 가능성이 있는 것을 나타내었습니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-10.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34651" alt="45 feature 은정고 (10)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-10.png" width="496" height="426" /></a></p>
<p>말이 죽지 않는 경우를 더 큰 가치로 둔다면 (1,3)의 비숍이 (4,0)의 룩을 먹는 것이 가장 큰 가치이겠지만 만약 User의 말을 먹는 경우, User의 가치가 높은 말을 먹는 가치를 중점으로 둔다면 Computer의 비숍이 User의 나이트에게 먹힐 위험이 있더라도 User의 퀸을 먹을 것입니다. 저는 우선적으로 후자에 더 큰 비중을 두어 결과는 다음과 같습니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-11.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34652" alt="45 feature 은정고 (11)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-11.png" width="496" height="152" /></a></p>
<p><strong><span style="color: #33cccc">3.2. 전체 시스템 구성</span></strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-12.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34653" alt="45 feature 은정고 (12)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-12.png" width="496" height="219" /></a></p>
<p><span style="color: #33cccc"><strong>3.3. 개발 환경(개발 언어, Tool, 사용 시스템 등)</strong></span><br />
저는 많은 개발툴 중에서 ti사의 tms320f2809pza를 제어하기 위해 source insight를 이용하였습니다. source insight는 언어로 개발 시 수천개에서 수만개의 소스코드를 포함하여 코드를 분석하고 디버깅 및 개발할때 이만한 툴이 없기 때문에 source insight를 사용하였습니다.<br />
또한 teraterm이라는 프로그램을 이용하여 pc와 ti 사의 tms320f2809보드 간에 sci 통신을 하여 ti사의 보드에 접근할 수 있었습니다.<br />
이로 인해 Printf를 이용하여 에러처리나 알고리즘 확인 및 디버깅을 할수 있었습니다.</p>
<p><span style="color: #0000ff"><strong>4. 단계별 제작 과정</strong></span><br />
전자전기공학부 학부생으로써 자료구조 공부나 AI 공부를 하는 동안 하드웨어를 먼저 완성하였습니다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-13.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34654" alt="45 feature 은정고 (13)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-13.png" width="496" height="146" /></a></p>
<p><span style="color: #0000ff"><strong>5. 기타(회로도, 소스코드, 참고문헌 등)</strong></span><br />
<span style="color: #00ccff"><strong>5.1.1. 소스코드</strong></span><br />
<strong>Ⅰ. 센서</strong><br />
(1) reed switch로 말의 위치 알아낸 후 정보 저장<br />
센서의 위치를 알아내는 소스</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void Position_check()<br />
{<br />
Uint16 i,j = 0;<br />
switch(COL)<br />
{<br />
case 0 : GpioDataRegs.GPASET.bit.GPIO20 = 1; break;<br />
case 1 : Check_0ROW(); GpioDataRegs.GPACLEAR.bit.GPIO20 = 1; break;<br />
case 2 : GpioDataRegs.GPASET.bit.GPIO21 = 1; break;<br />
case 3 : Check_1ROW(); GpioDataRegs.GPACLEAR.bit.GPIO21 = 1; break;<br />
case 4 : GpioDataRegs.GPASET.bit.GPIO22 = 1; break;<br />
case 5 : Check_2ROW(); GpioDataRegs.GPACLEAR.bit.GPIO22 = 1; break;<br />
case 6 : GpioDataRegs.GPASET.bit.GPIO23 = 1; break;<br />
case 7 : Check_3ROW(); GpioDataRegs.GPACLEAR.bit.GPIO23 = 1; break;<br />
case 8 : GpioDataRegs.GPASET.bit.GPIO24 = 1; break;<br />
case 9 : Check_4ROW(); GpioDataRegs.GPACLEAR.bit.GPIO24 = 1; break;<br />
case 10 : GpioDataRegs.GPASET.bit.GPIO25 = 1; break;<br />
case 11 : Check_5ROW(); GpioDataRegs.GPACLEAR.bit.GPIO25 = 1; break;<br />
case 12 : GpioDataRegs.GPASET.bit.GPIO26 = 1; break;<br />
case 13 : Check_6ROW(); GpioDataRegs.GPACLEAR.bit.GPIO26 = 1; break;<br />
case 14 : GpioDataRegs.GPASET.bit.GPIO27 = 1; break;<br />
case 15 : Check_7ROW(); GpioDataRegs.GPACLEAR.bit.GPIO27 = 1; break;<br />
case 16 : Data_change(); break;</p>
<p>default : break;<br />
}<br />
COL++;<br />
DELAY_US(10000);<br />
if(COL &gt; 16)<br />
COL = 0;<br />
</div>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void Check_0ROW()<br />
{<br />
if( ROW_0 ) next_position[0][0] = 1;<br />
else next_position[0][0] = 0;<br />
if( ROW_1 ) next_position[0][1] = 1;<br />
else next_position[0][1] = 0;<br />
if( ROW_2 next_position[0][2] = 1;<br />
else next_position[0][2] = 0;<br />
if( ROW_3 ) next_position[0][3] = 1;<br />
else next_position[0][3] = 0;<br />
if( ROW_4 ) next_position[0][4] = 1;<br />
else next_position[0][4] = 0;<br />
if( ROW_5 ) next_position[0][5] = 1;<br />
else next_position[0][5] = 0;<br />
if( ROW_6 ) next_position[0][6] = 1;<br />
else next_position[0][6] = 0;<br />
if( ROW_7 ) next_position[0][7] = 1;<br />
else next_position[0][7] = 0;<br />
}<br />
</div>
말의 정보를 저장 및 바꾸는 함수</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void Data_change()<br />
{<br />
int16 i,j = 0;</p>
<p>for(i = 0 ; i &lt; 8 ; i++)<br />
{<br />
for(j = 0 ; j &lt; 8 ; j++)<br />
{<br />
if( (pre_position[i][j] &#8211; next_position[i][j]) &gt; 0 ) //들어올렷을ㄷ대<br />
{<br />
if(chess_position[i][j] &gt; W_MIN)<br />
{<br />
pre_position[i][j] = 0;<br />
chess_position[i][j] = 0;<br />
chess_score[i][j] = 0;<br />
continue;<br />
}<br />
prei = i;<br />
prej = j;</p>
<p>data_change = chess_position[i][j];<br />
score_change = chess_score[i][j];</p>
<p>chess_position[i][j] = 0;<br />
chess_score[i][j] = 0;<br />
TxPrintf(&#8220;pre : [%d][%d] &gt;&gt; &#8220;,i,j);<br />
pre_position[i][j] = next_position[i][j]; //000</p>
<p>g_u16_position_cnt++;<br />
}<br />
else if( (pre_position[i][j] &#8211; next_position[i][j]) &lt; 0 ) //내렷을ㄷ대<br />
{<br />
nexti = i;<br />
nextj = j;</p>
<p>chess_position[i][j] = data_change;<br />
chess_score[i][j] = score_change;</p>
<p>TxPrintf(&#8220;next : [%d][%d]\n&#8221;,i,j);<br />
pre_position[i][j] = next_position[i][j]; //111<br />
g_u16_position_cnt++;<br />
first_cnt++;</p>
<p>Evaluate();</p>
<p>for(i = 0 ; i &lt; 8 ; i++)<br />
{<br />
for(j = 0 ; j &lt; 8 ; j++)<br />
{<br />
TxPrintf(&#8220;%d &#8220;, chess_score[7-i][7-j]);<br />
}<br />
TxPrintf(&#8220;\n\n&#8221;);<br />
}<br />
TxPrintf(&#8220;\n&#8221;);<br />
}<br />
else;<br />
}<br />
}<br />
}<br />
</div>
<p><strong>(2) 모터</strong><br />
motor의 속도, 가속도를 제어하기 위한 함수</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void Motor_CalBaseMotionValue(mot_str*pM )<br />
{<br />
if(pM-&gt;iqNextV &lt; pM-&gt;iqTargetV)<br />
{<br />
pM-&gt;iqVelo = pM-&gt;iqNextV;<br />
pM-&gt;iqAmpyS = _IQmpy(STEP_2D, pM-&gt;iqTargetA);//2as<br />
pM-&gt;iqNextV = _IQ6toIQ( _IQ6sqrt( _IQtoIQ6(pM-&gt;iqAmpyS) + _IQ6mpy(_IQtoIQ6(pM-&gt;iqVelo),_IQtoIQ6(pM-&gt;iqVelo))));//2as=v^2-v0^2</p>
<p>pM-&gt;iq24TargetA_1 = ( _IQmpy(pM-&gt;iqTargetA, _IQ(0.01)) &lt;&lt; 7);<br />
pM-&gt;iq24TargetA_1 = _IQ24div(_IQ24(1.0), pM-&gt;iq24TargetA_1);<br />
pM-&gt;iq24TargetA_1 = _IQ24mpy(pM-&gt;iq24TargetA_1, _IQ24(0.01));</p>
<p>pM-&gt;iq24TimeValue = _IQ24mpy( _IQtoIQ24((pM-&gt;iqNextV &#8211; pM-&gt;iqVelo)), pM-&gt;iq24TargetA_1);</p>
<p>pM-&gt;u32_Period = (Uint32)( _IQmpy( _IQ17mpyIQX( _IQ1(100000), 1, pM-&gt;iq24TimeValue, 24), _IQ(1.0) ) &gt;&gt; 17); //pM-&gt;iqHandle<br />
pM-&gt;u32_Period_Cnt = 0;</p>
<p>if( pM-&gt;iqTargetV &lt;= pM-&gt;iqNextV )<br />
pM-&gt;iqNextV = pM-&gt;iqTargetV;<br />
}<br />
else // pM-&gt;iqNextV &gt; pM-&gt;iqTargetV<br />
{<br />
pM-&gt;iqVelo = pM-&gt;iqNextV;<br />
pM-&gt;iqAmpyS = _IQmpy(STEP_2D, pM-&gt;iqTargetA);<br />
pM-&gt;iqNextV = _IQ6toIQ( _IQ6sqrt( _IQ6mpy(_IQtoIQ6(pM-&gt;iqVelo),_IQtoIQ6(pM-&gt;iqVelo)) &#8211; _IQtoIQ6(pM-&gt;iqAmpyS)));<br />
pM-&gt;iq24TargetA_1 = ( _IQmpy(pM-&gt;iqTargetA, _IQ(0.01)) &lt;&lt; 7);<br />
pM-&gt;iq24TargetA_1 = _IQ24div(_IQ24(1.0), pM-&gt;iq24TargetA_1);<br />
pM-&gt;iq24TargetA_1 = _IQ24mpy(pM-&gt;iq24TargetA_1, _IQ24(0.01));<br />
pM-&gt;iq24TimeValue = _IQ24mpy( _IQtoIQ24((pM-&gt;iqVelo &#8211; pM-&gt;iqNextV)), pM-&gt;iq24TargetA_1);</p>
<p>pM-&gt;u32_Period = (Uint32)( _IQmpy( _IQmpyIQX( _IQ1(100000), 1, pM-&gt;iq24TimeValue, 24), _IQ(1.0) ) &gt;&gt; 17);<br />
pM-&gt;u32_Period_Cnt = 0;<br />
if( pM-&gt;iqTargetV &gt;= pM-&gt;iqNextV )<br />
pM-&gt;iqNextV = pM-&gt;iqTargetV;}<br />
}<br />
</div>
Timer Interrupt를 이용해서 모터를 제어하는 함수</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>interrupt void ISR()<br />
{<br />
if(g_u16_Xmotor_flag == ON)<br />
{<br />
if( ++XMotor.u32_Period_Cnt &gt;= XMotor.u32_Period)<br />
{<br />
Motor_CalBaseMotionValue( &amp;XMotor );<br />
X_MOTOR;<br />
}<br />
}<br />
else if(g_u16_XBmotor_flag == ON)<br />
{<br />
if( ++XMotor.u32_Period_Cnt &gt;= XMotor.u32_Period)<br />
{<br />
Motor_CalBaseMotionValue( &amp;XMotor );<br />
X_BMOTOR;<br />
}<br />
}<br />
else if(g_u16_Ymotor_flag == ON)<br />
{<br />
if( ++YMotor.u32_Period_Cnt &gt;= YMotor.u32_Period)<br />
{<br />
Motor_CalBaseMotionValue( &amp;YMotor );<br />
Y_MOTOR;<br />
}<br />
}<br />
else if(g_u16_YBmotor_flag == ON)<br />
{<br />
if( ++YMotor.u32_Period_Cnt &gt;= YMotor.u32_Period)<br />
{<br />
Motor_CalBaseMotionValue( &amp;YMotor );<br />
Y_BMOTOR;<br />
}<br />
}<br />
else<br />
{<br />
MOTOR_OFF;<br />
}</p>
<p>}<br />
</div>
모터를 +x, +y로 움직이기 위한 함수</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void MoveXY(int16 x, int16 y)<br />
{<br />
int16 X = (int16)(_IQ17mpy(_IQ17(abs(x)),_IQ17(167))&gt;&gt;17) &#8211; 83;<br />
int16 Y = (int16)(_IQ17mpy(_IQ17(abs(y)),_IQ17(167))&gt;&gt;17) &#8211; 83;<br />
DELAY_US(1000);<br />
StartCpuTimer2();</p>
<p>while(1)<br />
{<br />
g_u16_Xmotor_flag = ON;<br />
DELAY_US(1);<br />
if(g_u16_Xstep &gt; X)<br />
{<br />
XMotor.iqTargetV = _IQ(0.0);<br />
XMotor.iqTargetA = _IQ(1200.0);<br />
g_u16_Xmotor_flag = OFF;<br />
MOTOR_OFF;<br />
StopCpuTimer2();<br />
motor_vari_init();<br />
DELAY_US(100000);<br />
break;<br />
}<br />
}</p>
<p>DELAY_US(1000);<br />
StartCpuTimer2();<br />
while(1)<br />
{<br />
g_u16_Ymotor_flag = ON;<br />
DELAY_US(1);<br />
if(g_u16_Ystep &gt; Y)<br />
{<br />
YMotor.iqTargetV = _IQ(0.0);<br />
YMotor.iqTargetA = _IQ(1200.0);<br />
g_u16_Ymotor_flag = OFF;<br />
MOTOR_OFF;<br />
StopCpuTimer2();<br />
motor_vari_init();<br />
DELAY_US(100000);<br />
break;<br />
}<br />
}<br />
}<br />
</div>
<p><strong>(3) Algorithm</strong></p>
<p>Min-Max 알고리즘을 기반으로 한 Alpha-Beta Algorithm</p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>void MAX_min()<br />
{<br />
int16 i,j,k = 0;<br />
Value.Alpha = 0;<br />
Value.To_x = 0;<br />
Value.To_y = 0;<br />
Value.From_x = 0;<br />
Value.From_y = 0;</p>
<p>for(i = 0 ; i &lt; Chess_cnt_1st ; i++)<br />
{<br />
if(Cnt_2nd[i].dont == 1) //먹힐때<br />
continue;</p>
<p>TxPrintf(&#8220;\n1&gt;&gt; Cnt : %d (%d,%d) &gt; (%d,%d) Value : %d\n&#8221;,Node_1st[i].Cnt,Node_1st[i].From_x,Node_1st[i].From_y,Node_1st[i].To_x,<br />
Node_1st[i].To_y,Node_1st[i].Chess_value);<br />
min(i);<br />
if(Node_1st[i].Chess_value &gt; Value.Alpha)<br />
{<br />
Value.Alpha = Node_1st[i].Chess_value;<br />
Value.cnt = i;<br />
Value.To_x = Node_1st[i].To_x;<br />
Value.To_y = Node_1st[i].To_y;<br />
Value.From_x = Node_1st[i].From_x;<br />
Value.From_y = Node_1st[i].From_y;</p>
<p>//TxPrintf(&#8220;\n\nFINAL cnt : %d (%d,%d) -&gt; (%d,%d) score : %d\n\n&#8221;,Value.cnt,Value.From_x,Value.From_y,Value.To_x,Value.To_y,Value.Alpha);<br />
}<br />
}<br />
if(Value.Alpha &lt; 1000)<br />
{<br />
//점수가 같을경우 2노드에가서 가치평가를 다시함<br />
Value.Beta = Node_2nd[Cnt_2nd[0].First].Chess_value;<br />
for(i = 0 ; i &lt; Chess_cnt_1st ; i++)<br />
{<br />
for(j = Cnt_2nd[i].First ; j &lt;= Cnt_2nd[i].Last ; j++)<br />
{<br />
if(Cnt_2nd[i].dont == 1) //먹힐때<br />
continue;</p>
<p>if(Node_2nd[j].Chess_value &gt; Value.Beta)<br />
{<br />
Value.Alpha = Node_1st[i].Chess_value;<br />
Value.cnt = i;<br />
Value.To_x = Node_1st[i].To_x;<br />
Value.To_y = Node_1st[i].To_y;<br />
Value.From_x = Node_1st[i].From_x;<br />
Value.From_y = Node_1st[i].From_y;<br />
Value.Beta = Node_2nd[j].Chess_value;<br />
Beta_cnt = Node_2nd[j].Cnt;<br />
}<br />
}<br />
}<br />
}<br />
</div>
<p>&nbsp;</p>
<p><strong>Ⅱ. 회로도</strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-14.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34655" alt="45 feature 은정고 (14)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-14.png" width="496" height="294" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-15.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34656" alt="45 feature 은정고 (15)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-15.png" width="496" height="334" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-16.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34657" alt="45 feature 은정고 (16)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-16.png" width="496" height="311" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-17.png" rel="lightbox[34452]"><img class="alignnone  wp-image-34658" alt="45 feature 은정고 (17)" src="http://www.ntrexgo.com/wp-content/uploads/2017/11/45-feature-은정고-17.png" width="496" height="331" /></a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ntrexgo.com/archives/34452/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[44호]참! 잘했어요!</title>
		<link>http://www.ntrexgo.com/archives/34383</link>
		<comments>http://www.ntrexgo.com/archives/34383#comments</comments>
		<pubDate>Fri, 22 Sep 2017 00:00:30 +0000</pubDate>
		<dc:creator>디바이스마트 매거진</dc:creator>
				<category><![CDATA[blog-posts]]></category>
		<category><![CDATA[디바이스마트 매거진]]></category>
		<category><![CDATA[특집]]></category>
		<category><![CDATA[44호]]></category>
		<category><![CDATA[ict]]></category>
		<category><![CDATA[공모전]]></category>
		<category><![CDATA[디바이스마트]]></category>
		<category><![CDATA[매거진]]></category>
		<category><![CDATA[융합]]></category>
		<category><![CDATA[입선작]]></category>
		<category><![CDATA[프로젝트]]></category>

		<guid isPermaLink="false">http://www.ntrexgo.com/?p=34383</guid>
		<description><![CDATA[디바이스마트 매거진 44호 &#124; “참잘했어요”는 일상을 요약하고 분석통계를 통해 하루를 피드백받는 팔찌와 어플리케이션 시스템으로, 데이터를 분석통계하여 사용자에게 보여주어 본인 피드백을 돕는 어플리케이션이다. ]]></description>
				<content:encoded><![CDATA[<p><strong style="font-size: large"><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-2.png" rel="lightbox[34383]"><img class="alignnone size-large wp-image-34772" alt="44 feature 잘했어요 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-2-620x233.png" width="620" height="233" /></a></strong></p>
<p><span style="font-size: large"><strong>2017  ICT 융합 프로젝트 공모전 입선작</strong></span></p>
<p><span style="font-size: x-large"><strong>참! 잘했어요! </strong></span></p>
<p style="text-align: right"><strong>글 | 건국대학교 신한순, 고형규</strong></p>
<p><span style="color: #0000ff"><strong>심사평</strong></span><br />
<strong>칩센</strong> 빅데이터 활용을 위한 스마트폰용 어플리케이션이 주된 내용으로 보이고, 그것을 구현하기 위한 제품에 대한 고민은 크게 많지 않았던 듯 합니다. 음성 인식 및 수집을 위한 장치로 블루투스를 고려하였는데, 만약 블루투스 팔찌가 스마트폰과 연결되어 있는 상태라 치더라도, 사용자가 말하는 자연어가 그대로 전송될 수 있을 만큼 무선 성능이 뒷받침 되는지의 여부와, 대화에서 사용자의 어휘인지 아니면 상대의 어휘인지를 구분해야하는 방법에 대한 고민이 필요할 듯 하고, 휴대용 무선기기의 가장 큰 이슈는 배터리 이슈인데, 이 부분에 대한 고려도 필요합니다. 화자의 단어를 명확하게 인식, 전송 및 sorting이 가능하다면 활용도 면에서는 다양한 방법으로 사용이 가능할 듯 한 가능성은 보입니다.</p>
<p><strong>뉴티씨</strong> 제가 생각하기에 아주 괜찮은 작품 같습니다. 전체적인 완성도와 작품성이 좋습니다. 다만, 이 기능을 어플리케이션으로만 구현해도 되지 않았을까 싶은 작품입니다.</p>
<p><strong>위드로봇</strong> 일상의 음성을 녹음하여 통계적으로 분석한 수치를 제공하는 아이디어가 참신합니다. 음성 인식률이 관건인데, 이 부분에 대한 추가 연구가 필요할 것으로 보입니다.</p>
<p><span style="color: #0000ff"><strong>작품개요</strong></span><br />
<span style="color: #00ccff"><strong>작품 개발동기</strong></span><br />
본 아이템은 일기장이나, 녹음기와 같이 ‘그 날 있었던 일을 쉽게 돌아보면 좋지 않을까’라는 생각으로 생각하게 되었다. 사람은 24시간 필기하며 다니지 않는 이상 당일 무슨 일이 있었는지, 대화나 회의의 내용은 어떠했는지, 어떤 실수를 했는지, 말버릇이나 습관이 어떤 지를 본인 스스로 잘 알 수 없다.<br />
위와 같은 기억하지 못하는 상황을 해결함과 동시에 이를 분석해 여러 그래픽 통계자료로 보여준다면 유의미한 결과가 있을 것이라 생각했다. 사용자가 오늘에 있어 가장 관심이 있었던 키워드는 무엇인지, 의도적으로 피하려고 했던 키워드는 무엇인지를 녹음과 분석을 통해 알 수 있다는 것은 보다 쉽고 간편하게 사용자의 하루를 피드백 해주는 것이라 생각했다. 따라서 이러한 사용자에게 일상을 피드백 해 생활에 도움이 될 수 있도록 하는 팔찌와 어플리케이션 시스템을 만들어보고자 한다.</p>
<p><span style="color: #00ccff"><strong>추진 배경</strong></span><br />
본 팀은 건국대학교 글로컬캠퍼스에 소재하고 있는 컴퓨터공학과 창업동아리 IctShare 팀의 구성원으로 이루어져 있어, 창업 아이디어 창출과 아이템 개발 경험이 많고 관련 기술을 다수 보유하고 있다. 이러한 배경 속에서 아두이노를 이용할 수 있는 숙련자들이 있음과 동시에 빅데이터를 이용한 아이디어로써 창업 아이템을 만들 수 있을 것이라 생각했다.<br />
아두이노와 블루투스 모듈 등 시제품 제작에 필요한 물품 지원은 건국대학교 글로컬캠퍼스 컴퓨터공학과 학과사무실을 통해 지원받을 수 있도록 하고, 어플리케이션은 자체 제작할 수 있도록 한다. 1차 시제품 제작을 통해 아이디어의 효과 검증과 실현 가능성을 실험해보았고, 다음으로 사업성을 검증하기 위해 여러 실험과 설문조사를 진행하고 있다.</p>
<p><span style="color: #0000ff"><strong>아이템 개요</strong></span></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-3.png" rel="lightbox[34383]"><img class="alignnone  wp-image-34773" alt="44 feature 잘했어요 (3)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-3.png" width="496" height="287" /></a></p>
<p>“참 잘했어요”는 일상을 요약하고 분석통계를 통해 하루를 피드백받는 팔찌와 어플리케이션 시스템이다. 팔찌를 통해 일상에서 있는 대화를 녹음하고 이 음성데이터를 스마트폰으로 송신한다. 데이터를 분석통계하여 사용자에게 보여주어 본인 피드백을 돕는 어플리케이션이다. 이를 통해 사용자는 쉽게 본인의 습관, 관심사 등을 파악할 수 있을 것이라 예상하고, 더 나아가 생활 전반의 개선을 가져다 줄 것으로 예상된다.</p>
<p><span style="color: #0000ff"><strong>기대효과</strong></span><br />
이러한 효과를 이용한다면 스스로에게 어떠한 말버릇이 있는지, 실수는 하지 않았는지, 자신이 당장 필요한 것이 무엇인지 등을 확인하여 사용자들의 니즈를 충족시켜 줄 뿐만 아니라 더욱 스마트한 삶을 살 수 있을 것으로 예상된다.<br />
본인의 하루를 피드백할 수 있게 된다. 오늘 자신이 말한 말들을 알 수 있고, 그것을 쉽고 간편한 통계로서 접하게 된다면 일기를 쓰는 것과 같은 효과를 얻을 수 있다고 생각된다. 이를 통해 사용자는 스스로 하루을 피드백을 할 수 있고 실수 혹은 본인이 인지하지 못했던 주제 등을 파악할 수 있다.<br />
위와 마찬가지로 관심사, 습관 등을 파악할 수도 있다. 주간, 월간 통계를 통해서 과거를 쉽게 정리해서 본다는 것은 그를 통해서 앞으로의 일을 예측할 수도 있게 된다. 이에 따라 사용자는 본인의 진로에 대한 흥미로운 자료들을 쉽게 찾을 수 있게 된다.</p>
<p><span style="color: #0000ff"><strong>작품 설명</strong></span><br />
<span style="color: #00ccff"><strong>주요동작 및 특징</strong></span><br />
본 아이템은 사용자가 했던 말을 팔찌를 통해 기능은 녹음과 블루투스 송수신을 하고, 스마트폰에 전송시켜 사용자에게 특정 단어를 얼마나 말했는지, 특정 시간에 있었던 내용은 주로 어떠한 내용이었는지를 알게 해주는 시스템이다.</p>
<p>이는 크게 일간, 주간 통계로 나뉘며 일간 통계의 경우 워드 클라우드와 선 그래프로 보이며 워드 클라우드는 가장 많이 말했던 단어와 크기를 비례해 한 눈에 볼 수 있다. 선 그래프의 경우 시간 단위로 끊어 언제 어떠한 단어를 몇 번 사용하였는지 사용자에게 알려주게 된다. 또한 특정 단어와 같이 말했던 단어들도 볼 수 있다.</p>
<p>주간 통계의 경우 꺾은선 그래프로만 이루어져 있으며 하루 단위로 나누어 사용자에게 어떠한 단어를 얼마나 사용하였는지 알려주게 된다. 이를 이용하여 사용자는 자신의 성향에 대해 알 수 있으며, 당일 있었던 내용을 상기시켜 다시 이해할 수 있고 자신의 악습관을 스스로 피드백 할 수 있는 기회가 생긴다.</p>
<p>팔찌와 연동되는 스마트폰 어플리케이션은 팔찌와 블루투스 연결이 되어있는 동안 수신받는 단어들을 저장하며, 시간, 일간으로 나누어 같은 단어의 개수가 추가될 때마다 카운터를 한 개씩 올리며 통계를 낸 뒤 워드 클라우드와 그래프로 사용자에게 보여주게 된다.</p>
<p>또한 사용자가 특정 단어를 왜 사용하였는지 모르는 경우에 특정 단어를 기준으로 5초 동안의 정보를 단어에 저장하여, 소비자는 왜 그 단어를 사용하였는지 기억할 수 있고 해당 내용에 대해 이해하기가 매우 수월해질 수 있다.</p>
<p>그래프 화면에서 특정 단어를 클릭하게 되면 특정 단어를 당일 얼마나 사용했는지, 주간동안 얼마나 사용하였는지, 월간동안 얼마나 사용하였는지 숫자를 이용한 수치로 사용자에게 보이게 된다. 또한 특정 단어의 화면에서 같이 사용됐던 단어들을 나오게 하여 왜 그러한 단어를 사용하였는지 사용자가 알아차릴 수 있게 도와준다.</p>
<p><span style="color: #0000ff"><strong>하드웨어 개요</strong></span><br />
본 아이템의 팔찌는 기존의 스마트 워치와 비슷한 소재를 사용하여 주 소재는 스테인리스를 사용해 제작할 예정이다. 이를 통해 최대한 사용자에게 거부감 없는, 가벼운 소재의 제품을 제공할 계획이다.<br />
팔찌에는 스마트폰과 통신할 수 있는 5V 블루투스 모듈과, 사용자의 목소리를 수신할 수 있는 마이크가 삽입되어 있다. 팔찌는 수은전지로 전원을 넣게 되며, 블루투스의 전원 버튼을 누르고 스마트폰의 블루투스 모드를 ON하게 되면 팔찌 내의 블루투스와 스마트폰이 페어링을 하게 됨으로써 스마트폰과 블루투스가 통신을 할 수 있게 된다.</p>
<p><span style="color: #0000ff"><strong>소프트웨어 개요</strong></span></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-4.png" rel="lightbox[34383]"><img class="alignnone  wp-image-34774" alt="44 feature 잘했어요 (4)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-4.png" width="254" height="478" /></a></p>
<p>본 아이템은 음성데이터를 분석통계하는 어플리케이션이다. 팔찌로부터 음성을 읽어들여 단어를 저장하고 사용자가 사용했던 단어들을 확인하며 스스로 관리할 수 있도록 한다. 팔찌로부터 단어가 수신될 때마다 모든 단어들을 저장하며 그래프로 통계를 내어 사용자에게 보여주고 단어를 사용한 횟수 또한 사용자에게 보여지게 된다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-5.png" rel="lightbox[34383]"><img class="alignnone  wp-image-34775" alt="44 feature 잘했어요 (5)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-5.png" width="496" height="476" /></a></p>
<p>본 어플리케이션의 첫 화면인 일간통계 화면에서의 워드 클라우드는 정해놓은 여러 가지의 색을 기준으로 특정 단어의 카운터 수와 크기가 비례하도록 프로그래밍 하여 사용자에게 핵심단어를 시각화 하고 대화의 키워드, 개념을 직관적으로 파악할 수 있도록 한다.</p>
<p>워드 클라우드는 사용자가 특정한 단어를 말할 때 마다 항상 어플리케이션에 업데이트가 되며 각각의 단어의 크기와 색이 변하게 된다. 그래프의 경우 특정 단어에 대해 시간과 횟수를 기준으로 사용자에게 보여지게 된다.</p>
<p>통계는 일간, 주간, 월간을 기준으로 나눠놓았으며 각각 24시간, 한 주, 한 달을 지정해놓아 저장되어 있는 데이터를 불러오는 방식이다.<br />
일간통계 화면에서 좌측으로 드래그를 하게 되면 주간통계 화면으로 넘어가게 된다. 주간통계 화면 부분에는 워드 클라우드가 없으며 그래프로 한 주간 사용했던 데이터들을 모아 사용자가 한눈에 볼 수 있도록 해준다.</p>
<p>일간, 주간통계 그래프에서 특정 단어를 클릭할 때 특정 단어에 대한 정보가 나오게 된다. 해당 화면 하단에는 특정 단어와 5초 간격으로 사용된 단어들을 표시해 준다. 이 단어들로 하여금 사용자는 자신이 사용했던 단어들을 쉽게 이해할 수 있으며 특정 시간에 왜 이러한 단어를 사용하였는지 기억을 되새길 수 있다.</p>
<p>하지만 아직까지 음성데이터의 인식률 문제가 있어, 한국어 음성인식 같은 경우는 비슷할 뿐 존재하지 않는 단어를 출력하여 사용자에게 보여주게 된다.<br />
본 아이템의 어플리케이션은 음성인식의 신뢰성을 높이기 위해 어플리케이션 내에 한글단어 데이터베이스를 추가하고, 이를 이용한 ‘오인식 글자 수정 시스템’을 도입할 예정이다.</p>
<p>음성인식에 대한 신뢰성이 높지 않은 시스템들과 비교하자면, 본 아이템은 틀린 단어와 철자를 비교해 비슷한 철자의 단어를 사용자에게 보여주어 일치하는지 아닌지 판단하게끔 한다.</p>
<p>예를 들어, ‘마우스’라는 단어가 수신하고자 할 경우 컴퓨터는 ‘미우스’, ‘마오스’ 등 잘못된 수신을 할 수 있다. 하지만 한글단어 데이터베이스를 사용하여 특정 단어와 잘못 수신된 단어의 철자가 비슷한지의 여부를 확인하고 비슷할 경우 사용자에게 ‘미우스’라는 단어가 ‘마우스’가 맞는지에 대한 판단를 물어보게 된다. 사용자에게 주어진 선택지를 이용하여 해당 단어가 맞다고 판단할 경우 ‘미우스’라고 수신되는 단어는 항상 ‘마우스’ 주소로 가게 된다.</p>
<p>하지만 ‘미우스’라는 단어가 ‘마우스’가 아니라고 사용자가 판단할 경우 ‘미우스’라는 단어는 더 이상 ‘마우스’가 아닌 것을 데이터베이스에 저장하여 음성인식의 신뢰도를 더욱 높일 수 있다.</p>
<p>이러한 오인식 글자 수정 시스템은 특정 단어화면에서 뿐만 아니라 상태 바에 알림으로도 나오며 바탕화면 위젯을 이용하여 사용자에게 인식 판단 여부를 물어볼 수 있다. 사용자에게 많은 참여를 이끌고 이를 데이터화한다면 음성 인식률을 더욱 향상시킬 수 있을 것으로 예상된다.</p>
<p>한글 단어 데이터베이스를 활용하여 단어를 분류한 뒤 사용자에게 분류한 통계자료를 보여줄 수 있다.<br />
예를 들어 ‘설거지’라는 단어가 수신되어 어플리케이션에 등록이 될 경우 ‘설거지’ 라는 단어의 분류는 사전에서 ‘가정, 생활’분류에 포함되기 때문에 사용자는 어떠한 분류에 관심이 많은지, 어떠한 분류에 관심이 없는지 스스로가 보며 자기 자신을 피드백 할 수 있다.</p>
<p>본 아이템의 어플리케이션에서 제공되는 단어 사용 횟수, 어플리케이션에서 제공하는 단어의 정보에 대한 분류 후 특정 단어에 대한 정보, 특정 시간에서의 단어 사용을 사용자가 알고 이를 통하여 사용자는 자기 자신에 대해 알아가며 자신의 성향은 어떠한지 자신의 진로는 어떠한지에 대해 스스로를 피드백 할 수 있다.</p>
<p><span style="color: #0000ff"><strong>개발 환경(개발 언어, Tool, 사용 시스템 등)</strong></span><br />
어플리케이션 개발에 있어서 오픈소스와 안드로이드 SDK, 앱 인벤터를 주요 툴로 사용하였고, 개발 환경으로는 리눅스 환경, Python, Java 등을 주로 이용하였다.</p>
<p><span style="color: #0000ff"><strong>단계별 제작과정</strong></span><br />
<span style="color: #00ccff"><strong>하드웨어 제작과정</strong></span><br />
본 아이디어에 대해 실현가능성을 검토하기 위해서 본 팀은 아두이노와 아두이노 블루투스 모듈, 간이 녹음기를 통해 실험을 했으며 결과, 그 데이터가 스마트폰으로 정상 수신되는 것을 확인할 수 있었다.</p>
<p>그 후 1차 시제품을 만드는 데에 성공했다. 1차 시제품의 구성은 고무밴드에 아두이노 기반 블루투스 마이크 모듈을 설치하고 모듈에 전원을 넣을 수 있는 5V 수은전지 공간을 추가하여 제작했으며, 이 시제품으로 기능 작동 실험에 성공했다.</p>
<p>1차 시제품 이후에는 가벼운 스테인리스 소재나 플라스틱 등 기존 스마트워치 디자인 중 하나를 택해 팔찌를 제작할 예정이다. 2차 시제품은 사용자는 휴대하기 편리하고 신체에 무리가 가지 않으며 사용하기도 간편한 재료로 제작을 진행할 예정이다.</p>
<p><span style="color: #00ccff"><strong>소프트웨어 제작과정</strong></span><br />
본 아이템은 안드로이드 SDK 오픈소스를 기반하여 제작하였다. 팔찌는 블루투스를 이용하여 스마트폰과 통신을 한다.</p>
<p>어플리케이션에는 한글단어 데이터베이스를 입력하여 수신된 단어와 비교한다. 예를 들어, 팔찌에 ‘친구야’라는 단어가 수신될 경우 스마트폰과 블루투스로 통신하여 어플리케이션은 ‘친구야’라는 단어를 수신 받는다. 어플리케이션은 내장되어있는 한글단어 데이터베이스와 비교하여 어플리케이션에게 존재하는 단어인지 검증한다. 위 검증 과정에서 수신받은 단어가 존재하는 단어가 아니라고 판단될 경우 수신받은 단어와 자음이나 모음이 비슷한 단어가 있는지 검증하게 된다. 아니라고 할 경우 단어를 수신받기 전으로 돌아가 단어를 수신 받을 때까지 대기상태가 된다. 대기상태에서 단어를 수신 받을 경우 이 과정을 다시 거치게 된다.</p>
<p>수신받은 단어와 검증한 단어가 일치할 경우 시스템 상에서 해당 단어를 카운트해 저장할 수 있도록 하고, 이를 통계에 포함시킨다.</p>
<p>위 과정을 거치며 처음에는 신뢰성이 떨어질 수 있으나 점차 많은 단어가 수신될수록 신뢰도는 급격히 높아질 것이다.</p>
<p>‘친구’라는 단어를 카운트한 경우 ‘친구’라는 단어가 수신된 시작에서 5초 범위로 같이 수신된 단어들을 입력하여 같이 쓰인 단어 목록에 추가된다. 이후 카운트된 ‘친구’라는 단어는 일간 통계에 전송된 후 주간 통계로 보내지게 된다.</p>
<p>주간 통계는 그래프 통계로, 매주 업데이트 되며 위 어플리케이션에서 소셜, 생활, 문학, 음식, 지식 등 분류해 놓은 목록들과 ‘친구’라는 단어를 비교한 뒤 퍼센트 단위로 구분하여 그래프로 사용자에게 한눈에 보여준다. 일간 통계는 단어 클라우드와 그래프 통계가 수시로 업데이트 되어 사용자에게 보여지게 된다. 단어 클라우드는 사용한 단어의 횟수와 단어 크기를 비례하여 보여준다. 단어 사용 빈도를 기준으로 가운데서부터 끝으로 갈수록 점차 단어의 크기가 작아지게 된다. 그래프 통계는 사용 빈도가 높은 단어를 기준하여 내림차순으로 사용자에게 보여지게 된다.</p>
<p><span style="color: #0000ff"><strong>어플리케이션 워드클라우드 생성 소스코드(리눅스, 파이썬)</strong></span></p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>from __future__ import division<br />
import warnings<br />
from random import Random<br />
import os<br />
import re<br />
import sys<br />
import colorsys<br />
import numpy as np<br />
from operator import itemgetter<br />
from PIL import Image<br />
from PIL import ImageColor<br />
from PIL import ImageDraw<br />
from PIL import ImageFont<br />
from .query_integral_image import query_integral_image<br />
from .tokenization import unigrams_and_bigrams, process_tokens<br />
item1 = itemgetter(1)<br />
FONT_PATH = os.environ.get(&#8220;FONT_PATH&#8221;, os.path.join(os.path.dirname(__file__),<br />
&#8220;DroidSansMono.ttf&#8221;))<br />
STOPWORDS = set([x.strip() for x in open(<br />
os.path.join(os.path.dirname(__file__), 'stopwords')).read().split('\n')])<br />
class IntegralOccupancyMap(object):<br />
def __init__(self, height, width, mask):<br />
self.height = height<br />
self.width = width<br />
if mask is not None:<br />
# the order of the cumsum&#8217;s is important for speed ?!<br />
self.integral = np.cumsum(np.cumsum(255 * mask, axis=1),<br />
axis=0).astype(np.uint32)<br />
else:<br />
self.integral = np.zeros((height, width), dtype=np.uint32)<br />
def sample_position(self, size_x, size_y, random_state):<br />
return query_integral_image(self.integral, size_x, size_y,<br />
random_state)<br />
def update(self, img_array, pos_x, pos_y):<br />
partial_integral = np.cumsum(np.cumsum(img_array[pos_x:, pos_y:],<br />
axis=1), axis=0)<br />
# paste recomputed part into old image<br />
# if x or y is zero it is a bit annoying<br />
if pos_x &gt; 0:<br />
if pos_y &gt; 0:<br />
partial_integral += (self.integral[pos_x - 1, pos_y:]
- self.integral[pos_x - 1, pos_y - 1])<br />
else:<br />
partial_integral += self.integral[pos_x - 1, pos_y:]
if pos_y &gt; 0:<br />
partial_integral += self.integral[pos_x:, pos_y - 1][:, np.newaxis]
self.integral[pos_x:, pos_y:] = partial_integral<br />
def random_color_func(word=None, font_size=None, position=None,<br />
orientation=None, font_path=None, random_state=None):<br />
&#8220;&#8221;"Random hue color generation.<br />
Default coloring method. This just picks a random hue with value 80% and<br />
lumination 50%.<br />
Parameters<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
word, font_size, position, orientation : ignored.<br />
random_state : random.Random object or None, (default=None)<br />
If a random object is given, this is used for generating random<br />
numbers.<br />
&#8220;&#8221;"<br />
if random_state is None:<br />
random_state = Random()<br />
return &#8220;hsl(%d, 80%%, 50%%)&#8221; % random_state.randint(0, 255)<br />
class colormap_color_func(object):<br />
&#8220;&#8221;"Color func created from matplotlib colormap.<br />
Parameters<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
colormap : string or matplotlib colormap<br />
Colormap to sample from<br />
Example<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
&gt;&gt;&gt; WordCloud(color_func=colormap_color_func(&#8220;magma&#8221;))<br />
&#8220;&#8221;"<br />
def __init__(self, colormap):<br />
import matplotlib.pyplot as plt<br />
self.colormap = plt.cm.get_cmap(colormap)<br />
def __call__(self, word, font_size, position, orientation,<br />
random_state=None, **kwargs):<br />
if random_state is None:<br />
random_state = Random()<br />
r, g, b, _ = 255 * np.array(self.colormap(random_state.uniform(0, 1)))<br />
return &#8220;rgb({:.0f}, {:.0f}, {:.0f})&#8221;.format(r, g, b)<br />
def get_single_color_func(color):<br />
&#8220;&#8221;"Create a color function which returns a single hue and saturation with.<br />
different values (HSV). Accepted values are color strings as usable by<br />
PIL/Pillow.<br />
&gt;&gt;&gt; color_func1 = get_single_color_func(&#8216;deepskyblue&#8217;)<br />
&gt;&gt;&gt; color_func2 = get_single_color_func(&#8216;#00b4d2&#8242;)<br />
&#8220;&#8221;"<br />
old_r, old_g, old_b = ImageColor.getrgb(color)<br />
rgb_max = 255.<br />
h, s, v = colorsys.rgb_to_hsv(old_r / rgb_max, old_g / rgb_max,<br />
old_b / rgb_max)<br />
def single_color_func(word=None, font_size=None, position=None,<br />
orientation=None, font_path=None, random_state=None):<br />
&#8220;&#8221;"Random color generation.<br />
Additional coloring method. It picks a random value with hue and<br />
saturation based on the color given to the generating function.<br />
Parameters<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
word, font_size, position, orientation : ignored.<br />
random_state : random.Random object or None, (default=None)<br />
If a random object is given, this is used for generating random<br />
numbers.<br />
&#8220;&#8221;"<br />
if random_state is None:<br />
random_state = Random()<br />
r, g, b = colorsys.hsv_to_rgb(h, s, random_state.uniform(0.2, 1))<br />
return &#8216;rgb({:.0f}, {:.0f}, {:.0f})&#8217;.format(r * rgb_max, g * rgb_max,<br />
b * rgb_max)<br />
return single_color_func<br />
class WordCloud(object):<br />
&#8220;&#8221;"Word cloud object for generating and drawing.<br />
Parameters<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
font_path : string<br />
Font path to the font that will be used (OTF or TTF).<br />
Defaults to DroidSansMono path on a Linux machine. If you are on<br />
another OS or don&#8217;t have this font, you need to adjust this path.<br />
width : int (default=400)<br />
Width of the canvas.<br />
height : int (default=200)<br />
Height of the canvas.<br />
prefer_horizontal : float (default=0.90)<br />
The ratio of times to try horizontal fitting as opposed to vertical.<br />
If prefer_horizontal &lt; 1, the algorithm will try rotating the word<br />
if it doesn&#8217;t fit. (There is currently no built-in way to get only vertical words.)<br />
mask : nd-array or None (default=None)<br />
If not None, gives a binary mask on where to draw words. If mask is not<br />
None, width and height will be ignored and the shape of mask will be<br />
used instead. All white (#FF or #FFFFFF) entries will be considerd<br />
&#8220;masked out&#8221; while other entries will be free to draw on. [This<br />
changed in the most recent version!]
scale : float (default=1)<br />
Scaling between computation and drawing. For large word-cloud images,<br />
using scale instead of larger canvas size is significantly faster, but<br />
might lead to a coarser fit for the words.<br />
min_font_size : int (default=4)<br />
Smallest font size to use. Will stop when there is no more room in this size.<br />
font_step : int (default=1)<br />
Step size for the font. font_step &gt; 1 might speed up computation but<br />
give a worse fit.<br />
max_words : number (default=200)<br />
The maximum number of words.<br />
stopwords : set of strings or None<br />
The words that will be eliminated. If None, the build-in STOPWORDS<br />
list will be used.<br />
background_color : color value (default=&#8221;black&#8221;)<br />
Background color for the word cloud image.<br />
max_font_size : int or None (default=None)<br />
Maximum font size for the largest word. If None, height of the image is<br />
used.<br />
mode : string (default=&#8221;RGB&#8221;)<br />
Transparent background will be generated when mode is &#8220;RGBA&#8221; and<br />
background_color is None.<br />
relative_scaling : float (default=.5)<br />
Importance of relative word frequencies for font-size. With<br />
relative_scaling=0, only word-ranks are considered. With<br />
relative_scaling=1, a word that is twice as frequent will have twice<br />
the size. If you want to consider the word frequencies and not only<br />
their rank, relative_scaling around .5 often looks good.<br />
.. versionchanged: 2.0<br />
Default is now 0.5.<br />
color_func : callable, default=None<br />
Callable with parameters word, font_size, position, orientation,<br />
font_path, random_state that returns a PIL color for each word.<br />
Overwrites &#8220;colormap&#8221;.<br />
See colormap for specifying a matplotlib colormap instead.<br />
regexp : string or None (optional)<br />
Regular expression to split the input text into tokens in process_text.<br />
If None is specified, &#8220;r&#8221;\w[\w']+&#8221;&#8220; is used.<br />
collocations : bool, default=True<br />
Whether to include collocations (bigrams) of two words.<br />
.. versionadded: 2.0<br />
colormap : string or matplotlib colormap, default=&#8221;viridis&#8221;<br />
Matplotlib colormap to randomly draw colors from for each word.<br />
Ignored if &#8220;color_func&#8221; is specified.<br />
.. versionadded: 2.0<br />
normalize_plurals : bool, default=True<br />
Whether to remove trailing &#8216;s&#8217; from words. If True and a word<br />
appears with and without a trailing &#8216;s&#8217;, the one with trailing &#8216;s&#8217;<br />
is removed and its counts are added to the version without<br />
trailing &#8216;s&#8217; &#8212; unless the word ends with &#8216;ss&#8217;.<br />
Attributes<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
&#8220;words_&#8220; : dict of string to float<br />
Word tokens with associated frequency.<br />
.. versionchanged: 2.0<br />
&#8220;words_&#8220; is now a dictionary<br />
&#8220;layout_&#8220; : list of tuples (string, int, (int, int), int, color))<br />
Encodes the fitted word cloud. Encodes for each word the string, font<br />
size, position, orientation and color, Notes<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
Larger canvases with make the code significantly slower. If you need a<br />
large word cloud, try a lower canvas size, and set the scale parameter.<br />
The algorithm might give more weight to the ranking of the words<br />
than their actual frequencies, depending on the &#8220;max_font_size&#8220; and the<br />
scaling heuristic.<br />
&#8220;&#8221;"<br />
def __init__(self, font_path=None, width=400, height=200, margin=2,<br />
ranks_only=None, prefer_horizontal=.9, mask=None, scale=1,<br />
color_func=None, max_words=200, min_font_size=4,<br />
stopwords=None, random_state=None, background_color=&#8217;black&#8217;,<br />
max_font_size=None, font_step=1, mode=&#8221;RGB&#8221;,<br />
relative_scaling=.5, regexp=None, collocations=True,<br />
colormap=None, normalize_plurals=True):<br />
if font_path is None:<br />
font_path = FONT_PATH<br />
if color_func is None and colormap is None:<br />
# we need a color map<br />
import matplotlib<br />
version = matplotlib.__version__<br />
if version[0] &lt; &#8220;2&#8243; and version[2] &lt; &#8220;5&#8243;:<br />
colormap = &#8220;hsv&#8221;<br />
else:<br />
colormap = &#8220;viridis&#8221;<br />
self.colormap = colormap<br />
self.collocations = collocations<br />
self.font_path = font_path<br />
self.width = width<br />
self.height = height<br />
self.margin = margin<br />
self.prefer_horizontal = prefer_horizontal<br />
self.mask = mask<br />
self.scale = scale<br />
self.color_func = color_func or colormap_color_func(colormap)<br />
self.max_words = max_words<br />
self.stopwords = stopwords if stopwords is not None else STOPWORDS<br />
self.min_font_size = min_font_size<br />
self.font_step = font_step<br />
self.regexp = regexp<br />
if isinstance(random_state, int):<br />
random_state = Random(random_state)<br />
self.random_state = random_state<br />
self.background_color = background_color<br />
self.max_font_size = max_font_size<br />
self.mode = mode<br />
if relative_scaling &lt; 0 or relative_scaling &gt; 1:<br />
raise ValueError(&#8220;relative_scaling needs to be &#8221;<br />
&#8220;between 0 and 1, got %f.&#8221; % relative_scaling)<br />
self.relative_scaling = relative_scaling<br />
if ranks_only is not None:<br />
warnings.warn(&#8220;ranks_only is deprecated and will be removed as&#8221;<br />
&#8221; it had no effect. Look into relative_scaling.&#8221;,<br />
DeprecationWarning)<br />
self.normalize_plurals = normalize_plurals<br />
def fit_words(self, frequencies):<br />
&#8220;&#8221;"Create a word_cloud from words and frequencies.<br />
Alias to generate_from_frequencies.<br />
Parameters<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
frequencies : array of tuples<br />
A tuple contains the word and its frequency.<br />
Returns<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
self<br />
&#8220;&#8221;"<br />
return self.generate_from_frequencies(frequencies)<br />
def generate_from_frequencies(self, frequencies, max_font_size=None):<br />
&#8220;&#8221;"Create a word_cloud from words and frequencies.<br />
Parameters<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
frequencies : dict from string to float<br />
A contains words and associated frequency.<br />
max_font_size : int<br />
Use this font-size instead of self.max_font_size<br />
Returns<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
self<br />
&#8220;&#8221;"<br />
# make sure frequencies are sorted and normalized<br />
frequencies = sorted(frequencies.items(), key=item1, reverse=True)<br />
if len(frequencies) &lt;= 0:<br />
raise ValueError(&#8220;We need at least 1 word to plot a word cloud, &#8221;<br />
&#8220;got %d.&#8221; % len(frequencies))<br />
frequencies = frequencies[:self.max_words]
# largest entry will be 1<br />
max_frequency = float(frequencies[0][1])<br />
frequencies = [(word, freq / max_frequency)<br />
for word, freq in frequencies]
if self.random_state is not None:<br />
random_state = self.random_state<br />
else:<br />
random_state = Random()<br />
if self.mask is not None:<br />
mask = self.mask<br />
width = mask.shape[1]
height = mask.shape[0]
if mask.dtype.kind == &#8216;f&#8217;:<br />
warnings.warn(&#8220;mask image should be unsigned byte between 0&#8243;<br />
&#8221; and 255. Got a float array&#8221;)<br />
if mask.ndim == 2:<br />
boolean_mask = mask == 255<br />
elif mask.ndim == 3:<br />
# if all channels are white, mask out<br />
boolean_mask = np.all(mask[:, :, :3] == 255, axis=-1)<br />
else:<br />
raise ValueError(&#8220;Got mask of invalid shape: %s&#8221;<br />
% str(mask.shape))<br />
else:<br />
boolean_mask = None<br />
height, width = self.height, self.width<br />
occupancy = IntegralOccupancyMap(height, width, boolean_mask)<br />
# create image<br />
img_grey = Image.new(&#8220;L&#8221;, (width, height))<br />
draw = ImageDraw.Draw(img_grey)<br />
img_array = np.asarray(img_grey)<br />
font_sizes, positions, orientations, colors = [], [], [], []
last_freq = 1.<br />
if max_font_size is None:<br />
# if not provided use default font_size<br />
max_font_size = self.max_font_size<br />
if max_font_size is None:<br />
# figure out a good font size by trying to draw with<br />
# just the first two words<br />
if len(frequencies) == 1:<br />
# we only have one word. We make it big!<br />
font_size = self.height<br />
else:<br />
self.generate_from_frequencies(dict(frequencies[:2]),<br />
max_font_size=self.height)<br />
# find font sizes<br />
sizes = [x[1] for x in self.layout_]
font_size = int(2 * sizes[0] * sizes[1] / (sizes[0] + sizes[1]))<br />
else:<br />
font_size = max_font_size<br />
# we set self.words_ here because we called generate_from_frequencies<br />
# above&#8230; hurray for good design?<br />
self.words_ = dict(frequencies)<br />
# start drawing grey image<br />
for word, freq in frequencies:<br />
# select the font size<br />
rs = self.relative_scaling<br />
if rs != 0:<br />
font_size = int(round((rs * (freq / float(last_freq))<br />
+ (1 &#8211; rs)) * font_size))<br />
if random_state.random() &lt; self.prefer_horizontal:<br />
orientation = None<br />
else:<br />
orientation = Image.ROTATE_90<br />
tried_other_orientation = False<br />
while True:<br />
# try to find a position<br />
font = ImageFont.truetype(self.font_path, font_size)<br />
# transpose font optionally<br />
transposed_font = ImageFont.TransposedFont(<br />
font, orientation=orientation)<br />
# get size of resulting text<br />
box_size = draw.textsize(word, font=transposed_font)<br />
# find possible places using integral image:<br />
result = occupancy.sample_position(box_size[1] + self.margin,<br />
box_size[0] + self.margin,<br />
# if we didn&#8217;t find a place, make font smaller<br />
# but first try to rotate!<br />
if not tried_other_orientation and self.prefer_horizontal &lt; 1:<br />
orientation = (Image.ROTATE_90 if orientation is None else<br />
Image.ROTATE_90)<br />
tried_other_orientation = True<br />
else:<br />
font_size -= self.font_step<br />
orientation = None<br />
if font_size &lt; self.min_font_size:<br />
# we were unable to draw any more<br />
break<br />
x, y = np.array(result) + self.margin // 2<br />
# actually draw the text<br />
draw.text((y, x), word, fill=&#8221;white&#8221;, font=transposed_font)<br />
positions.append((x, y))<br />
orientations.append(orientation)<br />
font_sizes.append(font_size)<br />
colors.append(self.color_func(word, font_size=font_size,<br />
position=(x, y),<br />
, _ = process_tokens(words, self.normalize_plurals)<br />
return word_counts<br />
def generate_from_text(self, text):<br />
&#8220;&#8221;"Generate wordcloud from text.<br />
Calls process_text and generate_from_frequencies.<br />
..versionchanged:: 1.2.2<br />
Argument of generate_from_frequencies() is not return of<br />
process_text() any more.<br />
Returns<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
self<br />
&#8220;&#8221;"<br />
words = self.process_text(text)<br />
self.generate_from_frequencies(words)<br />
return self<br />
def generate(self, text):<br />
&#8220;&#8221;"Generate wordcloud from text.<br />
Alias to generate_from_text.<br />
Calls process_text and generate_from_frequencies.<br />
Returns<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
self<br />
&#8220;&#8221;"<br />
return self.generate_from_text(text)<br />
def _check_generated(self):<br />
&#8220;&#8221;"Check if &#8220;layout_&#8220; was computed, otherwise raise error.&#8221;"&#8221;<br />
if not hasattr(self, &#8220;layout_&#8221;):<br />
raise ValueError(&#8220;WordCloud has not been calculated, call generate&#8221;<br />
&#8221; first.&#8221;)<br />
def to_image(self):<br />
self._check_generated()<br />
if self.mask is not None:<br />
width = self.mask.shape[1]
height = self.mask.shape[0]
else:<br />
height, width = self.height, self.width<br />
img = Image.new(self.mode, (int(width * self.scale),<br />
int(height * self.scale)),<br />
self.background_color)<br />
draw = ImageDraw.Draw(img)<br />
for (word, count), font_size, position, orientation, color in self.layout_:<br />
font = ImageFont.truetype(self.font_path,<br />
int(font_size * self.scale))<br />
transposed_font = ImageFont.TransposedFont(<br />
font, orientation=orientation)<br />
pos = (int(position[1] * self.scale),<br />
int(position[0] * self.scale))<br />
draw.text(pos, word, fill=color, font=transposed_font)<br />
return img<br />
def recolor(self, random_state=None, color_func=None, colormap=None):<br />
&#8220;&#8221;"Recolor existing layout.<br />
Applying a new coloring is much faster than generating the whole<br />
wordcloud.<br />
Parameters<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
random_state : RandomState, int, or None, default=None<br />
If not None, a fixed random state is used. If an int is given, this<br />
is used as seed for a random.Random state.<br />
color_func : function or None, default=None<br />
Function to generate new color from word count, font size, position<br />
and orientation. If None, self.color_func is used.<br />
colormap : string or matplotlib colormap, default=None<br />
Use this colormap to generate new colors. Ignored if color_func<br />
is specified. If None, self.color_func (or self.color_map) is used.<br />
Returns<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
self<br />
&#8220;&#8221;"<br />
if isinstance(random_state, int):<br />
random_state = Random(random_state)<br />
self._check_generated()<br />
if color_func is None:<br />
if colormap is None:<br />
color_func = self.color_func<br />
else:<br />
color_func = colormap_color_func(colormap)<br />
self.layout_ = [(word_freq, font_size, position, orientation,<br />
color_func(word=word_freq[0], font_size=font_size,<br />
position=position, orientation=orientation,<br />
random_state=random_state,<br />
font_path=self.font_path))<br />
for word_freq, font_size, position, orientation, _<br />
in self.layout_]
return self<br />
def to_file(self, filename):<br />
&#8220;&#8221;"Export to image file.<br />
Parameters<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
filename : string<br />
Location to write to.<br />
Returns<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
self<br />
&#8220;&#8221;"<br />
img = self.to_image()<br />
img.save(filename)<br />
return self<br />
def to_array(self):<br />
&#8220;&#8221;"Convert to numpy array.<br />
Returns<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
image : nd-array size (width, height, 3)<br />
Word cloud image as numpy matrix.<br />
&#8220;&#8221;"<br />
return np.array(self.to_image())<br />
def __array__(self):<br />
&#8220;&#8221;"Convert to numpy array.<br />
Returns<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
image : nd-array size (width, height, 3)<br />
Word cloud image as numpy matrix.<br />
&#8220;&#8221;"<br />
return self.to_array()<br />
def to_html(self):<br />
raise NotImplementedError(&#8220;FIXME!!!&#8221;)<br />
</div>
<p><span style="color: #0000ff"><strong>기타(회로도, 소스코드, 참고문헌 등)</strong></span></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-6.png" rel="lightbox[34383]"><img class="alignnone size-large wp-image-34776" alt="44 feature 잘했어요 (6)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-6-620x379.png" width="620" height="379" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-1.png" rel="lightbox[34383]"><img class="alignnone size-large wp-image-34771" alt="44 feature 잘했어요 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-잘했어요-1-620x454.png" width="620" height="454" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ntrexgo.com/archives/34383/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[44호]실내주행 로봇 플랫폼과 부착식 위치인식용 데이터 수집 시스템</title>
		<link>http://www.ntrexgo.com/archives/34390</link>
		<comments>http://www.ntrexgo.com/archives/34390#comments</comments>
		<pubDate>Fri, 22 Sep 2017 00:00:23 +0000</pubDate>
		<dc:creator>디바이스마트 매거진</dc:creator>
				<category><![CDATA[blog-posts]]></category>
		<category><![CDATA[디바이스마트 매거진]]></category>
		<category><![CDATA[특집]]></category>
		<category><![CDATA[44호]]></category>
		<category><![CDATA[ict]]></category>
		<category><![CDATA[공모전]]></category>
		<category><![CDATA[디바이스마트]]></category>
		<category><![CDATA[매거진]]></category>
		<category><![CDATA[융합]]></category>
		<category><![CDATA[입선작]]></category>
		<category><![CDATA[프로젝트]]></category>

		<guid isPermaLink="false">http://www.ntrexgo.com/?p=34390</guid>
		<description><![CDATA[디바이스마트 매거진 44호 &#124; 실내위치인식은 로봇 분야에서 지속적으로 관심을 받고 연구가 진행되어온 분야이며 로봇의 위치인식 뿐만 아니라 스마트폰 등에 부착된 센서를 이용한 사람의 위치인식 또한 활발히 연구되고 있다.]]></description>
				<content:encoded><![CDATA[<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-1.png" rel="lightbox[34390]"><img class="alignnone size-large wp-image-34781" alt="44 feature 실내주행 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-1-620x233.png" width="620" height="233" /></a></p>
<p><span style="font-size: large"><strong>2017  ICT 융합 프로젝트 공모전 입선작</strong></span></p>
<p><span style="font-size: x-large"><strong>실내주행 로봇 플랫폼과 부착식 위치인식용 </strong></span></p>
<p><span style="font-size: x-large"><strong>데이터 수집 시스템</strong></span></p>
<p style="text-align: right"><strong>글 | 한국과학기술원 김규광, 김재우, 김휘민</strong></p>
<p style="text-align: left">
<p><span style="color: #0000ff"><strong>심사평</strong></span><br />
<strong>칩센</strong> 실내 위치 인식은 로봇 분야의 관심을 넘어 최근 들어 산업 분야 전반에서 관심을 가지고 있는 기술 분야입니다. 작품 개발자들의 의도는 주행 로봇을 이용하여 위치 인식의 기반이 될 데이터 수집을 수행하는 것으로 보이나, 위치 인식은 작품 개발자들도 인지하고 있듯이, 단순한 몇가지 방식의 측량으로 원하는 수준을 이끌어내기 쉽지 않은 것이 사실입니다. 그럼에도 불구하고, 자기장 factor 로그를 이용하여 위치에 따른 차이점이 명확하게 구분되는 것을 결과로 보여준 점은 위치 측량을 위한 하나의 데이터로 사용이 충분히 가능할 것으로 보입니다. 이 데이터를 기반으로 사물 또는 사람의 위치를 실제로 측정할 수 있는지를 확인해 볼수 있는 방법에 대해 조금 고민을 하면 좋을듯 합니다.</p>
<p><strong>뉴티씨</strong> 케이스 등 디자인만 수정하고 조금만 다듬으면 바로 제품에 적용해도 될 것 같습니다. 그리고 ‘사용 예’ 같은 것을 제시하면 좀 더 좋은 제품이 될 것 같습니다. 예를 들어, 실내 상호간 전투사격 시뮬레이터, 웨어러블 장비를 통한 HID 인터페이스 등에 추가하는 등의 어플리케이션을 구축할 수 있겠습니다.</p>
<p><strong>위드로봇</strong> localization을 수행하는 것이 아닌, 이에 필요한 데이터를 수집하는 모바일 로봇을 만든 점이 재미있습니다. 단, 이 로봇 또한 맵과 자율 주행 기능이 필요한데, 이 부분에 대한 언급이 있으면 더 좋았을 것 같습니다.</p>
<p><span style="color: #0000ff"><strong>작품 개요</strong></span><br />
실내 위치 인식은 로봇 분야에서 지속적으로 관심을 받고 연구가 진행되어온 분야이며 로봇의 위치인식 뿐만 아니라 스마트폰 등에 부착된 센서를 이용한 사람의 위치인식 또한 활발히 연구되고 있다. IPIN 등의 실내위치인식을 테마로 한 top tier 국제학회들도 개최되고 있으며 MobiSys 등의 모바일 시스템 컨퍼런스에서도 여러 위치인식 논문들이 발표되고 있다.</p>
<p>실내 위치 인식에는 엔코더를 이용한 간단한 데드레코닝 방법부터 LIDAR 스캐너, Kinect 등을 이용한 pointcloud, 이미지 마커나 NFC/RFID 태그까지 다양한 방법이 사용될 수 있고 이들을 이용한 particle filter 및 SLAM 등의 여러 알고리즘 또한 개발되어 왔다. 이러한 위치인식 방법들 중 Wi-Fi의 신호 세기를 이용한 위치인식 방법이 스마트폰과 Wi-Fi 존의 보급으로 추가적인 인프라를 설치하지 않고도 이를 이용해 위치인식이 가능하다는 점, 특별한 외부 센서 없이 스마트폰에 내장된 시스템만 가지고도 구현이 가능하다는 이점에 힘입어 코엑스 위치인식 앱 등에 적용되기도 하였다.</p>
<p>Wi-Fi를 위시한 전파 비콘의 경우 위치를 아는 전파 발생원으로부터 전파의 세기를 측정하고 이를 이용해 삼각 측량으로 수신자의 위치를 계산하는 방법이 일반적이나 실내 구조물을 통과하면서 발생하는 신호의 감쇄, 복도 및 벽 등에 반사되어서 들어오는 전파, 안테나의 타입과 측정 중 방향 등 여러가지 요인으로 인해 단순 삼각측량으로는 정확한 위치측정이 매우 어렵다. 이러한 문제를 해결하기 위해 RANSAC 알고리즘을 이용하여 전파 신호를 균일화하고 여러 지점에서 다수의 AP의 전파 신호를 측정해 fingerprinting과 기계학습을 하는 방식이 개발되었다.</p>
<p>최근 또 다른 실내위치인식 방법으로 각광받는 것은 자기장 왜곡을 이용한 위치인식이다. 건물은 기본적으로 철골 구조로 만들어지며 전류가 흐르는 전선 등이 복도나 벽에 설치되는 등 자연적으로 측정되는 지구 자기장을 제외하고도 자기장을 발생시키는 물체들이 존재한다. 지자기센서를 이용하여 이러한 자기장 anomaly를 구역별로 수집해 이를 사용해 SLAM을 하는 방식이 제안되었으며 GPS나 Wi-Fi와는 다르게 전파 방해 요인들에 영향 받지 않으며 스마트폰에 내장된 센서로도 측정 가능하다는 장점이 있어 각광받고 있다.</p>
<p>이러한 요소들로 실내 위치 인식을 위해서는 충분히 많은 지점에서 위치인식에 사용할 데이터를 수집하여야 하고 특히 기계학습을 위해서는 지점마다 충분한 데이터를 확보해 주어야 할 필요가 있다. 연구 목적 및 알고리즘 실험용으로는 사람이 로봇 혹은 센서를 들거나 몰고 돌아다니면서 ground truth에 대한 위치 신호를 수집할 수 있으나 실제로 이러한 실내 위치인식을 적용하려 하는 대형 빌딩, 박물관, 전시장 같은 곳은 그 크기로 인해 이러한 수동 데이터 수집이 매우 힘들다.</p>
<p>본 프로젝트에서는 이러한 문제를 해결하기 위해 Wi-Fi 신호와 3축 지자기 센서의 값을 자동 수집하는 센서 logger를 제작했으며 이러한 센서 태그를 사람이 직접 들고 다니는 것이 아닌 넓은 실내 시설의 내부 청소에 사용되는 로봇 청소기 및 청소 카트 등의 시스템에 부착한다. 또한 이러한 로봇/이동형 플랫폼들이 청소 작업을 위해 시설 내부를 돌아다니면서 위치인식에 필요한 데이터를 자연스럽게 수집할 수 있는 방식을 제안하고자 한다. 로봇청소기에 부착 가능하며 해당 로봇의 엔코더에 연결되는 개조 키트를 개발하였다. 또한 굳이 청소로봇에 부착하지 않더라도 센서와 함께 자율주행이 가능한 로봇의 경우 이러한 데이터 수집에 사용될 수 있어 호환 가능한 이동형 플랫폼 또한 제작하였다.</p>
<p><span style="color: #0000ff"><strong>작품 설명</strong></span><br />
<span style="color: #33cccc"><strong>주요 동작 및 특징</strong></span><br />
본 프로젝트에서 제출하는 작품은 크게 세 부분으로 구성되어 있다.<br />
A. 로봇청소기용 모터를 갖추고 비슷한 형체를 가진 실제 로봇청소기를 모사한 형태의 이동형 플랫폼<br />
B. 지자기센서, Wi-Fi 모듈과 SD 카드 리더기가 부착되어 위치인식용 센서로 데이터를 logging하는 센서 태그<br />
C. 이동형 플랫폼과 지그비 통신을 통해 이를 무선 제어하는 아두이노 esplora 기반 조종기</p>
<p><span style="color: #33cccc"><strong>이동식 로봇청소기 플랫폼</strong></span></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-2.png" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34782" alt="44 feature 실내주행 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-2.png" width="620" height="440" /></a><br />
로봇청소기 플랫폼은 탐색하려는 공간을 원활히 돌아다니며, 원하는 데이터를 최대한 수집하는 것을 목표로 한다. 이를 위해서는 무선 통신으로 조종이 가능하며 안정적인 주행 능력이 요구된다.</p>
<p>로봇의 주 제어장치는 ARM Cortex-M3 기반의 Arduino Due를 사용하였으며 센서와 하드웨어 인터페이스를 위해 Arduino Uno를 부착하였다. 복원력을 가지는 바퀴를 이용해 문턱 등의 장애물을 손쉽게 넘어갈 수 있으며 모터에 지자기 센서와 모터에 부착된 엔코더를 사용해 현재 위치를 계산한다. 엔코더를 읽기 위한 카운터는 LSI/CSI 사의 LS7366을 사용하였다. 로봇의 모터는 아두이노 부트로더가 내장된 Atmega328p 칩과 Toshiba 사의 TA7291P 모터 드라이버를 사용하여 제어된다.</p>
<p>무선 통신을 위해 XBEE radio module로 통신하는 방법을 택하였다. 통신 알고리즘을 간단히 소개하자면 다음과 같다. 송신기 역할을 하는 Arduino Esplora에서는 100ms 간격으로 눌린 버튼의 종류를 검사하여 신호를 보낸다. 전진, 좌회전, 우회전, 또는 아무 것도 눌리지 않은 경우에는 정지 신호를 보낸다. 로봇청소기 플랫폼에서 신호를 수신하면, Arduino Due 는 UART 버퍼에 들어 있는 마지막으로 수신된 데이터를 읽고 버퍼를 모두 비운다. (이는 다른 명령을 처리하던 중 들어온 데이터 중 가장 최근의 명령을 사용하기 위함이다.) 그리고, 100ms 간격으로 엔코더의 상태를 비교하여 바퀴의 속도를 측정한다. 이렇게 측정된 각 바퀴의 실제 구동 속도, 원하는 바퀴의 구동 속도, 동작의 종류를 I2C 인터페이스를 통해 모터를 제어하는 Atmega328 칩으로 전송한다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-3.png" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34783" alt="44 feature 실내주행 (3)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-3.png" width="620" height="610" /></a></p>
<p>구동 명령과 바퀴의 실제 구동 속도를 받은 Atmega328은 PID 제어를 통해 바퀴의 실제 구동 속도가 이상적인 구동 속도에 맞도록 제어한다. 여기서 PID 제어를 사용하는 이유는 양쪽 바퀴의 약간의 출력 차이나 무게 배분의 차이로 인해 로봇이 한 쪽으로 불균형적으로 움직이는 현상을 최소화하기 위함이다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-4.png" rel="lightbox[34390]"><img class="alignnone size-large wp-image-34784" alt="44 feature 실내주행 (4)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-4-616x620.png" width="616" height="620" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-5.png" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34785" alt="44 feature 실내주행 (5)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-5.png" width="620" height="316" /></a></p>
<p>전원은 12000mAh의 Li-ion 배터리를 사용하였으며, LM7805와 DC-DC 모듈로 전자 회로를 제어한다. 모터는 배터리 전원을 그대로 사용해 구동하였다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-5.png" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34785" alt="44 feature 실내주행 (5)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-5.png" width="620" height="316" /></a></p>
<p><span style="color: #33cccc"><strong>데이터 logging 모듈</strong></span><br />
데이터 logging 모듈은 아두이노를 기반으로 지자기센서, Wi-Fi 쉴드를 사용하여 제작되었다. 지자기센서는 I2C 통신으로 3축 지자기 값을 받아오며 단위는 Gauss이다. Wi-Fi 쉴드는 Wi-Fi AP 스캔과 SD카드 데이터 저장에 사용된다. Wi-Fi 모듈과 SD카드는 SPI 통신을 이용한다. Wi-Fi AP 스캔 프로그램은 아두이노 예제 중에 ScanNetworks를 변형하여 작성하였고, SD카드 라이브러리를 이용하여 데이터를 저장하였다.</p>
<p>아두이노는 지자기센서와 Wi-Fi모듈의 데이터를 받아 아래 그림과 같은 Log_data 형태로 시간과 함께 데이터를 저장한다. 추가적으로 PCInt(Pin Change Interrupt)처리로 A, B상 Pulse Encoder 값을 측정하여 누적값을 기록한다. 바퀴를 PID 제어하는 데에 사용했던 엔코더 카운터와는 별도로 엔코더 신호를 측정한다. 사용한 지자기센서 모듈인 GY-86의 경우 MPU6050의 자체 I2C 포트에 지자기센서가 연결되어 있고 MPU6050의 I2C가 pinout으로 연결되어서 mpu6050의 bypass 모드를 활성화시켜준 후 지자기센서 HMC5883L의 데이터를 요청하는 방법으로 구현하였다.</p>
<p>해당 모듈을 제작된 로봇청소기에 장착했으며 로봇청소기의 엔코더 모듈에서 신호를 bypass시켜 로봇과 부착된 수집 모듈 둘다 독립적으로 구동이 가능하게끔 제작하였다.<br />
엔코더를 이용한 간단한 주행 실험과 이동 궤적 획득, 그리고 수집된 자기장과 전파 신호를 이용한 fingerprinting 기반 위치인식 실험 또한 진행하였다. 실험의 경우 건물 복도에서 일정 간격의 바닥 타일을 기준점 삼아 L자 형태의 궤적을 잡고 이를 따라 주행하며 엔코더 값이 정상적으로 획득되는지를 확인하였다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-6.png" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34786" alt="44 feature 실내주행 (6)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-6.png" width="620" height="457" /></a></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-1.jpg" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34787" alt="44 feature 실내주행 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-1.jpg" width="620" height="363" /></a></p>
<p>자기장/전파 기반 위치인식 실험의 경우 로봇을 같은 방향을 바라보게 한 다음 L자 궤적 내 5개 지점을 선별하여 각각의 지점으로 이동시켜 데이터를 수집한 후 신경망(Multilayer Perceptron)을 사용하여 데이터를 학습시켜 위치인식을 하는 방식으로 진행하였다. 신경망의 경우 최근 다층 hidden layer와 입력 부분에 복층의 convolution/pooling 구조를 가지는 이미지 분류용 합성곱신경망(CNN), 혹은 음성 데이터 등의 연속 데이터 등을 처리하기 위한 RNN으로 대표되는 딥 러닝이 주로 사용되고 있으나 샘플 수가 적고 입력 데이터가 이미지나 연속 신호가 아닌 독립 항목인 점, 그리고 학습에 필요한 연산 속도 등의 여러 편의성으로 인해 기존의 1개 hidden layer를 가지는 구식 신경망(MLP)를 사용하였다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-2.jpg" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34788" alt="44 feature 실내주행 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-2.jpg" width="620" height="488" /></a> <a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-7.png" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34789" alt="44 feature 실내주행 (7)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-7.png" width="620" height="234" /></a> <a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-8.png" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34790" alt="44 feature 실내주행 (8)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-8.png" width="620" height="488" /></a> <a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-9.png" rel="lightbox[34390]"><img class="alignnone size-full wp-image-34791" alt="44 feature 실내주행 (9)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-9.png" width="620" height="424" /></a></p>
<p>&nbsp;</p>
<p>딥 러닝 이전 기계학습과 분류에는 주로 Support Vector Machine이 사용되었으나 기본적으로 이진 분류기라는 특성이 있어, 비록 여러 성능상의 단점이 제기되어 왔으나 가장 간편한 multiclass 분류기라는 장점이 있는 신경망을 사용하였다. Python으로 tanh 함수를 activation function으로 사용하는 신경망을 제작했으며 입력단의 경우 최소 3개 (3축 지자기)에 wifi 신호당 하나의 node를 더 추가했고 hidden layer의 경우 30개, output의 경우 5개 위치를 구분하기를 원하므로 5개의 hidden layer를 달아 주었다.</p>
<p>Learning rate의 경우 0.001로 설정하였으며 약 1000회의 iteration을 거쳐 forward propagation, softmax를 이용한 확률 계산, 학습 데이터셋의 답안과의 오차 계산과 해당 오차의 back propagation을 진행하며 신경망 내 행렬들의 가중치와 bias를 갱신하였다. 지자기 데이터가 각 구역별로 특징이 워낙 뚜렷해 지속적인 갱신 시 Wi-Fi 신호의 가중치가 사실상 0으로 수렴할 것이 예상되어 기본적인 성능 테스트는 지자기센서의 값으로만 진행하였다. Training iteration을 반복함으로써 loss function의 출력이 점점 감소하는 것을 확인하였고 overfitting 여부를 확인하기 위해 training set이 아닌 각 구역과 비슷한 값의 지자기 값을 생성하여 test set으로 사용했으며 100%의 정답률로 구분해 내는 것을 확인하였다.</p>
<p>비록 안테나 방향과 XYZ 축 문제로 방향을 고정시킨 상태에서 수집한 데이터지만 SLAM 알고리즘 등을 사용하여 자기장 map을 만들고 3축 벡터의 norm으로 측정하는 등의 sensor fusion 알고리즘을 더한다면 더 좋은 위치인식이 가능할 것으로 기대된다. 위치인식 데이터를 자동 수집해주는 모듈이 본 프로젝트의 중점이므로 이를 위한 간단한 구별 데모를 보였다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-10.png" rel="lightbox[34390]"><img class="alignnone size-large wp-image-34792" alt="44 feature 실내주행 (10)" src="http://www.ntrexgo.com/wp-content/uploads/2017/09/44-feature-실내주행-10-566x620.png" width="566" height="620" /></a></p>
<p>&nbsp;</p>
<p><span style="color: #0000ff"><strong>개발 환경</strong></span><br />
아두이노와 아두이노 IDE를 사용하였으며 신경망 코드는 x86 컴퓨터에서 Python과 numpy Python 라이브러리를 사용하여 제작하였다.</p>
<p><span style="color: #0000ff"><strong>소스코드</strong></span><br />
<strong><span style="color: #33cccc">로봇 주행 코드</span></strong></p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>//#include &lt;PID_AutoTune_v0.h&gt;<br />
#include &lt;PID_v1.h&gt;<br />
#include&lt;Wire.h&gt;<br />
#include&lt;EEPROM.h&gt;<br />
boolean tuningL=true,tuningR=true;<br />
byte valL,valR;<br />
double Setpoint, InputL, OutputL,InputR,OutputR;<br />
PID myPID(&amp;InputL, &amp;OutputL, &amp;Setpoint,0.8,1.0,0, DIRECT);<br />
PID mePID(&amp;InputR,&amp;OutputR,&amp;Setpoint,0.8,1.0,0,DIRECT);<br />
/*PID_ATune AmyPID(&amp;InputL, &amp;OutputL);<br />
PID_ATune AmePID(&amp;InputR, &amp;OutputR);*/<br />
const float robotradius=1;<br />
const float transfactor=0.5;<br />
float rightspeed,leftspeed;<br />
byte Mode;<br />
byte Speed,angleSpeed;<br />
boolean FB;<br />
long timeoutbefore,timeoutafter;<br />
byte Hradius,Lradius; uint16_t radius;<br />
float rpm, leftrpm, rightrpm; float t;<br />
float leftvalue, rightvalue;<br />
float value; byte acceleration;<br />
void setup() {<br />
Serial.begin(9600);<br />
Serial.print(&#8220;Serial begin\n&#8221;);<br />
pinMode(13,OUTPUT);//busy flag LED<br />
pinMode(12,OUTPUT);//busy flag signal<br />
pinMode(8,OUTPUT);//timeout flag detection<br />
digitalWrite(12,HIGH);// busy flag enabled<br />
digitalWrite(13,HIGH);//busy led on<br />
digitalWrite(8,HIGH);//error led off<br />
Wire.begin(0x0A);//Adress of motor controller MCU<br />
Wire.onReceive(receiveEvent);<br />
myPID.SetMode(AUTOMATIC);<br />
mePID.SetMode(AUTOMATIC);<br />
myPID.SetControllerDirection(DIRECT);<br />
mePID.SetControllerDirection(DIRECT);<br />
/*AmyPID.SetOutputStep(20);<br />
AmePID.SetOutputStep(20);<br />
AmyPID.SetControlType(1);<br />
AmePID.SetControlType(1);<br />
AmyPID.SetNoiseBand(2);<br />
AmePID.SetNoiseBand(2);<br />
AmyPID.SetLookbackSec(2);<br />
AmePID.SetLookbackSec(2);<br />
*/<br />
myPID.SetOutputLimits(50,95);<br />
mePID.SetOutputLimits(30,85);<br />
leftspeed=0; rightspeed=0; Speed=0; angleSpeed=0; FB=true;<br />
timeoutbefore=millis();//previous value of millis for timeout detection<br />
Mode=0&#215;00;<br />
digitalWrite(13,LOW); digitalWrite(12,LOW); digitalWrite(8,HIGH);<br />
}<br />
void loop() {<br />
if(Wire.available()&lt;=0){// if there&#8217;s no signal from i2c bus<br />
//Serial.print(&#8220;ready\n&#8221;);<br />
//delay(10);<br />
}<br />
timeoutafter=millis();<br />
/*<br />
Motor stops automatically when there is no signal certain period<br />
*/<br />
if(timeoutafter-timeoutbefore&gt;300){//0.3seconds before stop<br />
analogWrite(11,0); analogWrite(5,0);<br />
analogWrite(10,0); analogWrite(6,0);<br />
digitalWrite(8,LOW);//for timeout error indication //Serial.print(&#8220;Timeout\n&#8221;);<br />
leftspeed=0; rightspeed=0; Speed=0; angleSpeed=0;<br />
}<br />
if(Mode!=0&#215;00){// receiveEvent function changes Mode<br />
digitalWrite(13,HIGH);//busy flag indication by LED<br />
digitalWrite(12,HIGH);//busy flag<br />
// Serial.print(&#8220;busy flag set\n&#8221;);<br />
// Serial.print(&#8220;Mode:&#8221;); Serial.println(Mode,HEX); Serial.print(&#8220;\n&#8221;);<br />
digitalWrite(8,HIGH);//activated LOW<br />
if(Wire.available()&gt;0){ // Mode=Wire.read();<br />
///<br />
if(Mode==0&#215;01){//forward mode with accleration<br />
Serial.print(&#8220;forward Mode\n&#8221;);<br />
Speed=Wire.read();<br />
acceleration=Wire.read();<br />
Serial.print(Speed);<br />
Serial.print(&#8220;\n&#8221;);<br />
Serial.print(acceleration);<br />
Serial.print(&#8220;\n&#8221;);<br />
leftspeed=Speed; rightspeed=Speed; FB=true;<br />
rpm=transfactor*Speed;//speed to rpm<br />
t=Speed/acceleration; t=t*1000;//seconds to milliseconds<br />
analogWrite(10,0); analogWrite(5,0); //drives motor forward<br />
value=0;<br />
Serial.print(&#8220;rpm:&#8221;);<br />
Serial.print(rpm/t);<br />
Serial.print(&#8220;\n&#8221;);<br />
while(rpm&gt;=value){//accleration code<br />
Serial.print(value);<br />
Serial.print(&#8220;\n&#8221;);<br />
analogWrite(11,value);//attatched to motor drivers<br />
analogWrite(6,value);<br />
value+=(rpm/t);<br />
delay(1);<br />
}<br />
}<br />
///<br />
if(Mode==0&#215;02){//backward mode with accleration<br />
Speed=Wire.read();<br />
acceleration=Wire.read();<br />
leftspeed=Speed; rightspeed=Speed; FB=false;<br />
rpm=transfactor*Speed;//speed to rpm<br />
t=Speed/acceleration; t=t*1000;//seconds to milliseconds<br />
analogWrite(11,0); analogWrite(6,0); //drives motor backward<br />
value=0;<br />
while(rpm&gt;=value){//accleration code<br />
analogWrite(10,value);//attatched to motor drivers<br />
analogWrite(5,value);<br />
value+=(rpm/t);<br />
delay(1);<br />
}<br />
}<br />
///<br />
if(Mode==0&#215;03){//robot moves on a circle anticlockwise with accleration<br />
Hradius=Wire.read();<br />
Lradius=Wire.read();<br />
radius=(Hradius&lt;&lt;8)|Lradius;<br />
Speed=Wire.read();<br />
acceleration=Wire.read(); FB=true;<br />
t=Speed/acceleration; t=t*1000;//get time value required to acclerate to appropriate speed<br />
rightspeed=(1+robotradius/radius)*Speed; rightrpm=transfactor*rightspeed;<br />
leftspeed=(1-robotradius/radius)*Speed; leftrpm=transfactor*leftspeed;<br />
analogWrite(10,0); analogWrite(5,0);<br />
leftvalue=0; rightvalue=0;<br />
while((leftrpm&gt;=leftvalue)&amp;&amp;(rightrpm&gt;=rightvalue)){<br />
analogWrite(11,leftvalue);<br />
analogWrite(6,rightvalue);<br />
leftvalue+=(leftrpm/t);<br />
rightvalue+=(rightrpm/t);<br />
delay(1);<br />
}<br />
}<br />
///<br />
if(Mode==0&#215;04){//robot moves on a circle clockwise with accleration<br />
Hradius=Wire.read();<br />
Lradius=Wire.read();<br />
radius=(Hradius&lt;&lt;8)|Lradius;<br />
Serial.print(radius);<br />
Speed=Wire.read();<br />
acceleration=Wire.read(); FB=true;<br />
t=Speed/acceleration; t=t*1000;//get time value required to acclerateto appropriate speed<br />
leftspeed=(1+robotradius/radius)*Speed; leftrpm=transfactor*leftspeed;<br />
rightspeed=(1-robotradius/radius)*Speed; rightrpm=transfactor*rightspeed;<br />
analogWrite(10,0); analogWrite(5,0);<br />
leftvalue=0;rightvalue=0;<br />
while((leftrpm&gt;=leftvalue)&amp;&amp;(rightrpm&gt;=rightvalue)){<br />
analogWrite(11,leftvalue);<br />
analogWrite(6,rightvalue);<br />
leftvalue+=(leftrpm/t);<br />
rightvalue+=(rightrpm/t);<br />
delay(1);<br />
}<br />
}<br />
///<br />
if(Mode==0&#215;05){//motor stops slowly with acceleration value<br />
acceleration=Wire.read();<br />
t=Speed/acceleration; t=t*1000;<br />
leftrpm=leftspeed*transfactor; rightrpm=rightspeed*transfactor;<br />
Serial.print(&#8220;leftrpm: &#8220;); Serial.print(leftrpm);<br />
Serial.print(&#8220;rightrpm: &#8220;); Serial.print(rightrpm);<br />
if(FB==true){analogWrite(10,0); analogWrite(5,0);}else{<br />
analogWrite(11,0); analogWrite(6,0);<br />
}<br />
float r=rightrpm; float l=leftrpm;<br />
while(leftrpm!=0||rightrpm!=0){<br />
leftrpm=leftrpm-(l/t);<br />
rightrpm=rightrpm-(r/t);<br />
if(leftrpm&lt;=0)leftrpm=0;<br />
if(rightrpm&lt;=0)rightrpm=0;<br />
/* Serial.print(&#8220;leftrpm: &#8220;); Serial.print(leftrpm);<br />
Serial.print(&#8220;rightrpm: &#8220;); Serial.print(rightrpm); */<br />
if(FB==true){analogWrite(11,leftrpm); analogWrite(6,rightrpm);}else{<br />
analogWrite(10,leftrpm); analogWrite(5,rightrpm);}<br />
delay(1);<br />
}<br />
}<br />
///<br />
if(Mode==0&#215;06){//go forward<br />
Speed=Wire.read();<br />
Serial.print(&#8220;\nSpeed:&#8221;);<br />
Serial.print(Speed);<br />
byte Currentleftspeed=Wire.read();<br />
byte Currentrightspeed=Wire.read();<br />
Serial.print(&#8220;\nCurrentleftspeed:&#8221;);<br />
Serial.print(Currentleftspeed);<br />
Serial.print(&#8220;\nCurrentrightspeed:&#8221;);<br />
Serial.print(Currentrightspeed);<br />
//if(abs(Setpoint-Currentleftspeed)&gt;=70){<br />
// myPID.SetTunings(0.8,1.5,0); mePID.SetTunings(0.8,1.5,0);<br />
//}else{<br />
myPID.SetTunings(0.45,0.6,0); mePID.SetTunings(0.23,0.6,0);<br />
//}<br />
Setpoint=Speed;<br />
InputL=Currentleftspeed;<br />
/* if(tuningL){ valL=(AmyPID.Runtime());}<br />
if(valL!=0){<br />
tuningL=false;<br />
}*/<br />
myPID.Compute();<br />
leftrpm=OutputL;<br />
Serial.print(&#8220;leftrpm:&#8221;);<br />
Serial.print(leftrpm);<br />
InputR=Currentrightspeed;<br />
/*if(tuningL){ valR=(AmyPID.Runtime());}<br />
if(valR!=0){<br />
tuningL=false;<br />
}*/<br />
mePID.Compute();<br />
/* if(tuningL==false&amp;&amp;tuningR==false){<br />
double pR=AmyPID.GetKp(); double iR=AmyPID.GetKi(); double dR=AmyPID.GetKd();<br />
double pL=AmePID.GetKp(); double iL=AmePID.GetKi(); double dL=AmePID.GetKd();<br />
Serial.print(&#8220;\npR:&#8221;);Serial.print(pR); Serial.print(&#8221; iR:&#8221;); Serial.print(iR); Serial.print(&#8221; dR:&#8221;);Serial.print(dR);<br />
Serial.print(&#8220;\npL:&#8221;);Serial.print(pL); Serial.print(&#8221; iL:&#8221;);Serial.print(iL); Serial.print(&#8221; dL:&#8221;);Serial.print(dL);<br />
while(true);<br />
}*/<br />
rightrpm=OutputR+5;<br />
Serial.print(&#8221; rightrpm:&#8221;);<br />
Serial.print(rightrpm);<br />
leftspeed=Speed; rightspeed=Speed;<br />
analogWrite(10,0); analogWrite(5,0);<br />
analogWrite(11,rightrpm); analogWrite(6,leftrpm);<br />
}<br />
///<br />
if(Mode==0&#215;07){//go backward<br />
Speed=Wire.read();<br />
rpm=transfactor*Speed;<br />
leftspeed=Speed; rightspeed=Speed;<br />
analogWrite(11,0); analogWrite(6,0);<br />
analogWrite(10,rpm); analogWrite(5,rpm);<br />
}<br />
///<br />
if(Mode==0&#215;08){//turn on the circle anticlockwise<br />
Hradius=Wire.read();<br />
Lradius=Wire.read();<br />
radius=(Hradius&lt;&lt;8)|Lradius;<br />
Speed=Wire.read();<br />
rightspeed=(1+robotradius/radius)*Speed; rightrpm=transfactor*rightspeed;<br />
leftspeed=(1-robotradius/radius)*Speed; leftrpm=transfactor*leftspeed;<br />
// Serial.print(rightspeed);<br />
//Serial.print(leftspeed);<br />
analogWrite(10,0); analogWrite(5,0);<br />
analogWrite(11,leftrpm); analogWrite(6,rightrpm);<br />
}<br />
///<br />
if(Mode==0&#215;09){//turn on the circle clockwise<br />
Hradius=Wire.read();<br />
Lradius=Wire.read();<br />
radius=(Hradius&lt;&lt;8)|Lradius;<br />
Speed=Wire.read();<br />
leftspeed=(1+robotradius/radius)*Speed; leftrpm=transfactor*leftspeed;<br />
rightspeed=(1-robotradius/radius)*Speed; rightrpm=transfactor*rightspeed;<br />
analogWrite(10,0); analogWrite(5,0);<br />
analogWrite(11,leftrpm); analogWrite(6,rightrpm);<br />
}<br />
///<br />
if(Mode==0x0B){//robot turns right<br />
angleSpeed=Wire.read();<br />
rightspeed=angleSpeed*robotradius; rightrpm=transfactor*rightspeed;<br />
leftspeed=angleSpeed*robotradius; leftrpm=transfactor*leftspeed;<br />
Serial.print(&#8220;rightspeed: &#8220;);<br />
Serial.print(rightspeed);<br />
Serial.print(&#8220;leftspeed: &#8220;);<br />
Serial.print(leftspeed);<br />
analogWrite(10,0); analogWrite(6,0);<br />
analogWrite(11,leftrpm); analogWrite(5,rightrpm);<br />
}<br />
///<br />
if(Mode==0x0C){//robot turns left<br />
angleSpeed=Wire.read();<br />
rightspeed=angleSpeed*robotradius; rightrpm=transfactor*rightspeed;<br />
leftspeed=angleSpeed*robotradius; leftrpm=transfactor*leftspeed;<br />
analogWrite(11,0); analogWrite(5,0);<br />
analogWrite(10,leftrpm); analogWrite(6,rightrpm);<br />
}<br />
///<br />
if(Mode==0x0D){//motor stops immediately<br />
Speed=Wire.read();<br />
leftspeed=Speed; rightspeed=Speed;<br />
analogWrite(11,255); analogWrite(5,255);<br />
analogWrite(10,255); analogWrite(6,255);<br />
}<br />
///<br />
if(Mode==0x0E){//motor stops normally<br />
Speed=Wire.read();<br />
leftspeed=Speed; rightspeed=Speed;<br />
analogWrite(11,0); analogWrite(5,0);<br />
analogWrite(10,0); analogWrite(6,0);<br />
}<br />
///<br />
if(Mode==0x0F){<br />
byte afterSpeed=Wire.read();<br />
acceleration=Wire.read();<br />
byte afterleftspeed=afterSpeed; byte afterrightspeed=afterSpeed;<br />
byte afterleftrpm=afterleftspeed*transfactor; byte afterrightrpm=afterrightspeed*transfactor;<br />
leftrpm=leftspeed*transfactor; rightrpm=rightspeed*transfactor;<br />
int L=abs(leftrpm-afterleftrpm); int R=abs(rightrpm-afterrightrpm);<br />
t=abs(Speed-afterSpeed)/acceleration; t=t*1000;<br />
if(FB==true){analogWrite(10,0); analogWrite(5,0);}else{<br />
analogWrite(11,0); analogWrite(6,0);}<br />
while(leftrpm&gt;afterleftrpm || rightrpm&gt;afterrightrpm){<br />
if(FB==true){analogWrite(11,leftrpm); analogWrite(6,rightrpm);}else{<br />
analogWrite(10,leftrpm); analogWrite(5,rightrpm);}<br />
leftrpm=leftrpm-(L/t); rightrpm=rightrpm-(R/t);<br />
delay(1);<br />
}<br />
while(leftrpm&lt;afterleftrpm || rightrpm&lt;afterrightrpm){<br />
if(FB==true){analogWrite(11,leftrpm); analogWrite(6,rightrpm);}else{<br />
analogWrite(10,leftrpm); analogWrite(5,rightrpm);}<br />
leftrpm=leftrpm+(L/t); rightrpm=rightrpm+(R/t);<br />
delay(1);<br />
}<br />
if(FB==true){analogWrite(11,leftrpm); analogWrite(6,rightrpm);}else{<br />
analogWrite(10,leftrpm); analogWrite(5,rightrpm);}<br />
}<br />
///<br />
if(Mode==0&#215;10){<br />
byte rpm=Wire.read();<br />
analogWrite(9,rpm);<br />
}<br />
///<br />
if(Mode==0&#215;11){<br />
byte brush=Wire.read();<br />
analogWrite(3,brush);<br />
}<br />
}<br />
timeoutbefore=millis();<br />
Mode=0&#215;00;<br />
//Serial.print(&#8220;busy flag cleared\n&#8221;);<br />
delay(40);<br />
digitalWrite(13,LOW);digitalWrite(12,LOW);<br />
}<br />
}<br />
void receiveEvent(int howmany){<br />
digitalWrite(12,HIGH);<br />
Mode=Wire.read();<br />
}<br />
</div>
<p><span style="color: #33cccc"><strong>Arduino esplora 기반 조종기 코드</strong></span></p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>#include&lt;Esplora.h&gt;<br />
const byte wait=150;<br />
void setup() { // put your setup code here, to run once:<br />
Serial1.begin(9600);<br />
}<br />
void loop() {<br />
// put your main code here, to run repeatedly:<br />
//Serial1.write(104);<br />
if(Esplora.readButton(SWITCH_UP)==LOW)<br />
{<br />
Serial1.write(0xA0);delay(wait);//forward<br />
}else if(Esplora.readButton(SWITCH_RIGHT)==LOW)<br />
{<br />
Serial1.write(0xB0);delay(wait);//turn right while staying still<br />
}else if(Esplora.readButton(SWITCH_LEFT)==LOW)<br />
{<br />
Serial1.write(0xC0);delay(wait);//turn left while staying still<br />
}else if(Esplora.readButton(SWITCH_DOWN)==LOW)<br />
{<br />
Serial1.write(0xD0);delay(wait);// go forward for 30cm<br />
}else<br />
{<br />
Serial1.write(0xE0);delay(wait);Serial.print(&#8220;E0\n&#8221;);//stops if none of the buttons are pressed<br />
}<br />
}<br />
</div>
<p><span style="color: #33cccc"><strong>Arduino esplora 기반 조종기 코드</strong></span></p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>#include &lt;SPI.h&gt;<br />
#include &lt;WiFi.h&gt;<br />
#include &lt;SD.h&gt;<br />
#include &lt;Wire.h&gt;<br />
#include &#8220;MPU6050.h&#8221;<br />
#include &#8220;HMC5883L.h&#8221;<br />
MPU6050 mpu;<br />
HMC5883L compass;<br />
const int chipSelect = 4;<br />
long encoder_tick;<br />
long encoder_tick2;<br />
File dataFile;<br />
void log_data(String str);<br />
//&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-//<br />
void setup() { //Initialize serial and wait for port to open:<br />
Serial.begin(9600);<br />
Wire.begin();<br />
while (!Serial) {<br />
; // wait for serial port to connect. Needed for native USB port only<br />
} // check for the presence of the shield:<br />
if (WiFi.status() == WL_NO_SHIELD) {<br />
Serial.println(&#8220;WiFi shield not present&#8221;); // don&#8217;t continue:<br />
while (true);<br />
}<br />
String fv = WiFi.firmwareVersion();<br />
if (fv != &#8220;1.1.0&#8243;) {<br />
Serial.println(&#8220;Please upgrade the firmware&#8221;);<br />
}<br />
Serial.print(&#8220;Initializing SD card&#8230;&#8221;);<br />
// see if the card is present and can be initialized:<br />
if (!SD.begin(chipSelect)) {<br />
Serial.println(&#8220;Card failed, or not present&#8221;); // don&#8217;t do anything more:<br />
return;<br />
}<br />
Serial.println(&#8220;card initialized.&#8221;);<br />
File dataFile = SD.open(&#8220;datalog.txt&#8221;, FILE_WRITE);<br />
if (dataFile) {<br />
dataFile.println(&#8220;&#8221;);<br />
dataFile.close();<br />
}<br />
else {<br />
Serial.println(&#8220;error opening datalog.txt&#8221;);<br />
return;<br />
}<br />
// Sensor init //<br />
mpu.init();<br />
mpu.setMasterEnabled(0);<br />
mpu.setBypassEnabled(1);<br />
compass.init();<br />
pinMode(A0, INPUT_PULLUP);<br />
pinMode(A1, INPUT_PULLUP);<br />
pinMode(A2, INPUT_PULLUP);<br />
pinMode(A3, INPUT_PULLUP); //Encoder PinChangeInterrupt Setting//<br />
PCMSK1 |= 0b00001111; // pin A0, A1 set interrupt<br />
PCICR |= 0b00000010; // PortC set interrupt<br />
sei();<br />
}<br />
void loop() {<br />
long timer;<br />
compass.update();<br />
int numSsid = WiFi.scanNetworks();<br />
if (numSsid == -1) {<br />
Serial.println(&#8220;Couldn&#8217;t get a wifi connection&#8221;);<br />
while (true);<br />
}<br />
dataFile = SD.open(&#8220;datalog.txt&#8221;, FILE_WRITE);<br />
if (!dataFile) Serial.println(&#8220;error opening datalog.txt&#8221;);<br />
timer = micros();<br />
log_data(String(timer));<br />
log_data(String(&#8216;,&#8217;));<br />
log_data(String(numSsid));<br />
for (int thisNet = 0; thisNet &lt; numSsid; thisNet++) {<br />
log_data(String(&#8216;,&#8217;));<br />
log_data(String(WiFi.SSID(thisNet)));<br />
log_data(String(&#8216;,&#8217;));<br />
log_data(String(WiFi.RSSI(thisNet)));<br />
}<br />
log_data(String(&#8216;,&#8217;));<br />
log_data(String(compass.getCompassX(), 2));<br />
log_data(String(&#8216;,&#8217;));<br />
log_data(String(compass.getCompassY(), 2));<br />
log_data(String(&#8216;,&#8217;));<br />
log_data(String(compass.getCompassZ(), 2));<br />
log_data(String(&#8216;,&#8217;));<br />
log_data(String(encoder_tick));<br />
log_data(String(&#8216;,&#8217;));<br />
log_data(String(encoder_tick2));<br />
log_data(String(&#8220;\r\n&#8221;));<br />
dataFile.close();<br />
}<br />
//&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;/<br />
void log_data(String str) {<br />
dataFile.print(str);<br />
Serial.print(str);<br />
}<br />
//&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-//<br />
ISR(PCINT1_vect) {<br />
static uint8_t preMask = 0&#215;00000011;<br />
static uint8_t preMask2 = 0&#215;00000011;<br />
uint8_t mask;<br />
uint8_t mask2;<br />
//Encoder//<br />
mask = (PINC &gt;&gt; 0) &amp; 0b00000011;<br />
if (mask != preMask) {<br />
switch (preMask) {<br />
case 0:<br />
if (mask == 0&#215;01) encoder_tick&#8211;;<br />
else if (mask == 0&#215;02) encoder_tick++;<br />
break;<br />
case 1:<br />
if (mask == 0&#215;03) encoder_tick&#8211;;<br />
else if (mask == 0&#215;00) encoder_tick++;<br />
break;<br />
case 2:<br />
if (mask == 0&#215;00) encoder_tick&#8211;;<br />
else if (mask == 0&#215;03) encoder_tick++;<br />
break;<br />
case 3:<br />
if (mask == 0&#215;02) encoder_tick&#8211;;<br />
else if (mask == 0&#215;01) encoder_tick++;<br />
break;<br />
}<br />
preMask = mask;<br />
}<br />
mask2 = (PINC &gt;&gt; 2) &amp; 0b00000011;<br />
if (mask2 != preMask2) {<br />
switch (preMask2) {<br />
case 0:<br />
if (mask2 == 0&#215;01) encoder_tick2&#8211;;<br />
else if (mask2 == 0&#215;02) encoder_tick2++;<br />
break;<br />
case 1:<br />
if (mask2 == 0&#215;03) encoder_tick2&#8211;;<br />
else if (mask2 == 0&#215;00) encoder_tick2++;<br />
break;<br />
case 2:<br />
if (mask2 == 0&#215;00) encoder_tick2&#8211;;<br />
else if (mask2 == 0&#215;03) encoder_tick2++;<br />
break;<br />
case 3:<br />
if (mask2 == 0&#215;02) encoder_tick2&#8211;;<br />
else if (mask2 == 0&#215;01) encoder_tick2++;<br />
break;<br />
}<br />
preMask2 = mask2;<br />
}<br />
}</p>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ntrexgo.com/archives/34390/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[33호]원격지 작업이 가능한 이동 로봇 팔 관리 시스템</title>
		<link>http://www.ntrexgo.com/archives/29848</link>
		<comments>http://www.ntrexgo.com/archives/29848#comments</comments>
		<pubDate>Sun, 15 Nov 2015 07:41:11 +0000</pubDate>
		<dc:creator>디바이스마트 매거진</dc:creator>
				<category><![CDATA[디바이스마트 매거진]]></category>
		<category><![CDATA[특집]]></category>
		<category><![CDATA[33호]]></category>
		<category><![CDATA[Feature]]></category>
		<category><![CDATA[ict]]></category>
		<category><![CDATA[공모전]]></category>
		<category><![CDATA[디바이스마트]]></category>
		<category><![CDATA[매거진]]></category>
		<category><![CDATA[융합]]></category>
		<category><![CDATA[입선작]]></category>
		<category><![CDATA[프로젝트]]></category>

		<guid isPermaLink="false">http://www.ntrexgo.com/?p=29848</guid>
		<description><![CDATA[디바이스마트매거진 33호 &#124; 2015 ICT 융합 프로젝트 공모전 입선작으로 한자리에 고정된 로봇이 아닌, 이동이 가능하며 원거리에서 사람이 영상을 보며 로봇 팔을 원격제어 할 수 있게 한다. ]]></description>
				<content:encoded><![CDATA[<p><img alt="2014 ict 메인" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/2014-ict-메인-620x150.jpg" width="620" height="150" /></p>
<p>&nbsp;</p>
<p><span style="font-size: x-large;color: #000080"><strong>2015 ICT 융합 프로젝트 공모전 입선</strong></span></p>
<p><span style="font-size: large;color: #0000ff"><strong>원격지 작업이 가능한 이동 로봇 팔 관리 시스템</strong></span></p>
<p style="text-align: right">글 | 부경대학교 공영훈, 김준영, 김지수, 김민지, 김인준, 이동환</p>
<p style="text-align: right">
<p style="text-align: left"><span style="font-size: medium;color: #ffffff;background-color: #000080"><strong>심사평</strong></span></p>
<p><strong><span style="color: #0000ff">JK전자 </span></strong>이동을 하면서 섬세한 작업까지 가능한 이동형 로봇팔은 여러 분야에서 활용이 가능한 작품이다. 라즈베리파이에서 획득한 영상을 YUV형태 그대로 비 압축으로 영상을 전송하기 보다는 압축된 형태의 스트리밍으로 전송했다면 영상 전송의 프레임 속도가 조금 더 좋았을것 같다. 그리고 한가지 아쉬운 점이 로봇팔 제어를 위해 사용된 부품이 상용제품을 사용하여 약간은 쉽게 구현하였는데, 이 부분까지 직접 구현해 보았으면 좋았겠다.</p>
<p><span style="color: #0000ff"><strong>뉴티씨</strong></span> 재난 상황에서 쓸 수 있는 원격제어 로봇팔은 매우 유용하게 사용될 수도 있다. 재난 상황이라는 것은 의도해서 생기는 상황이 아니므로, 언제든지 일어날 수 있다고 가정하고 대비하는 것이 필요하다. 하지만, 이러한 상황은 자주 일어나는 것이 아니라서, 해당 기술개발 등은 국가나 관련단체의 후원 없이는 개발되기 어려운 것이 현실이다. 하지만, 이번 작품은 이러한 상황에서 사용될 수 있는 로봇을 제작하였다. 다만, 현실적으로 사용된 로봇팔의 모터 등이 얼마나 내구성이 있으며, 방수나 화공약품 등을 방어하는 능력이 어느 정도인지는 의문이다. 현재 구하여 쓸 수 있는 모터 중에서 기능 구현을 위하여 사용한 것으로 생각되지만, 앞으로는 이러한 부분에서도 좀 더 고민하여 작품을 제작하였으면 하는 생각이 든다. 창의성과 실용성, 기술성, 작품완성도 등 다방면에서 높은 점수를 주었다.</p>
<p><strong><span style="color: #0000ff">칩센</span></strong> 원격을 이용하여 로봇을 조정한다는 점은 유용한 것으로 생각되나, 복합적인 기능을 하기에는 너무 한정적으로 되어 있다.</p>
<p><span style="color: #0000ff"><strong>위드로봇</strong></span> 로봇팔을 원격에서 제어하기 위해 텔레 로보틱스 관점에서 마스터 제어기를 만들어 적용한 작품이다. 영상을 통해 로봇팔의 상황을 인지하여 조작하는 구조로 작성했지만, 실제 로봇팔이 놓인 상황을 모니터링 하려면, 현재 카메라 배치 및 제어 구조로는 어렵다. 그럼에도 불구하고, 마스터 제어기를 만들고, 이로부터 입력을 받아 동작하는 전체 시스템을 구현한 완성도에는 높은 점수를 준다.</p>
<p><span style="font-size: medium;color: #ffffff;background-color: #000080"><strong>작품개요</strong></span></p>
<p>원격 제어는 로봇을 제어하기 위해 없어서는 안 될 한 분야이다. 원격제어는 우주탐사나 재해 현장과 같은 인간이 접근하기 힘든 위험한 환경에서 그 필요성이 부각된다. 따라서 이 작품은 기존의 산업이나 재해 현장에서 사용하던 로봇 팔의 역할을 그대로 수행하면서 한자리에 고정된 로봇이 아닌, 이동이 가능하며 원거리에서 사람이 영상을 보며 로봇 팔을 원격제어 할 수 있게 한다. 그리고 윈도우 Application을 제작해 로봇 팔에서 전송된 영상데이터를 확인하여 현재 상태를 체크하고 관리하는 시스템을 제작한다.</p>
<p><span style="font-size: medium;color: #ffffff;background-color: #000080"><strong>작품 설명</strong></span></p>
<p><strong><span style="color: #ffffff;background-color: #ff0000">주요 동작 및 특징</span></strong><br />
<span style="color: #ffffff"><strong><span style="background-color: #ff6600">이동용 로봇 팔</span></strong></span><br />
<strong>MCU : ATmega128</strong><br />
원격지 작업이 가능하기 위해서 주행 모드와 로봇 팔 임무수행 모드 2가지로 분류한다. 각 모드는 Embedded Board인 Raspberry Pi에게 명령을 받아서 동작한다. 원격지로 이동하기 위한 이동 수단으로 바퀴를 선택하였고 (주)디엔지위드 사의 RA-35GM 07TYPE의 DC모터를 사용하며, L298N모터 드라이버를 이용하여 모터를 구동한다. 임무수행을 위해 다이나믹셀로 만든 로봇 팔이 장착되어있다. 다이나믹셀은 로보티즈 사에서 제공되는 프레임을 통해서 모터를 로봇 팔의 형태로 조립하기 쉽고 UART를 이용하여 구동하기 때문에 모터의 제어가 쉬운 장점이 있다. 하드웨어적으로 데이지체인 형태로 연결되어 있다. 제품을 구입할 때 로보티즈사에서 제공하는 다이나믹셀 전용 controller cm-5가 있지만 우리는 cm-5를 대신하여 AVR ATmega128을 사용하여 데이터를 전송한다. 데이터를 전송하기 위해선 다이나믹셀만의 통신 패킷이 필요하다. 다이나믹셀의 데이터시트를 확인하여 그것만의 통신패킷을 구성한다. packet이란 주로 데이터 통신 분야에서 사용되는 용어로 특정의 형식으로 배열되어 있는 비트 열이다. 아래 그림은 다이나믹셀 전용 통신 패킷이고 아래 그림에서 표현하는 순서대로 packet의 역할을 할 배열을 구성한다.</p>
<p><img class="alignnone size-full wp-image-30032" alt="33 ICT 원격지 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-1.png" width="476" height="129" /></p>
<p><img class="alignnone size-full wp-image-30034" alt="33 ICT 원격지 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-2.png" width="473" height="284" /><br />
위 그림과 같이 두개의 시작비트 0xff와 모터의 ID, packet의 길이, 다이나믹셀 전용 명령어, 모터의 각도와 속도 등의 명령어 그리고 마지막으로 check sum으로 배열을 구성한다. Check Sum은 배열에 담길 각 데이터들이 정상적으로 도착하였는지 수신부에서 확인하기 위해서 존재하는 것으로 각 회사마다 고유의 check sum 계산 공식이 있으며, 송신부에서 패킷을 전송하면 수신부는 수신 받은 데이터를 사용해 check sum 계산 공식에 대입하여 check sum을 계산해 수신 받은 데이터의 정확성을 확인한 뒤 모터를 구동한다. 각 instruction들은 데이터시트의 Control Table을 통해서 parameter들의 값을 확인할 수 있다. Instruction Set은 다이나믹셀 명령어 집합으로 한 개의 모터를 제어하는 명령어, 여러 개의 모터를 제어하는 명령어, 모터의 현재 상태를 받으려는 명령어, 모터의 기본설정을 바꾸는 명령어 등 다양한 명령어들로 구성되어있다. 그 중 우리가 사용한 명령어는 SYNC WRITE란 명령어로 ATmega128에 연결되어 있는 모터들에게 동시에 데이터를 전송해서 여러 모터를 구동한다. 일반 WRITE 명령어와 비교하였을 때 SYNC WRITE 명령어가 모터가 동시에 동작하게 하여 WRITE 명령어의 단점인 각 모터의 딜레이를 최대한 줄여줄 수 있다. Raspberry Pi와 ATmega128간에 모드 선택 시 사용한 통신은 i2c를 사용한다. i2c는 동기화 통신 방식으로 SCK선으로 동기시킬 클록이 이동하고, SDA선으로 데이터가 이동해야 한다. 데이터 송수신은 SDA선을 한 개 이용한다. 따라서 송수신간 데이터 충돌을 피하기 위한 일정한 프로토콜이 있고, ATmega128을 Slave로 선택하여 구동한다.</p>
<p><strong>(2) Embedded Board : Raspberry Pi</strong><br />
Embedded Board 역할을 할 Raspberry Pi는 이더넷에 연결하고 UVC카메라에서 획득한 영상데이터를 윈도우 Application으로 전송하며 ATmega128의 모드 관리에 사용된다. Raspberry Pi를 사용한 이유는 영상 데이터를 획득하는 것과 이더넷을 이용하기에 ATmega128보다 편리하기 때문이다. 카메라의 영상을 획득하기 위해 먼저 V4L2에 접근한다. V4L2는 영상을 획득하기 위한 Linux 드라이버로 획득한 영상을 RGB형태로 배열에 담을 수 있다. 이더넷에 접근해 데이터를 주고받기 위해선 먼저 소켓 통신이 가능해야한다. 소켓 통신은 쉽게 말해 인터넷을 통해 데이터를 송, 수신하는 방법으로 tcp 소켓, udp 소켓 두 가지 통신이 존재한다. tcp 소켓 통신은 Server가 데이터를 송신하고 수신부에서 송신한 데이터를 수신하였는지 Server가 확인한 뒤 다음 데이터를 전송하고, udp 소켓 통신은 수신부가 데이터를 수신하였는지 Server가 확인하지 않고 계속해서 다음 데이터를 전송한다. udp에 비해 tcp가 안정적이지만 데이터 전송 속도는 udp가 더 빠르다. 우리는 영상데이터의 전송에서 속도 대신 안정성을 선택하였고, tcp 소켓 통신을 이용하여 영상을 전송한다. Udp 소켓 통신은 ATmega128의 모드 설정 시 사용한다. 카메라에서 획득한 영상이 tcp 소켓 통신을 이용하여 윈도우 Application으로 전송되면 사용자는 윈도우 Application에서 이동 형 로봇 팔의 현재 상태를 영상을 통해 확인할 수 있으며, 사용자가 현재 필요한 모드를 결정하고 모드 변경의 명령을 내리면 udp 소켓 통신을 통해 Raspberry Pi로 전달되고 전달된 명령은 Raspberry Pi에서 ATmega128로 i2c를 통해서 전송된다. i2c는 Raspberry Pi를 master로 설정하여 구동한다. tcp 소켓과 udp 소켓을 관리해주기 위해 각각을 하나의 쓰레드로 구성한다. 쓰레드는 실행분기를 나누는 것으로 하나의 프로세서가 여러 개의 실행 흐름을 가지기 위해서 사용된다.</p>
<p><img class="alignnone size-full wp-image-30036" alt="33 ICT 원격지 (3)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-3.png" width="505" height="290" /><br />
위 그림과 같이 쓰레드 1은 영상데이터를 윈도우 Application으로 전송하는 역할 tcp 소켓으로 구성하고, 쓰레드 2는 윈도우 Application으로부터 방향에 대한 명령을 받은 후 i2c 통신을 통해 이동 로봇 팔로 전송하여 이동형 로봇 팔의 모드를 설정하며 udp 소켓으로 구성한다.</p>
<p><span style="color: #ffffff;background-color: #ff6600"><strong>무선 컨트롤러</strong></span></p>
<p><img class="alignnone size-full wp-image-30033" alt="33 ICT 원격지 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-2.jpg" width="205" height="226" /></p>
<p>모형 로봇 팔은 포맥스를 통해서 로봇 팔을 조립한 형태의 비율로 축소해서 제작한다. 모형 로봇팔의 각 관절 부분은 가변 저항으로 구성한다.<br />
가변저항의 ADC를 이동형 로봇 팔로 전송해서 모션을 제작할 수 있다. ADC 데이터를 무선 컨트롤러의 MCU에서 로봇 팔의 MCU로 전송할 땐 블루투스 통신을 사용한다. 제작된 모션은 EEPROM에 저장한다. EEPROM에 모션을 저장하고 반복 재생하게 하기 위해 각 가변저항의 ADC를 EEPROM에 저장해 두었다가 다시 불러오는 방법을 이용한다. 모션의 저장과 반복은 Push 버튼을 통해서 구현한다. 3개의 Push 버튼을 외부 인터럽트 핀에 연결하여 Push 버튼에서 발생한 신호를 외부 인터럽트 신호를 확인하여 각 버튼을 누름에 따라 모션의 저장, 반복, 리셋이 가능하게 하였다.</p>
<p><span style="color: #ffffff;background-color: #ff6600"><strong>윈도우 Application</strong></span><br />
로봇 팔의 관리 시스템으로 동작할 윈도우 Application은 UI를 제작하고 쓰레드를 통해 tcp통신과 udp통신을 나누어 관리해주었다. 먼저 UI는 picture box와 button으로 구성하였다. 기본적인 UI구성은 아래와 같이 구성한다. 구성된 UI는 아래 그림을 통해 확인할 수 있다.</p>
<p><img class="alignnone size-full wp-image-30035" alt="33 ICT 원격지 (3)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-3.jpg" width="475" height="196" /><br />
UI의 왼쪽 부분은 Raspberry Pi에서 획득한 영상을 소켓 통신을 통해 전송받은 뒤 출력하는 Picture Box부분이다. image 버튼을 클릭 할시 영상이 출력된다. 영상은 picture Box를 통해 지속적인 출력이 가능하며, 출력된 영상을 통해 사용자는 로봇팔의 현재 상태를 확인할 수 있고, 그에 따라 버튼을 통해서 주행 모드와 임무 수행 모드 두 가지 중 한 가지 모드를 선택하여 명령을 내릴 수 있다. 로봇 팔 모드 설정을 위해 먼저 mode start 버튼을 클릭한다. 그 뒤 중간 radio button으로 ‘drive mode’와 ‘mission mode’ 2가지가 있다. ‘drive mode’를 클릭하고, ‘mode change’ button을 클릭할 시 우측에 ‘go’, ‘stop’, ‘back’, ‘left’, ‘rigth’ 버튼을 통해 로봇의 이동 방향을 조절할 수 있다. ‘mission mode’를 클릭하고 ‘mode change’ 버튼을 클릭할 시 모형 로봇 팔을 통해서 로봇 팔을 제어할 수 있다. 아래는 ‘image’ 버튼을 클릭 시 Raspberry Pi에서 전송된 영상을 출력하고 있는 모습을 Capture 한 그림이다.</p>
<p><img class="alignnone size-full wp-image-30037" alt="33 ICT 원격지 (4)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-4.jpg" width="476" height="217" /></p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>전체 시스템 구성</strong></span></p>
<p><img class="alignnone size-full wp-image-30038" alt="33 ICT 원격지 (5)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-5.jpg" width="474" height="341" /></p>
<p>전체적으로보면 AVR ATmega128 2개를 사용하고 DC motor, Raspberry Pi 등의 제품들을 사용한다. 작품의 세부적인 흐름으로 블록도를 구성하면 아래 그림들과 같다.</p>
<p><img class="alignnone size-full wp-image-30039" alt="33 ICT 원격지 (6)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-6.jpg" width="270" height="395" /></p>
<p>모형 로봇 팔의 모션을 이동형 로봇 팔로 전달하고 메모리에 저장하는 것을 나타낸 블록도이다. 윈도우 어플리케이션에서 로봇팔의 모드를 임무수행으로 선택한다면 AVR2는 모형 로봇 팔을 구성하는 가변저항의 ADC를 블루투스를 이용하여 AVR1로 전달한다. AVR1은 전달받은 ADC를 로봇 팔의 각도 데이터로 변환하고 변환한 데이터를 UART를 이용하여 다이나믹셀로 전송하여 모형 로봇 팔의 모션을 이동 형 로봇 팔에서 그대로 따라할 수 있게 한다. 모션의 저장은 AVR2의 내부 EEPROM을 이용한다. 모형 로봇 팔의 ADC를 획득한 뒤 EEPROM에 저장하고 블루투스를 이용하여 로봇팔로 전송하는 순서로 모형 로봇 팔의 모션을 저장하고, 저장한 모션은 가변저항의 ADC는 무시하고 EEPROM에 저장된 ADC를 반복적으로 불러와 다이나믹셀로 전송하여 저장한 모션의 반복재생을 구현하였다.</p>
<p><img class="alignnone size-full wp-image-30040" alt="33 ICT 원격지 (7)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-7.jpg" width="476" height="297" /></p>
<p>윈도우 Application을 통해 로봇 팔을 관리하는 부분이다. 먼저 UVC camera를 통해 Raspberry Pi는 영상데이터를 획득하고 이더넷에 연결하여 윈도우 Application으로 영상데이터를 전달하는 모습을 볼 수 있다. 사용자는 수신한 영상을 토대로 로봇 팔의 현재 상황을 판단하고 목적지까지 도달하기 위해 주행하여야 하는지 목적지에 도달해서 임무수행이 가능한지를 판단할 수 있다. 목적지에 도달하였다면 임무수행 radio button을 선택하고 무선 컨트롤러로 로봇 팔을 제어하며, 주행 radio button 선택 시 윈도우 Application에서 조향에 관한 명령을 이더넷을 통해 Raspberry Pi로 전송하고, Raspberry Pi가 AVR에게 i2c를 이용해 전달하여 DC모터를 제어하는 모습을 볼 수 있다.</p>
<p><img class="alignnone size-full wp-image-30041" alt="33 ICT 원격지 (8)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-8.jpg" width="474" height="321" /></p>
<p>전체 블록도를 구성할 프레임 별로 나누어 보면 무선 컨트롤러의 역할을 할 프레임은 프레임1로 리모컨이란 이름을 붙여두었고, 이동형 로봇 팔의 프레임은 프레임2로 차량, 로봇팔이란 이름을 붙여두었다. 마지막으로 프레임은 아니지만 하나의 관리 서버 역할을 할 윈도우 Application을 하나의 프레임으로 구분해 주었다.</p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>개발환경</strong></span></p>
<p><img class="alignnone size-full wp-image-30042" alt="33 ICT 원격지 (9)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-9.jpg" width="476" height="134" /></p>
<p><span style="font-size: medium;color: #ffffff;background-color: #000080"><strong>단계별 제작 과정</strong></span></p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>모형 로봇팔, 이동형 로봇팔 납땜</strong></span></p>
<p><img class="alignnone  wp-image-30043" alt="33 ICT 원격지 (10)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-10-352x620.jpg" width="400" /></p>
<p><strong><span style="color: #ffffff;background-color: #ff0000">이동형 로봇팔 제작</span></strong></p>
<p><img class="alignnone  wp-image-30044" alt="33 ICT 원격지 (11)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-11-434x620.jpg" width="400" /></p>
<p><img class="alignnone  wp-image-30045" alt="33 ICT 원격지 (12)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-12-454x620.jpg" width="400" /></p>
<p><img class="alignnone  wp-image-30046" alt="33 ICT 원격지 (13)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-13.jpg" width="400" /></p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>모형 로봇팔 제작</strong></span></p>
<p><img class="alignnone  wp-image-30047" alt="33 ICT 원격지 (14)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-14-393x620.jpg" width="400" /></p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>실험 사진</strong></span></p>
<p><img class="alignnone  wp-image-30048" alt="33 ICT 원격지 (15)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-15-364x620.jpg" width="400" /></p>
<p><span style="font-size: medium;color: #ffffff;background-color: #000080"><strong>기타</strong></span></p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>회로도</strong></span></p>
<p><img class="alignnone  wp-image-30049" alt="33 ICT 원격지 (16)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-16.jpg" width="400" /></p>
<p><img class="alignnone  wp-image-30050" alt="33 ICT 원격지 (17)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-17.jpg" width="400" /> <img class="alignnone  wp-image-30051" alt="33 ICT 원격지 (18)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-18.jpg" width="400" /></p>
<p><img alt="33 ICT 원격지 (19)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-19.jpg" width="400" /></p>
<p><a href="http://www.ntrexgo.com/archives/29848/33-ict-%ec%9b%90%ea%b2%a9%ec%a7%80-1" rel="attachment wp-att-30031"><img class="alignnone size-large wp-image-30031" alt="33 ICT 원격지 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ICT-원격지-1-559x620.jpg" width="559" height="620" /></a></p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>소스코드</strong></span></p>
<p><span style="color: #ffffff;background-color: #ff9900">AVR</span></p>
<p><strong><span style="color: #ffffff;background-color: #ff9900">이동형 로봇팔</span></strong></p>
<div class="symple-box gray none" style="text-align:left; width:100%;"> 
<p>#include &lt;iom128.h&gt;<br />
#include &lt;ina90.h&gt;<br />
#include “Header/DcPid.h”<br />
#include “Header/i2c_k.h”<br />
#include “Header/dynamixel_k.h”<br />
unsigned char PortCtrl = 0&#215;00;<br />
unsigned int SecFlag = 0, tim_count = 0;<br />
unsigned int LeftObjectSpeed, RightObjectSpeed, LeftPresentSpeed, RightPresentSpeed;<br />
unsigned int DutyA, DutyB;<br />
unsigned int LeftEncoder = 0, RightEncoder = 0;<br />
int RightOutput, LeftOutput, LeftBeforeError, RightBeforeError;<br />
int LeftErrorSum = 0, RightErrorSum = 0;<br />
int LeftError, RightError;<br />
void StartDc(void)<br />
{<br />
if(twi_ch[2] == 1)//drive mode<br />
{<br />
BasicRobotArm();<br />
PortCtrl = 0&#215;00;<br />
if(twi_ch[0] &gt; 5)<br />
{<br />
LeftObjectSpeed = twi_ch[0] &#8211; 2;<br />
PortCtrl |= 0&#215;01;<br />
}<br />
else if (twi_ch[0] &lt; 5)<br />
{<br />
LeftObjectSpeed = 8 &#8211; twi_ch[0];<br />
PortCtrl |= 0&#215;02;<br />
}<br />
else<br />
{<br />
LeftObjectSpeed = 0;<br />
PortCtrl |= 0&#215;01;<br />
}</p>
<p>if(twi_ch[1] &gt; 5)<br />
{<br />
RightObjectSpeed = twi_ch[1] &#8211; 2;<br />
PortCtrl |= 0&#215;04;<br />
}<br />
else if(twi_ch[1] &lt; 5)<br />
{<br />
RightObjectSpeed = 8 &#8211; twi_ch[1];<br />
PortCtrl |= 0&#215;08;<br />
}<br />
else<br />
{<br />
RightObjectSpeed = 0;<br />
PortCtrl |= 0&#215;04;<br />
}<br />
}<br />
else // mission perform mode<br />
{<br />
twi_ch[0] = 5;<br />
twi_ch[1] = 5;<br />
PortCtrl = 0&#215;00;<br />
}</p>
<p>if(SecFlag == 1)<br />
{<br />
__disable_interrupt();<br />
LeftPresentSpeed = (LeftEncoder*312)/1000;<br />
LeftError = LeftObjectSpeed &#8211; LeftPresentSpeed;</p>
<p>if(LeftError &gt; 0)<br />
{<br />
/*LeftOutput = (58*LeftError)/100 + (500*(LeftError-LeftBeforeError))/100 + (10*LeftErrorSum)/100 ;*/<br />
LeftOutput = (L_K_P*LeftError + L_K_D*(LeftError-LeftBeforeError)<br />
+ L_K_I*LeftErrorSum)/SCALING_FACTOR;<br />
DutyA += LeftOutput;<br />
if(DutyA &gt; 1000)<br />
DutyA = 1000;<br />
}<br />
else if( LeftError &lt; 0)<br />
{<br />
DutyA -= 5;<br />
if(DutyA &lt; 1)<br />
DutyA = 0;</p>
<p>…</p>
<p>…</p>
<p>…</p>
<p>…</p>
<p>전체소스는 하단 pdf를 참고하시기 바랍니다.</p>
</div>
<div id='wpdm_file_14' class='wpdm_file wpdm-only-button'><div class='cont'><div class='btn_outer'><div class='btn_outer_c' style='background-image: url(http://www.ntrexgo.com/wp-content/plugins/download-manager/icon/file_extension_pdf.png);'><a class='btn_left  ' rel='14' title='[33호]원격지 작업이 가능한 이동 로봇 팔 관리 시스템 소스코드 ' href='http://www.ntrexgo.com/?wpdmact=process&did=MTQuaG90bGluaw=='  >Download</a><span class='btn_right'>&nbsp;</span></div></div><div class='clear'></div></div></div>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>참고문헌</strong></span><br />
· 열혈 TCP/IP 소켓 프로그래밍<br />
· C# 5.0 프로그래밍<br />
· 라즈베리파이<br />
· AVR ATmega128 정복<br />
· 닷넷 프로그래밍 정복<br />
· 열혈 C프로그래밍</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ntrexgo.com/archives/29848/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[33호]지능형 화재 감시 자율 주행 로봇</title>
		<link>http://www.ntrexgo.com/archives/29843</link>
		<comments>http://www.ntrexgo.com/archives/29843#comments</comments>
		<pubDate>Sun, 15 Nov 2015 06:05:43 +0000</pubDate>
		<dc:creator>디바이스마트 매거진</dc:creator>
				<category><![CDATA[디바이스마트 매거진]]></category>
		<category><![CDATA[특집]]></category>
		<category><![CDATA[33호]]></category>
		<category><![CDATA[Feature]]></category>
		<category><![CDATA[ict]]></category>
		<category><![CDATA[공모전]]></category>
		<category><![CDATA[디바이스마트]]></category>
		<category><![CDATA[매거진]]></category>
		<category><![CDATA[융합]]></category>
		<category><![CDATA[입선작]]></category>
		<category><![CDATA[프로젝트]]></category>

		<guid isPermaLink="false">http://www.ntrexgo.com/?p=29843</guid>
		<description><![CDATA[디바이스마트매거진 33호 &#124; 2015 ICT 융합 프로젝트 공모전 입선작으로 화재에 대한 이상 징후를 검출하고 모니터링 함으로써 대형 화재로의 확산을 방지하는 지능형 화재 감시 자율 주행 로봇을 소개한다.]]></description>
				<content:encoded><![CDATA[<p><img class="alignnone size-large wp-image-29038" alt="ict main2-01" src="http://www.ntrexgo.com/wp-content/uploads/2015/09/ict-main2-01-620x150.jpg" width="620" height="150" /></p>
<p><span style="font-size: x-large;color: #003366"><strong>ICT 융합 프로젝트 공모전 입선</strong></span></p>
<p><span style="font-size: large;color: #008080"><strong>지능형 화재 감시 자율 주행 로봇</strong></span></p>
<p style="text-align: right">글 | 상명대학교 이정욱, 조형문, 김한수, 신인섭</p>
<p>&nbsp;</p>
<p><span style="color: #ffffff;font-size: medium;background-color: #0000ff"><strong>심사평</strong></span></p>
<p><span style="color: #0000ff"><strong>펌테크</strong></span> 비콘을 이용한 로봇의 좌표와 방위각 제어 방법에 관련된 기술접목에 관련된 아이디어 접근 방식은 훌륭하나, 로봇의 하우징 및 구동방식은 기존의 화재 감시 로봇과의 차이점이 없어 보입니다.<br />
<span style="color: #0000ff"><strong>JK전자</strong></span> 전원공급에 대한 부분이 해결이 된다면 굉장히 유용하게 활용이 가능한 작품이다. 사람이 직접 감시하기에는 위험한 지역에 로봇을 설치하여 지속적으로 위험 요소를 감지하여 인간에게 유익하도록 활용이 되는 것이 로봇 개발의 주된 목적인데, 이에 맞는 작품이다.<br />
<span style="color: #0000ff"><strong>뉴티씨</strong></span> 기구부, 회로도 및 소프트웨어가 조화를 이루고 아이디어 또한 참신하다. 또한 솔루션 개발을 위한 특허 등록까지 모두 마친 상황이기도 하다. 앞으로의 솔루션 전개가 기대된다.<br />
칩센 현재 출시된 다른 기능형 로봇에 비해서 지능형 화재 감시 자율 주행 로봇은 평지에서만 활동이 가능한 것으로 보여 활동범위가 좁아 다양한 곳에서 사용하기에는 부적합하며, RF비콘을 사용하여 좌표와 방위각 산출은 RF비콘에 화재가 일어날 경우 원하는 곳으로 이동이 불가능할 것으로 보여 제 임무를 소화할 수 있을지 의문. 그리고 배터리 자동 충전등 사용상의 불편함을 해소할 수 있는 기능이 추가되어야 할 것으로 보인다.<br />
<span style="color: #0000ff"><strong>위드로봇</strong></span> 작품 보고서 제목에는 “자율 주행”이라는 표현이 있는데, 보고서에는 로봇이 자신의 위치를 인지하기 위한 방식의 설명이 누락되어 있습니다. RF와 초음파를 이용한다는 언급이 있는데, 이 기술을 자체적으로 구현한 것인지, 구현했다면 어떻게 구현한 것인지에 대한 설명이 없어 “자율 주행”이 가능한 것인지 파악하기 어렵습니다. 또한 이동로봇의 holonomic contraint에 대한 학습을 통해 메카넘 휠의 필요성이 설명되었으면 더 좋은 보고서가 되었을 것이라는 생각이 듭니다. 화재를 검출하기 위한 각종 센서 동작도, 검출 거리 및 오동작에 대한 대비책이 언급되어 있지 않아 아쉬웠습니다.</p>
<p><span style="font-size: medium"><strong><span style="color: #ffffff;background-color: #0000ff">작품 개요</span></strong></span><br />
■ 전통적인 화재감지 시스템의 문제점<br />
· 화재 발생 시, 기 설치된 센서의 감지 영역까지 화재가 확산되어야 감지가 가능<br />
· 화재에 대한 위치, 확산 정도 등의 정확한 정보 파악이 힘듦<br />
■ 상기 문제점을 보완할 수 있도록, 자율 주행을 통한 화재 감지 및 모니터링을 수행하는 자율 주행 로봇 기반 화재 조기경보 시스템 개발<br />
■ 무인화, 경량화, 소형화를 통한 인력 대체 및 위험 시설에 대한 능동적이고 빠른 화재 경보로 산업 시설물의 대형 화재 방지</p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>개발 대상 기술의 개요 및 필요성</strong></span><br />
현재 대부분의 부착형 화재감지 시스템은 적외선 센서나 광학센서, 이온 센서 등을 사용하여 화재로부터 발생되는 연기, 열, 복사에너지 등을 감지하도록 설계되었습니다. 이러한 부착형 화재감지 시스템의 문제점은 화재가 발생하더라도 센서의 감지 영역까지 확산되어야 감지할 수 있다는 문제점과 감지한 화재에 대한 위치, 확산 정도 등의 정확한 정보를 제공할 수 없다는 문제점이 있었습니다. 이러한 문제점은 다량 설치를 통해 해결이 가능하지만, 이는 과도한 설치비용으로 인해 적절한 해결방법이 될 수 없습니다. 또한, 최근 화재가 발생하여 많은 피해를 주고 있는 위험물 취급 장소나 지하 공동구처럼 인간이 활동하기에 위험요소가 내포되어 있거나 불편한 장소에 로봇을 사용한다면 화재를 능동적으로 감지하여, 초기진압을 도울 수 있을 것으로 보입니다.<br />
본 기술은 화재에 대한 이상 징후를 검출하고 모니터링 함으로써 대형화재로의 확산을 방지하는 지능형 화재 감시 자율 주행 로봇입니다. 지능형 화재 감시 자율 주행 로봇은 RF와 초음파 등을 활용하여 삼각측량법으로 거리를 측정하는 beacon 시스템을 통해 로봇의 자율 주행을 위한 현재 좌표와 방위각을 산출하여 지정된 경로로 이동할 수 있습니다. 화재가 발생했거나 화재 발생 징후를 가스 누출 및 온도 변화 등 다양한 화재를 감지할 수 있는 센서를 통해 탐지하여 무선 네트워크를 활용하여 모니터링 시스템에 알림으로써 초기에 화재를 감지하여 대형화재로의 확산을 방지하는 기능을 수행합니다.</p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>기술 개발 시 예상 효과 및 활용방안</strong></span><br />
<span style="color: #ffffff;background-color: #ff6600"><strong>예상 효과</strong></span><br />
소방방재청의 14년 4월말 화재발생현황을 살펴보면 비주거(교육시설, 판매-업무시설, 집합시설, 의료복지시설, 산업시설, 운수-자동차시설, 문화재시설, 생활서비스시설, 기타 건축물-시설물)의 화재 발생률이 가장 높다는 것을 알 수 있으며, 비주거 화재 발생에 대한 세부현황을 살펴보면, 산업시설이 35.84%(557건)로 가장 높은 발생률을 나타내고 있습니다.</p>
<p><img class="alignnone size-full wp-image-29981" alt="33 ict 지능형 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-1.png" width="600" height="276" /></p>
<p><img class="alignnone size-full wp-image-29983" alt="33 ict 지능형 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-2.png" width="600" height="276" /></p>
<p>이는 산업시설의 시설물 관리 소홀과 화재감지기의 사각지역 및 인력 투입이 힘든 장소의 화재로 인한 것이 가장 큰 원인으로 파악됩니다.</p>
<p><img class="alignnone size-full wp-image-29985" alt="33 ict 지능형 (3)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-3.png" width="600" height="391" /><br />
기술의 최종 결과물인 지능형 화재 감시 자율 주행 로봇은 이동형 무인 화재 감지 시스템으로 인건비 절감효과가 있으며, 종래의 화재감지기를 사용할 수 없는 장소의 화재 감지를 수행할 수 있다는 장점이 있습니다. 이는 산업시설에서 발생하는 화재를 초기에 진압할 수 있는 효과가 있으며, 또한, 지능형 화재 감시 자율 주행 로봇을 통하여 화재감지기 분야의 세계 시장을 개척하고 선점할 수 있는 효과가 있을 것으로 판단됩니다.</p>
<p><strong><span style="color: #ffffff;background-color: #ff6600">활용 방안</span></strong><br />
본 기술은 지능적으로 이동하며 화재를 감지하는 장치로 아래와 같이 인간이 활동하기에 위험요소가 내포되어 있거나 불편한 장소에서 자율 주행하며 화재를 감시합니다. 이를 통해 아래와 같은 산업 시설물 등에서 발생할 수 있는 화재를 초기에 발견하여 대형화재로의 확산을 방지할 수 있을 것입니다.</p>
<p><img class="alignnone size-full wp-image-29980" alt="33 ict 지능형 (1)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-1.jpg" width="600" height="483" /></p>
<p><span style="color: #ffffff;font-size: medium;background-color: #0000ff"><strong>작품 설명</strong></span><br />
기술개술의 목표는 부착형 화재감지기의 사각지역을 해소할 수 있는 로봇을 개발하는 것이므로, 지능형 화재 감지 자율 주행 시스템은 화재 발생 및 이상 징후가 있는 곳을 조기에 탐지하여 좌표 산출 장치를 통해 위치를 파악하고 이를 무선 네트워크를 통해 모니터링 시스템에 전송함으로써 화재를 조기에 파악하고 초기 진압을 가능하게 하는 솔루션을 포함하고 있습니다. 또한, 소형 및 경량화된 화재 감지 자율 주행 로봇으로 위험물 취급 장소와 산업시설의 좁은 구역 등 인간이 활동하기에 위험요소가 내포되어 있거나 불편한 장소에 적용이 가능하며, 중소형 산업시설에도 공급될 수 있도록 보급형 시스템이 개발중입니다.</p>
<p><strong><span style="color: #ffffff;background-color: #ff0000">주요동작 및 특징</span></strong><br />
■ 초음파, RF 비콘을 활용해 좌표와 방위각을 산출하여 지정된 좌표대로 이동하는 소형/경량 자율 주행 로봇 개발<br />
■ 온도, 가스, 연기 등의 센서를 통한 화재 및 화재 발생 가능성이 있는 위치를 알아내고 필요에 따라 영상 정보를 추출하여 전송할 수 있는 장치 개발<br />
■ 화재 감지 시 모니터링 시스템에 정보를 전송하는 무선 전송장치 개발</p>
<p><span style="color: #ffffff;background-color: #ff6600"><strong>주요동작</strong></span><br />
비콘을 활용해 좌표와 방위각을 산출하는 좌표 산출부와 지정된 좌표대로 이동하는 자율 주행부가 있으며, 또한, 온도, 가스, 연기 등의 센서를 부착하여 화재 및 이상 징후 발생 가능성이 있는 위치를 산출하는 화재 감지부, 그리고 화재 및 이상 징후 감지 시 모니터링 시스템에 정보를 전송하는 무선 네트워크부로 구성되어 있습니다.</p>
<p><img class="alignnone size-full wp-image-30000" alt="33 ict 지능형 (4)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-4.png" width="600" height="450" /><br />
기술 개발을 위해 위와 같이 주행부, 화재 감지부, 동력부를 실장하기 위한 하체부를 설계하였으며, 메카넘휠의 전우좌우 운동방향과 같은 기타 활동성요소에 대한 개발을 완료하였습니다.</p>
<p><strong><span style="color: #ffffff;background-color: #ff6600">특징</span></strong><br />
비콘을 활용해 좌표와 방위각을 산출하여 지정된 좌표대로 이동하는 자율 주행 시스템과 온도, 가스, 연기 등의 센서를 부착하여 화재 및 화재 발생 가능성이 있는 위치를 알아내고 필요에 따라 영상정보를 추출하여 전송할 수 있는 장치가 있습니다.</p>
<p><strong><span style="color: #ffffff;background-color: #ff0000">전체 시스템 구성</span></strong><br />
<strong>주행 장치 및 위치 인식 시스템</strong><br />
· 자율 주행을 위한 모터 컨트롤러 설계<br />
· 초음파 비콘 시스템을 활용하여 위치 인식 시험환경 구축<br />
· 주행 장치 및 동력부 실장을 위한 하체 외주 제작<br />
· 초음파 비콘 시스템의 수신기, 모터 컨트롤러, 제어부를 통합하여 위치 인식에 따른 자율 주행 알고리즘 검증 및 보완</p>
<p><img class="alignnone size-full wp-image-29982" alt="33 ict 지능형 (2)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-2.jpg" width="526" height="709" /></p>
<p><strong>화재 감지 센서부</strong><br />
· 화재 발생 징후 파악을 위한 화재 감지 센서 선정<br />
· 온도, 연기, 불꽃, 가스 센서의 실제 성능 측정<br />
· 온도, 연기, 불꽃, 가스 센서를 통합한 화재 감지 센서부 설계</p>
<p><span style="font-size: small;color: #000000"><strong>지능형 화재 감시 자율 주행 로봇</strong></span><br />
· 주행 장치, 위치 인식 시스템, 화재 감지 센서부를 통합한 전체 시스템 시작품 제작<br />
· 시작품 성능 테스트 및 보완</p>
<p>지능형 화재 감지 자율 주행 로봇은 비콘을 활용하여 좌표를 산출하고, 이를 통하여 이동하며 화재 발생 및 이상 징후를 감지함으로써 사람이 진입하지 못하는 시설물 또는 오염 지역 등에 적용할 수 있도록 설계하였습니다.</p>
<p><img class="alignnone size-full wp-image-29984" alt="33 ict 지능형 (3)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-3.jpg" width="526" height="306" /></p>
<p>위 사진에 나타나 있는 메카넘휠이란 시스템을 활용하여 기존 주행 방식의 단점인 회전 반경 등을 해결하였으며, 메카넘휠은 <em>그림 9. 메카넘휠의 회전 운동 방향</em>에 나타나 있는 이동 방식과 같이 회전 반경이 없이 제자리에서 회전이 가능합니다. 따라서 전진, 후진뿐만 아니라 좌, 우, 대각선 등 자유로운 이동이 가능합니다. 이를 통해 위험물 취급 장소와 산업시설의 좁은 구역 등 인간이 활동하기에 위험요소가 내포되어 있거나 불편한 장소에 적용이 가능하며, 중소형 산업시설에도 공급될 수 있는 보급형 시스템으로 각종 발전소 및 폐기물처리시설, 유류저장 및 송유설비, 가스공급설비 등에 적용이 가능하여 기존 소방방재 부품 시장을 증대시킬 수 있다고 보고 있습니다.</p>
<p><img class="alignnone size-full wp-image-29986" alt="33 ict 지능형 (4)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-4.jpg" width="526" height="859" /></p>
<p><img class="alignnone size-full wp-image-29988" alt="33 ict 지능형 (5)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-5.jpg" width="526" height="210" /></p>
<p><span style="color: #ffffff;font-size: medium;background-color: #0000ff"><strong>지능형 화재 감시 자율 주행 로봇</strong></span></p>
<p><img class="alignnone size-full wp-image-29989" alt="33 ict 지능형 (6)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-6.jpg" width="526" height="614" /></p>
<p><strong><span style="font-size: medium;color: #ffffff;background-color: #0000ff">기타(개발환경, 사용된 제품, 회로도, 참고자료 등)</span></strong><br />
<span style="color: #ffffff;background-color: #ff0000"><strong>개발환경(개발언어, Tool, 사용시스템 등)</strong></span></p>
<p><img class="alignnone size-full wp-image-29990" alt="33 ict 지능형 (7)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-7.jpg" width="526" height="379" /></p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>사용된 제품군</strong></span></p>
<p><img class="alignnone size-full wp-image-29991" alt="33 ict 지능형 (8)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-8.jpg" width="526" height="169" /></p>
<p><span style="color: #ffffff;font-size: small;background-color: #ff0000"><strong>회로도</strong></span></p>
<p><img class="alignnone size-full wp-image-29992" alt="33 ict 지능형 (9)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-9.jpg" width="526" height="701" /></p>
<p><img class="alignnone size-full wp-image-29993" alt="33 ict 지능형 (10)" src="http://www.ntrexgo.com/wp-content/uploads/2015/11/33-ict-지능형-10.jpg" width="526" height="502" /></p>
<p><span style="color: #ffffff;background-color: #ff0000"><strong>참고 자료</strong></span><br />
· 자율 주행 로봇 및 그 제어 방법, 출원번호 : 특허출원 제2012-0020220호</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ntrexgo.com/archives/29843/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[6호]2010 캠스톤 디자인 공모전 &#8211; 소형 인공위성(CANSAT) 개발</title>
		<link>http://www.ntrexgo.com/archives/8782</link>
		<comments>http://www.ntrexgo.com/archives/8782#comments</comments>
		<pubDate>Fri, 25 Mar 2011 05:56:12 +0000</pubDate>
		<dc:creator>디바이스마트 매거진</dc:creator>
				<category><![CDATA[디바이스마트 매거진]]></category>
		<category><![CDATA[특집]]></category>
		<category><![CDATA[6호]]></category>
		<category><![CDATA[공모전]]></category>
		<category><![CDATA[소형]]></category>
		<category><![CDATA[인공위성]]></category>
		<category><![CDATA[입선작]]></category>
		<category><![CDATA[캡스톤]]></category>

		<guid isPermaLink="false">http://www.ntrexgo.com/?p=8782</guid>
		<description><![CDATA[디바이스마트 매거진 6호 &#124; 2010 디바이스마트 캡스톤 디자인 공모전 입선작. 음료 캔 사이즈와 무게(400g이하)를 가진 소형 인공위성이다. 학생들의 인공위성에 대한 전반적인 이해 증진에 목적이 있으며, 이번에 우리가 개발한 모델은 KUMSAT-1A이다.]]></description>
				<content:encoded><![CDATA[<h3><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD027.jpg" rel="lightbox[8782]"><img class="wp-image-9093 alignleft" alt="06FCAPD027" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD027-225x300.jpg" width="162" height="216" /></a>2010 디바이스마트</h3>
<h3>캡스톤 디자인 공모전 입선작</h3>
<h2><strong><span style="color: #0000b7">소형 인공위성(CANSAT) 개발</span></strong></h2>
<p>&nbsp;</p>
<p style="text-align: right">팀명 : Dreamers</p>
<p style="text-align: right">참가자 : 한국항공대학교 항공우주 및 기계공학부<br />
[이원규, 최미미, 이찬형, 안도현, 장병규]
<p>&nbsp;</p>
<p><strong style="color: #2863d6">DREAMERS 팀 구성 및 소개</strong></p>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD068.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9134" alt="06FCAPD068" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD068-300x199.jpg" width="216" height="143" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAP075.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9143" alt="06FCAP075" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAP075-300x225.jpg" width="189" height="142" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAP076.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9142" alt="06FCAP076" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAP076-300x225.jpg" width="189" height="142" /></a></td>
</tr>
</tbody>
</table>
<table style="border-collapse: collapse;width: 620px" border="1" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td style="text-align: center">성명</td>
<td style="text-align: center">이원규</td>
<td style="text-align: center">최미미</td>
<td style="text-align: center">이찬형</td>
<td style="text-align: center">안도현</td>
<td style="text-align: center">장병규</td>
</tr>
<tr>
<td style="text-align: center">담당분야</td>
<td style="text-align: center">SYSTEM<br />
(팀장, GPS)</td>
<td style="text-align: center">PAYLOAD<br />
(카메라,온도센서)</td>
<td style="text-align: center">CS(통신),<br />
EPS(전력)</td>
<td style="text-align: center">OBC<br />
(마이크로콘트롤러)</td>
<td style="text-align: center">SMS<br />
(외형설계 및 하드웨어)</td>
</tr>
</tbody>
</table>
<p>현재 한국 항공대학교 항공우주 및 기계공학부에 재학 중인 4학년 학생들로 구성된 팀이며, 평소에 마이크로 컨트롤러를 이용한 로봇제작에 관심이 많은 이들로 구성되었다.<br />
현재 학교 연구 과제에서도, 같은 팀으로 활동하고 있으며, 디바이스마트 캡스톤 공모전에 제출한 작품「고도 500m에서 무선 통신이 가능한 소형 인공위성(CANSAT)」개발과 유사한 로켓에 탑재하여, 대기 정보를 얻을 수 있는 소형 인공위성(캔셋)을 제작 중에 있다.</p>
<p><span style="color: #2863d6"><strong>캔셋(CANSAT)의 정의</strong></span></p>
<p>음료 캔 사이즈와 무게(400g이하)를 가진 소형 인공위성이다. 학생들의 인공위성에 대한 전반적인 이해 증진에 목적이 있으며, 이에 매년 국제적인 경진대회가 열리기도 한다 (www.cansatcompetition.com/Main.html). 이런 캔셋으로 이번에 우리가 개발한 모델은 KUMSAT-1A이다.</p>
<p><span style="color: #2863d6"><strong>임무목적 및 시나리오</strong></span></p>
<div id="attachment_9067" class="wp-caption aligncenter" style="width: 506px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD001.jpg" rel="lightbox[8782]"><img class=" wp-image-9067" alt="06FCAPD001" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD001-620x348.jpg" width="496" height="278" /></a><p class="wp-caption-text">그림1 KUMSAT-1A의 임무 흐름도</p></div>
<p>KUMSAT-1A는 그림1에서 보듯이 기상 측정용 헬륨 풍선을 이용하여 고도 500m에서 사진 촬영과 온도 측정, 위치정보의 송신 등의 임무를 수행한다.<br />
주어진 임무는 크게 2단계로, ‘초기단계’와 ‘임무단계’로 나뉘어져 운용된다. 우선 전원을 켜면 초기단계가 시작되는데, 이 상태로 GPS 신호를 안정적으로 받을 때까지 대기한다. GPS 신호가 안정적으로 수신되는 것을 확인한 후(약 5분소요)에, 임무단계로 돌입하며 기상 관측용 풍선과 연결 된 상태로 고도 500m까지 상승한다. 목표 고도 500m(또는 1km도 가능)에 이르러 지상국은 KUMSAT-1A로부터 무선으로 사진을 전송 받는다.<br />
지상국에서는 무선으로 사진을 전송받기 위해서 KUMSAT-1A에 Command를 전송하게 된다. 무선으로 사진을 전송 받는데 소요되는 시간은 약 15분 정도이다. 무선 사진 전송이 완료된 후 지상국에서 보내는 Command에 의해, WCD(Wire Cutting Device)의 니크롬선이 가열되어, 기상풍선과 연결되어있는 선을 끊고, KUMSAT-1A는 풍선으로부터 분리된다. 분리 이후 낙하산을 전개하여 낙하하며, 매 6초마다 촬영한 이미지 데이터를 메모리카드에 저장하고, 동시에 낙하하면서 측정한 대기온도와 위치정보를 지상국에 전송한다. 마지막으로 지상국에서 수신된 위치정보(경도·위도)를 토대로 지상국 소프트웨어를 이용하여 KUMSAT-1A의 위치를 파악하여 회수한다.<br />
KUMSAT-1A의 시스템은 실제 인공위성과 같이 크게 탑재체와 버스(Bus)시스템으로 나눌 수 있으며, 버스시스템은 다시 기계 및 구조계(Structure Mechanical Subsystem, SMS), 탑재컴퓨터(On Board Computer, OBC), 전력계(Electrical Power Subsystem, EPS) 및 통신계(Communication Subsystem, CS)의 네 개의 서브시스템으로 구성된다.</p>
<p><span style="color: #2863d6"><strong>기계 및 구조계 (SMS)</strong></span></p>
<p>기계 및 구조계는 전체 시스템의 제한된 질량 요구조건 내에서 탑재체와 탑재컴퓨터, 통신계, 전력계의 각 부품이 구조적으로 간섭과 배선을 고려하여 개발하였다.<br />
KUMSAT-1A의 질량배분은 표1과 같다.</p>
<table style="border-collapse: collapse;width: 500px" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="text-align: center">Subsystem</td>
<td style="text-align: center">부품명</td>
<td style="text-align: center">무게(g)</td>
</tr>
<tr>
<td style="text-align: center" rowspan="3">탑재체</td>
<td style="text-align: center"> Camera</td>
<td style="text-align: center"> 10</td>
</tr>
<tr>
<td style="text-align: center"> GPS</td>
<td style="text-align: center">10</td>
</tr>
<tr>
<td style="text-align: center"> Thermometer</td>
<td style="text-align: center">5</td>
</tr>
<tr>
<td style="text-align: center" rowspan="2">기계 및 구조계</td>
<td style="text-align: center">Main structure</td>
<td style="text-align: center">130</td>
</tr>
<tr>
<td style="text-align: center"> Wire Cutting Device</td>
<td style="text-align: center">5</td>
</tr>
<tr>
<td style="text-align: center" rowspan="2">탑재 컴퓨터</td>
<td style="text-align: center"> Main board</td>
<td style="text-align: center">40</td>
</tr>
<tr>
<td style="text-align: center"> SD Memory &amp; Memory socket</td>
<td style="text-align: center">10</td>
</tr>
<tr>
<td style="text-align: center" rowspan="2">전력계</td>
<td style="text-align: center"> Battery</td>
<td style="text-align: center">80</td>
</tr>
<tr>
<td style="text-align: center"> EPS board</td>
<td style="text-align: center"> 50</td>
</tr>
<tr>
<td style="text-align: center" rowspan="2">통신계</td>
<td style="text-align: center"> RF modem</td>
<td style="text-align: center"> 40</td>
</tr>
<tr>
<td style="text-align: center"> Antenna</td>
<td style="text-align: center"> 30</td>
</tr>
<tr>
<td style="text-align: center">TOTAL</td>
<td style="text-align: center">KUMSAT</td>
<td style="text-align: center"> 410</td>
</tr>
<tr>
<td style="text-align: center" colspan="3">표1 질량배분</td>
</tr>
</tbody>
</table>
<p>KUMSAT-1A 구조체의 재료는 Aluminum-7075를 사용하였으며, Aluminum-7075의 물성치는 표2와 같다.</p>
<table style="border-collapse: collapse;width: 500px" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>재료</td>
<td>Aluminum-7075</td>
</tr>
<tr>
<td>극한응력 (MPa)</td>
<td>570</td>
</tr>
<tr>
<td>항복응력 (MPa)</td>
<td>280</td>
</tr>
<tr>
<td>절대신장율 (%)</td>
<td>11</td>
</tr>
<tr>
<td>탄성계수 (GPa)</td>
<td>72</td>
</tr>
<tr>
<td style="text-align: center" colspan="2">표2. 재료의 물성치</td>
</tr>
</tbody>
</table>
<p>로켓이 발사되는 과정에서 KUMSAT-1A는 약 9G의 중력가속도를 받게 되고, 설계상의 안전계수 2를 적용하면 88.2N의 하중을 받게 된다. 이 경우를 고려하여 ANSYS를 이용, 응력 해석 결과는 그림2와 같다. KUMSAT-1A에 작용하는 최대 응력은 18.756MPa로 재료의 항복 응력인 280MPa에 비하여 매우 작아 하중을 충분히 견딜 수 있을 것으로 보인다.</p>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD002.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9068" alt="06FCAPD002" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD002-300x177.jpg" width="240" height="142" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD003.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9069" alt="06FCAPD003" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD003-300x251.jpg" width="170" height="142" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD004.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9070" alt="06FCAPD004" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD004-300x226.jpg" width="168" height="126" /></a></td>
</tr>
<tr>
<td style="text-align: center">그림 2</td>
<td style="text-align: center">그림 3</td>
<td style="text-align: center">그림 4</td>
</tr>
</tbody>
</table>
<p>KUMSAT의 기본 골격은 그림3과 도면1~5와 같이 Main Panel, 두 개의 Side Frame 그리고 Top Cap과 Bottom Cap으로 구성되어 있다. Side Frame의 크기는 2mm×20mm×123mm이며 하중을 지탱하고, 외부에서 올 수 있는 충격 요소로부터 내부를 보호하는 역할을 한다. Main Panel은 2mm×66mm×119mm의 크기로 각 서브시스템의 부품들을 고정하고 GPS 수신기와 카메라가 위 아래 Cap에 잘 고정되도록 설계하였다. Top Cap과 Bottom Cap은 크기가 Φ66mm×10mm로 카메라와 안테나, GPS가 외부를 지향할 수 있도록 하며, 특히 Bottom Cap에는 낙하산을 연결할 수 있도록 하였다.<br />
각 부품의 배치는 그림4에 나타내었다. 이것은 KUMSAT-1A의 부품 배치인데, 풍선과의 연결을 끊기 위한 Wire Cutting Device(WCD) 때문에 배터리는 두 개가 장착된다.</p>
<div class="symple-toggle"><h3 class="symple-toggle-trigger">도면 1 ~ 5 보기</h3><div class="symple-toggle-container">
<div id="attachment_9071" class="wp-caption alignnone" style="width: 568px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD005.jpg" rel="lightbox[8782]"><img class=" wp-image-9071 " alt="도면 1. Top Cap" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD005-620x412.jpg" width="558" height="371" /></a><p class="wp-caption-text">도면 1. Top Cap</p></div>
<div id="attachment_9072" class="wp-caption alignnone" style="width: 568px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD006.jpg" rel="lightbox[8782]"><img class=" wp-image-9072 " alt="도면 2. Bottom Cap" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD006-620x376.jpg" width="558" height="338" /></a><p class="wp-caption-text">도면 2. Bottom Cap</p></div>
<div id="attachment_9073" class="wp-caption alignnone" style="width: 568px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD007.jpg" rel="lightbox[8782]"><img class=" wp-image-9073 " alt="도면 3. Side Frame" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD007-620x337.jpg" width="558" height="303" /></a><p class="wp-caption-text">도면 3. Side Frame</p></div>
<div id="attachment_9074" class="wp-caption alignnone" style="width: 568px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD008.jpg" rel="lightbox[8782]"><img class=" wp-image-9074 " alt="도면 4. Side Frame" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD008-620x367.jpg" width="558" height="330" /></a><p class="wp-caption-text">도면 4. Side Frame</p></div>
<div id="attachment_9075" class="wp-caption alignnone" style="width: 568px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD009.jpg" rel="lightbox[8782]"><img class=" wp-image-9075 " alt="도면 5. Main Panel" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD009-620x468.jpg" width="558" height="421" /></a><p class="wp-caption-text">도면 5. Main Panel</p></div>
</div></div>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD010.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9076" alt="06FCAPD010" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD010-620x464.jpg" width="318" height="238" /></a><br />
조립을 위한 준비물은 다음과 같다. 원할 한 조립을 위해 니퍼와 롱노즈 및 칼, 육각렌치 등을 준비하였다.</p>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD012.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9078" alt="06FCAPD012" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD012.jpg" width="172" height="230" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD011.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9077" alt="06FCAPD011" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD011-300x225.jpg" width="300" height="225" /></a></td>
</tr>
</tbody>
</table>
<p>위에 사진과 같이 탑재체를 볼트와 너트로 고정하는 방식을 채택하였다.<br />
<a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD013.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9079" alt="06FCAPD013" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD013.jpg" width="334" height="250" /></a><br />
볼트와 너트로 체결이 힘든 곳은 케이블 타이를 이용하여 체결한다.</p>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD014.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9080" alt="06FCAPD014" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD014-300x224.jpg" width="300" height="224" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD015.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9081" alt="06FCAPD015" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD015-300x225.jpg" width="300" height="225" /></a></td>
</tr>
</tbody>
</table>
<p>카메라와 안테나는 아래를 지향하게 배치하며, GPS는 하늘을 향하게 배치한다.</p>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD016.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9082" alt="06FCAPD016" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD016-300x225.jpg" width="300" height="225" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD017.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9083" alt="06FCAPD017" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD017-300x225.jpg" width="300" height="225" /></a></td>
</tr>
<tr>
<td style="text-align: center">OBC와 EPS</td>
<td style="text-align: center">RF Modem, SD Memory, Batteries</td>
</tr>
</tbody>
</table>
<p>체결을 완성한 모습이다.</p>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD018.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9084" alt="06FCAPD018" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD018-300x225.jpg" width="300" height="225" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD019.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9085" alt="06FCAPD019" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD019-300x225.jpg" width="300" height="225" /></a></td>
</tr>
<tr>
<td style="text-align: center">WCD(Wire Cutting Device)가 장착된 모습</td>
<td style="text-align: center">완성된 KUMSAT</td>
</tr>
</tbody>
</table>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD020.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9086" alt="06FCAPD020" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD020-300x225.jpg" width="300" height="225" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD021.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9087" alt="06FCAPD021" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD021-300x225.jpg" width="300" height="225" /></a></td>
</tr>
</tbody>
</table>
<p>낙하산은 위에 사진과 같이 장착이 되며, 낙하 속도 3m/s로 설계하여 장착하였다.<br />
기상 풍선은 KUMSAT-1A의 무게 및 낙하산 무게, 낚시 줄 및 기타 외부환경요인을 감안하여 2330g을 들어 올릴 수 있는 것으로 선택, 사이언티픽 세일즈에서 30$에 구매를 하였다.</p>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD022.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9088" alt="06FCAPD022" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD022-300x93.jpg" width="300" height="93" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD023.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9089" alt="06FCAPD023" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD023-300x181.jpg" width="300" height="181" /></a></td>
</tr>
<tr>
<td colspan="2"><a href="http://www.scientificsales.com/8237-Weather-Balloon-300-Grams-Natural-p/8237.htm" target="_blank">http://www.scientificsales.com/8237-Weather-Balloon-300-Grams-Natural-p/8237.htm</a></td>
</tr>
<tr>
<td><img class="wp-image-9091 aligncenter" alt="06FCAPD025" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD025-300x225.jpg" width="270" height="203" /></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD028.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9094" alt="06FCAPD028" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD028-300x200.jpg" width="300" height="200" /></a></td>
</tr>
</tbody>
</table>
<p>KUMSAT은 기상풍선과 낚시줄로 연결되며 낙하산 또한 낚시줄에 의해 감싸지는 형태를 가진다.</p>
<table border="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD06.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9092" alt="06FCAPD06" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD06-225x300.jpg" width="203" height="270" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD027.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9093" alt="06FCAPD027" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD027-225x300.jpg" width="203" height="270" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD029.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9095" alt="06FCAPD029" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD029-225x300.jpg" width="203" height="270" /></a></td>
</tr>
</tbody>
</table>
<p><span style="color: #3366ff"><strong>Payload</strong></span></p>
<p><strong>1) 탑재체 스펙 현황</strong></p>
<p>·카메라 모듈</p>
<table border="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD030.jpg" rel="lightbox[8782]"><img class="alignnone size-full wp-image-9096" alt="06FCAPD030" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD030.jpg" width="222" height="121" /></a></td>
</tr>
</tbody>
</table>
<p>- Image sensor type : CMOS<br />
- mage data type : JPEG<br />
- Size : W20 x L28 x H24<br />
- Mass : 11g<br />
- Interface : UART<br />
- Power consumption : 200mW</p>
<p>·GPS : FGPMMOPA1</p>
<table border="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD031.jpg" rel="lightbox[8782]"><img class="alignnone size-full wp-image-9097" alt="06FCAPD031" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD031.jpg" width="227" height="130" /></a></td>
</tr>
</tbody>
</table>
<p>- High Sensitivity -165dBm<br />
- Position Accuracy : &lt; 3m<br />
- Cold start under 37 seconds(typical)<br />
- Maximum acceleration of 4G<br />
- Power consumption : 335mW</p>
<p>· 온도센서 : Digital Thermometer</p>
<table border="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD032.jpg" rel="lightbox[8782]"><img class="alignnone size-full wp-image-9098" alt="06FCAPD032" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD032.jpg" width="153" height="128" /></a></td>
</tr>
</tbody>
</table>
<p>- 3-wire interface<br />
- 9-Bit Resolution<br />
- Measures temperature range : -55˚C ~ +125˚C<br />
- Converts temperature in 1s at least (max)<br />
- DIP package</p>
<p><strong>2) CMOS 카메라 모듈</strong></p>
<p>KUMSAT의 주요 탑재체는 CMOS 카메라이다. 이 카메라 모듈은 탑재컴퓨터와 UART 통신이 가능하며, 파일 크기가 작은 JPEG 형식을 지원하기 때문에 빠른 데이터 통신이 가능하다. 또한 이미지 크기를 640×480, 320×240, 160×128, 80×64(pixel)의 네 가지 중 하나로 선택할 수 있는데, 640&#215;480의 경우 파일 크기가 약 40kB 내외이며, 320×240의 경우에는 약 10kB 내외이다. 파일 크기가 10kB일 경우 메모리카드에 사진 한 장을 저장하기까지 약 6초의 시간이 걸리며, 이 시간은 파일 크기에 비례한다. 따라서 640×480 크기로 찍을 경우, 임무 시간 내에 촬영 가능한 사진 수에 큰 제약을 받으므로, 320×240의 크기로 약 20장의 사진을 촬영할 수 있도록 절충하였다.<br />
사진 촬영 프로그램의 알고리즘은 그림5에 나타내었다. 우선 카메라와 탑재컴퓨터의 통신을 위해, 동기를 맞추는 과정이 필요하다. 카메라가 Command를 받은 것을 확인하면 카메라는 탑재컴퓨터로 ACK 신호를 보낸다. 그 이후, 이미지 타입과 해상도 등의 기본 설정을 마치고, 사진을 찍으라는 Snapshot Command를 보내고 사진을 받는다. 기본 설정은 초기에 한 번만 수행하며, 그 이후로는 Snapshot과 Get Picture만 반복 수행한다.</p>
<div id="attachment_9099" class="wp-caption alignnone" style="width: 427px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD033.jpg" rel="lightbox[8782]"><img class=" wp-image-9099 " alt="그림5" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD033.jpg" width="417" height="202" /></a><p class="wp-caption-text">그림5</p></div>
<p><strong>3) GPS 수신기</strong><br />
KUMSAT-1A은 GPS 수신기에서 전송받은 위치 정보(경도·위도·고도)를 지상국에 전송하는 임무를 수행한다. GPS 수신기는 NMEA 형식의 데이터를 UART 통신을 이용하여 KUMSAT-1A의 탑재컴퓨터에 전송한다. 이 때문에, KUMSAT-1A에 필요한 정보를 선별하는 파싱(Parsing) 프로그램이 필요하다. KUMSAT-1A에 필요한 데이터를 파싱하는 소프트웨어의 알고리즘은 그림6과 같다. 탑재된 GPS 수신기는 3m의 위치 정밀도를 가지고 있으며, 정밀한 좌표 정보를 수신하기까지는 약 5분정도의 시간이 소요 된다.</p>
<div id="attachment_9100" class="wp-caption alignnone" style="width: 406px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD034.jpg" rel="lightbox[8782]"><img class="size-full wp-image-9100" alt="그림 6" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD034.jpg" width="396" height="146" /></a><p class="wp-caption-text">그림 6</p></div>
<p><strong>4) 온도센서</strong><br />
KUMSAT-1A는 대기온도를 측정하는 임무 또한 수행한다. 온도센서는 회로 구성이 간단한 1-wire 통신을 사용하고, 최소한 매 초마다 온도 변환을 할 수 있으며, 데이터를 디지털 값으로 받아들여 별도의 값 변환을 하지 않아도 되는, 디지털 온도센서를 사용하였다. 이 센서를 탑재한 KUMSAT-1A는 -55˚C 에서 125˚C 까지 온도측정이 가능하며 -10˚C 에서 85˚C 사이에서는 ±0.5˚C 의 정확도를 갖는다.</p>
<p><strong><span style="color: #3366ff">OBC</span></strong></p>
<p>KUMSAT-1A에는 하나의 마이크로컨트롤러와 외부메모리(SD카드)로 구성된다. 마이크로컨트롤러는 AVR계열의 ATmega2560을 사용하였다. ATmega2560은 다른 마이크로컨트롤러와 달리 UART Port가 4개까지 지원이 되기 때문에, 이 시스템에 적합하다. 외부메모리는 지상국으로 보내기 위한 카메라의 이미지데이터를 저장하기 위해 장착하였다. 이것 역시 마이크로컨트롤러의 기능을 사용할 수 있으나, 내장된 메모리가 작기 때문에 더 큰 용량의 외부메모리를 사용하였다.</p>
<table border="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD035.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9101" alt="06FCAPD035" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD035-300x247.jpg" width="180" height="148" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD036.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9102" alt="06FCAPD036" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD036-300x208.jpg" width="180" height="125" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD037.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9103" alt="06FCAPD037" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD037-300x154.jpg" width="240" height="123" /></a></td>
</tr>
</tbody>
</table>
<p><strong>1) 회로도</strong></p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD069.jpg" rel="lightbox[8782]"><img class="alignnone size-large wp-image-9135" alt="06FCAPD069" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD069-620x462.jpg" width="620" height="462" /></a><br />
<strong>2) 인터페이스</strong></p>
<div id="attachment_9104" class="wp-caption alignnone" style="width: 407px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD038.jpg" rel="lightbox[8782]"><img class=" wp-image-9104  " alt="그림 8. 인터페이스 다이어그램 " src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD038-620x350.jpg" width="397" height="224" /></a><p class="wp-caption-text">그림 8. 인터페이스 다이어그램</p></div>
<p>KUMSAT-1A는 2가지의 내부인터페이스를 사용하게 되는데, UART, 1-Wire를 사용하게 된다. 탑재컴퓨터의 마이크로컨트롤러와 각 서브의 마이크로컨트롤러는 UART를 통해 연결된다. 그리고 온도를 측정하는 센서와 절단을 위한 WCD는 1-Wire를 통해 연결된다.</p>
<p><strong>3) 프로그램</strong></p>
<p>codevisionAVR 2.03.4 를 사용하였으며, 대체적인 흐름은 다음과 같다.</p>
<p><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD039.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9105" alt="06FCAPD039" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD039-620x453.jpg" width="391" height="285" /></a><br />
<strong>4) OBC Board</strong><br />
KUMSAT-1A내에 효과적인 조립과, 다른 Sub System들과의 적절한 인터페이스 연결을 위해서 OBC Board를 구성하였다. ATMega2560 Module보호를 위해 Board 납땜을 직접적인 연결보다는, 핀헤더소켓 Dual 2&#215;40 Straight(2.0mm)를 이용하여 ATMega2560과 Board를 연결해 주었다.</p>
<p><strong>5) SD Board</strong><br />
SD Module보호를 위해, Board와 직접적인 납땜보다는 핀헤더소켓 Single 1&#215;40 Rightangle(2.54mm)을 납땜을 통해 연결하였고, 전원 및 ATMega2560과의 연결을 위해 몰렉스(male 2핀, 2.54mm피치타입, 스트레이트)를 납땜을 이용해 연결해주었다.</p>
<table border="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD044.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9110" alt="06FCAPD044" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD044-300x199.jpg" width="300" height="199" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD045.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9111" alt="06FCAPD045" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD045-300x199.jpg" width="300" height="199" /></a></td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p><span style="color: #3366ff"><strong>전력계</strong></span></p>
<p>EPS 전력계는 각 서브 시스템에서 요구하는 전력을 안정적으로 공급하는데 목적이 있다. 각 서브시스템에서 요구하는 전력량은 표-3와 같다.</p>
<table style="border-collapse: collapse;width: 500px" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>Sub</td>
<td>Model</td>
<td>V</td>
<td>mA</td>
</tr>
<tr>
<td rowspan="2"> OBC</td>
<td>SD Memory</td>
<td>5</td>
<td>60</td>
</tr>
<tr>
<td>MCU</td>
<td>5</td>
<td> 14</td>
</tr>
<tr>
<td> CS</td>
<td> RF Modem</td>
<td> 5</td>
<td> 60</td>
</tr>
<tr>
<td rowspan="3">Pay load</td>
<td> Thermometer</td>
<td> 5</td>
<td>1.5</td>
</tr>
<tr>
<td>Camera Module</td>
<td> 3.3</td>
<td> 60</td>
</tr>
<tr>
<td> GPS Receiver</td>
<td> 5</td>
<td> 67</td>
</tr>
<tr>
<td> SMS</td>
<td> WCD</td>
<td> 5</td>
<td>300</td>
</tr>
<tr>
<td style="text-align: center" colspan="4">표3 재료의 물성치</td>
</tr>
</tbody>
</table>
<p>풍선에 매달아 띄우고 하강시키는데까지 요구되는 미션 타임을 25분 정도로 예상하였고, WCD는 약 10초간 작동 할 것을 감안하였을 때 요구되는 전력량은 약 500mWh이다. 그리하여 시중에서 쉽게 구할 수 있는 9V, 550mAh 용량의 알카라인 배터리 2개를 병렬로 연결하였다.(요구하는 미션 타임에 변동에 따라 배터리수 를 늘리는 것이 가능 하리라 생각된다.) 각 서브시스템에서 요구하는 입력 전압을 공급하기 위하여 두 종류의 Linear type regulator(78T05, 78R33)를 사용하였다.</p>
<table border="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD050.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9116" alt="06FCAPD050" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD050-300x273.jpg" width="240" height="218" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD051.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9117" alt="06FCAPD051" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD051-300x286.jpg" width="240" height="229" /></a></td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p><span style="color: #3366ff"><strong>통신</strong></span></p>
<p>CS(통신계, Communication System)는 탑재컴퓨터(OBC)의 마이크로 컨트롤러(MCU)가 온도센서, GPS, 카메라로부터 수집한 데이터들을 지상국으로 보내기 위한 수단으로써, RF모뎀과 안테나로 구성되어지며 최대 500m거리에서의 안정적인 데이터 전송을 목적으로 한다. CS의 전체적인 Diagram은 그림9와 같다.</p>
<div id="attachment_9118" class="wp-caption alignnone" style="width: 310px"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD052.jpg" rel="lightbox[8782]"><img class="size-medium wp-image-9118" alt="그림 9. CS Diagram" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD052-300x226.jpg" width="300" height="226" /></a><p class="wp-caption-text">그림 9. CS Diagram</p></div>
<p>500m이상의 거리에서의 안정적인 통신을 위해 424MHz의 장거리 통신 모뎀을 선택하였으며, 사진전송 시 BER(Bit Error Rate)의 최소화를 위하여 2400bps의 Data Rate을 선택하였고, Link Budget은 다음과 같다.</p>
<table style="border-collapse: collapse;width: 500px" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="text-align: center">Item</td>
<td style="text-align: center">KUMSAT-1A</td>
</tr>
<tr>
<td style="text-align: center">Transmitter Power Output (dBm)</td>
<td style="text-align: center">10</td>
</tr>
<tr>
<td style="text-align: center">Transmitter Cable Attenuation (dB)</td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">Transmitter Antenna Gain (dBi)</td>
<td style="text-align: center">2</td>
</tr>
<tr>
<td style="text-align: center">Free Space Loss (dB)</td>
<td style="text-align: center">84.97</td>
</tr>
<tr>
<td style="text-align: center">Reciever Antenna Gain (dBi)</td>
<td style="text-align: center">2</td>
</tr>
<tr>
<td style="text-align: center">Reciever Cable Attenuation (dB)</td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">Reciever Sensitivity (dBm)</td>
<td style="text-align: center">-122</td>
</tr>
<tr>
<td style="text-align: center">Link Margin (dBm)</td>
<td style="text-align: center">51</td>
</tr>
<tr>
<td style="text-align: center" colspan="2">표4. Link Budget</td>
</tr>
</tbody>
</table>
<p>① 제품선정<br />
보통의 경우 Link Margin이 3dBm이면 통신이 가능하다고 판단하는데, 위의 경우 51dBm이므로 통신에는 아무런 문제가 없다고 할 수 있다. 다만 사진 데이터의 전송 시 BER(Bit Error Rate)로 인하여 온전한 상태의 사진을 전송받지 못할 수도 있다. 따라서 이에 대비하여 KUMSAT내부에 메모리카드를 장착하여 추후 수거가 가능하도록 하였다.<br />
지상에서부터 풍선을 이용하여 상공 500m까지 상승한 뒤 지상에 떨어질 때까지 KUMSAT이 전송하는 각종 데이터들의 프로토콜은 아래와 같다.</p>
<table style="border-collapse: collapse;width: 500px" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>GPS Time</td>
<td>고 도</td>
<td>위 도</td>
<td>경 도</td>
<td>온 도</td>
</tr>
</tbody>
</table>
<p>또한 위의 Protocol을 1회 전송했다는 것은, 한 장의 사진을 찍었다는 것을 의미하기도 한다. 그리고 RF모뎀의 기능적 특성에 의해서 각 프로토콜 간에는 10초의 지연을 주도록 한다.<br />
풍선으로 KUMSAT-1A를 상승시키기 때문에, 지형적 풍속이나 풍향 온도 등 대기상태를 예상할 수 없기 때문에, 오로지 지상국에서 받는 데이터에 의존해서 상황에 따라 Command를 전송하여 KUMSAT을 제어해야한다. 예를 들어 500m 고도에서 원하는 한 장의 사진을 지상국으로 전송하기 위해서는 “1”라는 Command를 그리고 풍선과 KUMSAT을 분리시키기 위해서는 “4”라는 Cut Command를 지상국으로부터 KUMSAT에 전송하여야 한다.<br />
지상국은 KUMSAT-1A의 CS와 마찬가지로 안테나와 RF모뎀, 그리고 PC로 구성된다. 이 때 사용되는 RF모뎀은 KUMSAT의 RF모뎀과 주파수 및 Data Rate이 같은 것을 사용하도록 한다. 또한 지상국은 위의 구성요소 외에, 실시간으로 송수신 Data의 저장 및 확인이 가능하고 제어가 가능한 소프트웨어를 사용한다.<br />
위와 같이 전체적인 개념을 잡고 실제 KUMSAT-1A에의 장착을 위한 제품 선정 및 Board를 세팅하면 아래와 같다.<br />
① 제품선정</p>
<table style="width: 600px" border="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD053.jpg" rel="lightbox[8782]"><img class="alignnone  wp-image-9119" alt="06FCAPD053" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD053-300x200.jpg" width="270" height="180" /></a></td>
<td>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>Item</td>
<td>IRF 4020P</td>
</tr>
<tr>
<td>Frequency (MHz)</td>
<td>424</td>
</tr>
<tr>
<td>Sensitivity (dBm)</td>
<td>-122</td>
</tr>
<tr>
<td>Data Rate (bps)</td>
<td>2400</td>
</tr>
<tr>
<td>Output Power (mW)</td>
<td>10</td>
</tr>
<tr>
<td>Current Consumption (mA)</td>
<td>60</td>
</tr>
<tr>
<td>Size (mm)</td>
<td>30X45X11</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table style="width: 600px" border="0">
<tbody>
<tr>
<td width="270"><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD054.jpg" rel="lightbox[8782]"><img class="size-full wp-image-9120 aligncenter" alt="06FCAPD054" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD054.jpg" width="48" height="178" /></a></td>
<td>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>Item</td>
<td>1/2 Ramda Whip Antenna</td>
</tr>
<tr>
<td>Radiation Pattern</td>
<td>Omni-Directional</td>
</tr>
<tr>
<td>Antenna Gain (dBi)</td>
<td>-≤2</td>
</tr>
<tr>
<td>Antenna Length (mm)</td>
<td>345</td>
</tr>
<tr>
<td>Operating Temperature (℃)</td>
<td>-30~+70℃</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p>② Board 추가<br />
KUMSAT내에 효과적인 조립과 다른 Sub System들과의 적절한 interface연결을 위해서, 인두기를 이용하여 추가적인 Board를 납땜한다.</p>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD055.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9121" alt="06FCAPD055" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD055-300x218.jpg" width="300" height="218" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD056.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9122" alt="06FCAPD056" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD056-300x210.jpg" width="300" height="210" /></a></td>
</tr>
</tbody>
</table>
<p>③ 조립<br />
실제 KUMSAT에 RF모뎀이 장착된 모습으로 RF모뎀 상단에 공간의 효율적 사용을 위해서 배터리 1개가 위치한다.<br />
<a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD057.jpg" rel="lightbox[8782]"><img class="alignnone size-full wp-image-9123" alt="06FCAPD057" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD057.jpg" width="231" height="280" /></a></p>
<p>④ KUMSAT상승 후 Data 전송 (지상국)<br />
<a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD059.jpg" rel="lightbox[8782]"><img class="alignnone size-large wp-image-9125" alt="06FCAPD059" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD059-620x187.jpg" width="620" height="187" /></a><br />
지상국은 앞장에서 말한 것처럼 KUMSAT-1A와 같은 사양의 안테나와 RF모뎀을 사용하며, ISP를 이용하여 PC에 연결한다. 이때, 지상국에서 안테나 Gain이 더 높은 지향성의(예, Yagi Antenna)안테나를 사용하면 더 높고, 먼 거리에서도 안정적으로 Data를 받을 수 있다. 다만 대상 물체를 비교적 정확하게(일정 각 범위내로) 조준하여야 한다는 제한조건이 붙는다. 지상국에서는 With Robot의 Comport Master라는 소프트웨어를 사용하였는데, 이는 With Robot(www.withrobot.com/118)에서 쉽게 다운받을 수 있으며, 이 프로그램을 통해서 KUMSAT과의 양방향통신 및 수신 데이터의 확인 및 저장이 가능하다.</p>
<p>⑤ 실제 수신 받은 Data</p>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD061.jpg" rel="lightbox[8782]"><img class="wp-image-9127 aligncenter" alt="06FCAPD061" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD061.jpg" width="186" height="303" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD060.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9126" alt="06FCAPD060" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD060-300x235.jpg" width="300" height="235" /></a></td>
</tr>
<tr>
<td style="text-align: center">3D Route(위도, 경도, 고도)</td>
<td style="text-align: center">2D Route(위도, 경도)</td>
</tr>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD062.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9128" alt="06FCAPD062" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD062-300x170.jpg" width="300" height="170" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD063.jpg" rel="lightbox[8782]"><img class="alignnone size-medium wp-image-9129" alt="06FCAPD063" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD063-300x225.jpg" width="300" height="225" /></a></td>
</tr>
<tr>
<td style="text-align: center">Altitude-Temperature Data</td>
<td style="text-align: center">Image Data</td>
</tr>
</tbody>
</table>
<table style="border-collapse: collapse" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD066.jpg" rel="lightbox[8782]"><img class="alignnone size-thumbnail wp-image-9132" alt="06FCAPD066" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD066-150x150.jpg" width="150" height="150" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD065.jpg" rel="lightbox[8782]"><img class="alignnone size-thumbnail wp-image-9131" alt="06FCAPD065" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD065-150x150.jpg" width="150" height="150" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD064.jpg" rel="lightbox[8782]"><img class="alignnone size-thumbnail wp-image-9130" alt="06FCAPD064" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD064-150x150.jpg" width="150" height="150" /></a></td>
<td><a href="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD067.jpg" rel="lightbox[8782]"><img class="alignnone size-thumbnail wp-image-9133" alt="06FCAPD067" src="http://www.ntrexgo.com/wp-content/uploads/2013/04/06FCAPD067-150x150.jpg" width="150" height="150" /></a></td>
</tr>
<tr>
<td style="text-align: center" colspan="4">SD 메모리카드에 저장된 Image Data</td>
</tr>
</tbody>
</table>
<div id='wpdm_file_2' class='wpdm_file wpdm-only-button'><div class='cont'><div class='btn_outer'><div class='btn_outer_c' style='background-image: url(http://www.ntrexgo.com/wp-content/plugins/download-manager/icon/file_extension_pdf.png);'><a class='btn_left  ' rel='2' title='6호 캡스톤 소형 인공위성 소스코드' href='http://www.ntrexgo.com/?wpdmact=process&did=Mi5ob3RsaW5r'  >소스코드 다운로드</a><span class='btn_right'>&nbsp;</span></div></div><div class='clear'></div></div></div>
]]></content:encoded>
			<wfw:commentRss>http://www.ntrexgo.com/archives/8782/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
