<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ko">
		<id>http://capstone.uos.ac.kr/mie/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=2012430017</id>
		<title>MIE capstone - 사용자 기여 [ko]</title>
		<link rel="self" type="application/atom+xml" href="http://capstone.uos.ac.kr/mie/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=2012430017"/>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php/%ED%8A%B9%EC%88%98:%EA%B8%B0%EC%97%AC/2012430017"/>
		<updated>2026-06-08T10:18:01Z</updated>
		<subtitle>사용자 기여</subtitle>
		<generator>MediaWiki 1.28.2</generator>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5924</id>
		<title>99-1=0 - Smart Mood Light with Sterilization</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5924"/>
				<updated>2020-06-23T06:02:09Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 최종 결과물 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 Smart Mood Light with Sterilization&lt;br /&gt;
 살균 기능을 탑재한 스마트 무드등&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 노*진 (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 박*훈 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 주*완 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 함*규 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20174300** 유*환 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
원룸은 타 주거환경에 비해 채광과 통풍 능력이 떨어진다. 공기 청정기를 이용할 수 있지만, 채광 문제를 해결하지 못한다. 다행히 넓은 공간을 가진 주거환경에 비해 규모가 작아 상용 자외선을 통한 살균 효과는 비해 뛰어나지만, 살균기의 제어는 사용자가 원룸 내 없을 때 이루어 져야 한다. 위의 문제를 해결하기 위해 스마트폰을 이용하여 원격 제어를 실행한다. 또한 원격 제어를 하면서 사용자가 원룸 내 있을 때 활용할 수 없는 살균기가 공간을 비효율적으로 차지하게 되는 문제를 해결하기 위해 이를 무드등과 결합 한다. 결과적으로 사용자가 가장 많이 시간을 보내는 공간을 살균할 수 있으며 공간의 비효율성과 안전성, 더불어 무드등의 원격제어로 인한 편의성을 제공한다. 사용자는 외부에 있을 때, 스마트폰을 이용하여 살균기 동작을 제어할 수 있다. 사용자가 내부에 있을 때 별도의 조치 없이도 자외선 동작 제어는 비활성화 되며 무드등의 원격 제어가 가능하다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 1. 배경&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_최근4년간국내미세먼지수치.png|500픽셀]]&lt;br /&gt;
[[파일:99-1=0_최근전세계적바이러스주기.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
최근 국내 미세먼지 수치가 꾸준히 증가함에 따라 국민들의 미세먼지 정화에 관한 관심이 높아지고 있다. 또한, 전 세계적인 바이러스 발생 주기가 짧아짐에 따라 사람들의 살균 제품에 대한 관심도가 높아지고 있다. &lt;br /&gt;
&lt;br /&gt;
햇빛은 면역력 향상이나 기분 전환의 효과뿐 아니라 살균 기능을 가지고 있다. 원룸은 구조적으로 대부분 하나의 창문을 가지고 있어 충분한 빛을 받지 못하며 공기의 입/출구가 하나이기에 통풍이 원활하지 않다. 이러한 조건은 미생물이 자라나기 쉽고 미세먼지의 배출이 어렵다. 이들을 모두 정화하는 데 맞는 조건을 가진 것이 자외선이다. 자외선을 통해 공기 중에 살균이 가능하며 미세먼지 중금속을 분해해 공기를 정화할 수 있다. 하지만 자외선 살균기를 집안에 두고 사용하는 것에는 다음과 같은 문제점들이 있다.&lt;br /&gt;
&lt;br /&gt;
- 살균 자외선은 인간에게 유해하므로 사용자에게 직접적인 자외선이 가해지는 것은 위험하다. 사용자가 없는 곳에서 자외선 살균기가 작동해야 하며 사용자가 있을 때 확실한 방법으로 조작 가능성을 배제 시켜야 한다.&lt;br /&gt;
&lt;br /&gt;
- 사용자가 자주 쓰는 공간의 살균일수록 의미가 있으므로 자외선 살균기는 사용자가 자주 쓰는 공간에 배치되어야 한다. 그러나 위의 이유로 사용자가 실제 방에 있을 때 자외선 살균기를 사용하는 것은 위험하므로 사용자가 있을 때 별다른 기능 없이 공간만을 차지하게 된다.&lt;br /&gt;
&lt;br /&gt;
원룸 내 거주자가 가장 오래 머무르는 공간으로 예상되는 곳은 침대류 이다. 침대류 근처에 있는 물체에 살균기를 결합하여 제품을 만들 수 있다면 효율적으로 문제를 해결할 수 있다. 우리는 이 조건을 만족하는 물체가 무드등이라는 점에서 문제 해결을 시작했다. 또한, 첫 번째 문제 해결을 위해 임베디드 시스템을 설계하여 사용자가 없는 공간에서의 작동을 가능하게 하는 것뿐 아니라 무드등에 다양한 기능을 추가하여 편리성과 기능의 다양함을 추가하려고 한다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 기대효과&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_UV자외선을이용한살균 미세먼지정화과정.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
사용자는 집 밖에서 애플리케이션을 통해 자외선 모듈을 가동함으로써 안전한 방법으로 공간을 살균 및 미세먼지 분해 기능을 작동시킬 수 있다. &lt;br /&gt;
&lt;br /&gt;
또한, 사용자가 개발 제품과 같은 공간에 있다고 판단 시 애플리케이션 자동 스위칭 기능을 통해 자외선 컨트롤 기능이 아닌 무드등 컨트롤 기능을 제어하게 된다. 따로 조작 없이 방 안/밖이라는 공간의 변화에 따라 스위칭 되기에 조작에 신경 쓸 필요가 없다. 무드등 기능으로썬 조도 조정, 빛의 색상 선택, 주변 음악에 따른 색상 변화 기능 등이 있으며 사용자가 선택하는 옵션에 따라 기능을 수행할 수 있어 편리함과 다양성을 제공한다. 사용자의 유무에 상관없이 개발제품은 유용한 도구로써 사용되기에 공간 활용성 또한 확보할 수 있을 것으로 생각된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:99-1=0_목적계통도.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 안전성&lt;br /&gt;
자외선 활성/비활성화 기능은 가스 자동밸브 잠금과 비슷한 원리로 앱 구동 시 스마트폰 GPS 기능을 통해 사용자의 위치정보를 받아 주거 위치와 비교하여 앱 인터페이스 비활성화 여부를 결정한다. GPS 오작동으로 인한 비활성화가 적절히 이루어지지 않았을 때를 대비해 앱 내 수동 비활성화 버튼을 추가한다. 앱 인터페이스가 비활성화 되어 있어 사용자의 실수로 인한 신호 전달 가능성을 사전에 차단할 수 있다. 또한 회로의 긴급 정지 스위치를 통해 하드웨어 오동작 시 회로 자체의 전류를 제어할 수 있다. 또한 인체 및 환경에 유해한 정도를 판단하여 인체에 비교적 덜 유해한 UV-A의 자외선 영역을 사용했고, 수은으로 환경 파괴 문제를 일으키는 UV-램프를 대신하여 UV-LED를 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 실용성&lt;br /&gt;
먼저 자외선 영역의 성능을 비교하면 살균력 기준으로는 분 단위 살균력을 가진 UV-C가 시 단위 살균력을 가진 UV-A보다 월등히 강력하다. 하지만 살균 범위를 보면 UV-A는 공기 중 3-5m의 반경을 가진 것에 비해 UV-C는 공기 중 투과력이 매우 낮아 넓은 범위를 살균할 때에는 UV-A가 더 적합하다. 따라서 거주자 없이 비어있는 시간이 긴 원룸에 맞추어 UV-405nm 자외선을 이용했다. 이 때, 자외선 LED의 살균 방향을 15도 정도 아래쪽으로 내려주어 침구류 및 사용자가 자주 사용하는 물건도 더불어 살균하게 된다. 모터의 경우, 자외선 살균기의 상하 운동이 완료된 후에 해당 위치에서 고정이 되어야 하는데, 서보 모터는 고정 기능이 없고, 스탭 모터는 고정 기능을 사용할 때 소모 전력이 많다. 따라서 적은 소모 전력으로 고정 기능을 수행할 수 있는 웜 기어 모터를 사용했다.&lt;br /&gt;
공간 효율성을 기준으로는 제품 구성 시 얼마나 효율적인 공간 활용 여부를 비교했다. 따라서 각 제품의 특성에 맞추어 무드등 조명에는 필요 전선량이 많은 LED 전구 대신 네오 픽셀을 사용했고, 자외선 조명에는 1cm 이하의 조명 크기를 가지는 UV LED를 사용했다. 또한 사용자 위치를 판별하기 위해서 출입문 주변의 많은 공간을 차지하는 적외선 센서 활용 출/입 카운터 대신 외부 공간을 이용하지 않는 안드로이드 모바일 앱 내의 GPS 기능을 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 편의성&lt;br /&gt;
사용자 입장에서 제품을 이용할 때의 반응성을 기준으로 비교했다. 호스팅 제공 S/W에서는 외부의 데이터베이스를 이용하는 파이썬 애니웨어 대신 자체적으로 실시간 데이터베이스 기능을 제공하는 파이어 베이스를 사용했다. 또한 사용자 위치 판별법에서는 사용자의 출/입 여부를 얼마나 빠르게 판단할 수 있는지에 대한 비교를 했는데, 이는 적외선 센서 활용 카운터가 더 빠른 응답성을 보였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 경제성&lt;br /&gt;
제품에 대한 경제적인 측면은 제품을 구성하는데 사용하는 비용과 유지비로 생각할 수 있다. 먼저 제품 구성비용은 프로젝트의 해당 제품을 구매하는 비용이다. 따라서 보다 더 값이 싼 네오픽셀, UV-A, UV-램프가 더 적합했다. 또한 호스팅 기능 S/W에서는 무료로 이용할 수 있는 범위가 더 넓은 파이어 베이스가 적합했다. 또한 유지비는 제품의 수명이 얼마나 긴지, 배터리 소모량이 얼마나 적은지, 부품 교체 비용이 얼마나 큰지에 대해 비교했다. 따라서 부품 교체 비용이 더 저렴한 네오픽셀, 수명이 더 긴 UV-LED가 더 적합했다. 또한 항상 동작시키는 적외선 센서보다 필요시에만 동작시킬 수 있는 GPS 기능이 배터리 소모량이 거의 없어 더 적합했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 개발 용이성&lt;br /&gt;
개발제품 특성상 데이터베이스 및 호스팅 서비스가 필요하다. 개발자 독자적으로 데이터 베이스와 호스팅 서비스를 개발할 필요 없이 이러한 서비스를 제공하는 서버는 파이어 베이스이다. 개발 난이도는 물론 개발자의 서버 개발 시간을 최소화 하여 다른 분야의 성능 개발을 최대화 시킬 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_개발용이성.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Application과 라즈베리 파이의 통신 중간 다리로 파이어 베이스 서버를 이용한다. 물론 무드등 제어를 위한 라즈베리 파이 서버를 구축하여 통시에 이용하면 원룸 내 무드등 제어 동작 속도를 향상시킬 수도 있다. 그러나 하나의 서버를 이용하면 통신 오류나, 오류가 발생했을 때 오류를 제어하기 쉽다. 또한  추후에 기능 추가가 용이하다. 파이어 베이스는 서버 특성상 서버 사용자가 많아지게 되면 부하가 발생할 수 있다. 그러나 개발 상품의 경우 서버는 한명의 사용자만이 이용한다는 점을 고려하여 이러한 단점을 고려하지 않을 수 있다. 따라서 해당 프로젝트에 가장 적합하고, 통신 복잡도가 적은 파이어 베이스와 안드로이드 스튜디오를 사용했다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_동작시나리오.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_역할분담.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_세부개발일정표.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_시스템구성도.png|800픽셀]]&lt;br /&gt;
&lt;br /&gt;
===기구부 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_무드등모드3D설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_자외선살균모드3D.png|166픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
먼저 기구부에서는 최종적으로 제품의 형상과 베벨 기어로 사각 LED 기둥을 올리는 Mechanism과 자외선 LED가 부착되어 있는 Slide Pin Mechanism을 구현하였다. &lt;br /&gt;
제품의 형상의 경우에는 제품의 무드등 기능과 자외선 살균 기능을 정상적으로 구현할 수 있게 필요한 요소들을 적절히 배치할 수 있는 공간을 만들었고, 사용자의 안전성을 위해 Mood Light를 위한 NeoPixel과 UV-A LED를 구분하여 인체에 해로운 자외선 LED는 기능을 사용하지 않을 때 내부 공간에 위치하도록 구현하였다. &lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism은 첫 번째 자외선 LED가 설치된 사각기둥을 올리는 Motor Mechanism과 두 번째 자외선 LED가 적절한 각도로 펼쳐지는 슬라이드 핀 Mechanism이다. 첫 번째 Motor Mechanism은 모터와 베벨 기어 및 랙 피니언 시스템을 통해 모터의 회전 운동을 직선 운동으로 전환하여 구현하였다. 또한 두 번째 Slide Pin Mechanism은 슬라이드 핀을 이용하여 중력에 의해 자동으로 사각기둥 상승 시 펼쳐지고, 하강 시 접히도록 구현한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''기구부 설계'''&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 1. Motor Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_모터설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_모터구현.png|191픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism 중 자외선 LED가 설치된 사각 기둥을 올릴 베벨 기어와 랙 피니언을 사용한 Motor Mechanism 시스템 구현을 완료했다. CATIA CAD 프로그램을 이용하여 베벨 기어 및 랙 피니언 설계를 완료했고, 3D 프린터를 이용하여 규격에 맞추어 실제 제품을 만들었다. 기존에는 랙 피니언 한 쌍만을 직접 모터에 연결하여 사각기둥의 상하 운동을 구현하려고 했지만, 대칭성이 잘 맞지 않아 한쪽으로 쏠리는 현상이 발생하여, 추가적으로 베벨 기어 등을 사용하여 대칭성을 고려하여 양쪽에서 사각 기둥의 상하 운동을 구현했다.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. Slide Pin Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_슬라이드핀설계.png|200픽셀]]&lt;br /&gt;
[[파일:Slide.jpg|145픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Motor Mechanism을 통해 자외선 LED가 설치된 사각기둥이 상하운동을 하면, LED가 적절한 각도를 이루어 펼쳐지도록 구현하였다. 따라서 Slide Pin Mechanism을 이용하여 사각 기둥이 상승할 때 중력에 의해 자동으로 LED가 펼쳐지고, 하강할 때 자동으로 접힌다. CATIA CAD 프로그램을 이용하여 3D 설계를 하고, 3D 프린팅을 통해 실제로 외관을 만들었다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_LED각도설계.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LED가 펼쳐지는 적절한 각도로는 30도를 정했다. 각도를 정하는 과정에서 두 가지의 고려 사항을 정했는데, 첫 번째는 자외선 LED 끝이 직각으로 도달하는 지점의 거리가 원룸이라는 공간을 고려하여 1m 이상이 되고, 두 번째는 제품과 근접한 곳의 LED 사각지대가 제품의 끝으로부터 10cm 내가 되는 기준을 두었다. 따라서 CATIA CAD 프로그램의 2D 설계를 통해 적절한 각도 30도를 설정했다. (실제로 빛은 직각으로만 전달되는 것이 아니라 사방으로 퍼지기 때문에 실제 최대 도달 거리는 기준값인 1m 보다 더 커진다.)&lt;br /&gt;
&lt;br /&gt;
'''기구부 구현'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_사각기둥.jpg|250픽셀]]&lt;br /&gt;
[[파일:99-1=0_Mood.jpg|250픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_완성본.jpg|250픽셀]]&lt;br /&gt;
[[파일:위에서_본_모습.PNG|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
UV-A LED를 장착할 사각 기둥을 3D Printing을 통해 만들었다. 또한 Mood Light를 위한 NeoPixel은 사각 기둥 밖에 다른 기구에 물결 모양으로 48개를 달았다. 최종적으로 아크릴판에 불투명 시트를 붙여 등을 만들었다.&lt;br /&gt;
&lt;br /&gt;
'''최종 작품'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:파랑이.jpg|250픽셀]][[파일:UV-A_LED.jpg|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
최종으로 Mood Light에서 단색 모드로 파랑색 조명을 킬 때와, UV LED Mode에서 On버튼을 눌렀을 때 결과이다.&lt;br /&gt;
&lt;br /&gt;
===제어부 및 회로 구현===&lt;br /&gt;
 UV-A LED, Motor 회로도&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_제어회로도1.PNG|600픽셀]][[파일:99-1=0_내부회로도2.jpg|600픽셀]]&lt;br /&gt;
 &lt;br /&gt;
SMPS로부터 220V 정격 전압을 12V 정격 전압으로 나누어 아두이노와 Relay Switch를 통해 UV-LED의 전원 On/Off 구현이 가능하다. 모터와 UV LED는 아두이노로 제어하기 때문에 라즈베리파이에서 데이터베이스의 값을 실시간으로 확인하다가 UV LED 동작이 지시되면 아두이노에 신호를 보내준다. 데이터베이스의 값은 안드로이드 App을 통해 갱신되므로 결국은 안드로이드 App을 통해 UV-LED의 On/Off 제어가 가능하다. 라즈베리파이와 아누이노간의 데이터 통신은 USB 케이블을 이용한 시리얼 통신을 이용한다.(라즈베리파이와 아두이노 간의 거리가 가깝고 전송하는 데이터가 단순하기 때문에 간단한 통신 방법 선택) 구현 언어는 파이썬이며 사용한 파이썬 모듈은 serial이다. 이와 관련하여 사용한 함수들은 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
UV-LED 동작을 설정하는 값이 “PowerON”일 경우 USB 포트로 연결된 아두이노로 UTF-8로 인코딩된 문자 u를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 u를 받아 UV-LED On 동작을 수행한다. 만약 UV-LED 동작 설정값에 “PowerON” 외의 값이 저장되었을 경우 (“PowerOFF“의 경우가 일반적이지만 예외 처리를 위해 else로 조건 판단) 아두이노로 UTF-8로 인코딩된 문자 d를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 d를 받아 UV-LED Off 동작을 수행한다.&lt;br /&gt;
&lt;br /&gt;
 NeoPixel 회로도&lt;br /&gt;
[[파일:99-1=0_네오픽셀_회로도.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
Mood Light구현은 NeoPixel과 Raspberry PI를 통해 구현했다. 사용자가 Application을 통해 색을 바꾸면 Database의 값이 아래 표에 Data로 바뀌면서 Mood Light 색이 변한다. NeoPixel의 Digital input을 통해 값이 들어가고, 그에 맞는 색을 자유롭게 바꿀 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:색깔.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:App_insidemode_screenshot.jpg|200픽셀]] [[파일:App_outsidemode_screenshot.jpg|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
 앱&lt;br /&gt;
&lt;br /&gt;
앱을 처음 설치하고 실행하면 앱은 팝업 윈도우로 모바일 디바이스의 위치 정보 권한을 요청하며 사용자는 이를 최초 1회만 수락하면 된다. 앱은 실행되면 즉시 모바일 디바이스의 현재 위치 정보를 받고 데이터베이스에 저장된 집의 위치 정보와의 거리를 계산한다. 이 거리가 일정 거리 이상이면 앱은 실외 모드로 자동 설정되고, 일정 거리 미만이면 실내 모드로 자동 설정된다. 실내 모드와 실외 모드의 전환은 기본적으로 비활성화되어 있으나 사용자가 우측 하단의 스위치를 켜면 활성화되어 강제 모드 전환이 가능하게 된다. 그래서 앱을 실행했을 때 자동 모드 설정이 잘못되면 사용자가 직접 모드를 전환해서 원하는 기능을 사용할 수 있다.&lt;br /&gt;
&lt;br /&gt;
앱은 크게 실내 모드와 실외 모드로 구분된다. 각 모드는 Fragment로 정의됐고 MainActivity의 bottomnavigationview를 통해 replace해서 전환할 수 있다. bottomnavigationview는 각 모드에 배치된 스위치가 꺼져 있으면 disabled되고 스위치가 켜지면 enabled된다. 사용자는 실내 모드에서 무드등을 제어하고 실외 모드에서 자외선 살균기를 제어한다.&lt;br /&gt;
&lt;br /&gt;
 실내 모드&lt;br /&gt;
실내 모드에선 무드등의 색상 선택, 밝기 조절, 전원 작동, 스페셜 모드 기능을 이용할 수 있다.&lt;br /&gt;
* colorpicker&lt;br /&gt;
: colorpicker의 색상은 초기에 12시의 연두색으로 설정되어 있고 사용자가 버튼을 드래그해서 원하는 색상을 선택할 수 있다. colorpicker의 중앙에 있는 원은 왼쪽 반원에 이전 색상을 표시하고 오른쪽 반원에 현재 색상을 표시하여 전후 색상을 비교하기 좋게 만들어준다. 사용자가 무드등의 전원을 키면 이전 색상이 표시된 반원이 현재 색상으로 교체된다. 색상은 여섯 자리의 16진수 #nnnnnn으로 표현되며 정수형으로 저장된다. 사용자가 colorpicker로 다양한 색상을 연속적인 범위에서 선택할 수 있기 때문에 선택의 폭이 넓고 세밀한 색상 선택이 가능하다. 무드등의 전원이 켜져 있으면 색상의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 밝기&lt;br /&gt;
: 무드등의 밝기 조절은 실내 모드 중앙의 seekbar를 통해 할 수 있다. 밝기 값은 최소 0에서 최대 255까지 선택 가능하다. colorpicker와 같이 무드등의 전원이 켜져 있으면 밝기의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 전원&lt;br /&gt;
: Power ON 버튼을 누르면 무드등의 전원이 켜진다. 전원 버튼 하단의 얇은 띠가 전원이 꺼져있을 때 회색, 전원이 켜져있을 때 녹색을 띠면서 사용자가 무드등의 전원이 켜져있는지를 직관적으로 알 수 있게 해준다. 전원 작동 데이터가 데이터베이스에 성공적으로 전송되면 앱 화면 하단에 Toast 텍스트를 표시하여 사용자에게 전원의 작동을 알려준다. 전원 버튼의 Power ON/Power OFF 텍스트는 colorpicker에서 선택된 색상과 실시간으로 동일하게 적용되어 사용자에게 무드등이 어떤 색으로 켜질지 더욱 직관적으로 와닿게 만들어준다.&lt;br /&gt;
* 스페셜 모드&lt;br /&gt;
: 앱을 실행하면 무드등의 모드는 기본 모드로 설정되어 있는 상태이다. 스페셜 모드 버튼을 누르면 순차적으로 웨이브 모드 1, 웨이브 모드 2, 레인보우 모드 1, 레인보우 모드 2가 설정되며 그후 다시 기본 모드로 돌아온다. 현재 설정된 모드는 스페셜 모드 버튼의 텍스트로 나타나서 사용자가 이를 알 수 있다. 또한 버튼을 누를수록 단계적으로 버튼의 배경 색이 진해지며 버튼의 깊이감을 더해준다. 스페셜 모드 버튼의 아래엔 모드의 전체 개수와 현재 모드가 몇 번째인지를 시각적으로 쉽게 알게 해주는 공백 칸들이 있다. 이 칸들은 기본 모드에서 모두 비어있게 되고 스페셜 버튼이 눌릴 때마다 한 개씩 채워지며 모두 채워진 후엔 다시 모두 빈 칸으로 돌아온다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스페셜 모드는 무드등이 켜져 있을 때에만 작동이 유효하다. 무드등의 전원이 꺼져 있을 때 스페셜 모드를 작동하면 무드등에서 아무 일도 일어나지 않는다. 이런 상황을 예방하기 위해 앱에서 무드등의 전원이 꺼진 상태에서 스페셜 모드만 작동하는 일이 일어나지 않게 한다. 무드등의 전원이 꺼진 상태에서 스페셜 모드 버튼을 누르면, 앱은 무드등의 전원도 키고 스페셜 모드도 작동한다. 또한 스페셜 모드가 설정된 상태에서 무드등의 전원을 끄면 앱은 스페셜 모드를 기본 모드로 설정하고 무드등의 전원을 끈다. 만약 사용자가 레인보우 모드를 바로 사용하고 싶다면 전원 버튼을 누를 필요 없이 바로 스페셜 모드 버튼을 3회 누르면 된다. 전원 버튼을 누르는 단계를 거치지 않아도 되기 때문에 빠르고 간편하게 즉시 스페셜 모드를 이용할 수 있다. 결과적으로 무드등의 전원이 꺼진 상태에서 무드등의 모드는 항상 기본 모드로 설정되어 있고, 스페셜 모드가 켜진 상태면 무드등의 전원도 항상 켜진 상태가 된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 실외 모드&lt;br /&gt;
실외 모드에선 위치 정보 설정, 자외선 살균기의 전원 작동, 타이머 기능을 이용할 수 있다.&lt;br /&gt;
* 위치 정보 설정&lt;br /&gt;
: HOME LOCATION 버튼을 누르면 모바일 디바이스의 현재 위치 정보를 데이터베이스에 저장하여 집의 위치로 설정한다. 설정한 집의 위치 정보는 앱이 실행됐을 때 집과 사용자 사이의 거리를 계산하는 데 이용된다. 데이터베이스에 저장된 위치 정보는 사용자가 새롭게 설정하지 않는 한 항상 일정하게 유지된다. 그래서 앱을 재설치했을 때 집의 위치 정보 설정을 다시 할 필요가 없다. 상수의 변수명을 대문자 알파벳으로 선언하는 프로그래밍 관습을 응용해서 위치 정보 설정 버튼의 텍스트도 대문자 알파벳으로 하였다. &lt;br /&gt;
* 자외선 살균기&lt;br /&gt;
: 실외 모드에서 Power ON 버튼을 누르면 자외선 살균기의 전원이 켜진다. 전원 버튼 위의 spinner에서 타이머가 작동할 시간을 선택할 수 있으며 초기 타이머는 1시간으로 설정된 상태이다. 타이머는 시간 단위로 선택할 수 있고 최대 12시간까지 선택 가능하다. 자외선 살균기의 전원을 켜면 전원 버튼 아래에 textview가 표시되어 타이머의 남은 시간을 알려준다. 설정한 타이머가 종료되면 앱이 자동적으로 자외선 살균기의 전원을 끈다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Firebase&lt;br /&gt;
[[파일:99-1=0_파이어베이스스크린샷.jpg|300픽셀]]&lt;br /&gt;
[[파일:99-1=0_파이어베이스연동.jpg|500픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Firebase는 Google 사의 모바일 및 웹 개발 플랫폼이다. Firebase는 많은 유용한 기능을 지원하지만 여기선 실시간 데이터베이스를 사용한다. Firebase의 장점은 안드로이드 및 iOS 앱과 연동이 용이하다는 것이다. 또한, Firebase는 시간과 장소에 구애받지 않고, 데이터가 안전하게 보존되며, 데이터를 읽고 쓰는 것이 간편하다. 그래서 Firebase는 사용자의 집의 위치 정보를 저장하기에 적합하다. 이 데이터는 제 3자가 접근할 수 없으며 사용자가 새롭게 설정하지 않는 한 영구히 보존된다. 앱은 Firebase의 데이터베이스에 집의 위치 정보를 저장하기도 하지만 읽어오기도 한다. 앱은 실행되면 데이터베이스의 집의 위치 정보를 읽어와서 현재 사용자의 위치 사이의 거리를 계산하고 실내/실외를 판별한다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 작동할 때에도 Firebase를 이용한다. 사용자가 앱으로 작동한 무드등의 색상, 밝기, 전원, 모드 데이터와 자외선 살균기의 전원, 타이머 데이터는 Firebase의 데이터베이스에 저장된다. 앱은 무드등의 전원이 켜진 상태에서 색상이나 밝기가 변경되면 변경 사항을 즉시 데이터베이스에 반영한다. 자외선 살균기의 타이머는 데이터베이스에 저장되지만 남은 타이머 시간까지 일일이 저장되진 않는다. 앱에서 설정한 시간이 지나면 자외선 살균기의 전원을 끄는 데이터를 저장할 뿐이다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 직접 구동시키는 것은 라즈베리파이와 아두이노이다. 라즈베리파이는 데이터베이스에 저장된 무드등의 색상, 밝기, 전원, 모드 데이터를 읽어와 라즈베리파이에 직접 연결된 네오픽셀 LED의 작동을 지시한다. 또한 살균기의 전원 데이터를 읽어와 아두이노에 살균기를 들어올리는 모터의 동작과 살균기 LED의 동작을 지시한다. 아두이노에서는 라즈베리파이의 지시를 대기하다가 살균기의 ON/OFF 동작 신호를 받아 살균기를 켜고 끈다. 이 때 살균기가 부착된 구동부의 높이는 아두이노에 연결된 초음파센서로 거리를 측정하여 조절한다.&lt;br /&gt;
&lt;br /&gt;
===UV-LED 곰팡이 저해 실험===&lt;br /&gt;
&lt;br /&gt;
본 실험은 UV-A LED의 곰팡이 생육 저해 능력을 검증하기 위한 실험이다. 구체적인 저해율의 수치를 나타내기 위해 1차 살균 실험에 이어 2차 곰팡이로 실험을 하여 저해율을 나타낼 것이다.&lt;br /&gt;
&lt;br /&gt;
: 1. 실험 준비&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비1.png|200픽셀|ㅇ|왼쪽|Petri Dish, Sterile cork borer, 곰팡이]]    [[파일:99-1=0_세균실험준비2.png|173픽셀]]&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비3.png|200픽셀]]    [[파일:99-1=0_세균실험준비4.png|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 실험 과정&lt;br /&gt;
&lt;br /&gt;
:: 1) 먼저 3종의 곰팡이(Cladosporium sp, Fusarium sp, Aspergillus sp)를 키울 배지를 만들기 위해 Difo회사 제품인 PDA 12g과 Agar 10g을 비커에 넣은 후 500ml distilled water를 넣어준 후 autoclave에서 121℃에서 15분간 돌려준 뒤 petri dish에 부어 1~2일 동안 말린다.&lt;br /&gt;
&lt;br /&gt;
:: 2) 곰팡이가 PDA배지에서 충분히 성장했다면 이를 각각 Petri Dish에 접종한다. 이 때 일정한 양의 곰팡이를 접종하기 위해 그림에 있는 Sterile cork borer를 사용한다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정1.png|200픽셀]]    [[파일:99-1=0_세균실험과정2.png|182픽셀]]&lt;br /&gt;
: [[파일:99-1=0_세균실험과정3.png|200픽셀]]    [[파일:99-1=0_세균실험과정4.png|195픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 3) 3종의 곰팡이를 0.5m, 0.75m, 1m 간격으로 배치시키고, 다른 하나는 대조군으로 UV-A LED에 노출시키지 않은 채로 성장시킨다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정5.png|200픽셀]]    [[파일:99-1=0_세균실험과정6.png|172픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 4) Petri Dish 안에서 곰팡이의 성장률을 원의 반지름으로 근사시킵니다. 또한 생리식염수를 10마이크로미터 떨어트린 후, 소량의 곰팡이를 접종해준 뒤 커버 글라스를 붙이고 광학현미경으로 관찰한다.&lt;br /&gt;
&lt;br /&gt;
:: 5) 곰팡이가 자란 원의 반지름을 측정하고, 이를 저해율로 나타낸다. 저해율의 식은 다음과 같다. (저해율(%)= (대조군의 반지름 - 실험군의 반지름)/대조군의 반지름 * 100)&lt;br /&gt;
: [[파일:99-1=0_곰팡이_측정.jpg|400픽셀]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 3. 실험 결과&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 1) Cladosporium sp(벽지 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Cladosporium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 2) Fusarium sp(토양 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Fusarium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 3) Aspergillus sp(누룩 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Aspergillus_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
아래 표는 현미경을 통해 관측한 곰팡이의 반지름(cm)이고, 이를 위해 저해율식에 대입하여 곰팡이 저해율을 계산하였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:길이값.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:저해율.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:5시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:10시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 4. 결론&lt;br /&gt;
: : 최종 저해율로 나타낸 결과 UV-A LED에 5시간, 10시간에 따른 차이는 거의 없었지만 거리에 따른 저해율의 차이는 눈에 띄었다. 0.5m 거리에선 약 80%의 저해율을 보였고, 0.75m 에서는 Cladosporuim sp와 Fusarium sp은 약 50%의 저해율을 보였지만 Aspergillus sp는 30%의 저해율을 보였습니다. 1m 거리에선 저해율이 많이 떨어졌는데, Cladosporium sp는 39%, Fusarium sp는 25%, Aspergillus sp는 6%의 저해율을 보였습니다. 1m에서의 살균력은 많이 떨어졌고, 이를 보완하기 위해 기존 실험은 25cm UV-A LED 2개를 설치했는데, 제품에는 한 슬라이드 당 3개로 늘려 설치했다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''구동 예시 동영상'''&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic1.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/8/87/99-1%3D0_%EC%98%81%EC%83%81.avi]]  [[파일:99-1=0_videopic2.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/c/ce/99-1%3D0_%EC%98%81%EC%83%812.avi]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''시나리오 동영상'''&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic3.png|300픽셀|link=https://capstone.uos.ac.kr/mie/images/8/82/99-1%3D0_%EC%98%81%EC%83%813.mp4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
'''UV-A LED Mode &amp;amp; Mood Light mode'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:UV-A LED.jpg|250픽셀]][[파일:99-1=0_무드등.jpg|250픽셀|link=https://capstone.uos.ac.kr/mie/images/99-1=0_영상.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_미구현내용.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_평가항목.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
[[파일:99-1=0_평가_결과.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
노*진: 프로젝트의 주제를 결정하기 전까지만 하더라도 라즈베리파이와 각종 모듈에 대한 지식이 없어 막막했습니다. 하지만 잘 모르는 만큼 팀원들과 더욱 조사를 열심히 하고, 구체적으로 설계를 하였습니다. 개념설계 발표 때까지 ‘과연 의도한대로 동작할까?’ 라는 의문점을 가졌지만 중간발표 준비를 하면서 원하는 기능들이 하나 둘씩 구현되고, 기구부의 Mechanism들이 완성되는 것을 보면서 재밌었고, 성취감을 느꼈습니다. 프로젝트를 진행하면서 전공 지식이 아닌 전기 회로도와 관련된 일들을 구글링과 여러 사이트에 물어보면서 해결을 했고, UV-LED의 살균력을 검증하기 위해 생명공학 전공인 지인의 도움을 받으면서 프로젝트의 문제들을 해결했습니다. 이 과정에서 잘 모르는 부분을 어떤 방법으로 해결할 지에 대한 능력을 기를 수 있었습니다. 마지막으로 내장형시스템 과목을 통해 기계정보공학과의 매력을 느낄 수 있었고, 앞으로 프로젝트를 수행할 때 자신감을 가질 수 있을 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
박*훈 : 기계정보공학을 전공한지 4학년 1학기에 처음으로 컴퓨터 관련 프로젝트를 진행했습니다. 그동안 기계 관련 CAD 프로젝트나 CAE 프로젝트는 다수 경험이 있었지만 컴퓨터 분야는 경험이 없었기 때문에 프로젝트 과정 중 가장 처음 과정인 주제 선정에서부터 엄청난 난관에 부딪혔습니다. 하지만 주어진 시간 안에 주어진 모듈로 할 수 있는 주제를 생각해냈고, 프로젝트를 시작했습니다. 프로젝트 진행 과정 중에 한계에 도달하는 일도 있었고 뜻처럼 진행되지 않는 일도 있었지만 팀원들과 함께 돌파구를 찾아가며 차근차근 프로젝트를 완성했습니다. 프로젝트를 진행하면서 기계정보공학의 융합적 전공의 취지에 맞게 기계적인 메커니즘과 임베디드 시스템을 융합한 완성품을 제작하기 위해 노력을 했고, 실제로 저는 기구부의 메커니즘 쪽을 맡아 CAD 프로그램인 CATIA를 통해 기어 및 랙 피니언 세트와 슬라이드 핀 등을 설계하고 3D 프린터를 이용하여 실제 구현을 완료했습니다. 기구부 제작 과정 중 값비싼 3D 프린터 제작의 비용과 3D 프린터 제작품의 과도한 무게 등의 문제가 있었지만, 팀원들과 협의한 끝에 해결 방안을 고안하여 이러한 문제를 해결했습니다. 이번 내장형 프로젝트의 경험을 통해 팀원들과의 소통 능력과 문제 해결 능력을 한층 성장시켰고, 앞으로 있을 프로젝트에 큰 도움이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
주*완: 이번 학기에 내장형시스템및실습을 수강하며 처음으로 주제 선정부터 완제품 제작까지 모든 것을 직접 경험했습니다. 주제 선정부터 시작해 모든 과정이 쉽지 않았고 걸림돌에 걸려서 결국 설계를 바꾸기도 했습니다. 하지만 한계를 극복할 방법을 고민하고 공부하고 질문하고 찾아 헤매면서 많은 것을 배웠습니다. 특히 제가 맡은 부분인 안드로이드 앱을 개발하기 위해 1학년 때 배웠지만 모두 잊어버린 java를 처음부터 다시 공부했고, 안드로이드를 아무것도 모르는 상태에서 인터넷 검색만으로 하나하나 공부하고 직접 코드를 짜서 완성까지 했습니다. 과정 중에 알 수 없는 이유때문에 앱이 원하는 대로 안 되거나 강제 종료 될 때가 매우 매우 많았는데, 스스로 계속 공부하고 원인을 찾고 고쳐 나가서 마침내 앱을 완성할 수 있었고, 스스로 문제를 해결하는 능력을 기를 수 있었습니다. 또한 기구부와 외형을 이루는 부분을 만들 때 자르고 다듬는 일이 많이 필요했는데 서로 필요한 부분이 있으면 도와주면서 협동하여 결과가 잘 나온 것 같아 만족스럽습니다. 프로젝트를 하며 값진 경험을 했고 앞으로 다른 프로젝트를 하게 된다면 이 경험이 소중한 밑바탕이 돼서 도움이 많이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
함*규: 임베디드 시스템을 처음 접해보기에 주제를 정하는 과정이 가장 어려웠던 것 같습니다. 처음에는 주어진 시간 내에 해낼 수 없는 비현실적인 주제에 대해 고민하며 기능적 한계에 부딪혀보기도 하고, 챗바퀴 도는 듯한 생각이 들었지만, 이 과정 속에서 전체적인 임베디드 시스템에 대한 이해와 할 수있는 것과 할 수 없는 것에 대해 구분할 수 있는 능력을 얻을 수 있었습니다. 이후에 직접 정한 주제를 바탕으로 프로젝트를 수행해나가면서, 초기 설계로는 해결할 수 없는 문제들을 팀원들과 함께 협동하여 풀어나가는 과정은 정말 힘들기도 했지만 되돌아 보면 책상에서는 배울 수 없는 많은 경험들을 배웠다고 생각합니다. 4-1학기를 떠올리면 많은 희노애락이 있었던 내장형 밖에 생각나지 않을 것 같은 데, 정말정말 재밌었고(농담이 아닙니다.) 후배들에게 꼭 추천해주고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
유*환: 내장형 시스템 프로젝트의 주제를 정하는 과정이 가장 어려웠습니다. 라즈베리파이에 센서나 모터, 또다른 보드를 연결하여 사용한 경험이 없어 주어진 시간 내에 완성할 수 있는 정도를 알기 어려웠습니다. 그래서 처음에는 학기 내에 완성이 불가능한 주제들을 생각했었습니다. 결국 마지막까지 주제를 정하지 못하다가 막바지에 급하게 살균 무드등을 만들기로 했습니다. 주제가 정해지고 자료조사를 마쳤을 때만 해도 힘든일이 끝나고 모든 일이 수월하게 진행될 것만 같았습니다. 그런데 출입 감지 방법, 음악 주파수 분석 등이 뜻대로 되지 않자 다시 조마조마해졌습니다. 하지만 적절한 대안을 찾고 해결해나가면서 완성되가는 프로젝트를 보며 뿌듯했습니다. 그리고 살균 무드등을 완성하고 영상까지 마무리 지었을 때는 그동안 밤을 새며 고생했던 시간들에 대해 보상받는 느낌이 들었습니다.&lt;br /&gt;
무엇보다 팀원 형들이 프로젝트에서 각자가 맡은 열할을 너무 잘 수행해주어서 감사했습니다. 유일하게 라즈베리파이 경험이 있는 저보다도 잘해주어서 미안하기도 하고 대단하다는 느낌이 들었습니다. 내장형 프로젝트는 과정은 정말 힘들었지만 팀원들과 살균 무드등을 만드는 것이 재밌었고 프로젝트 진행에 대한 경험치가 쌓여 좋은 기회가 되었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EC%BA%94.mp4&amp;diff=5923</id>
		<title>파일:캔.mp4</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EC%BA%94.mp4&amp;diff=5923"/>
				<updated>2020-06-22T05:27:50Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: 2020ese 1님이 파일:캔.mp4의 새 판을 올렸습니다&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5922</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5922"/>
				<updated>2020-06-22T05:24:50Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 시연영상 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
[[파일:개발일정_착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
===영상인식 결과===&lt;br /&gt;
[[파일:영상인식.JPG|600픽셀|가운데|섬네일|그림9. 학습 결과 손실함수 및 IOU]]&lt;br /&gt;
===판별 정확도===&lt;br /&gt;
[[파일:정확도.JPG|600픽셀|가운데|섬네일|그림10. 판별정확도]]&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;br /&gt;
&lt;br /&gt;
윤*상 - RVM을 개발하면서 어느정도 수준의 하드웨어 제작이 필요한 시스템을 개발할 때에는 기본적인 하드웨어 시스템을 구현해놓고 소프트웨어를 개발하는 것이 효율적인 것을 느꼈습니다. 이번 프로젝트에선 대부분의 소프트웨어를 개발한 뒤 하드웨어를 제작하였는데 이 과정에서 예상치 못한 문제들이 발생하였습니다. 모터와 센서의 개수가 많아 전력 공급에서 문제가 생기고 배선과 같은 부분에서 제대로 작동하지 않아 오랜 시간이 걸렸습니다. 하지만 우리 팀원 모두 열심히 프로젝트에 임해주었고 소통도 활발히 되었으며 개개인의 능력 모두 뛰어났기 때문에 빠르게 문제를 해결하여 프로젝트를 마무리 할 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
심*헌 - 이번 프로젝트에서 pyqt5를 통해 라즈베리파이와 같은 제한된 조건에서 유저 인터페이스를 개발하였습니다. 평소 자바스크립트를 통한 웹개발을 하고있어 ui 개발을 맡았지만 디자인과 같은 좀 더 전문성이나 예술에 가까운 영역에는 한계가 있어 팀원들과 소통을 통해 만들었습니다. 새로운 ui 플랫폼 개발의 기회가 된거 같아 도움이 되었고 팀원들과 하나의 큰 결과물을 내어 보람을 느낍니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5921</id>
		<title>99-1=0 - Smart Mood Light with Sterilization</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5921"/>
				<updated>2020-06-22T03:08:34Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 소프트웨어 설계 및 구현 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 Smart Mood Light with Sterilization&lt;br /&gt;
 살균 기능을 탑재한 스마트 무드등&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 노*진 (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 박*훈 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 주*완 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 함*규 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20174300** 유*환 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
원룸은 타 주거환경에 비해 채광과 통풍 능력이 떨어진다. 공기 청정기를 이용할 수 있지만, 채광 문제를 해결하지 못한다. 다행히 넓은 공간을 가진 주거환경에 비해 규모가 작아 상용 자외선을 통한 살균 효과는 비해 뛰어나지만, 살균기의 제어는 사용자가 원룸 내 없을 때 이루어 져야 한다. 위의 문제를 해결하기 위해 스마트폰을 이용하여 원격 제어를 실행한다. 또한 원격 제어를 하면서 사용자가 원룸 내 있을 때 활용할 수 없는 살균기가 공간을 비효율적으로 차지하게 되는 문제를 해결하기 위해 이를 무드등과 결합 한다. 결과적으로 사용자가 가장 많이 시간을 보내는 공간을 살균할 수 있으며 공간의 비효율성과 안전성, 더불어 무드등의 원격제어로 인한 편의성을 제공한다. 사용자는 외부에 있을 때, 스마트폰을 이용하여 살균기 동작을 제어할 수 있다. 사용자가 내부에 있을 때 별도의 조치 없이도 자외선 동작 제어는 비활성화 되며 무드등의 원격 제어가 가능하다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 1. 배경&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_최근4년간국내미세먼지수치.png|500픽셀]]&lt;br /&gt;
[[파일:99-1=0_최근전세계적바이러스주기.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
최근 국내 미세먼지 수치가 꾸준히 증가함에 따라 국민들의 미세먼지 정화에 관한 관심이 높아지고 있다. 또한, 전 세계적인 바이러스 발생 주기가 짧아짐에 따라 사람들의 살균 제품에 대한 관심도가 높아지고 있다. &lt;br /&gt;
&lt;br /&gt;
햇빛은 면역력 향상이나 기분 전환의 효과뿐 아니라 살균 기능을 가지고 있다. 원룸은 구조적으로 대부분 하나의 창문을 가지고 있어 충분한 빛을 받지 못하며 공기의 입/출구가 하나이기에 통풍이 원활하지 않다. 이러한 조건은 미생물이 자라나기 쉽고 미세먼지의 배출이 어렵다. 이들을 모두 정화하는 데 맞는 조건을 가진 것이 자외선이다. 자외선을 통해 공기 중에 살균이 가능하며 미세먼지 중금속을 분해해 공기를 정화할 수 있다. 하지만 자외선 살균기를 집안에 두고 사용하는 것에는 다음과 같은 문제점들이 있다.&lt;br /&gt;
&lt;br /&gt;
- 살균 자외선은 인간에게 유해하므로 사용자에게 직접적인 자외선이 가해지는 것은 위험하다. 사용자가 없는 곳에서 자외선 살균기가 작동해야 하며 사용자가 있을 때 확실한 방법으로 조작 가능성을 배제 시켜야 한다.&lt;br /&gt;
&lt;br /&gt;
- 사용자가 자주 쓰는 공간의 살균일수록 의미가 있으므로 자외선 살균기는 사용자가 자주 쓰는 공간에 배치되어야 한다. 그러나 위의 이유로 사용자가 실제 방에 있을 때 자외선 살균기를 사용하는 것은 위험하므로 사용자가 있을 때 별다른 기능 없이 공간만을 차지하게 된다.&lt;br /&gt;
&lt;br /&gt;
원룸 내 거주자가 가장 오래 머무르는 공간으로 예상되는 곳은 침대류 이다. 침대류 근처에 있는 물체에 살균기를 결합하여 제품을 만들 수 있다면 효율적으로 문제를 해결할 수 있다. 우리는 이 조건을 만족하는 물체가 무드등이라는 점에서 문제 해결을 시작했다. 또한, 첫 번째 문제 해결을 위해 임베디드 시스템을 설계하여 사용자가 없는 공간에서의 작동을 가능하게 하는 것뿐 아니라 무드등에 다양한 기능을 추가하여 편리성과 기능의 다양함을 추가하려고 한다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 기대효과&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_UV자외선을이용한살균 미세먼지정화과정.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
사용자는 집 밖에서 애플리케이션을 통해 자외선 모듈을 가동함으로써 안전한 방법으로 공간을 살균 및 미세먼지 분해 기능을 작동시킬 수 있다. &lt;br /&gt;
&lt;br /&gt;
또한, 사용자가 개발 제품과 같은 공간에 있다고 판단 시 애플리케이션 자동 스위칭 기능을 통해 자외선 컨트롤 기능이 아닌 무드등 컨트롤 기능을 제어하게 된다. 따로 조작 없이 방 안/밖이라는 공간의 변화에 따라 스위칭 되기에 조작에 신경 쓸 필요가 없다. 무드등 기능으로썬 조도 조정, 빛의 색상 선택, 주변 음악에 따른 색상 변화 기능 등이 있으며 사용자가 선택하는 옵션에 따라 기능을 수행할 수 있어 편리함과 다양성을 제공한다. 사용자의 유무에 상관없이 개발제품은 유용한 도구로써 사용되기에 공간 활용성 또한 확보할 수 있을 것으로 생각된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:99-1=0_목적계통도.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 안전성&lt;br /&gt;
자외선 활성/비활성화 기능은 가스 자동밸브 잠금과 비슷한 원리로 앱 구동 시 스마트폰 GPS 기능을 통해 사용자의 위치정보를 받아 주거 위치와 비교하여 앱 인터페이스 비활성화 여부를 결정한다. GPS 오작동으로 인한 비활성화가 적절히 이루어지지 않았을 때를 대비해 앱 내 수동 비활성화 버튼을 추가한다. 앱 인터페이스가 비활성화 되어 있어 사용자의 실수로 인한 신호 전달 가능성을 사전에 차단할 수 있다. 또한 회로의 긴급 정지 스위치를 통해 하드웨어 오동작 시 회로 자체의 전류를 제어할 수 있다. 또한 인체 및 환경에 유해한 정도를 판단하여 인체에 비교적 덜 유해한 UV-A의 자외선 영역을 사용했고, 수은으로 환경 파괴 문제를 일으키는 UV-램프를 대신하여 UV-LED를 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 실용성&lt;br /&gt;
먼저 자외선 영역의 성능을 비교하면 살균력 기준으로는 분 단위 살균력을 가진 UV-C가 시 단위 살균력을 가진 UV-A보다 월등히 강력하다. 하지만 살균 범위를 보면 UV-A는 공기 중 3-5m의 반경을 가진 것에 비해 UV-C는 공기 중 투과력이 매우 낮아 넓은 범위를 살균할 때에는 UV-A가 더 적합하다. 따라서 거주자 없이 비어있는 시간이 긴 원룸에 맞추어 UV-405nm 자외선을 이용했다. 이 때, 자외선 LED의 살균 방향을 15도 정도 아래쪽으로 내려주어 침구류 및 사용자가 자주 사용하는 물건도 더불어 살균하게 된다. 모터의 경우, 자외선 살균기의 상하 운동이 완료된 후에 해당 위치에서 고정이 되어야 하는데, 서보 모터는 고정 기능이 없고, 스탭 모터는 고정 기능을 사용할 때 소모 전력이 많다. 따라서 적은 소모 전력으로 고정 기능을 수행할 수 있는 웜 기어 모터를 사용했다.&lt;br /&gt;
공간 효율성을 기준으로는 제품 구성 시 얼마나 효율적인 공간 활용 여부를 비교했다. 따라서 각 제품의 특성에 맞추어 무드등 조명에는 필요 전선량이 많은 LED 전구 대신 네오 픽셀을 사용했고, 자외선 조명에는 1cm 이하의 조명 크기를 가지는 UV LED를 사용했다. 또한 사용자 위치를 판별하기 위해서 출입문 주변의 많은 공간을 차지하는 적외선 센서 활용 출/입 카운터 대신 외부 공간을 이용하지 않는 안드로이드 모바일 앱 내의 GPS 기능을 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 편의성&lt;br /&gt;
사용자 입장에서 제품을 이용할 때의 반응성을 기준으로 비교했다. 호스팅 제공 S/W에서는 외부의 데이터베이스를 이용하는 파이썬 애니웨어 대신 자체적으로 실시간 데이터베이스 기능을 제공하는 파이어 베이스를 사용했다. 또한 사용자 위치 판별법에서는 사용자의 출/입 여부를 얼마나 빠르게 판단할 수 있는지에 대한 비교를 했는데, 이는 적외선 센서 활용 카운터가 더 빠른 응답성을 보였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 경제성&lt;br /&gt;
제품에 대한 경제적인 측면은 제품을 구성하는데 사용하는 비용과 유지비로 생각할 수 있다. 먼저 제품 구성비용은 프로젝트의 해당 제품을 구매하는 비용이다. 따라서 보다 더 값이 싼 네오픽셀, UV-A, UV-램프가 더 적합했다. 또한 호스팅 기능 S/W에서는 무료로 이용할 수 있는 범위가 더 넓은 파이어 베이스가 적합했다. 또한 유지비는 제품의 수명이 얼마나 긴지, 배터리 소모량이 얼마나 적은지, 부품 교체 비용이 얼마나 큰지에 대해 비교했다. 따라서 부품 교체 비용이 더 저렴한 네오픽셀, 수명이 더 긴 UV-LED가 더 적합했다. 또한 항상 동작시키는 적외선 센서보다 필요시에만 동작시킬 수 있는 GPS 기능이 배터리 소모량이 거의 없어 더 적합했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 개발 용이성&lt;br /&gt;
개발제품 특성상 데이터베이스 및 호스팅 서비스가 필요하다. 개발자 독자적으로 데이터 베이스와 호스팅 서비스를 개발할 필요 없이 이러한 서비스를 제공하는 서버는 파이어 베이스이다. 개발 난이도는 물론 개발자의 서버 개발 시간을 최소화 하여 다른 분야의 성능 개발을 최대화 시킬 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_개발용이성.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Application과 라즈베리 파이의 통신 중간 다리로 파이어 베이스 서버를 이용한다. 물론 무드등 제어를 위한 라즈베리 파이 서버를 구축하여 통시에 이용하면 원룸 내 무드등 제어 동작 속도를 향상시킬 수도 있다. 그러나 하나의 서버를 이용하면 통신 오류나, 오류가 발생했을 때 오류를 제어하기 쉽다. 또한  추후에 기능 추가가 용이하다. 파이어 베이스는 서버 특성상 서버 사용자가 많아지게 되면 부하가 발생할 수 있다. 그러나 개발 상품의 경우 서버는 한명의 사용자만이 이용한다는 점을 고려하여 이러한 단점을 고려하지 않을 수 있다. 따라서 해당 프로젝트에 가장 적합하고, 통신 복잡도가 적은 파이어 베이스와 안드로이드 스튜디오를 사용했다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_동작시나리오.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_역할분담.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_세부개발일정표.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_시스템구성도.png|800픽셀]]&lt;br /&gt;
&lt;br /&gt;
===기구부 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_무드등모드3D설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_자외선살균모드3D.png|166픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
먼저 기구부에서는 최종적으로 제품의 형상과 베벨 기어로 사각 LED 기둥을 올리는 Mechanism과 자외선 LED가 부착되어 있는 Slide Pin Mechanism을 구현하였다. &lt;br /&gt;
제품의 형상의 경우에는 제품의 무드등 기능과 자외선 살균 기능을 정상적으로 구현할 수 있게 필요한 요소들을 적절히 배치할 수 있는 공간을 만들었고, 사용자의 안전성을 위해 Mood Light를 위한 NeoPixel과 UV-A LED를 구분하여 인체에 해로운 자외선 LED는 기능을 사용하지 않을 때 내부 공간에 위치하도록 구현하였다. &lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism은 첫 번째 자외선 LED가 설치된 사각기둥을 올리는 Motor Mechanism과 두 번째 자외선 LED가 적절한 각도로 펼쳐지는 슬라이드 핀 Mechanism이다. 첫 번째 Motor Mechanism은 모터와 베벨 기어 및 랙 피니언 시스템을 통해 모터의 회전 운동을 직선 운동으로 전환하여 구현하였다. 또한 두 번째 Slide Pin Mechanism은 슬라이드 핀을 이용하여 중력에 의해 자동으로 사각기둥 상승 시 펼쳐지고, 하강 시 접히도록 구현한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''기구부 설계'''&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 1. Motor Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_모터설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_모터구현.png|191픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism 중 자외선 LED가 설치된 사각 기둥을 올릴 베벨 기어와 랙 피니언을 사용한 Motor Mechanism 시스템 구현을 완료했다. CATIA CAD 프로그램을 이용하여 베벨 기어 및 랙 피니언 설계를 완료했고, 3D 프린터를 이용하여 규격에 맞추어 실제 제품을 만들었다. 기존에는 랙 피니언 한 쌍만을 직접 모터에 연결하여 사각기둥의 상하 운동을 구현하려고 했지만, 대칭성이 잘 맞지 않아 한쪽으로 쏠리는 현상이 발생하여, 추가적으로 베벨 기어 등을 사용하여 대칭성을 고려하여 양쪽에서 사각 기둥의 상하 운동을 구현했다.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. Slide Pin Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_슬라이드핀설계.png|200픽셀]]&lt;br /&gt;
[[파일:Slide.jpg|145픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Motor Mechanism을 통해 자외선 LED가 설치된 사각기둥이 상하운동을 하면, LED가 적절한 각도를 이루어 펼쳐지도록 구현하였다. 따라서 Slide Pin Mechanism을 이용하여 사각 기둥이 상승할 때 중력에 의해 자동으로 LED가 펼쳐지고, 하강할 때 자동으로 접힌다. CATIA CAD 프로그램을 이용하여 3D 설계를 하고, 3D 프린팅을 통해 실제로 외관을 만들었다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_LED각도설계.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LED가 펼쳐지는 적절한 각도로는 30도를 정했다. 각도를 정하는 과정에서 두 가지의 고려 사항을 정했는데, 첫 번째는 자외선 LED 끝이 직각으로 도달하는 지점의 거리가 원룸이라는 공간을 고려하여 1m 이상이 되고, 두 번째는 제품과 근접한 곳의 LED 사각지대가 제품의 끝으로부터 10cm 내가 되는 기준을 두었다. 따라서 CATIA CAD 프로그램의 2D 설계를 통해 적절한 각도 30도를 설정했다. (실제로 빛은 직각으로만 전달되는 것이 아니라 사방으로 퍼지기 때문에 실제 최대 도달 거리는 기준값인 1m 보다 더 커진다.)&lt;br /&gt;
&lt;br /&gt;
'''기구부 구현'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_사각기둥.jpg|250픽셀]]&lt;br /&gt;
[[파일:99-1=0_Mood.jpg|250픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_완성본.jpg|250픽셀]]&lt;br /&gt;
[[파일:위에서_본_모습.PNG|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
UV-A LED를 장착할 사각 기둥을 3D Printing을 통해 만들었다. 또한 Mood Light를 위한 NeoPixel은 사각 기둥 밖에 다른 기구에 물결 모양으로 48개를 달았다. 최종적으로 아크릴판에 불투명 시트를 붙여 등을 만들었다.&lt;br /&gt;
&lt;br /&gt;
'''최종 작품'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:파랑이.jpg|250픽셀]][[파일:UV-A_LED.jpg|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
최종으로 Mood Light에서 단색 모드로 파랑색 조명을 킬 때와, UV LED Mode에서 On버튼을 눌렀을 때 결과이다.&lt;br /&gt;
&lt;br /&gt;
===제어부 및 회로 구현===&lt;br /&gt;
 UV-A LED, Motor 회로도&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_제어회로도1.PNG|600픽셀]][[파일:99-1=0_내부회로도2.jpg|600픽셀]]&lt;br /&gt;
 &lt;br /&gt;
SMPS로부터 220V 정격 전압을 12V 정격 전압으로 나누어 아두이노와 Relay Switch를 통해 UV-LED의 전원 On/Off 구현이 가능하다. 모터와 UV LED는 아두이노로 제어하기 때문에 라즈베리파이에서 데이터베이스의 값을 실시간으로 확인하다가 UV LED 동작이 지시되면 아두이노에 신호를 보내준다. 데이터베이스의 값은 안드로이드 App을 통해 갱신되므로 결국은 안드로이드 App을 통해 UV-LED의 On/Off 제어가 가능하다. 라즈베리파이와 아누이노간의 데이터 통신은 USB 케이블을 이용한 시리얼 통신을 이용한다.(라즈베리파이와 아두이노 간의 거리가 가깝고 전송하는 데이터가 단순하기 때문에 간단한 통신 방법 선택) 구현 언어는 파이썬이며 사용한 파이썬 모듈은 serial이다. 이와 관련하여 사용한 함수들은 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
UV-LED 동작을 설정하는 값이 “PowerON”일 경우 USB 포트로 연결된 아두이노로 UTF-8로 인코딩된 문자 u를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 u를 받아 UV-LED On 동작을 수행한다. 만약 UV-LED 동작 설정값에 “PowerON” 외의 값이 저장되었을 경우 (“PowerOFF“의 경우가 일반적이지만 예외 처리를 위해 else로 조건 판단) 아두이노로 UTF-8로 인코딩된 문자 d를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 d를 받아 UV-LED Off 동작을 수행한다.&lt;br /&gt;
&lt;br /&gt;
 NeoPixel 회로도&lt;br /&gt;
[[파일:99-1=0_네오픽셀_회로도.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
Mood Light구현은 NeoPixel과 Raspberry PI를 통해 구현했다. 사용자가 Application을 통해 색을 바꾸면 Database의 값이 아래 표에 Data로 바뀌면서 Mood Light 색이 변한다. NeoPixel의 Digital input을 통해 값이 들어가고, 그에 맞는 색을 자유롭게 바꿀 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:색깔.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:App_insidemode_screenshot.jpg|200픽셀]] [[파일:App_outsidemode_screenshot.jpg|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
 앱&lt;br /&gt;
&lt;br /&gt;
앱을 처음 설치하고 실행하면 앱은 팝업 윈도우로 모바일 디바이스의 위치 정보 권한을 요청하며 사용자는 이를 최초 1회만 수락하면 된다. 앱은 실행되면 즉시 모바일 디바이스의 현재 위치 정보를 받고 데이터베이스에 저장된 집의 위치 정보와의 거리를 계산한다. 이 거리가 일정 거리 이상이면 앱은 실외 모드로 자동 설정되고, 일정 거리 미만이면 실내 모드로 자동 설정된다. 실내 모드와 실외 모드의 전환은 기본적으로 비활성화되어 있으나 사용자가 우측 하단의 스위치를 켜면 활성화되어 강제 모드 전환이 가능하게 된다. 그래서 앱을 실행했을 때 자동 모드 설정이 잘못되면 사용자가 직접 모드를 전환해서 원하는 기능을 사용할 수 있다.&lt;br /&gt;
&lt;br /&gt;
앱은 크게 실내 모드와 실외 모드로 구분된다. 각 모드는 Fragment로 정의됐고 MainActivity의 bottomnavigationview를 통해 replace해서 전환할 수 있다. bottomnavigationview는 각 모드에 배치된 스위치가 꺼져 있으면 disabled되고 스위치가 켜지면 enabled된다. 사용자는 실내 모드에서 무드등을 제어하고 실외 모드에서 자외선 살균기를 제어한다.&lt;br /&gt;
&lt;br /&gt;
 실내 모드&lt;br /&gt;
실내 모드에선 무드등의 색상 선택, 밝기 조절, 전원 작동, 스페셜 모드 기능을 이용할 수 있다.&lt;br /&gt;
* colorpicker&lt;br /&gt;
: colorpicker의 색상은 초기에 12시의 연두색으로 설정되어 있고 사용자가 버튼을 드래그해서 원하는 색상을 선택할 수 있다. colorpicker의 중앙에 있는 원은 왼쪽 반원에 이전 색상을 표시하고 오른쪽 반원에 현재 색상을 표시하여 전후 색상을 비교하기 좋게 만들어준다. 사용자가 무드등의 전원을 키면 이전 색상이 표시된 반원이 현재 색상으로 교체된다. 색상은 여섯 자리의 16진수 #nnnnnn으로 표현되며 정수형으로 저장된다. 사용자가 colorpicker로 다양한 색상을 연속적인 범위에서 선택할 수 있기 때문에 선택의 폭이 넓고 세밀한 색상 선택이 가능하다. 무드등의 전원이 켜져 있으면 색상의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 밝기&lt;br /&gt;
: 무드등의 밝기 조절은 실내 모드 중앙의 seekbar를 통해 할 수 있다. 밝기 값은 최소 0에서 최대 255까지 선택 가능하다. colorpicker와 같이 무드등의 전원이 켜져 있으면 밝기의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 전원&lt;br /&gt;
: Power ON 버튼을 누르면 무드등의 전원이 켜진다. 전원 버튼 하단의 얇은 띠가 전원이 꺼져있을 때 회색, 전원이 켜져있을 때 녹색을 띠면서 사용자가 무드등의 전원이 켜져있는지를 직관적으로 알 수 있게 해준다. 전원 작동 데이터가 데이터베이스에 성공적으로 전송되면 앱 화면 하단에 Toast 텍스트를 표시하여 사용자에게 전원의 작동을 알려준다. 전원 버튼의 Power ON/Power OFF 텍스트는 colorpicker에서 선택된 색상과 실시간으로 동일하게 적용되어 사용자에게 무드등이 어떤 색으로 켜질지 더욱 직관적으로 와닿게 만들어준다.&lt;br /&gt;
* 스페셜 모드&lt;br /&gt;
: 앱을 실행하면 무드등의 모드는 기본 모드로 설정되어 있는 상태이다. 스페셜 모드 버튼을 누르면 순차적으로 웨이브 모드 1, 웨이브 모드 2, 레인보우 모드 1, 레인보우 모드 2가 설정되며 그후 다시 기본 모드로 돌아온다. 현재 설정된 모드는 스페셜 모드 버튼의 텍스트로 나타나서 사용자가 이를 알 수 있다. 또한 버튼을 누를수록 단계적으로 버튼의 배경 색이 진해지며 버튼의 깊이감을 더해준다. 스페셜 모드 버튼의 아래엔 모드의 전체 개수와 현재 모드가 몇 번째인지를 시각적으로 쉽게 알게 해주는 공백 칸들이 있다. 이 칸들은 기본 모드에서 모두 비어있게 되고 스페셜 버튼이 눌릴 때마다 한 개씩 채워지며 모두 채워진 후엔 다시 모두 빈 칸으로 돌아온다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스페셜 모드는 무드등이 켜져 있을 때에만 작동이 유효하다. 무드등의 전원이 꺼져 있을 때 스페셜 모드를 작동하면 무드등에서 아무 일도 일어나지 않는다. 이런 상황을 예방하기 위해 앱에서 무드등의 전원이 꺼진 상태에서 스페셜 모드만 작동하는 일이 일어나지 않게 한다. 무드등의 전원이 꺼진 상태에서 스페셜 모드 버튼을 누르면, 앱은 무드등의 전원도 키고 스페셜 모드도 작동한다. 또한 스페셜 모드가 설정된 상태에서 무드등의 전원을 끄면 앱은 스페셜 모드를 기본 모드로 설정하고 무드등의 전원을 끈다. 만약 사용자가 레인보우 모드를 바로 사용하고 싶다면 전원 버튼을 누를 필요 없이 바로 스페셜 모드 버튼을 3회 누르면 된다. 전원 버튼을 누르는 단계를 거치지 않아도 되기 때문에 빠르고 간편하게 즉시 스페셜 모드를 이용할 수 있다. 결과적으로 무드등의 전원이 꺼진 상태에서 무드등의 모드는 항상 기본 모드로 설정되어 있고, 스페셜 모드가 켜진 상태면 무드등의 전원도 항상 켜진 상태가 된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 실외 모드&lt;br /&gt;
실외 모드에선 위치 정보 설정, 자외선 살균기의 전원 작동, 타이머 기능을 이용할 수 있다.&lt;br /&gt;
* 위치 정보 설정&lt;br /&gt;
: HOME LOCATION 버튼을 누르면 모바일 디바이스의 현재 위치 정보를 데이터베이스에 저장하여 집의 위치로 설정한다. 설정한 집의 위치 정보는 앱이 실행됐을 때 집과 사용자 사이의 거리를 계산하는 데 이용된다. 데이터베이스에 저장된 위치 정보는 사용자가 새롭게 설정하지 않는 한 항상 일정하게 유지된다. 그래서 앱을 재설치했을 때 집의 위치 정보 설정을 다시 할 필요가 없다. 상수의 변수명을 대문자 알파벳으로 선언하는 프로그래밍 관습을 응용해서 위치 정보 설정 버튼의 텍스트도 대문자 알파벳으로 하였다. &lt;br /&gt;
* 자외선 살균기&lt;br /&gt;
: 실외 모드에서 Power ON 버튼을 누르면 자외선 살균기의 전원이 켜진다. 전원 버튼 위의 spinner에서 타이머가 작동할 시간을 선택할 수 있으며 초기 타이머는 1시간으로 설정된 상태이다. 타이머는 시간 단위로 선택할 수 있고 최대 12시간까지 선택 가능하다. 자외선 살균기의 전원을 켜면 전원 버튼 아래에 textview가 표시되어 타이머의 남은 시간을 알려준다. 설정한 타이머가 종료되면 앱이 자동적으로 자외선 살균기의 전원을 끈다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Firebase&lt;br /&gt;
[[파일:99-1=0_파이어베이스스크린샷.jpg|300픽셀]]&lt;br /&gt;
[[파일:99-1=0_파이어베이스연동.jpg|500픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Firebase는 Google 사의 모바일 및 웹 개발 플랫폼이다. Firebase는 많은 유용한 기능을 지원하지만 여기선 실시간 데이터베이스를 사용한다. Firebase의 장점은 안드로이드 및 iOS 앱과 연동이 용이하다는 것이다. 또한, Firebase는 시간과 장소에 구애받지 않고, 데이터가 안전하게 보존되며, 데이터를 읽고 쓰는 것이 간편하다. 그래서 Firebase는 사용자의 집의 위치 정보를 저장하기에 적합하다. 이 데이터는 제 3자가 접근할 수 없으며 사용자가 새롭게 설정하지 않는 한 영구히 보존된다. 앱은 Firebase의 데이터베이스에 집의 위치 정보를 저장하기도 하지만 읽어오기도 한다. 앱은 실행되면 데이터베이스의 집의 위치 정보를 읽어와서 현재 사용자의 위치 사이의 거리를 계산하고 실내/실외를 판별한다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 작동할 때에도 Firebase를 이용한다. 사용자가 앱으로 작동한 무드등의 색상, 밝기, 전원, 모드 데이터와 자외선 살균기의 전원, 타이머 데이터는 Firebase의 데이터베이스에 저장된다. 앱은 무드등의 전원이 켜진 상태에서 색상이나 밝기가 변경되면 변경 사항을 즉시 데이터베이스에 반영한다. 자외선 살균기의 타이머는 데이터베이스에 저장되지만 남은 타이머 시간까지 일일이 저장되진 않는다. 앱에서 설정한 시간이 지나면 자외선 살균기의 전원을 끄는 데이터를 저장할 뿐이다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 직접 구동시키는 것은 라즈베리파이와 아두이노이다. 라즈베리파이는 데이터베이스에 저장된 무드등의 색상, 밝기, 전원, 모드 데이터를 읽어와 라즈베리파이에 직접 연결된 네오픽셀 LED의 작동을 지시한다. 또한 살균기의 전원 데이터를 읽어와 아두이노에 살균기를 들어올리는 모터의 동작과 살균기 LED의 동작을 지시한다. 아두이노에서는 라즈베리파이의 지시를 대기하다가 살균기의 ON/OFF 동작 신호를 받아 살균기를 켜고 끈다. 이 때 살균기가 부착된 구동부의 높이는 아두이노에 연결된 초음파센서로 거리를 측정하여 조절한다.&lt;br /&gt;
&lt;br /&gt;
===UV-LED 곰팡이 저해 실험===&lt;br /&gt;
&lt;br /&gt;
본 실험은 UV-A LED의 곰팡이 생육 저해 능력을 검증하기 위한 실험이다. 구체적인 저해율의 수치를 나타내기 위해 1차 살균 실험에 이어 2차 곰팡이로 실험을 하여 저해율을 나타낼 것이다.&lt;br /&gt;
&lt;br /&gt;
: 1. 실험 준비&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비1.png|200픽셀|ㅇ|왼쪽|Petri Dish, Sterile cork borer, 곰팡이]]    [[파일:99-1=0_세균실험준비2.png|173픽셀]]&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비3.png|200픽셀]]    [[파일:99-1=0_세균실험준비4.png|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 실험 과정&lt;br /&gt;
&lt;br /&gt;
:: 1) 먼저 3종의 곰팡이(Cladosporium sp, Fusarium sp, Aspergillus sp)를 키울 배지를 만들기 위해 Difo회사 제품인 PDA 12g과 Agar 10g을 비커에 넣은 후 500ml distilled water를 넣어준 후 autoclave에서 121℃에서 15분간 돌려준 뒤 petri dish에 부어 1~2일 동안 말린다.&lt;br /&gt;
&lt;br /&gt;
:: 2) 곰팡이가 PDA배지에서 충분히 성장했다면 이를 각각 Petri Dish에 접종한다. 이 때 일정한 양의 곰팡이를 접종하기 위해 그림에 있는 Sterile cork borer를 사용한다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정1.png|200픽셀]]    [[파일:99-1=0_세균실험과정2.png|182픽셀]]&lt;br /&gt;
: [[파일:99-1=0_세균실험과정3.png|200픽셀]]    [[파일:99-1=0_세균실험과정4.png|195픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 3) 3종의 곰팡이를 0.5m, 0.75m, 1m 간격으로 배치시키고, 다른 하나는 대조군으로 UV-A LED에 노출시키지 않은 채로 성장시킨다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정5.png|200픽셀]]    [[파일:99-1=0_세균실험과정6.png|172픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 4) Petri Dish 안에서 곰팡이의 성장률을 원의 반지름으로 근사시킵니다. 또한 생리식염수를 10마이크로미터 떨어트린 후, 소량의 곰팡이를 접종해준 뒤 커버 글라스를 붙이고 광학현미경으로 관찰한다.&lt;br /&gt;
&lt;br /&gt;
:: 5) 곰팡이가 자란 원의 반지름을 측정하고, 이를 저해율로 나타낸다. 저해율의 식은 다음과 같다. (저해율(%)= (대조군의 반지름 - 실험군의 반지름)/대조군의 반지름 * 100)&lt;br /&gt;
: [[파일:99-1=0_곰팡이_측정.jpg|400픽셀]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 3. 실험 결과&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 1) Cladosporium sp(벽지 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Cladosporium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 2) Fusarium sp(토양 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Fusarium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 3) Aspergillus sp(누룩 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Aspergillus_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
아래 표는 현미경을 통해 관측한 곰팡이의 반지름(cm)이고, 이를 위해 저해율식에 대입하여 곰팡이 저해율을 계산하였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:길이값.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:저해율.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:5시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:10시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 4. 결론&lt;br /&gt;
: : 최종 저해율로 나타낸 결과 UV-A LED에 5시간, 10시간에 따른 차이는 거의 없었지만 거리에 따른 저해율의 차이는 눈에 띄었다. 0.5m 거리에선 약 80%의 저해율을 보였고, 0.75m 에서는 Cladosporuim sp와 Fusarium sp은 약 50%의 저해율을 보였지만 Aspergillus sp는 30%의 저해율을 보였습니다. 1m 거리에선 저해율이 많이 떨어졌는데, Cladosporium sp는 39%, Fusarium sp는 25%, Aspergillus sp는 6%의 저해율을 보였습니다. 1m에서의 살균력은 많이 떨어졌고, 이를 보완하기 위해 기존 실험은 25cm UV-A LED 2개를 설치했는데, 제품에는 한 슬라이드 당 3개로 늘려 설치했다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic1.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/8/87/99-1%3D0_%EC%98%81%EC%83%81.avi]]  [[파일:99-1=0_videopic2.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/c/ce/99-1%3D0_%EC%98%81%EC%83%812.avi]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic3.png|300픽셀|link=https://capstone.uos.ac.kr/mie/images/8/82/99-1%3D0_%EC%98%81%EC%83%813.mp4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
'''UV-A LED Mode &amp;amp; Mood Light mode'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:UV-A LED.jpg|250픽셀]][[파일:99-1=0_무드등.jpg|250픽셀|link=https://capstone.uos.ac.kr/mie/images/99-1=0_영상.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_미구현내용.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_평가항목.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
[[파일:99-1=0_평가_결과.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
노*진: 프로젝트의 주제를 결정하기 전까지만 하더라도 라즈베리파이와 각종 모듈에 대한 지식이 없어 막막했습니다. 하지만 잘 모르는 만큼 팀원들과 더욱 조사를 열심히 하고, 구체적으로 설계를 하였습니다. 개념설계 발표 때까지 ‘과연 의도한대로 동작할까?’ 라는 의문점을 가졌지만 중간발표 준비를 하면서 원하는 기능들이 하나 둘씩 구현되고, 기구부의 Mechanism들이 완성되는 것을 보면서 재밌었고, 성취감을 느꼈습니다. 프로젝트를 진행하면서 전공 지식이 아닌 전기 회로도와 관련된 일들을 구글링과 여러 사이트에 물어보면서 해결을 했고, UV-LED의 살균력을 검증하기 위해 생명공학 전공인 지인의 도움을 받으면서 프로젝트의 문제들을 해결했습니다. 이 과정에서 잘 모르는 부분을 어떤 방법으로 해결할 지에 대한 능력을 기를 수 있었습니다. 마지막으로 내장형시스템 과목을 통해 기계정보공학과의 매력을 느낄 수 있었고, 앞으로 프로젝트를 수행할 때 자신감을 가질 수 있을 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
박*훈 : 기계정보공학을 전공한지 4학년 1학기에 처음으로 컴퓨터 관련 프로젝트를 진행했습니다. 그동안 기계 관련 CAD 프로젝트나 CAE 프로젝트는 다수 경험이 있었지만 컴퓨터 분야는 경험이 없었기 때문에 프로젝트 과정 중 가장 처음 과정인 주제 선정에서부터 엄청난 난관에 부딪혔습니다. 하지만 주어진 시간 안에 주어진 모듈로 할 수 있는 주제를 생각해냈고, 프로젝트를 시작했습니다. 프로젝트 진행 과정 중에 한계에 도달하는 일도 있었고 뜻처럼 진행되지 않는 일도 있었지만 팀원들과 함께 돌파구를 찾아가며 차근차근 프로젝트를 완성했습니다. 프로젝트를 진행하면서 기계정보공학의 융합적 전공의 취지에 맞게 기계적인 메커니즘과 임베디드 시스템을 융합한 완성품을 제작하기 위해 노력을 했고, 실제로 저는 기구부의 메커니즘 쪽을 맡아 CAD 프로그램인 CATIA를 통해 기어 및 랙 피니언 세트와 슬라이드 핀 등을 설계하고 3D 프린터를 이용하여 실제 구현을 완료했습니다. 기구부 제작 과정 중 값비싼 3D 프린터 제작의 비용과 3D 프린터 제작품의 과도한 무게 등의 문제가 있었지만, 팀원들과 협의한 끝에 해결 방안을 고안하여 이러한 문제를 해결했습니다. 이번 내장형 프로젝트의 경험을 통해 팀원들과의 소통 능력과 문제 해결 능력을 한층 성장시켰고, 앞으로 있을 프로젝트에 큰 도움이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
주*완: 이번 학기에 내장형시스템및실습을 수강하며 처음으로 주제 선정부터 완제품 제작까지 모든 것을 직접 경험했습니다. 주제 선정부터 시작해 모든 과정이 쉽지 않았고 걸림돌에 걸려서 결국 설계를 바꾸기도 했습니다. 하지만 한계를 극복할 방법을 고민하고 공부하고 질문하고 찾아 헤매면서 많은 것을 배웠습니다. 특히 제가 맡은 부분인 안드로이드 앱을 개발하기 위해 1학년 때 배웠지만 모두 잊어버린 java를 처음부터 다시 공부했고, 안드로이드를 아무것도 모르는 상태에서 인터넷 검색만으로 하나하나 공부하고 직접 코드를 짜서 완성까지 했습니다. 과정 중에 알 수 없는 이유때문에 앱이 원하는 대로 안 되거나 강제 종료 될 때가 매우 매우 많았는데, 스스로 계속 공부하고 원인을 찾고 고쳐 나가서 마침내 앱을 완성할 수 있었고, 스스로 문제를 해결하는 능력을 기를 수 있었습니다. 또한 기구부와 외형을 이루는 부분을 만들 때 자르고 다듬는 일이 많이 필요했는데 서로 필요한 부분이 있으면 도와주면서 협동하여 결과가 잘 나온 것 같아 만족스럽습니다. 프로젝트를 하며 값진 경험을 했고 앞으로 다른 프로젝트를 하게 된다면 이 경험이 소중한 밑바탕이 돼서 도움이 많이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
함*규: 임베디드 시스템을 처음 접해보기에 주제를 정하는 과정이 가장 어려웠던 것 같습니다. 처음에는 주어진 시간 내에 해낼 수 없는 비현실적인 주제에 대해 고민하며 기능적 한계에 부딪혀보기도 하고, 챗바퀴 도는 듯한 생각이 들었지만, 이 과정 속에서 전체적인 임베디드 시스템에 대한 이해와 할 수있는 것과 할 수 없는 것에 대해 구분할 수 있는 능력을 얻을 수 있었습니다. 이후에 직접 정한 주제를 바탕으로 프로젝트를 수행해나가면서, 초기 설계로는 해결할 수 없는 문제들을 팀원들과 함께 협동하여 풀어나가는 과정은 정말 힘들기도 했지만 되돌아 보면 책상에서는 배울 수 없는 많은 경험들을 배웠다고 생각합니다. 4-1학기를 떠올리면 많은 희노애락이 있었던 내장형 밖에 생각나지 않을 것 같은 데, 정말정말 재밌었고(농담이 아닙니다.) 후배들에게 꼭 추천해주고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
유*환: 내장형 시스템 프로젝트의 주제를 정하는 과정이 가장 어려웠습니다. 라즈베리파이에 센서나 모터, 또다른 보드를 연결하여 사용한 경험이 없어 주어진 시간 내에 완성할 수 있는 정도를 알기 어려웠습니다. 그래서 처음에는 학기 내에 완성이 불가능한 주제들을 생각했었습니다. 결국 마지막까지 주제를 정하지 못하다가 막바지에 급하게 살균 무드등을 만들기로 했습니다. 주제가 정해지고 자료조사를 마쳤을 때만 해도 힘든일이 끝나고 모든 일이 수월하게 진행될 것만 같았습니다. 그런데 출입 감지 방법, 음악 주파수 분석 등이 뜻대로 되지 않자 다시 조마조마해졌습니다. 하지만 적절한 대안을 찾고 해결해나가면서 완성되가는 프로젝트를 보며 뿌듯했습니다. 그리고 살균 무드등을 완성하고 영상까지 마무리 지었을 때는 그동안 밤을 새며 고생했던 시간들에 대해 보상받는 느낌이 들었습니다.&lt;br /&gt;
무엇보다 팀원 형들이 프로젝트에서 각자가 맡은 열할을 너무 잘 수행해주어서 감사했습니다. 유일하게 라즈베리파이 경험이 있는 저보다도 잘해주어서 미안하기도 하고 대단하다는 느낌이 들었습니다. 내장형 프로젝트는 과정은 정말 힘들었지만 팀원들과 살균 무드등을 만드는 것이 재밌었고 프로젝트 진행에 대한 경험치가 쌓여 좋은 기회가 되었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5920</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5920"/>
				<updated>2020-06-22T02:24:24Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 느낀점 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
[[파일:개발일정_착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
===영상인식 결과===&lt;br /&gt;
[[파일:영상인식.JPG|600픽셀|가운데|섬네일|그림9. 학습 결과 손실함수 및 IOU]]&lt;br /&gt;
===판별 정확도===&lt;br /&gt;
[[파일:정확도.JPG|600픽셀|가운데|섬네일|그림10. 판별정확도]]&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;br /&gt;
&lt;br /&gt;
윤*상 - RVM을 개발하면서 어느정도 수준의 하드웨어 제작이 필요한 시스템을 개발할 때에는 기본적인 하드웨어 시스템을 구현해놓고 소프트웨어를 개발하는 것이 효율적인 것을 느꼈습니다. 이번 프로젝트에선 대부분의 소프트웨어를 개발한 뒤 하드웨어를 제작하였는데 이 과정에서 예상치 못한 문제들이 발생하였습니다. 모터와 센서의 개수가 많아 전력 공급에서 문제가 생기고 배선과 같은 부분에서 제대로 작동하지 않아 오랜 시간이 걸렸습니다. 하지만 우리 팀원 모두 열심히 프로젝트에 임해주었고 소통도 활발히 되었으며 개개인의 능력 모두 뛰어났기 때문에 빠르게 문제를 해결하여 프로젝트를 마무리 할 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
심*헌 - 이번 프로젝트에서 pyqt5를 통해 라즈베리파이와 같은 제한된 조건에서 유저 인터페이스를 개발하였습니다. 평소 자바스크립트를 통한 웹개발을 하고있어 ui 개발을 맡았지만 디자인과 같은 좀 더 전문성이나 예술에 가까운 영역에는 한계가 있어 팀원들과 소통을 통해 만들었습니다. 새로운 ui 플랫폼 개발의 기회가 된거 같아 도움이 되었고 팀원들과 하나의 큰 결과물을 내어 보람을 느낍니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5919</id>
		<title>99-1=0 - Smart Mood Light with Sterilization</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5919"/>
				<updated>2020-06-22T02:05:20Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 프로젝트 요약 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 Smart Mood Light with Sterilization&lt;br /&gt;
 살균 기능을 탑재한 스마트 무드등&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 노*진 (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 박*훈 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 주*완 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 함*규 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20174300** 유*환 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
원룸은 타 주거환경에 비해 채광과 통풍 능력이 떨어진다. 공기 청정기를 이용할 수 있지만, 채광 문제를 해결하지 못한다. 다행히 넓은 공간을 가진 주거환경에 비해 규모가 작아 상용 자외선을 통한 살균 효과는 비해 뛰어나지만, 살균기의 제어는 사용자가 원룸 내 없을 때 이루어 져야 한다. 위의 문제를 해결하기 위해 스마트폰을 이용하여 원격 제어를 실행한다. 또한 원격 제어를 하면서 사용자가 원룸 내 있을 때 활용할 수 없는 살균기가 공간을 비효율적으로 차지하게 되는 문제를 해결하기 위해 이를 무드등과 결합 한다. 결과적으로 사용자가 가장 많이 시간을 보내는 공간을 살균할 수 있으며 공간의 비효율성과 안전성, 더불어 무드등의 원격제어로 인한 편의성을 제공한다. 사용자는 외부에 있을 때, 스마트폰을 이용하여 살균기 동작을 제어할 수 있다. 사용자가 내부에 있을 때 별도의 조치 없이도 자외선 동작 제어는 비활성화 되며 무드등의 원격 제어가 가능하다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 1. 배경&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_최근4년간국내미세먼지수치.png|500픽셀]]&lt;br /&gt;
[[파일:99-1=0_최근전세계적바이러스주기.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
최근 국내 미세먼지 수치가 꾸준히 증가함에 따라 국민들의 미세먼지 정화에 관한 관심이 높아지고 있다. 또한, 전 세계적인 바이러스 발생 주기가 짧아짐에 따라 사람들의 살균 제품에 대한 관심도가 높아지고 있다. &lt;br /&gt;
&lt;br /&gt;
햇빛은 면역력 향상이나 기분 전환의 효과뿐 아니라 살균 기능을 가지고 있다. 원룸은 구조적으로 대부분 하나의 창문을 가지고 있어 충분한 빛을 받지 못하며 공기의 입/출구가 하나이기에 통풍이 원활하지 않다. 이러한 조건은 미생물이 자라나기 쉽고 미세먼지의 배출이 어렵다. 이들을 모두 정화하는 데 맞는 조건을 가진 것이 자외선이다. 자외선을 통해 공기 중에 살균이 가능하며 미세먼지 중금속을 분해해 공기를 정화할 수 있다. 하지만 자외선 살균기를 집안에 두고 사용하는 것에는 다음과 같은 문제점들이 있다.&lt;br /&gt;
&lt;br /&gt;
- 살균 자외선은 인간에게 유해하므로 사용자에게 직접적인 자외선이 가해지는 것은 위험하다. 사용자가 없는 곳에서 자외선 살균기가 작동해야 하며 사용자가 있을 때 확실한 방법으로 조작 가능성을 배제 시켜야 한다.&lt;br /&gt;
&lt;br /&gt;
- 사용자가 자주 쓰는 공간의 살균일수록 의미가 있으므로 자외선 살균기는 사용자가 자주 쓰는 공간에 배치되어야 한다. 그러나 위의 이유로 사용자가 실제 방에 있을 때 자외선 살균기를 사용하는 것은 위험하므로 사용자가 있을 때 별다른 기능 없이 공간만을 차지하게 된다.&lt;br /&gt;
&lt;br /&gt;
원룸 내 거주자가 가장 오래 머무르는 공간으로 예상되는 곳은 침대류 이다. 침대류 근처에 있는 물체에 살균기를 결합하여 제품을 만들 수 있다면 효율적으로 문제를 해결할 수 있다. 우리는 이 조건을 만족하는 물체가 무드등이라는 점에서 문제 해결을 시작했다. 또한, 첫 번째 문제 해결을 위해 임베디드 시스템을 설계하여 사용자가 없는 공간에서의 작동을 가능하게 하는 것뿐 아니라 무드등에 다양한 기능을 추가하여 편리성과 기능의 다양함을 추가하려고 한다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 기대효과&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_UV자외선을이용한살균 미세먼지정화과정.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
사용자는 집 밖에서 애플리케이션을 통해 자외선 모듈을 가동함으로써 안전한 방법으로 공간을 살균 및 미세먼지 분해 기능을 작동시킬 수 있다. &lt;br /&gt;
&lt;br /&gt;
또한, 사용자가 개발 제품과 같은 공간에 있다고 판단 시 애플리케이션 자동 스위칭 기능을 통해 자외선 컨트롤 기능이 아닌 무드등 컨트롤 기능을 제어하게 된다. 따로 조작 없이 방 안/밖이라는 공간의 변화에 따라 스위칭 되기에 조작에 신경 쓸 필요가 없다. 무드등 기능으로썬 조도 조정, 빛의 색상 선택, 주변 음악에 따른 색상 변화 기능 등이 있으며 사용자가 선택하는 옵션에 따라 기능을 수행할 수 있어 편리함과 다양성을 제공한다. 사용자의 유무에 상관없이 개발제품은 유용한 도구로써 사용되기에 공간 활용성 또한 확보할 수 있을 것으로 생각된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:99-1=0_목적계통도.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 안전성&lt;br /&gt;
자외선 활성/비활성화 기능은 가스 자동밸브 잠금과 비슷한 원리로 앱 구동 시 스마트폰 GPS 기능을 통해 사용자의 위치정보를 받아 주거 위치와 비교하여 앱 인터페이스 비활성화 여부를 결정한다. GPS 오작동으로 인한 비활성화가 적절히 이루어지지 않았을 때를 대비해 앱 내 수동 비활성화 버튼을 추가한다. 앱 인터페이스가 비활성화 되어 있어 사용자의 실수로 인한 신호 전달 가능성을 사전에 차단할 수 있다. 또한 회로의 긴급 정지 스위치를 통해 하드웨어 오동작 시 회로 자체의 전류를 제어할 수 있다. 또한 인체 및 환경에 유해한 정도를 판단하여 인체에 비교적 덜 유해한 UV-A의 자외선 영역을 사용했고, 수은으로 환경 파괴 문제를 일으키는 UV-램프를 대신하여 UV-LED를 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 실용성&lt;br /&gt;
먼저 자외선 영역의 성능을 비교하면 살균력 기준으로는 분 단위 살균력을 가진 UV-C가 시 단위 살균력을 가진 UV-A보다 월등히 강력하다. 하지만 살균 범위를 보면 UV-A는 공기 중 3-5m의 반경을 가진 것에 비해 UV-C는 공기 중 투과력이 매우 낮아 넓은 범위를 살균할 때에는 UV-A가 더 적합하다. 따라서 거주자 없이 비어있는 시간이 긴 원룸에 맞추어 UV-405nm 자외선을 이용했다. 이 때, 자외선 LED의 살균 방향을 15도 정도 아래쪽으로 내려주어 침구류 및 사용자가 자주 사용하는 물건도 더불어 살균하게 된다. 모터의 경우, 자외선 살균기의 상하 운동이 완료된 후에 해당 위치에서 고정이 되어야 하는데, 서보 모터는 고정 기능이 없고, 스탭 모터는 고정 기능을 사용할 때 소모 전력이 많다. 따라서 적은 소모 전력으로 고정 기능을 수행할 수 있는 웜 기어 모터를 사용했다.&lt;br /&gt;
공간 효율성을 기준으로는 제품 구성 시 얼마나 효율적인 공간 활용 여부를 비교했다. 따라서 각 제품의 특성에 맞추어 무드등 조명에는 필요 전선량이 많은 LED 전구 대신 네오 픽셀을 사용했고, 자외선 조명에는 1cm 이하의 조명 크기를 가지는 UV LED를 사용했다. 또한 사용자 위치를 판별하기 위해서 출입문 주변의 많은 공간을 차지하는 적외선 센서 활용 출/입 카운터 대신 외부 공간을 이용하지 않는 안드로이드 모바일 앱 내의 GPS 기능을 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 편의성&lt;br /&gt;
사용자 입장에서 제품을 이용할 때의 반응성을 기준으로 비교했다. 호스팅 제공 S/W에서는 외부의 데이터베이스를 이용하는 파이썬 애니웨어 대신 자체적으로 실시간 데이터베이스 기능을 제공하는 파이어 베이스를 사용했다. 또한 사용자 위치 판별법에서는 사용자의 출/입 여부를 얼마나 빠르게 판단할 수 있는지에 대한 비교를 했는데, 이는 적외선 센서 활용 카운터가 더 빠른 응답성을 보였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 경제성&lt;br /&gt;
제품에 대한 경제적인 측면은 제품을 구성하는데 사용하는 비용과 유지비로 생각할 수 있다. 먼저 제품 구성비용은 프로젝트의 해당 제품을 구매하는 비용이다. 따라서 보다 더 값이 싼 네오픽셀, UV-A, UV-램프가 더 적합했다. 또한 호스팅 기능 S/W에서는 무료로 이용할 수 있는 범위가 더 넓은 파이어 베이스가 적합했다. 또한 유지비는 제품의 수명이 얼마나 긴지, 배터리 소모량이 얼마나 적은지, 부품 교체 비용이 얼마나 큰지에 대해 비교했다. 따라서 부품 교체 비용이 더 저렴한 네오픽셀, 수명이 더 긴 UV-LED가 더 적합했다. 또한 항상 동작시키는 적외선 센서보다 필요시에만 동작시킬 수 있는 GPS 기능이 배터리 소모량이 거의 없어 더 적합했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 개발 용이성&lt;br /&gt;
개발제품 특성상 데이터베이스 및 호스팅 서비스가 필요하다. 개발자 독자적으로 데이터 베이스와 호스팅 서비스를 개발할 필요 없이 이러한 서비스를 제공하는 서버는 파이어 베이스이다. 개발 난이도는 물론 개발자의 서버 개발 시간을 최소화 하여 다른 분야의 성능 개발을 최대화 시킬 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_개발용이성.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Application과 라즈베리 파이의 통신 중간 다리로 파이어 베이스 서버를 이용한다. 물론 무드등 제어를 위한 라즈베리 파이 서버를 구축하여 통시에 이용하면 원룸 내 무드등 제어 동작 속도를 향상시킬 수도 있다. 그러나 하나의 서버를 이용하면 통신 오류나, 오류가 발생했을 때 오류를 제어하기 쉽다. 또한  추후에 기능 추가가 용이하다. 파이어 베이스는 서버 특성상 서버 사용자가 많아지게 되면 부하가 발생할 수 있다. 그러나 개발 상품의 경우 서버는 한명의 사용자만이 이용한다는 점을 고려하여 이러한 단점을 고려하지 않을 수 있다. 따라서 해당 프로젝트에 가장 적합하고, 통신 복잡도가 적은 파이어 베이스와 안드로이드 스튜디오를 사용했다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_동작시나리오.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_역할분담.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_세부개발일정표.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_시스템구성도.png|800픽셀]]&lt;br /&gt;
&lt;br /&gt;
===기구부 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_무드등모드3D설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_자외선살균모드3D.png|166픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
먼저 기구부에서는 최종적으로 제품의 형상과 베벨 기어로 사각 LED 기둥을 올리는 Mechanism과 자외선 LED가 부착되어 있는 Slide Pin Mechanism을 구현하였다. &lt;br /&gt;
제품의 형상의 경우에는 제품의 무드등 기능과 자외선 살균 기능을 정상적으로 구현할 수 있게 필요한 요소들을 적절히 배치할 수 있는 공간을 만들었고, 사용자의 안전성을 위해 Mood Light를 위한 NeoPixel과 UV-A LED를 구분하여 인체에 해로운 자외선 LED는 기능을 사용하지 않을 때 내부 공간에 위치하도록 구현하였다. &lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism은 첫 번째 자외선 LED가 설치된 사각기둥을 올리는 Motor Mechanism과 두 번째 자외선 LED가 적절한 각도로 펼쳐지는 슬라이드 핀 Mechanism이다. 첫 번째 Motor Mechanism은 모터와 베벨 기어 및 랙 피니언 시스템을 통해 모터의 회전 운동을 직선 운동으로 전환하여 구현하였다. 또한 두 번째 Slide Pin Mechanism은 슬라이드 핀을 이용하여 중력에 의해 자동으로 사각기둥 상승 시 펼쳐지고, 하강 시 접히도록 구현한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''기구부 설계'''&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 1. Motor Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_모터설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_모터구현.png|191픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism 중 자외선 LED가 설치된 사각 기둥을 올릴 베벨 기어와 랙 피니언을 사용한 Motor Mechanism 시스템 구현을 완료했다. CATIA CAD 프로그램을 이용하여 베벨 기어 및 랙 피니언 설계를 완료했고, 3D 프린터를 이용하여 규격에 맞추어 실제 제품을 만들었다. 기존에는 랙 피니언 한 쌍만을 직접 모터에 연결하여 사각기둥의 상하 운동을 구현하려고 했지만, 대칭성이 잘 맞지 않아 한쪽으로 쏠리는 현상이 발생하여, 추가적으로 베벨 기어 등을 사용하여 대칭성을 고려하여 양쪽에서 사각 기둥의 상하 운동을 구현했다.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. Slide Pin Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_슬라이드핀설계.png|200픽셀]]&lt;br /&gt;
[[파일:Slide.jpg|145픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Motor Mechanism을 통해 자외선 LED가 설치된 사각기둥이 상하운동을 하면, LED가 적절한 각도를 이루어 펼쳐지도록 구현하였다. 따라서 Slide Pin Mechanism을 이용하여 사각 기둥이 상승할 때 중력에 의해 자동으로 LED가 펼쳐지고, 하강할 때 자동으로 접힌다. CATIA CAD 프로그램을 이용하여 3D 설계를 하고, 3D 프린팅을 통해 실제로 외관을 만들었다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_LED각도설계.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LED가 펼쳐지는 적절한 각도로는 30도를 정했다. 각도를 정하는 과정에서 두 가지의 고려 사항을 정했는데, 첫 번째는 자외선 LED 끝이 직각으로 도달하는 지점의 거리가 원룸이라는 공간을 고려하여 1m 이상이 되고, 두 번째는 제품과 근접한 곳의 LED 사각지대가 제품의 끝으로부터 10cm 내가 되는 기준을 두었다. 따라서 CATIA CAD 프로그램의 2D 설계를 통해 적절한 각도 30도를 설정했다. (실제로 빛은 직각으로만 전달되는 것이 아니라 사방으로 퍼지기 때문에 실제 최대 도달 거리는 기준값인 1m 보다 더 커진다.)&lt;br /&gt;
&lt;br /&gt;
'''기구부 구현'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_사각기둥.jpg|250픽셀]]&lt;br /&gt;
[[파일:99-1=0_Mood.jpg|250픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_완성본.jpg|250픽셀]]&lt;br /&gt;
[[파일:위에서_본_모습.PNG|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
UV-A LED를 장착할 사각 기둥을 3D Printing을 통해 만들었다. 또한 Mood Light를 위한 NeoPixel은 사각 기둥 밖에 다른 기구에 물결 모양으로 48개를 달았다. 최종적으로 아크릴판에 불투명 시트를 붙여 등을 만들었다.&lt;br /&gt;
&lt;br /&gt;
'''최종 작품'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:파랑이.jpg|250픽셀]][[파일:UV-A_LED.jpg|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
최종으로 Mood Light에서 단색 모드로 파랑색 조명을 킬 때와, UV LED Mode에서 On버튼을 눌렀을 때 결과이다.&lt;br /&gt;
&lt;br /&gt;
===제어부 및 회로 구현===&lt;br /&gt;
 UV-A LED, Motor 회로도&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_제어회로도1.PNG|600픽셀]][[파일:99-1=0_내부회로도2.jpg|600픽셀]]&lt;br /&gt;
 &lt;br /&gt;
SMPS로부터 220V 정격 전압을 12V 정격 전압으로 나누어 아두이노와 Relay Switch를 통해 UV-LED의 전원 On/Off 구현이 가능하다. 모터와 UV LED는 아두이노로 제어하기 때문에 라즈베리파이에서 데이터베이스의 값을 실시간으로 확인하다가 UV LED 동작이 지시되면 아두이노에 신호를 보내준다. 데이터베이스의 값은 안드로이드 App을 통해 갱신되므로 결국은 안드로이드 App을 통해 UV-LED의 On/Off 제어가 가능하다. 라즈베리파이와 아누이노간의 데이터 통신은 USB 케이블을 이용한 시리얼 통신을 이용한다.(라즈베리파이와 아두이노 간의 거리가 가깝고 전송하는 데이터가 단순하기 때문에 간단한 통신 방법 선택) 구현 언어는 파이썬이며 사용한 파이썬 모듈은 serial이다. 이와 관련하여 사용한 함수들은 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
UV-LED 동작을 설정하는 값이 “PowerON”일 경우 USB 포트로 연결된 아두이노로 UTF-8로 인코딩된 문자 u를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 u를 받아 UV-LED On 동작을 수행한다. 만약 UV-LED 동작 설정값에 “PowerON” 외의 값이 저장되었을 경우 (“PowerOFF“의 경우가 일반적이지만 예외 처리를 위해 else로 조건 판단) 아두이노로 UTF-8로 인코딩된 문자 d를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 d를 받아 UV-LED Off 동작을 수행한다.&lt;br /&gt;
&lt;br /&gt;
 NeoPixel 회로도&lt;br /&gt;
[[파일:99-1=0_네오픽셀_회로도.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
Mood Light구현은 NeoPixel과 Raspberry PI를 통해 구현했다. 사용자가 Application을 통해 색을 바꾸면 Database의 값이 아래 표에 Data로 바뀌면서 Mood Light 색이 변한다. NeoPixel의 Digital input을 통해 값이 들어가고, 그에 맞는 색을 자유롭게 바꿀 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:색깔.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:App_insidemode_screenshot.jpg|200픽셀]] [[파일:App_outsidemode_screenshot.jpg|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
 앱&lt;br /&gt;
&lt;br /&gt;
앱을 처음 설치하고 실행하면 앱은 팝업 윈도우로 모바일 디바이스의 위치 정보 권한을 요청하며 사용자는 이를 최초 1회만 수락하면 된다. 앱은 실행되면 즉시 모바일 디바이스의 현재 위치 정보를 받고 데이터베이스에 저장된 집의 위치 정보와의 거리를 계산한다. 이 거리가 일정 거리 이상이면 앱은 실외 모드로 자동 설정되고, 일정 거리 미만이면 실내 모드로 자동 설정된다. 실내 모드와 실외 모드의 전환은 기본적으로 비활성화되어 있으나 사용자가 우측 하단의 스위치를 켜면 활성화되어 강제 모드 전환이 가능하게 된다. 그래서 앱을 실행했을 때 자동 모드 설정이 잘못되면 사용자가 직접 모드를 전환해서 원하는 기능을 사용할 수 있다.&lt;br /&gt;
&lt;br /&gt;
앱은 크게 실내 모드와 실외 모드로 구분된다. 각 모드는 Fragment로 정의됐고 MainActivity의 bottomnavigationview를 통해 replace해서 전환할 수 있다. bottomnavigationview는 각 모드에 배치된 스위치가 꺼져 있으면 disabled되고 스위치가 켜지면 enabled된다. 사용자는 실내 모드에서 무드등을 제어하고 실외 모드에서 자외선 살균기를 제어한다.&lt;br /&gt;
&lt;br /&gt;
 실내 모드&lt;br /&gt;
실내 모드에선 무드등의 색상 선택, 밝기 조절, 전원 작동, 스페셜 모드 기능을 이용할 수 있다.&lt;br /&gt;
* colorpicker&lt;br /&gt;
: colorpicker의 색상은 초기에 12시의 연두색으로 설정되어 있고 사용자가 버튼을 드래그해서 원하는 색상을 선택할 수 있다. colorpicker의 중앙에 있는 원은 왼쪽 반원에 이전 색상을 표시하고 오른쪽 반원에 현재 색상을 표시하여 전후 색상을 비교하기 좋게 만들어준다. 사용자가 무드등의 전원을 키면 이전 색상이 표시된 반원이 현재 색상으로 교체된다. 색상은 여섯 자리의 16진수 #nnnnnn으로 표현되며 정수형으로 저장된다. 사용자가 colorpicker로 다양한 색상을 연속적인 범위에서 선택할 수 있기 때문에 선택의 폭이 넓고 세밀한 색상 선택이 가능하다. 무드등의 전원이 켜져 있으면 색상의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 밝기&lt;br /&gt;
: 무드등의 밝기 조절은 실내 모드 중앙의 seekbar를 통해 할 수 있다. 밝기 값은 최소 0에서 최대 255까지 선택 가능하다. colorpicker와 같이 무드등의 전원이 켜져 있으면 밝기의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 전원&lt;br /&gt;
: Power ON 버튼을 누르면 무드등의 전원이 켜진다. 전원 버튼 하단의 얇은 띠가 전원이 꺼져있을 때 회색, 전원이 켜져있을 때 녹색을 띠면서 사용자가 무드등의 전원이 켜져있는지를 직관적으로 알 수 있게 해준다. 전원 작동 데이터가 데이터베이스에 성공적으로 전송되면 앱 화면 하단에 Toast 텍스트를 표시하여 사용자에게 전원의 작동을 알려준다. 전원 버튼의 Power ON/Power OFF 텍스트는 colorpicker에서 선택된 색상과 실시간으로 동일하게 적용되어 사용자에게 무드등이 어떤 색으로 켜질지 더욱 직관적으로 와닿게 만들어준다.&lt;br /&gt;
* 스페셜 모드&lt;br /&gt;
: 앱을 실행하면 무드등의 모드는 기본 모드로 설정되어 있는 상태이다. 스페셜 모드 버튼을 누르면 순차적으로 웨이브 모드 1, 웨이브 모드 2, 레인보우 모드 1, 레인보우 모드 2가 설정되며 그후 다시 기본 모드로 돌아온다. 현재 설정된 모드는 스페셜 모드 버튼의 텍스트로 나타나서 사용자가 이를 알 수 있다. 또한 버튼을 누를수록 단계적으로 버튼의 배경 색이 진해지며 버튼의 깊이감을 더해준다. 스페셜 모드 버튼의 아래엔 모드의 전체 개수와 현재 모드가 몇 번째인지를 시각적으로 쉽게 알게 해주는 공백 칸들이 있다. 이 칸들은 기본 모드에서 모두 비어있게 되고 스페셜 버튼이 눌릴 때마다 한 개씩 채워지며 모두 채워진 후엔 다시 모두 빈 칸으로 돌아온다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스페셜 모드는 무드등이 켜져 있을 때에만 작동이 유효하다. 무드등의 전원이 꺼져 있을 때 스페셜 모드를 작동하면 무드등에서 아무 일도 일어나지 않는다. 이런 상황을 예방하기 위해 앱에서 무드등의 전원이 꺼진 상태에서 스페셜 모드만 작동하는 일이 일어나지 않게 한다. 무드등의 전원이 꺼진 상태에서 스페셜 모드 버튼을 누르면, 앱은 무드등의 전원도 키고 스페셜 모드도 작동한다. 또한 스페셜 모드가 설정된 상태에서 무드등의 전원을 끄면 앱은 스페셜 모드를 기본 모드로 설정하고 무드등의 전원을 끈다. 만약 사용자가 레인보우 모드를 바로 사용하고 싶다면 전원 버튼을 누를 필요 없이 바로 스페셜 모드 버튼을 3회 누르면 된다. 전원 버튼을 누르는 단계를 거치지 않아도 되기 때문에 빠르고 간편하게 즉시 스페셜 모드를 이용할 수 있다. 결과적으로 무드등의 전원이 꺼진 상태에서 무드등의 모드는 항상 기본 모드로 설정되어 있고, 스페셜 모드가 켜진 상태면 무드등의 전원도 항상 켜진 상태가 된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 실외 모드&lt;br /&gt;
실외 모드에선 위치 정보 설정, 자외선 살균기의 전원 작동, 타이머 기능을 이용할 수 있다.&lt;br /&gt;
* 위치 정보 설정&lt;br /&gt;
: HOME LOCATION 버튼을 누르면 모바일 디바이스의 현재 위치 정보를 데이터베이스에 저장하여 집의 위치로 설정한다. 설정한 집의 위치 정보는 앱이 실행됐을 때 집과 사용자 사이의 거리를 계산하는 데 이용된다. 데이터베이스에 저장된 위치 정보는 사용자가 새롭게 설정하지 않는 한 항상 일정하게 유지된다. 그래서 앱을 재설치했을 때 집의 위치 정보 설정을 다시 할 필요가 없다. 상수의 변수명을 대문자 알파벳으로 선언하는 프로그래밍 관습을 응용해서 위치 정보 설정 버튼의 텍스트도 대문자 알파벳으로 하였다. &lt;br /&gt;
* 자외선 살균기&lt;br /&gt;
: 실외 모드에서 Power ON 버튼을 누르면 자외선 살균기의 전원이 켜진다. 전원 버튼 위의 spinner에서 타이머가 작동할 시간을 선택할 수 있으며 초기 타이머는 1시간으로 설정된 상태이다. 타이머는 시간 단위로 선택할 수 있고 최대 12시간까지 선택 가능하다. 자외선 살균기의 전원을 켜면 전원 버튼 아래에 textview가 표시되어 타이머의 남은 시간을 알려준다. 설정한 타이머가 종료되면 앱이 자동적으로 자외선 살균기의 전원을 끈다.&lt;br /&gt;
* 시간 Spinner&lt;br /&gt;
: APP의 실외모드에선 자외선 살균기를 제어한다. 사용자는 작동 시간을 spinner로 선택하여 자외선 살균기 전원 버튼을 눌러 작동시킨다. 그러면 APP은 스피너에 선택된 시간과 전원 데이터를 데이터베이스에 전송한다. APP에서 사용자가 직접 자외선 살균기의 전원을 끌 수 있지만 설정한 시간이 경과되면 APP은 전원을 끄는 데이터를 데이터베이스에 전송한다. 만약 사용자가 작동 시간을 연장시키고 싶다면 자외선 살균기를 끄고 다시 작동하면 된다. 그러면 남은 시간이 다시 세팅되어 작동 시간이 증가하게 된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Firebase&lt;br /&gt;
[[파일:99-1=0_파이어베이스스크린샷.jpg|300픽셀]]&lt;br /&gt;
[[파일:99-1=0_파이어베이스연동.jpg|500픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Firebase는 Google 사의 모바일 및 웹 개발 플랫폼이다. Firebase는 많은 유용한 기능을 지원하지만 여기선 실시간 데이터베이스를 사용한다. Firebase의 장점은 안드로이드 및 iOS 앱과 연동이 용이하다는 것이다. 또한, Firebase는 시간과 장소에 구애받지 않고, 데이터가 안전하게 보존되며, 데이터를 읽고 쓰는 것이 간편하다. 그래서 Firebase는 사용자의 집의 위치 정보를 저장하기에 적합하다. 이 데이터는 제 3자가 접근할 수 없으며 사용자가 새롭게 설정하지 않는 한 영구히 보존된다. 앱은 Firebase의 데이터베이스에 집의 위치 정보를 저장하기도 하지만 읽어오기도 한다. 앱은 실행되면 데이터베이스의 집의 위치 정보를 읽어와서 현재 사용자의 위치 사이의 거리를 계산하고 실내/실외를 판별한다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 작동할 때에도 Firebase를 이용한다. 사용자가 앱으로 작동한 무드등의 색상, 밝기, 전원, 모드 데이터와 자외선 살균기의 전원, 타이머 데이터는 Firebase의 데이터베이스에 저장된다. 앱은 무드등의 전원이 켜진 상태에서 색상이나 밝기가 변경되면 변경 사항을 즉시 데이터베이스에 반영한다. 자외선 살균기의 타이머는 데이터베이스에 저장되지만 남은 타이머 시간까지 일일이 저장되진 않는다. 앱에서 설정한 시간이 지나면 자외선 살균기의 전원을 끄는 데이터를 저장할 뿐이다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 직접 구동시키는 것은 라즈베리파이와 아두이노이다. 라즈베리파이는 데이터베이스에 저장된 무드등의 색상, 밝기, 전원, 모드 데이터를 읽어와 라즈베리파이에 직접 연결된 네오픽셀 LED의 작동을 지시한다. 또한 살균기의 전원 데이터를 읽어와 아두이노에 살균기를 들어올리는 모터의 동작과 살균기 LED의 동작을 지시한다. 아두이노에서는 라즈베리파이의 지시를 대기하다가 살균기의 ON/OFF 동작 신호를 받아 살균기를 켜고 끈다. 이 때 살균기가 부착된 구동부의 높이는 아두이노에 연결된 초음파센서로 거리를 측정하여 조절한다.&lt;br /&gt;
&lt;br /&gt;
===UV-LED 곰팡이 저해 실험===&lt;br /&gt;
&lt;br /&gt;
본 실험은 UV-A LED의 곰팡이 생육 저해 능력을 검증하기 위한 실험이다. 구체적인 저해율의 수치를 나타내기 위해 1차 살균 실험에 이어 2차 곰팡이로 실험을 하여 저해율을 나타낼 것이다.&lt;br /&gt;
&lt;br /&gt;
: 1. 실험 준비&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비1.png|200픽셀|ㅇ|왼쪽|Petri Dish, Sterile cork borer, 곰팡이]]    [[파일:99-1=0_세균실험준비2.png|173픽셀]]&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비3.png|200픽셀]]    [[파일:99-1=0_세균실험준비4.png|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 실험 과정&lt;br /&gt;
&lt;br /&gt;
:: 1) 먼저 3종의 곰팡이(Cladosporium sp, Fusarium sp, Aspergillus sp)를 키울 배지를 만들기 위해 Difo회사 제품인 PDA 12g과 Agar 10g을 비커에 넣은 후 500ml distilled water를 넣어준 후 autoclave에서 121℃에서 15분간 돌려준 뒤 petri dish에 부어 1~2일 동안 말린다.&lt;br /&gt;
&lt;br /&gt;
:: 2) 곰팡이가 PDA배지에서 충분히 성장했다면 이를 각각 Petri Dish에 접종한다. 이 때 일정한 양의 곰팡이를 접종하기 위해 그림에 있는 Sterile cork borer를 사용한다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정1.png|200픽셀]]    [[파일:99-1=0_세균실험과정2.png|182픽셀]]&lt;br /&gt;
: [[파일:99-1=0_세균실험과정3.png|200픽셀]]    [[파일:99-1=0_세균실험과정4.png|195픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 3) 3종의 곰팡이를 0.5m, 0.75m, 1m 간격으로 배치시키고, 다른 하나는 대조군으로 UV-A LED에 노출시키지 않은 채로 성장시킨다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정5.png|200픽셀]]    [[파일:99-1=0_세균실험과정6.png|172픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 4) Petri Dish 안에서 곰팡이의 성장률을 원의 반지름으로 근사시킵니다. 또한 생리식염수를 10마이크로미터 떨어트린 후, 소량의 곰팡이를 접종해준 뒤 커버 글라스를 붙이고 광학현미경으로 관찰한다.&lt;br /&gt;
&lt;br /&gt;
:: 5) 곰팡이가 자란 원의 반지름을 측정하고, 이를 저해율로 나타낸다. 저해율의 식은 다음과 같다. (저해율(%)= (대조군의 반지름 - 실험군의 반지름)/대조군의 반지름 * 100)&lt;br /&gt;
: [[파일:99-1=0_곰팡이_측정.jpg|400픽셀]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 3. 실험 결과&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 1) Cladosporium sp(벽지 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Cladosporium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 2) Fusarium sp(토양 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Fusarium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 3) Aspergillus sp(누룩 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Aspergillus_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
아래 표는 현미경을 통해 관측한 곰팡이의 반지름(cm)이고, 이를 위해 저해율식에 대입하여 곰팡이 저해율을 계산하였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:길이값.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:저해율.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:5시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:10시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 4. 결론&lt;br /&gt;
: : 최종 저해율로 나타낸 결과 UV-A LED에 5시간, 10시간에 따른 차이는 거의 없었지만 거리에 따른 저해율의 차이는 눈에 띄었다. 0.5m 거리에선 약 80%의 저해율을 보였고, 0.75m 에서는 Cladosporuim sp와 Fusarium sp은 약 50%의 저해율을 보였지만 Aspergillus sp는 30%의 저해율을 보였습니다. 1m 거리에선 저해율이 많이 떨어졌는데, Cladosporium sp는 39%, Fusarium sp는 25%, Aspergillus sp는 6%의 저해율을 보였습니다. 1m에서의 살균력은 많이 떨어졌고, 이를 보완하기 위해 기존 실험은 25cm UV-A LED 2개를 설치했는데, 제품에는 한 슬라이드 당 3개로 늘려 설치했다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic1.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/8/87/99-1%3D0_%EC%98%81%EC%83%81.avi]]  [[파일:99-1=0_videopic2.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/c/ce/99-1%3D0_%EC%98%81%EC%83%812.avi]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic3.png|300픽셀|link=https://capstone.uos.ac.kr/mie/images/8/82/99-1%3D0_%EC%98%81%EC%83%813.mp4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
'''UV-A LED Mode &amp;amp; Mood Light mode'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:UV-A LED.jpg|250픽셀]][[파일:99-1=0_무드등.jpg|250픽셀|link=https://capstone.uos.ac.kr/mie/images/99-1=0_영상.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_미구현내용.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_평가항목.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
[[파일:99-1=0_평가_결과.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
노*진: 프로젝트의 주제를 결정하기 전까지만 하더라도 라즈베리파이와 각종 모듈에 대한 지식이 없어 막막했습니다. 하지만 잘 모르는 만큼 팀원들과 더욱 조사를 열심히 하고, 구체적으로 설계를 하였습니다. 개념설계 발표 때까지 ‘과연 의도한대로 동작할까?’ 라는 의문점을 가졌지만 중간발표 준비를 하면서 원하는 기능들이 하나 둘씩 구현되고, 기구부의 Mechanism들이 완성되는 것을 보면서 재밌었고, 성취감을 느꼈습니다. 프로젝트를 진행하면서 전공 지식이 아닌 전기 회로도와 관련된 일들을 구글링과 여러 사이트에 물어보면서 해결을 했고, UV-LED의 살균력을 검증하기 위해 생명공학 전공인 지인의 도움을 받으면서 프로젝트의 문제들을 해결했습니다. 이 과정에서 잘 모르는 부분을 어떤 방법으로 해결할 지에 대한 능력을 기를 수 있었습니다. 마지막으로 내장형시스템 과목을 통해 기계정보공학과의 매력을 느낄 수 있었고, 앞으로 프로젝트를 수행할 때 자신감을 가질 수 있을 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
박*훈 : 기계정보공학을 전공한지 4학년 1학기에 처음으로 컴퓨터 관련 프로젝트를 진행했습니다. 그동안 기계 관련 CAD 프로젝트나 CAE 프로젝트는 다수 경험이 있었지만 컴퓨터 분야는 경험이 없었기 때문에 프로젝트 과정 중 가장 처음 과정인 주제 선정에서부터 엄청난 난관에 부딪혔습니다. 하지만 주어진 시간 안에 주어진 모듈로 할 수 있는 주제를 생각해냈고, 프로젝트를 시작했습니다. 프로젝트 진행 과정 중에 한계에 도달하는 일도 있었고 뜻처럼 진행되지 않는 일도 있었지만 팀원들과 함께 돌파구를 찾아가며 차근차근 프로젝트를 완성했습니다. 프로젝트를 진행하면서 기계정보공학의 융합적 전공의 취지에 맞게 기계적인 메커니즘과 임베디드 시스템을 융합한 완성품을 제작하기 위해 노력을 했고, 실제로 저는 기구부의 메커니즘 쪽을 맡아 CAD 프로그램인 CATIA를 통해 기어 및 랙 피니언 세트와 슬라이드 핀 등을 설계하고 3D 프린터를 이용하여 실제 구현을 완료했습니다. 기구부 제작 과정 중 값비싼 3D 프린터 제작의 비용과 3D 프린터 제작품의 과도한 무게 등의 문제가 있었지만, 팀원들과 협의한 끝에 해결 방안을 고안하여 이러한 문제를 해결했습니다. 이번 내장형 프로젝트의 경험을 통해 팀원들과의 소통 능력과 문제 해결 능력을 한층 성장시켰고, 앞으로 있을 프로젝트에 큰 도움이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
주*완: 이번 학기에 내장형시스템및실습을 수강하며 처음으로 주제 선정부터 완제품 제작까지 모든 것을 직접 경험했습니다. 주제 선정부터 시작해 모든 과정이 쉽지 않았고 걸림돌에 걸려서 결국 설계를 바꾸기도 했습니다. 하지만 한계를 극복할 방법을 고민하고 공부하고 질문하고 찾아 헤매면서 많은 것을 배웠습니다. 특히 제가 맡은 부분인 안드로이드 앱을 개발하기 위해 1학년 때 배웠지만 모두 잊어버린 java를 처음부터 다시 공부했고, 안드로이드를 아무것도 모르는 상태에서 인터넷 검색만으로 하나하나 공부하고 직접 코드를 짜서 완성까지 했습니다. 과정 중에 알 수 없는 이유때문에 앱이 원하는 대로 안 되거나 강제 종료 될 때가 매우 매우 많았는데, 스스로 계속 공부하고 원인을 찾고 고쳐 나가서 마침내 앱을 완성할 수 있었고, 스스로 문제를 해결하는 능력을 기를 수 있었습니다. 또한 기구부와 외형을 이루는 부분을 만들 때 자르고 다듬는 일이 많이 필요했는데 서로 필요한 부분이 있으면 도와주면서 협동하여 결과가 잘 나온 것 같아 만족스럽습니다. 프로젝트를 하며 값진 경험을 했고 앞으로 다른 프로젝트를 하게 된다면 이 경험이 소중한 밑바탕이 돼서 도움이 많이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
함*규: 임베디드 시스템을 처음 접해보기에 주제를 정하는 과정이 가장 어려웠던 것 같습니다. 처음에는 주어진 시간 내에 해낼 수 없는 비현실적인 주제에 대해 고민하며 기능적 한계에 부딪혀보기도 하고, 챗바퀴 도는 듯한 생각이 들었지만, 이 과정 속에서 전체적인 임베디드 시스템에 대한 이해와 할 수있는 것과 할 수 없는 것에 대해 구분할 수 있는 능력을 얻을 수 있었습니다. 이후에 직접 정한 주제를 바탕으로 프로젝트를 수행해나가면서, 초기 설계로는 해결할 수 없는 문제들을 팀원들과 함께 협동하여 풀어나가는 과정은 정말 힘들기도 했지만 되돌아 보면 책상에서는 배울 수 없는 많은 경험들을 배웠다고 생각합니다. 4-1학기를 떠올리면 많은 희노애락이 있었던 내장형 밖에 생각나지 않을 것 같은 데, 정말정말 재밌었고(농담이 아닙니다.) 후배들에게 꼭 추천해주고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
유*환: 내장형 시스템 프로젝트의 주제를 정하는 과정이 가장 어려웠습니다. 라즈베리파이에 센서나 모터, 또다른 보드를 연결하여 사용한 경험이 없어 주어진 시간 내에 완성할 수 있는 정도를 알기 어려웠습니다. 그래서 처음에는 학기 내에 완성이 불가능한 주제들을 생각했었습니다. 결국 마지막까지 주제를 정하지 못하다가 막바지에 급하게 살균 무드등을 만들기로 했습니다. 주제가 정해지고 자료조사를 마쳤을 때만 해도 힘든일이 끝나고 모든 일이 수월하게 진행될 것만 같았습니다. 그런데 출입 감지 방법, 음악 주파수 분석 등이 뜻대로 되지 않자 다시 조마조마해졌습니다. 하지만 적절한 대안을 찾고 해결해나가면서 완성되가는 프로젝트를 보며 뿌듯했습니다. 그리고 살균 무드등을 완성하고 영상까지 마무리 지었을 때는 그동안 밤을 새며 고생했던 시간들에 대해 보상받는 느낌이 들었습니다.&lt;br /&gt;
무엇보다 팀원 형들이 프로젝트에서 각자가 맡은 열할을 너무 잘 수행해주어서 감사했습니다. 유일하게 라즈베리파이 경험이 있는 저보다도 잘해주어서 미안하기도 하고 대단하다는 느낌이 들었습니다. 내장형 프로젝트는 과정은 정말 힘들었지만 팀원들과 살균 무드등을 만드는 것이 재밌었고 프로젝트 진행에 대한 경험치가 쌓여 좋은 기회가 되었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5918</id>
		<title>99-1=0 - Smart Mood Light with Sterilization</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5918"/>
				<updated>2020-06-22T02:05:01Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 프로젝트 요약 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 Smart Mood Light with Sterilization&lt;br /&gt;
 살균 기능을 탑재한 스마트 무드등&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 노*진 (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 박*훈 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 주*완 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 함*규 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20174300** 유*환 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
 원룸은 타 주거환경에 비해 채광과 통풍 능력이 떨어진다. 공기 청정기를 이용할 수 있지만, 채광 문제를 해결하지 못한다. 다행히 넓은 공간을 가진 주거환경에 비해 규모가 작아 상용 자외선을 통한 살균 효과는 비해 뛰어나지만, 살균기의 제어는 사용자가 원룸 내 없을 때 이루어 져야 한다. 위의 문제를 해결하기 위해 스마트폰을 이용하여 원격 제어를 실행한다. 또한 원격 제어를 하면서 사용자가 원룸 내 있을 때 활용할 수 없는 살균기가 공간을 비효율적으로 차지하게 되는 문제를 해결하기 위해 이를 무드등과 결합 한다. 결과적으로 사용자가 가장 많이 시간을 보내는 공간을 살균할 수 있으며 공간의 비효율성과 안전성, 더불어 무드등의 원격제어로 인한 편의성을 제공한다. 사용자는 외부에 있을 때, 스마트폰을 이용하여 살균기 동작을 제어할 수 있다. 사용자가 내부에 있을 때 별도의 조치 없이도 자외선 동작 제어는 비활성화 되며 무드등의 원격 제어가 가능하다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 1. 배경&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_최근4년간국내미세먼지수치.png|500픽셀]]&lt;br /&gt;
[[파일:99-1=0_최근전세계적바이러스주기.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
최근 국내 미세먼지 수치가 꾸준히 증가함에 따라 국민들의 미세먼지 정화에 관한 관심이 높아지고 있다. 또한, 전 세계적인 바이러스 발생 주기가 짧아짐에 따라 사람들의 살균 제품에 대한 관심도가 높아지고 있다. &lt;br /&gt;
&lt;br /&gt;
햇빛은 면역력 향상이나 기분 전환의 효과뿐 아니라 살균 기능을 가지고 있다. 원룸은 구조적으로 대부분 하나의 창문을 가지고 있어 충분한 빛을 받지 못하며 공기의 입/출구가 하나이기에 통풍이 원활하지 않다. 이러한 조건은 미생물이 자라나기 쉽고 미세먼지의 배출이 어렵다. 이들을 모두 정화하는 데 맞는 조건을 가진 것이 자외선이다. 자외선을 통해 공기 중에 살균이 가능하며 미세먼지 중금속을 분해해 공기를 정화할 수 있다. 하지만 자외선 살균기를 집안에 두고 사용하는 것에는 다음과 같은 문제점들이 있다.&lt;br /&gt;
&lt;br /&gt;
- 살균 자외선은 인간에게 유해하므로 사용자에게 직접적인 자외선이 가해지는 것은 위험하다. 사용자가 없는 곳에서 자외선 살균기가 작동해야 하며 사용자가 있을 때 확실한 방법으로 조작 가능성을 배제 시켜야 한다.&lt;br /&gt;
&lt;br /&gt;
- 사용자가 자주 쓰는 공간의 살균일수록 의미가 있으므로 자외선 살균기는 사용자가 자주 쓰는 공간에 배치되어야 한다. 그러나 위의 이유로 사용자가 실제 방에 있을 때 자외선 살균기를 사용하는 것은 위험하므로 사용자가 있을 때 별다른 기능 없이 공간만을 차지하게 된다.&lt;br /&gt;
&lt;br /&gt;
원룸 내 거주자가 가장 오래 머무르는 공간으로 예상되는 곳은 침대류 이다. 침대류 근처에 있는 물체에 살균기를 결합하여 제품을 만들 수 있다면 효율적으로 문제를 해결할 수 있다. 우리는 이 조건을 만족하는 물체가 무드등이라는 점에서 문제 해결을 시작했다. 또한, 첫 번째 문제 해결을 위해 임베디드 시스템을 설계하여 사용자가 없는 공간에서의 작동을 가능하게 하는 것뿐 아니라 무드등에 다양한 기능을 추가하여 편리성과 기능의 다양함을 추가하려고 한다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 기대효과&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_UV자외선을이용한살균 미세먼지정화과정.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
사용자는 집 밖에서 애플리케이션을 통해 자외선 모듈을 가동함으로써 안전한 방법으로 공간을 살균 및 미세먼지 분해 기능을 작동시킬 수 있다. &lt;br /&gt;
&lt;br /&gt;
또한, 사용자가 개발 제품과 같은 공간에 있다고 판단 시 애플리케이션 자동 스위칭 기능을 통해 자외선 컨트롤 기능이 아닌 무드등 컨트롤 기능을 제어하게 된다. 따로 조작 없이 방 안/밖이라는 공간의 변화에 따라 스위칭 되기에 조작에 신경 쓸 필요가 없다. 무드등 기능으로썬 조도 조정, 빛의 색상 선택, 주변 음악에 따른 색상 변화 기능 등이 있으며 사용자가 선택하는 옵션에 따라 기능을 수행할 수 있어 편리함과 다양성을 제공한다. 사용자의 유무에 상관없이 개발제품은 유용한 도구로써 사용되기에 공간 활용성 또한 확보할 수 있을 것으로 생각된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:99-1=0_목적계통도.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 안전성&lt;br /&gt;
자외선 활성/비활성화 기능은 가스 자동밸브 잠금과 비슷한 원리로 앱 구동 시 스마트폰 GPS 기능을 통해 사용자의 위치정보를 받아 주거 위치와 비교하여 앱 인터페이스 비활성화 여부를 결정한다. GPS 오작동으로 인한 비활성화가 적절히 이루어지지 않았을 때를 대비해 앱 내 수동 비활성화 버튼을 추가한다. 앱 인터페이스가 비활성화 되어 있어 사용자의 실수로 인한 신호 전달 가능성을 사전에 차단할 수 있다. 또한 회로의 긴급 정지 스위치를 통해 하드웨어 오동작 시 회로 자체의 전류를 제어할 수 있다. 또한 인체 및 환경에 유해한 정도를 판단하여 인체에 비교적 덜 유해한 UV-A의 자외선 영역을 사용했고, 수은으로 환경 파괴 문제를 일으키는 UV-램프를 대신하여 UV-LED를 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 실용성&lt;br /&gt;
먼저 자외선 영역의 성능을 비교하면 살균력 기준으로는 분 단위 살균력을 가진 UV-C가 시 단위 살균력을 가진 UV-A보다 월등히 강력하다. 하지만 살균 범위를 보면 UV-A는 공기 중 3-5m의 반경을 가진 것에 비해 UV-C는 공기 중 투과력이 매우 낮아 넓은 범위를 살균할 때에는 UV-A가 더 적합하다. 따라서 거주자 없이 비어있는 시간이 긴 원룸에 맞추어 UV-405nm 자외선을 이용했다. 이 때, 자외선 LED의 살균 방향을 15도 정도 아래쪽으로 내려주어 침구류 및 사용자가 자주 사용하는 물건도 더불어 살균하게 된다. 모터의 경우, 자외선 살균기의 상하 운동이 완료된 후에 해당 위치에서 고정이 되어야 하는데, 서보 모터는 고정 기능이 없고, 스탭 모터는 고정 기능을 사용할 때 소모 전력이 많다. 따라서 적은 소모 전력으로 고정 기능을 수행할 수 있는 웜 기어 모터를 사용했다.&lt;br /&gt;
공간 효율성을 기준으로는 제품 구성 시 얼마나 효율적인 공간 활용 여부를 비교했다. 따라서 각 제품의 특성에 맞추어 무드등 조명에는 필요 전선량이 많은 LED 전구 대신 네오 픽셀을 사용했고, 자외선 조명에는 1cm 이하의 조명 크기를 가지는 UV LED를 사용했다. 또한 사용자 위치를 판별하기 위해서 출입문 주변의 많은 공간을 차지하는 적외선 센서 활용 출/입 카운터 대신 외부 공간을 이용하지 않는 안드로이드 모바일 앱 내의 GPS 기능을 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 편의성&lt;br /&gt;
사용자 입장에서 제품을 이용할 때의 반응성을 기준으로 비교했다. 호스팅 제공 S/W에서는 외부의 데이터베이스를 이용하는 파이썬 애니웨어 대신 자체적으로 실시간 데이터베이스 기능을 제공하는 파이어 베이스를 사용했다. 또한 사용자 위치 판별법에서는 사용자의 출/입 여부를 얼마나 빠르게 판단할 수 있는지에 대한 비교를 했는데, 이는 적외선 센서 활용 카운터가 더 빠른 응답성을 보였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 경제성&lt;br /&gt;
제품에 대한 경제적인 측면은 제품을 구성하는데 사용하는 비용과 유지비로 생각할 수 있다. 먼저 제품 구성비용은 프로젝트의 해당 제품을 구매하는 비용이다. 따라서 보다 더 값이 싼 네오픽셀, UV-A, UV-램프가 더 적합했다. 또한 호스팅 기능 S/W에서는 무료로 이용할 수 있는 범위가 더 넓은 파이어 베이스가 적합했다. 또한 유지비는 제품의 수명이 얼마나 긴지, 배터리 소모량이 얼마나 적은지, 부품 교체 비용이 얼마나 큰지에 대해 비교했다. 따라서 부품 교체 비용이 더 저렴한 네오픽셀, 수명이 더 긴 UV-LED가 더 적합했다. 또한 항상 동작시키는 적외선 센서보다 필요시에만 동작시킬 수 있는 GPS 기능이 배터리 소모량이 거의 없어 더 적합했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 개발 용이성&lt;br /&gt;
개발제품 특성상 데이터베이스 및 호스팅 서비스가 필요하다. 개발자 독자적으로 데이터 베이스와 호스팅 서비스를 개발할 필요 없이 이러한 서비스를 제공하는 서버는 파이어 베이스이다. 개발 난이도는 물론 개발자의 서버 개발 시간을 최소화 하여 다른 분야의 성능 개발을 최대화 시킬 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_개발용이성.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Application과 라즈베리 파이의 통신 중간 다리로 파이어 베이스 서버를 이용한다. 물론 무드등 제어를 위한 라즈베리 파이 서버를 구축하여 통시에 이용하면 원룸 내 무드등 제어 동작 속도를 향상시킬 수도 있다. 그러나 하나의 서버를 이용하면 통신 오류나, 오류가 발생했을 때 오류를 제어하기 쉽다. 또한  추후에 기능 추가가 용이하다. 파이어 베이스는 서버 특성상 서버 사용자가 많아지게 되면 부하가 발생할 수 있다. 그러나 개발 상품의 경우 서버는 한명의 사용자만이 이용한다는 점을 고려하여 이러한 단점을 고려하지 않을 수 있다. 따라서 해당 프로젝트에 가장 적합하고, 통신 복잡도가 적은 파이어 베이스와 안드로이드 스튜디오를 사용했다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_동작시나리오.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_역할분담.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_세부개발일정표.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_시스템구성도.png|800픽셀]]&lt;br /&gt;
&lt;br /&gt;
===기구부 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_무드등모드3D설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_자외선살균모드3D.png|166픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
먼저 기구부에서는 최종적으로 제품의 형상과 베벨 기어로 사각 LED 기둥을 올리는 Mechanism과 자외선 LED가 부착되어 있는 Slide Pin Mechanism을 구현하였다. &lt;br /&gt;
제품의 형상의 경우에는 제품의 무드등 기능과 자외선 살균 기능을 정상적으로 구현할 수 있게 필요한 요소들을 적절히 배치할 수 있는 공간을 만들었고, 사용자의 안전성을 위해 Mood Light를 위한 NeoPixel과 UV-A LED를 구분하여 인체에 해로운 자외선 LED는 기능을 사용하지 않을 때 내부 공간에 위치하도록 구현하였다. &lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism은 첫 번째 자외선 LED가 설치된 사각기둥을 올리는 Motor Mechanism과 두 번째 자외선 LED가 적절한 각도로 펼쳐지는 슬라이드 핀 Mechanism이다. 첫 번째 Motor Mechanism은 모터와 베벨 기어 및 랙 피니언 시스템을 통해 모터의 회전 운동을 직선 운동으로 전환하여 구현하였다. 또한 두 번째 Slide Pin Mechanism은 슬라이드 핀을 이용하여 중력에 의해 자동으로 사각기둥 상승 시 펼쳐지고, 하강 시 접히도록 구현한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''기구부 설계'''&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 1. Motor Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_모터설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_모터구현.png|191픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism 중 자외선 LED가 설치된 사각 기둥을 올릴 베벨 기어와 랙 피니언을 사용한 Motor Mechanism 시스템 구현을 완료했다. CATIA CAD 프로그램을 이용하여 베벨 기어 및 랙 피니언 설계를 완료했고, 3D 프린터를 이용하여 규격에 맞추어 실제 제품을 만들었다. 기존에는 랙 피니언 한 쌍만을 직접 모터에 연결하여 사각기둥의 상하 운동을 구현하려고 했지만, 대칭성이 잘 맞지 않아 한쪽으로 쏠리는 현상이 발생하여, 추가적으로 베벨 기어 등을 사용하여 대칭성을 고려하여 양쪽에서 사각 기둥의 상하 운동을 구현했다.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. Slide Pin Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_슬라이드핀설계.png|200픽셀]]&lt;br /&gt;
[[파일:Slide.jpg|145픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Motor Mechanism을 통해 자외선 LED가 설치된 사각기둥이 상하운동을 하면, LED가 적절한 각도를 이루어 펼쳐지도록 구현하였다. 따라서 Slide Pin Mechanism을 이용하여 사각 기둥이 상승할 때 중력에 의해 자동으로 LED가 펼쳐지고, 하강할 때 자동으로 접힌다. CATIA CAD 프로그램을 이용하여 3D 설계를 하고, 3D 프린팅을 통해 실제로 외관을 만들었다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_LED각도설계.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LED가 펼쳐지는 적절한 각도로는 30도를 정했다. 각도를 정하는 과정에서 두 가지의 고려 사항을 정했는데, 첫 번째는 자외선 LED 끝이 직각으로 도달하는 지점의 거리가 원룸이라는 공간을 고려하여 1m 이상이 되고, 두 번째는 제품과 근접한 곳의 LED 사각지대가 제품의 끝으로부터 10cm 내가 되는 기준을 두었다. 따라서 CATIA CAD 프로그램의 2D 설계를 통해 적절한 각도 30도를 설정했다. (실제로 빛은 직각으로만 전달되는 것이 아니라 사방으로 퍼지기 때문에 실제 최대 도달 거리는 기준값인 1m 보다 더 커진다.)&lt;br /&gt;
&lt;br /&gt;
'''기구부 구현'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_사각기둥.jpg|250픽셀]]&lt;br /&gt;
[[파일:99-1=0_Mood.jpg|250픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_완성본.jpg|250픽셀]]&lt;br /&gt;
[[파일:위에서_본_모습.PNG|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
UV-A LED를 장착할 사각 기둥을 3D Printing을 통해 만들었다. 또한 Mood Light를 위한 NeoPixel은 사각 기둥 밖에 다른 기구에 물결 모양으로 48개를 달았다. 최종적으로 아크릴판에 불투명 시트를 붙여 등을 만들었다.&lt;br /&gt;
&lt;br /&gt;
'''최종 작품'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:파랑이.jpg|250픽셀]][[파일:UV-A_LED.jpg|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
최종으로 Mood Light에서 단색 모드로 파랑색 조명을 킬 때와, UV LED Mode에서 On버튼을 눌렀을 때 결과이다.&lt;br /&gt;
&lt;br /&gt;
===제어부 및 회로 구현===&lt;br /&gt;
 UV-A LED, Motor 회로도&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_제어회로도1.PNG|600픽셀]][[파일:99-1=0_내부회로도2.jpg|600픽셀]]&lt;br /&gt;
 &lt;br /&gt;
SMPS로부터 220V 정격 전압을 12V 정격 전압으로 나누어 아두이노와 Relay Switch를 통해 UV-LED의 전원 On/Off 구현이 가능하다. 모터와 UV LED는 아두이노로 제어하기 때문에 라즈베리파이에서 데이터베이스의 값을 실시간으로 확인하다가 UV LED 동작이 지시되면 아두이노에 신호를 보내준다. 데이터베이스의 값은 안드로이드 App을 통해 갱신되므로 결국은 안드로이드 App을 통해 UV-LED의 On/Off 제어가 가능하다. 라즈베리파이와 아누이노간의 데이터 통신은 USB 케이블을 이용한 시리얼 통신을 이용한다.(라즈베리파이와 아두이노 간의 거리가 가깝고 전송하는 데이터가 단순하기 때문에 간단한 통신 방법 선택) 구현 언어는 파이썬이며 사용한 파이썬 모듈은 serial이다. 이와 관련하여 사용한 함수들은 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
UV-LED 동작을 설정하는 값이 “PowerON”일 경우 USB 포트로 연결된 아두이노로 UTF-8로 인코딩된 문자 u를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 u를 받아 UV-LED On 동작을 수행한다. 만약 UV-LED 동작 설정값에 “PowerON” 외의 값이 저장되었을 경우 (“PowerOFF“의 경우가 일반적이지만 예외 처리를 위해 else로 조건 판단) 아두이노로 UTF-8로 인코딩된 문자 d를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 d를 받아 UV-LED Off 동작을 수행한다.&lt;br /&gt;
&lt;br /&gt;
 NeoPixel 회로도&lt;br /&gt;
[[파일:99-1=0_네오픽셀_회로도.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
Mood Light구현은 NeoPixel과 Raspberry PI를 통해 구현했다. 사용자가 Application을 통해 색을 바꾸면 Database의 값이 아래 표에 Data로 바뀌면서 Mood Light 색이 변한다. NeoPixel의 Digital input을 통해 값이 들어가고, 그에 맞는 색을 자유롭게 바꿀 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:색깔.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:App_insidemode_screenshot.jpg|200픽셀]] [[파일:App_outsidemode_screenshot.jpg|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
 앱&lt;br /&gt;
&lt;br /&gt;
앱을 처음 설치하고 실행하면 앱은 팝업 윈도우로 모바일 디바이스의 위치 정보 권한을 요청하며 사용자는 이를 최초 1회만 수락하면 된다. 앱은 실행되면 즉시 모바일 디바이스의 현재 위치 정보를 받고 데이터베이스에 저장된 집의 위치 정보와의 거리를 계산한다. 이 거리가 일정 거리 이상이면 앱은 실외 모드로 자동 설정되고, 일정 거리 미만이면 실내 모드로 자동 설정된다. 실내 모드와 실외 모드의 전환은 기본적으로 비활성화되어 있으나 사용자가 우측 하단의 스위치를 켜면 활성화되어 강제 모드 전환이 가능하게 된다. 그래서 앱을 실행했을 때 자동 모드 설정이 잘못되면 사용자가 직접 모드를 전환해서 원하는 기능을 사용할 수 있다.&lt;br /&gt;
&lt;br /&gt;
앱은 크게 실내 모드와 실외 모드로 구분된다. 각 모드는 Fragment로 정의됐고 MainActivity의 bottomnavigationview를 통해 replace해서 전환할 수 있다. bottomnavigationview는 각 모드에 배치된 스위치가 꺼져 있으면 disabled되고 스위치가 켜지면 enabled된다. 사용자는 실내 모드에서 무드등을 제어하고 실외 모드에서 자외선 살균기를 제어한다.&lt;br /&gt;
&lt;br /&gt;
 실내 모드&lt;br /&gt;
실내 모드에선 무드등의 색상 선택, 밝기 조절, 전원 작동, 스페셜 모드 기능을 이용할 수 있다.&lt;br /&gt;
* colorpicker&lt;br /&gt;
: colorpicker의 색상은 초기에 12시의 연두색으로 설정되어 있고 사용자가 버튼을 드래그해서 원하는 색상을 선택할 수 있다. colorpicker의 중앙에 있는 원은 왼쪽 반원에 이전 색상을 표시하고 오른쪽 반원에 현재 색상을 표시하여 전후 색상을 비교하기 좋게 만들어준다. 사용자가 무드등의 전원을 키면 이전 색상이 표시된 반원이 현재 색상으로 교체된다. 색상은 여섯 자리의 16진수 #nnnnnn으로 표현되며 정수형으로 저장된다. 사용자가 colorpicker로 다양한 색상을 연속적인 범위에서 선택할 수 있기 때문에 선택의 폭이 넓고 세밀한 색상 선택이 가능하다. 무드등의 전원이 켜져 있으면 색상의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 밝기&lt;br /&gt;
: 무드등의 밝기 조절은 실내 모드 중앙의 seekbar를 통해 할 수 있다. 밝기 값은 최소 0에서 최대 255까지 선택 가능하다. colorpicker와 같이 무드등의 전원이 켜져 있으면 밝기의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 전원&lt;br /&gt;
: Power ON 버튼을 누르면 무드등의 전원이 켜진다. 전원 버튼 하단의 얇은 띠가 전원이 꺼져있을 때 회색, 전원이 켜져있을 때 녹색을 띠면서 사용자가 무드등의 전원이 켜져있는지를 직관적으로 알 수 있게 해준다. 전원 작동 데이터가 데이터베이스에 성공적으로 전송되면 앱 화면 하단에 Toast 텍스트를 표시하여 사용자에게 전원의 작동을 알려준다. 전원 버튼의 Power ON/Power OFF 텍스트는 colorpicker에서 선택된 색상과 실시간으로 동일하게 적용되어 사용자에게 무드등이 어떤 색으로 켜질지 더욱 직관적으로 와닿게 만들어준다.&lt;br /&gt;
* 스페셜 모드&lt;br /&gt;
: 앱을 실행하면 무드등의 모드는 기본 모드로 설정되어 있는 상태이다. 스페셜 모드 버튼을 누르면 순차적으로 웨이브 모드 1, 웨이브 모드 2, 레인보우 모드 1, 레인보우 모드 2가 설정되며 그후 다시 기본 모드로 돌아온다. 현재 설정된 모드는 스페셜 모드 버튼의 텍스트로 나타나서 사용자가 이를 알 수 있다. 또한 버튼을 누를수록 단계적으로 버튼의 배경 색이 진해지며 버튼의 깊이감을 더해준다. 스페셜 모드 버튼의 아래엔 모드의 전체 개수와 현재 모드가 몇 번째인지를 시각적으로 쉽게 알게 해주는 공백 칸들이 있다. 이 칸들은 기본 모드에서 모두 비어있게 되고 스페셜 버튼이 눌릴 때마다 한 개씩 채워지며 모두 채워진 후엔 다시 모두 빈 칸으로 돌아온다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스페셜 모드는 무드등이 켜져 있을 때에만 작동이 유효하다. 무드등의 전원이 꺼져 있을 때 스페셜 모드를 작동하면 무드등에서 아무 일도 일어나지 않는다. 이런 상황을 예방하기 위해 앱에서 무드등의 전원이 꺼진 상태에서 스페셜 모드만 작동하는 일이 일어나지 않게 한다. 무드등의 전원이 꺼진 상태에서 스페셜 모드 버튼을 누르면, 앱은 무드등의 전원도 키고 스페셜 모드도 작동한다. 또한 스페셜 모드가 설정된 상태에서 무드등의 전원을 끄면 앱은 스페셜 모드를 기본 모드로 설정하고 무드등의 전원을 끈다. 만약 사용자가 레인보우 모드를 바로 사용하고 싶다면 전원 버튼을 누를 필요 없이 바로 스페셜 모드 버튼을 3회 누르면 된다. 전원 버튼을 누르는 단계를 거치지 않아도 되기 때문에 빠르고 간편하게 즉시 스페셜 모드를 이용할 수 있다. 결과적으로 무드등의 전원이 꺼진 상태에서 무드등의 모드는 항상 기본 모드로 설정되어 있고, 스페셜 모드가 켜진 상태면 무드등의 전원도 항상 켜진 상태가 된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 실외 모드&lt;br /&gt;
실외 모드에선 위치 정보 설정, 자외선 살균기의 전원 작동, 타이머 기능을 이용할 수 있다.&lt;br /&gt;
* 위치 정보 설정&lt;br /&gt;
: HOME LOCATION 버튼을 누르면 모바일 디바이스의 현재 위치 정보를 데이터베이스에 저장하여 집의 위치로 설정한다. 설정한 집의 위치 정보는 앱이 실행됐을 때 집과 사용자 사이의 거리를 계산하는 데 이용된다. 데이터베이스에 저장된 위치 정보는 사용자가 새롭게 설정하지 않는 한 항상 일정하게 유지된다. 그래서 앱을 재설치했을 때 집의 위치 정보 설정을 다시 할 필요가 없다. 상수의 변수명을 대문자 알파벳으로 선언하는 프로그래밍 관습을 응용해서 위치 정보 설정 버튼의 텍스트도 대문자 알파벳으로 하였다. &lt;br /&gt;
* 자외선 살균기&lt;br /&gt;
: 실외 모드에서 Power ON 버튼을 누르면 자외선 살균기의 전원이 켜진다. 전원 버튼 위의 spinner에서 타이머가 작동할 시간을 선택할 수 있으며 초기 타이머는 1시간으로 설정된 상태이다. 타이머는 시간 단위로 선택할 수 있고 최대 12시간까지 선택 가능하다. 자외선 살균기의 전원을 켜면 전원 버튼 아래에 textview가 표시되어 타이머의 남은 시간을 알려준다. 설정한 타이머가 종료되면 앱이 자동적으로 자외선 살균기의 전원을 끈다.&lt;br /&gt;
* 시간 Spinner&lt;br /&gt;
: APP의 실외모드에선 자외선 살균기를 제어한다. 사용자는 작동 시간을 spinner로 선택하여 자외선 살균기 전원 버튼을 눌러 작동시킨다. 그러면 APP은 스피너에 선택된 시간과 전원 데이터를 데이터베이스에 전송한다. APP에서 사용자가 직접 자외선 살균기의 전원을 끌 수 있지만 설정한 시간이 경과되면 APP은 전원을 끄는 데이터를 데이터베이스에 전송한다. 만약 사용자가 작동 시간을 연장시키고 싶다면 자외선 살균기를 끄고 다시 작동하면 된다. 그러면 남은 시간이 다시 세팅되어 작동 시간이 증가하게 된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Firebase&lt;br /&gt;
[[파일:99-1=0_파이어베이스스크린샷.jpg|300픽셀]]&lt;br /&gt;
[[파일:99-1=0_파이어베이스연동.jpg|500픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Firebase는 Google 사의 모바일 및 웹 개발 플랫폼이다. Firebase는 많은 유용한 기능을 지원하지만 여기선 실시간 데이터베이스를 사용한다. Firebase의 장점은 안드로이드 및 iOS 앱과 연동이 용이하다는 것이다. 또한, Firebase는 시간과 장소에 구애받지 않고, 데이터가 안전하게 보존되며, 데이터를 읽고 쓰는 것이 간편하다. 그래서 Firebase는 사용자의 집의 위치 정보를 저장하기에 적합하다. 이 데이터는 제 3자가 접근할 수 없으며 사용자가 새롭게 설정하지 않는 한 영구히 보존된다. 앱은 Firebase의 데이터베이스에 집의 위치 정보를 저장하기도 하지만 읽어오기도 한다. 앱은 실행되면 데이터베이스의 집의 위치 정보를 읽어와서 현재 사용자의 위치 사이의 거리를 계산하고 실내/실외를 판별한다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 작동할 때에도 Firebase를 이용한다. 사용자가 앱으로 작동한 무드등의 색상, 밝기, 전원, 모드 데이터와 자외선 살균기의 전원, 타이머 데이터는 Firebase의 데이터베이스에 저장된다. 앱은 무드등의 전원이 켜진 상태에서 색상이나 밝기가 변경되면 변경 사항을 즉시 데이터베이스에 반영한다. 자외선 살균기의 타이머는 데이터베이스에 저장되지만 남은 타이머 시간까지 일일이 저장되진 않는다. 앱에서 설정한 시간이 지나면 자외선 살균기의 전원을 끄는 데이터를 저장할 뿐이다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 직접 구동시키는 것은 라즈베리파이와 아두이노이다. 라즈베리파이는 데이터베이스에 저장된 무드등의 색상, 밝기, 전원, 모드 데이터를 읽어와 라즈베리파이에 직접 연결된 네오픽셀 LED의 작동을 지시한다. 또한 살균기의 전원 데이터를 읽어와 아두이노에 살균기를 들어올리는 모터의 동작과 살균기 LED의 동작을 지시한다. 아두이노에서는 라즈베리파이의 지시를 대기하다가 살균기의 ON/OFF 동작 신호를 받아 살균기를 켜고 끈다. 이 때 살균기가 부착된 구동부의 높이는 아두이노에 연결된 초음파센서로 거리를 측정하여 조절한다.&lt;br /&gt;
&lt;br /&gt;
===UV-LED 곰팡이 저해 실험===&lt;br /&gt;
&lt;br /&gt;
본 실험은 UV-A LED의 곰팡이 생육 저해 능력을 검증하기 위한 실험이다. 구체적인 저해율의 수치를 나타내기 위해 1차 살균 실험에 이어 2차 곰팡이로 실험을 하여 저해율을 나타낼 것이다.&lt;br /&gt;
&lt;br /&gt;
: 1. 실험 준비&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비1.png|200픽셀|ㅇ|왼쪽|Petri Dish, Sterile cork borer, 곰팡이]]    [[파일:99-1=0_세균실험준비2.png|173픽셀]]&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비3.png|200픽셀]]    [[파일:99-1=0_세균실험준비4.png|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 실험 과정&lt;br /&gt;
&lt;br /&gt;
:: 1) 먼저 3종의 곰팡이(Cladosporium sp, Fusarium sp, Aspergillus sp)를 키울 배지를 만들기 위해 Difo회사 제품인 PDA 12g과 Agar 10g을 비커에 넣은 후 500ml distilled water를 넣어준 후 autoclave에서 121℃에서 15분간 돌려준 뒤 petri dish에 부어 1~2일 동안 말린다.&lt;br /&gt;
&lt;br /&gt;
:: 2) 곰팡이가 PDA배지에서 충분히 성장했다면 이를 각각 Petri Dish에 접종한다. 이 때 일정한 양의 곰팡이를 접종하기 위해 그림에 있는 Sterile cork borer를 사용한다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정1.png|200픽셀]]    [[파일:99-1=0_세균실험과정2.png|182픽셀]]&lt;br /&gt;
: [[파일:99-1=0_세균실험과정3.png|200픽셀]]    [[파일:99-1=0_세균실험과정4.png|195픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 3) 3종의 곰팡이를 0.5m, 0.75m, 1m 간격으로 배치시키고, 다른 하나는 대조군으로 UV-A LED에 노출시키지 않은 채로 성장시킨다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정5.png|200픽셀]]    [[파일:99-1=0_세균실험과정6.png|172픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 4) Petri Dish 안에서 곰팡이의 성장률을 원의 반지름으로 근사시킵니다. 또한 생리식염수를 10마이크로미터 떨어트린 후, 소량의 곰팡이를 접종해준 뒤 커버 글라스를 붙이고 광학현미경으로 관찰한다.&lt;br /&gt;
&lt;br /&gt;
:: 5) 곰팡이가 자란 원의 반지름을 측정하고, 이를 저해율로 나타낸다. 저해율의 식은 다음과 같다. (저해율(%)= (대조군의 반지름 - 실험군의 반지름)/대조군의 반지름 * 100)&lt;br /&gt;
: [[파일:99-1=0_곰팡이_측정.jpg|400픽셀]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 3. 실험 결과&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 1) Cladosporium sp(벽지 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Cladosporium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 2) Fusarium sp(토양 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Fusarium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 3) Aspergillus sp(누룩 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Aspergillus_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
아래 표는 현미경을 통해 관측한 곰팡이의 반지름(cm)이고, 이를 위해 저해율식에 대입하여 곰팡이 저해율을 계산하였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:길이값.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:저해율.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:5시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:10시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 4. 결론&lt;br /&gt;
: : 최종 저해율로 나타낸 결과 UV-A LED에 5시간, 10시간에 따른 차이는 거의 없었지만 거리에 따른 저해율의 차이는 눈에 띄었다. 0.5m 거리에선 약 80%의 저해율을 보였고, 0.75m 에서는 Cladosporuim sp와 Fusarium sp은 약 50%의 저해율을 보였지만 Aspergillus sp는 30%의 저해율을 보였습니다. 1m 거리에선 저해율이 많이 떨어졌는데, Cladosporium sp는 39%, Fusarium sp는 25%, Aspergillus sp는 6%의 저해율을 보였습니다. 1m에서의 살균력은 많이 떨어졌고, 이를 보완하기 위해 기존 실험은 25cm UV-A LED 2개를 설치했는데, 제품에는 한 슬라이드 당 3개로 늘려 설치했다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic1.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/8/87/99-1%3D0_%EC%98%81%EC%83%81.avi]]  [[파일:99-1=0_videopic2.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/c/ce/99-1%3D0_%EC%98%81%EC%83%812.avi]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic3.png|300픽셀|link=https://capstone.uos.ac.kr/mie/images/8/82/99-1%3D0_%EC%98%81%EC%83%813.mp4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
'''UV-A LED Mode &amp;amp; Mood Light mode'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:UV-A LED.jpg|250픽셀]][[파일:99-1=0_무드등.jpg|250픽셀|link=https://capstone.uos.ac.kr/mie/images/99-1=0_영상.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_미구현내용.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_평가항목.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
[[파일:99-1=0_평가_결과.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
노*진: 프로젝트의 주제를 결정하기 전까지만 하더라도 라즈베리파이와 각종 모듈에 대한 지식이 없어 막막했습니다. 하지만 잘 모르는 만큼 팀원들과 더욱 조사를 열심히 하고, 구체적으로 설계를 하였습니다. 개념설계 발표 때까지 ‘과연 의도한대로 동작할까?’ 라는 의문점을 가졌지만 중간발표 준비를 하면서 원하는 기능들이 하나 둘씩 구현되고, 기구부의 Mechanism들이 완성되는 것을 보면서 재밌었고, 성취감을 느꼈습니다. 프로젝트를 진행하면서 전공 지식이 아닌 전기 회로도와 관련된 일들을 구글링과 여러 사이트에 물어보면서 해결을 했고, UV-LED의 살균력을 검증하기 위해 생명공학 전공인 지인의 도움을 받으면서 프로젝트의 문제들을 해결했습니다. 이 과정에서 잘 모르는 부분을 어떤 방법으로 해결할 지에 대한 능력을 기를 수 있었습니다. 마지막으로 내장형시스템 과목을 통해 기계정보공학과의 매력을 느낄 수 있었고, 앞으로 프로젝트를 수행할 때 자신감을 가질 수 있을 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
박*훈 : 기계정보공학을 전공한지 4학년 1학기에 처음으로 컴퓨터 관련 프로젝트를 진행했습니다. 그동안 기계 관련 CAD 프로젝트나 CAE 프로젝트는 다수 경험이 있었지만 컴퓨터 분야는 경험이 없었기 때문에 프로젝트 과정 중 가장 처음 과정인 주제 선정에서부터 엄청난 난관에 부딪혔습니다. 하지만 주어진 시간 안에 주어진 모듈로 할 수 있는 주제를 생각해냈고, 프로젝트를 시작했습니다. 프로젝트 진행 과정 중에 한계에 도달하는 일도 있었고 뜻처럼 진행되지 않는 일도 있었지만 팀원들과 함께 돌파구를 찾아가며 차근차근 프로젝트를 완성했습니다. 프로젝트를 진행하면서 기계정보공학의 융합적 전공의 취지에 맞게 기계적인 메커니즘과 임베디드 시스템을 융합한 완성품을 제작하기 위해 노력을 했고, 실제로 저는 기구부의 메커니즘 쪽을 맡아 CAD 프로그램인 CATIA를 통해 기어 및 랙 피니언 세트와 슬라이드 핀 등을 설계하고 3D 프린터를 이용하여 실제 구현을 완료했습니다. 기구부 제작 과정 중 값비싼 3D 프린터 제작의 비용과 3D 프린터 제작품의 과도한 무게 등의 문제가 있었지만, 팀원들과 협의한 끝에 해결 방안을 고안하여 이러한 문제를 해결했습니다. 이번 내장형 프로젝트의 경험을 통해 팀원들과의 소통 능력과 문제 해결 능력을 한층 성장시켰고, 앞으로 있을 프로젝트에 큰 도움이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
주*완: 이번 학기에 내장형시스템및실습을 수강하며 처음으로 주제 선정부터 완제품 제작까지 모든 것을 직접 경험했습니다. 주제 선정부터 시작해 모든 과정이 쉽지 않았고 걸림돌에 걸려서 결국 설계를 바꾸기도 했습니다. 하지만 한계를 극복할 방법을 고민하고 공부하고 질문하고 찾아 헤매면서 많은 것을 배웠습니다. 특히 제가 맡은 부분인 안드로이드 앱을 개발하기 위해 1학년 때 배웠지만 모두 잊어버린 java를 처음부터 다시 공부했고, 안드로이드를 아무것도 모르는 상태에서 인터넷 검색만으로 하나하나 공부하고 직접 코드를 짜서 완성까지 했습니다. 과정 중에 알 수 없는 이유때문에 앱이 원하는 대로 안 되거나 강제 종료 될 때가 매우 매우 많았는데, 스스로 계속 공부하고 원인을 찾고 고쳐 나가서 마침내 앱을 완성할 수 있었고, 스스로 문제를 해결하는 능력을 기를 수 있었습니다. 또한 기구부와 외형을 이루는 부분을 만들 때 자르고 다듬는 일이 많이 필요했는데 서로 필요한 부분이 있으면 도와주면서 협동하여 결과가 잘 나온 것 같아 만족스럽습니다. 프로젝트를 하며 값진 경험을 했고 앞으로 다른 프로젝트를 하게 된다면 이 경험이 소중한 밑바탕이 돼서 도움이 많이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
함*규: 임베디드 시스템을 처음 접해보기에 주제를 정하는 과정이 가장 어려웠던 것 같습니다. 처음에는 주어진 시간 내에 해낼 수 없는 비현실적인 주제에 대해 고민하며 기능적 한계에 부딪혀보기도 하고, 챗바퀴 도는 듯한 생각이 들었지만, 이 과정 속에서 전체적인 임베디드 시스템에 대한 이해와 할 수있는 것과 할 수 없는 것에 대해 구분할 수 있는 능력을 얻을 수 있었습니다. 이후에 직접 정한 주제를 바탕으로 프로젝트를 수행해나가면서, 초기 설계로는 해결할 수 없는 문제들을 팀원들과 함께 협동하여 풀어나가는 과정은 정말 힘들기도 했지만 되돌아 보면 책상에서는 배울 수 없는 많은 경험들을 배웠다고 생각합니다. 4-1학기를 떠올리면 많은 희노애락이 있었던 내장형 밖에 생각나지 않을 것 같은 데, 정말정말 재밌었고(농담이 아닙니다.) 후배들에게 꼭 추천해주고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
유*환: 내장형 시스템 프로젝트의 주제를 정하는 과정이 가장 어려웠습니다. 라즈베리파이에 센서나 모터, 또다른 보드를 연결하여 사용한 경험이 없어 주어진 시간 내에 완성할 수 있는 정도를 알기 어려웠습니다. 그래서 처음에는 학기 내에 완성이 불가능한 주제들을 생각했었습니다. 결국 마지막까지 주제를 정하지 못하다가 막바지에 급하게 살균 무드등을 만들기로 했습니다. 주제가 정해지고 자료조사를 마쳤을 때만 해도 힘든일이 끝나고 모든 일이 수월하게 진행될 것만 같았습니다. 그런데 출입 감지 방법, 음악 주파수 분석 등이 뜻대로 되지 않자 다시 조마조마해졌습니다. 하지만 적절한 대안을 찾고 해결해나가면서 완성되가는 프로젝트를 보며 뿌듯했습니다. 그리고 살균 무드등을 완성하고 영상까지 마무리 지었을 때는 그동안 밤을 새며 고생했던 시간들에 대해 보상받는 느낌이 들었습니다.&lt;br /&gt;
무엇보다 팀원 형들이 프로젝트에서 각자가 맡은 열할을 너무 잘 수행해주어서 감사했습니다. 유일하게 라즈베리파이 경험이 있는 저보다도 잘해주어서 미안하기도 하고 대단하다는 느낌이 들었습니다. 내장형 프로젝트는 과정은 정말 힘들었지만 팀원들과 살균 무드등을 만드는 것이 재밌었고 프로젝트 진행에 대한 경험치가 쌓여 좋은 기회가 되었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5917</id>
		<title>99-1=0 - Smart Mood Light with Sterilization</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5917"/>
				<updated>2020-06-22T02:03:45Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 프로젝트 요약 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 Smart Mood Light with Sterilization&lt;br /&gt;
 살균 기능을 탑재한 스마트 무드등&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 노*진 (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 박*훈 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 주*완 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 함*규 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20174300** 유*환 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
원룸은 타 주거환경에 비해 채광과 통풍 능력이 떨어진다. 공기 청정기를 이용할 수 있지만, 채광 문제를 해결하지 못한다. 다행히 넓은 공간을 가진 주거환경에 비해 규모가 작아 상용 자외선을 통한 살균 효과는 비해 뛰어나지만, 살균기의 제어는 사용자가 원룸 내 없을 때 이루어 져야 한다. 위의 문제를 해결하기 위해 스마트폰을 이용하여 원격 제어를 실행한다. 또한 원격 제어를 하면서 사용자가 원룸 내 있을 때 활용할 수 없는 살균기가 공간을 비효율적으로 차지하게 되는 문제를 해결하기 위해 이를 무드등과 결합 한다. 결과적으로 사용자가 가장 많이 시간을 보내는 공간을 살균할 수 있으며 공간의 비효율성과 안전성, 더불어 무드등의 원격제어로 인한 편의성을 제공한다. 사용자는 외부에 있을 때, 스마트폰을 이용하여 살균기 동작을 제어할 수 있다. 사용자가 내부에 있을 때 별도의 조치 없이도 자외선 동작 제어는 비활성화 되며 무드등의 원격 제어가 가능하다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 1. 배경&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_최근4년간국내미세먼지수치.png|500픽셀]]&lt;br /&gt;
[[파일:99-1=0_최근전세계적바이러스주기.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
최근 국내 미세먼지 수치가 꾸준히 증가함에 따라 국민들의 미세먼지 정화에 관한 관심이 높아지고 있다. 또한, 전 세계적인 바이러스 발생 주기가 짧아짐에 따라 사람들의 살균 제품에 대한 관심도가 높아지고 있다. &lt;br /&gt;
&lt;br /&gt;
햇빛은 면역력 향상이나 기분 전환의 효과뿐 아니라 살균 기능을 가지고 있다. 원룸은 구조적으로 대부분 하나의 창문을 가지고 있어 충분한 빛을 받지 못하며 공기의 입/출구가 하나이기에 통풍이 원활하지 않다. 이러한 조건은 미생물이 자라나기 쉽고 미세먼지의 배출이 어렵다. 이들을 모두 정화하는 데 맞는 조건을 가진 것이 자외선이다. 자외선을 통해 공기 중에 살균이 가능하며 미세먼지 중금속을 분해해 공기를 정화할 수 있다. 하지만 자외선 살균기를 집안에 두고 사용하는 것에는 다음과 같은 문제점들이 있다.&lt;br /&gt;
&lt;br /&gt;
- 살균 자외선은 인간에게 유해하므로 사용자에게 직접적인 자외선이 가해지는 것은 위험하다. 사용자가 없는 곳에서 자외선 살균기가 작동해야 하며 사용자가 있을 때 확실한 방법으로 조작 가능성을 배제 시켜야 한다.&lt;br /&gt;
&lt;br /&gt;
- 사용자가 자주 쓰는 공간의 살균일수록 의미가 있으므로 자외선 살균기는 사용자가 자주 쓰는 공간에 배치되어야 한다. 그러나 위의 이유로 사용자가 실제 방에 있을 때 자외선 살균기를 사용하는 것은 위험하므로 사용자가 있을 때 별다른 기능 없이 공간만을 차지하게 된다.&lt;br /&gt;
&lt;br /&gt;
원룸 내 거주자가 가장 오래 머무르는 공간으로 예상되는 곳은 침대류 이다. 침대류 근처에 있는 물체에 살균기를 결합하여 제품을 만들 수 있다면 효율적으로 문제를 해결할 수 있다. 우리는 이 조건을 만족하는 물체가 무드등이라는 점에서 문제 해결을 시작했다. 또한, 첫 번째 문제 해결을 위해 임베디드 시스템을 설계하여 사용자가 없는 공간에서의 작동을 가능하게 하는 것뿐 아니라 무드등에 다양한 기능을 추가하여 편리성과 기능의 다양함을 추가하려고 한다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 기대효과&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_UV자외선을이용한살균 미세먼지정화과정.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
사용자는 집 밖에서 애플리케이션을 통해 자외선 모듈을 가동함으로써 안전한 방법으로 공간을 살균 및 미세먼지 분해 기능을 작동시킬 수 있다. &lt;br /&gt;
&lt;br /&gt;
또한, 사용자가 개발 제품과 같은 공간에 있다고 판단 시 애플리케이션 자동 스위칭 기능을 통해 자외선 컨트롤 기능이 아닌 무드등 컨트롤 기능을 제어하게 된다. 따로 조작 없이 방 안/밖이라는 공간의 변화에 따라 스위칭 되기에 조작에 신경 쓸 필요가 없다. 무드등 기능으로썬 조도 조정, 빛의 색상 선택, 주변 음악에 따른 색상 변화 기능 등이 있으며 사용자가 선택하는 옵션에 따라 기능을 수행할 수 있어 편리함과 다양성을 제공한다. 사용자의 유무에 상관없이 개발제품은 유용한 도구로써 사용되기에 공간 활용성 또한 확보할 수 있을 것으로 생각된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:99-1=0_목적계통도.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 안전성&lt;br /&gt;
자외선 활성/비활성화 기능은 가스 자동밸브 잠금과 비슷한 원리로 앱 구동 시 스마트폰 GPS 기능을 통해 사용자의 위치정보를 받아 주거 위치와 비교하여 앱 인터페이스 비활성화 여부를 결정한다. GPS 오작동으로 인한 비활성화가 적절히 이루어지지 않았을 때를 대비해 앱 내 수동 비활성화 버튼을 추가한다. 앱 인터페이스가 비활성화 되어 있어 사용자의 실수로 인한 신호 전달 가능성을 사전에 차단할 수 있다. 또한 회로의 긴급 정지 스위치를 통해 하드웨어 오동작 시 회로 자체의 전류를 제어할 수 있다. 또한 인체 및 환경에 유해한 정도를 판단하여 인체에 비교적 덜 유해한 UV-A의 자외선 영역을 사용했고, 수은으로 환경 파괴 문제를 일으키는 UV-램프를 대신하여 UV-LED를 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 실용성&lt;br /&gt;
먼저 자외선 영역의 성능을 비교하면 살균력 기준으로는 분 단위 살균력을 가진 UV-C가 시 단위 살균력을 가진 UV-A보다 월등히 강력하다. 하지만 살균 범위를 보면 UV-A는 공기 중 3-5m의 반경을 가진 것에 비해 UV-C는 공기 중 투과력이 매우 낮아 넓은 범위를 살균할 때에는 UV-A가 더 적합하다. 따라서 거주자 없이 비어있는 시간이 긴 원룸에 맞추어 UV-405nm 자외선을 이용했다. 이 때, 자외선 LED의 살균 방향을 15도 정도 아래쪽으로 내려주어 침구류 및 사용자가 자주 사용하는 물건도 더불어 살균하게 된다. 모터의 경우, 자외선 살균기의 상하 운동이 완료된 후에 해당 위치에서 고정이 되어야 하는데, 서보 모터는 고정 기능이 없고, 스탭 모터는 고정 기능을 사용할 때 소모 전력이 많다. 따라서 적은 소모 전력으로 고정 기능을 수행할 수 있는 웜 기어 모터를 사용했다.&lt;br /&gt;
공간 효율성을 기준으로는 제품 구성 시 얼마나 효율적인 공간 활용 여부를 비교했다. 따라서 각 제품의 특성에 맞추어 무드등 조명에는 필요 전선량이 많은 LED 전구 대신 네오 픽셀을 사용했고, 자외선 조명에는 1cm 이하의 조명 크기를 가지는 UV LED를 사용했다. 또한 사용자 위치를 판별하기 위해서 출입문 주변의 많은 공간을 차지하는 적외선 센서 활용 출/입 카운터 대신 외부 공간을 이용하지 않는 안드로이드 모바일 앱 내의 GPS 기능을 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 편의성&lt;br /&gt;
사용자 입장에서 제품을 이용할 때의 반응성을 기준으로 비교했다. 호스팅 제공 S/W에서는 외부의 데이터베이스를 이용하는 파이썬 애니웨어 대신 자체적으로 실시간 데이터베이스 기능을 제공하는 파이어 베이스를 사용했다. 또한 사용자 위치 판별법에서는 사용자의 출/입 여부를 얼마나 빠르게 판단할 수 있는지에 대한 비교를 했는데, 이는 적외선 센서 활용 카운터가 더 빠른 응답성을 보였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 경제성&lt;br /&gt;
제품에 대한 경제적인 측면은 제품을 구성하는데 사용하는 비용과 유지비로 생각할 수 있다. 먼저 제품 구성비용은 프로젝트의 해당 제품을 구매하는 비용이다. 따라서 보다 더 값이 싼 네오픽셀, UV-A, UV-램프가 더 적합했다. 또한 호스팅 기능 S/W에서는 무료로 이용할 수 있는 범위가 더 넓은 파이어 베이스가 적합했다. 또한 유지비는 제품의 수명이 얼마나 긴지, 배터리 소모량이 얼마나 적은지, 부품 교체 비용이 얼마나 큰지에 대해 비교했다. 따라서 부품 교체 비용이 더 저렴한 네오픽셀, 수명이 더 긴 UV-LED가 더 적합했다. 또한 항상 동작시키는 적외선 센서보다 필요시에만 동작시킬 수 있는 GPS 기능이 배터리 소모량이 거의 없어 더 적합했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 개발 용이성&lt;br /&gt;
개발제품 특성상 데이터베이스 및 호스팅 서비스가 필요하다. 개발자 독자적으로 데이터 베이스와 호스팅 서비스를 개발할 필요 없이 이러한 서비스를 제공하는 서버는 파이어 베이스이다. 개발 난이도는 물론 개발자의 서버 개발 시간을 최소화 하여 다른 분야의 성능 개발을 최대화 시킬 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_개발용이성.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Application과 라즈베리 파이의 통신 중간 다리로 파이어 베이스 서버를 이용한다. 물론 무드등 제어를 위한 라즈베리 파이 서버를 구축하여 통시에 이용하면 원룸 내 무드등 제어 동작 속도를 향상시킬 수도 있다. 그러나 하나의 서버를 이용하면 통신 오류나, 오류가 발생했을 때 오류를 제어하기 쉽다. 또한  추후에 기능 추가가 용이하다. 파이어 베이스는 서버 특성상 서버 사용자가 많아지게 되면 부하가 발생할 수 있다. 그러나 개발 상품의 경우 서버는 한명의 사용자만이 이용한다는 점을 고려하여 이러한 단점을 고려하지 않을 수 있다. 따라서 해당 프로젝트에 가장 적합하고, 통신 복잡도가 적은 파이어 베이스와 안드로이드 스튜디오를 사용했다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_동작시나리오.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_역할분담.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_세부개발일정표.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_시스템구성도.png|800픽셀]]&lt;br /&gt;
&lt;br /&gt;
===기구부 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_무드등모드3D설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_자외선살균모드3D.png|166픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
먼저 기구부에서는 최종적으로 제품의 형상과 베벨 기어로 사각 LED 기둥을 올리는 Mechanism과 자외선 LED가 부착되어 있는 Slide Pin Mechanism을 구현하였다. &lt;br /&gt;
제품의 형상의 경우에는 제품의 무드등 기능과 자외선 살균 기능을 정상적으로 구현할 수 있게 필요한 요소들을 적절히 배치할 수 있는 공간을 만들었고, 사용자의 안전성을 위해 Mood Light를 위한 NeoPixel과 UV-A LED를 구분하여 인체에 해로운 자외선 LED는 기능을 사용하지 않을 때 내부 공간에 위치하도록 구현하였다. &lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism은 첫 번째 자외선 LED가 설치된 사각기둥을 올리는 Motor Mechanism과 두 번째 자외선 LED가 적절한 각도로 펼쳐지는 슬라이드 핀 Mechanism이다. 첫 번째 Motor Mechanism은 모터와 베벨 기어 및 랙 피니언 시스템을 통해 모터의 회전 운동을 직선 운동으로 전환하여 구현하였다. 또한 두 번째 Slide Pin Mechanism은 슬라이드 핀을 이용하여 중력에 의해 자동으로 사각기둥 상승 시 펼쳐지고, 하강 시 접히도록 구현한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''기구부 설계'''&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 1. Motor Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_모터설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_모터구현.png|191픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism 중 자외선 LED가 설치된 사각 기둥을 올릴 베벨 기어와 랙 피니언을 사용한 Motor Mechanism 시스템 구현을 완료했다. CATIA CAD 프로그램을 이용하여 베벨 기어 및 랙 피니언 설계를 완료했고, 3D 프린터를 이용하여 규격에 맞추어 실제 제품을 만들었다. 기존에는 랙 피니언 한 쌍만을 직접 모터에 연결하여 사각기둥의 상하 운동을 구현하려고 했지만, 대칭성이 잘 맞지 않아 한쪽으로 쏠리는 현상이 발생하여, 추가적으로 베벨 기어 등을 사용하여 대칭성을 고려하여 양쪽에서 사각 기둥의 상하 운동을 구현했다.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. Slide Pin Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_슬라이드핀설계.png|200픽셀]]&lt;br /&gt;
[[파일:Slide.jpg|145픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Motor Mechanism을 통해 자외선 LED가 설치된 사각기둥이 상하운동을 하면, LED가 적절한 각도를 이루어 펼쳐지도록 구현하였다. 따라서 Slide Pin Mechanism을 이용하여 사각 기둥이 상승할 때 중력에 의해 자동으로 LED가 펼쳐지고, 하강할 때 자동으로 접힌다. CATIA CAD 프로그램을 이용하여 3D 설계를 하고, 3D 프린팅을 통해 실제로 외관을 만들었다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_LED각도설계.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LED가 펼쳐지는 적절한 각도로는 30도를 정했다. 각도를 정하는 과정에서 두 가지의 고려 사항을 정했는데, 첫 번째는 자외선 LED 끝이 직각으로 도달하는 지점의 거리가 원룸이라는 공간을 고려하여 1m 이상이 되고, 두 번째는 제품과 근접한 곳의 LED 사각지대가 제품의 끝으로부터 10cm 내가 되는 기준을 두었다. 따라서 CATIA CAD 프로그램의 2D 설계를 통해 적절한 각도 30도를 설정했다. (실제로 빛은 직각으로만 전달되는 것이 아니라 사방으로 퍼지기 때문에 실제 최대 도달 거리는 기준값인 1m 보다 더 커진다.)&lt;br /&gt;
&lt;br /&gt;
'''기구부 구현'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_사각기둥.jpg|250픽셀]]&lt;br /&gt;
[[파일:99-1=0_Mood.jpg|250픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_완성본.jpg|250픽셀]]&lt;br /&gt;
[[파일:위에서_본_모습.PNG|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
UV-A LED를 장착할 사각 기둥을 3D Printing을 통해 만들었다. 또한 Mood Light를 위한 NeoPixel은 사각 기둥 밖에 다른 기구에 물결 모양으로 48개를 달았다. 최종적으로 아크릴판에 불투명 시트를 붙여 등을 만들었다.&lt;br /&gt;
&lt;br /&gt;
'''최종 작품'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:파랑이.jpg|250픽셀]][[파일:UV-A_LED.jpg|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
최종으로 Mood Light에서 단색 모드로 파랑색 조명을 킬 때와, UV LED Mode에서 On버튼을 눌렀을 때 결과이다.&lt;br /&gt;
&lt;br /&gt;
===제어부 및 회로 구현===&lt;br /&gt;
 UV-A LED, Motor 회로도&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_제어회로도1.PNG|600픽셀]][[파일:99-1=0_내부회로도2.jpg|600픽셀]]&lt;br /&gt;
 &lt;br /&gt;
SMPS로부터 220V 정격 전압을 12V 정격 전압으로 나누어 아두이노와 Relay Switch를 통해 UV-LED의 전원 On/Off 구현이 가능하다. 모터와 UV LED는 아두이노로 제어하기 때문에 라즈베리파이에서 데이터베이스의 값을 실시간으로 확인하다가 UV LED 동작이 지시되면 아두이노에 신호를 보내준다. 데이터베이스의 값은 안드로이드 App을 통해 갱신되므로 결국은 안드로이드 App을 통해 UV-LED의 On/Off 제어가 가능하다. 라즈베리파이와 아누이노간의 데이터 통신은 USB 케이블을 이용한 시리얼 통신을 이용한다.(라즈베리파이와 아두이노 간의 거리가 가깝고 전송하는 데이터가 단순하기 때문에 간단한 통신 방법 선택) 구현 언어는 파이썬이며 사용한 파이썬 모듈은 serial이다. 이와 관련하여 사용한 함수들은 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
UV-LED 동작을 설정하는 값이 “PowerON”일 경우 USB 포트로 연결된 아두이노로 UTF-8로 인코딩된 문자 u를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 u를 받아 UV-LED On 동작을 수행한다. 만약 UV-LED 동작 설정값에 “PowerON” 외의 값이 저장되었을 경우 (“PowerOFF“의 경우가 일반적이지만 예외 처리를 위해 else로 조건 판단) 아두이노로 UTF-8로 인코딩된 문자 d를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 d를 받아 UV-LED Off 동작을 수행한다.&lt;br /&gt;
&lt;br /&gt;
 NeoPixel 회로도&lt;br /&gt;
[[파일:99-1=0_네오픽셀_회로도.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
Mood Light구현은 NeoPixel과 Raspberry PI를 통해 구현했다. 사용자가 Application을 통해 색을 바꾸면 Database의 값이 아래 표에 Data로 바뀌면서 Mood Light 색이 변한다. NeoPixel의 Digital input을 통해 값이 들어가고, 그에 맞는 색을 자유롭게 바꿀 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:색깔.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:App_insidemode_screenshot.jpg|200픽셀]] [[파일:App_outsidemode_screenshot.jpg|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
 앱&lt;br /&gt;
&lt;br /&gt;
앱을 처음 설치하고 실행하면 앱은 팝업 윈도우로 모바일 디바이스의 위치 정보 권한을 요청하며 사용자는 이를 최초 1회만 수락하면 된다. 앱은 실행되면 즉시 모바일 디바이스의 현재 위치 정보를 받고 데이터베이스에 저장된 집의 위치 정보와의 거리를 계산한다. 이 거리가 일정 거리 이상이면 앱은 실외 모드로 자동 설정되고, 일정 거리 미만이면 실내 모드로 자동 설정된다. 실내 모드와 실외 모드의 전환은 기본적으로 비활성화되어 있으나 사용자가 우측 하단의 스위치를 켜면 활성화되어 강제 모드 전환이 가능하게 된다. 그래서 앱을 실행했을 때 자동 모드 설정이 잘못되면 사용자가 직접 모드를 전환해서 원하는 기능을 사용할 수 있다.&lt;br /&gt;
&lt;br /&gt;
앱은 크게 실내 모드와 실외 모드로 구분된다. 각 모드는 Fragment로 정의됐고 MainActivity의 bottomnavigationview를 통해 replace해서 전환할 수 있다. bottomnavigationview는 각 모드에 배치된 스위치가 꺼져 있으면 disabled되고 스위치가 켜지면 enabled된다. 사용자는 실내 모드에서 무드등을 제어하고 실외 모드에서 자외선 살균기를 제어한다.&lt;br /&gt;
&lt;br /&gt;
 실내 모드&lt;br /&gt;
실내 모드에선 무드등의 색상 선택, 밝기 조절, 전원 작동, 스페셜 모드 기능을 이용할 수 있다.&lt;br /&gt;
* colorpicker&lt;br /&gt;
: colorpicker의 색상은 초기에 12시의 연두색으로 설정되어 있고 사용자가 버튼을 드래그해서 원하는 색상을 선택할 수 있다. colorpicker의 중앙에 있는 원은 왼쪽 반원에 이전 색상을 표시하고 오른쪽 반원에 현재 색상을 표시하여 전후 색상을 비교하기 좋게 만들어준다. 사용자가 무드등의 전원을 키면 이전 색상이 표시된 반원이 현재 색상으로 교체된다. 색상은 여섯 자리의 16진수 #nnnnnn으로 표현되며 정수형으로 저장된다. 사용자가 colorpicker로 다양한 색상을 연속적인 범위에서 선택할 수 있기 때문에 선택의 폭이 넓고 세밀한 색상 선택이 가능하다. 무드등의 전원이 켜져 있으면 색상의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 밝기&lt;br /&gt;
: 무드등의 밝기 조절은 실내 모드 중앙의 seekbar를 통해 할 수 있다. 밝기 값은 최소 0에서 최대 255까지 선택 가능하다. colorpicker와 같이 무드등의 전원이 켜져 있으면 밝기의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 전원&lt;br /&gt;
: Power ON 버튼을 누르면 무드등의 전원이 켜진다. 전원 버튼 하단의 얇은 띠가 전원이 꺼져있을 때 회색, 전원이 켜져있을 때 녹색을 띠면서 사용자가 무드등의 전원이 켜져있는지를 직관적으로 알 수 있게 해준다. 전원 작동 데이터가 데이터베이스에 성공적으로 전송되면 앱 화면 하단에 Toast 텍스트를 표시하여 사용자에게 전원의 작동을 알려준다. 전원 버튼의 Power ON/Power OFF 텍스트는 colorpicker에서 선택된 색상과 실시간으로 동일하게 적용되어 사용자에게 무드등이 어떤 색으로 켜질지 더욱 직관적으로 와닿게 만들어준다.&lt;br /&gt;
* 스페셜 모드&lt;br /&gt;
: 앱을 실행하면 무드등의 모드는 기본 모드로 설정되어 있는 상태이다. 스페셜 모드 버튼을 누르면 순차적으로 웨이브 모드 1, 웨이브 모드 2, 레인보우 모드 1, 레인보우 모드 2가 설정되며 그후 다시 기본 모드로 돌아온다. 현재 설정된 모드는 스페셜 모드 버튼의 텍스트로 나타나서 사용자가 이를 알 수 있다. 또한 버튼을 누를수록 단계적으로 버튼의 배경 색이 진해지며 버튼의 깊이감을 더해준다. 스페셜 모드 버튼의 아래엔 모드의 전체 개수와 현재 모드가 몇 번째인지를 시각적으로 쉽게 알게 해주는 공백 칸들이 있다. 이 칸들은 기본 모드에서 모두 비어있게 되고 스페셜 버튼이 눌릴 때마다 한 개씩 채워지며 모두 채워진 후엔 다시 모두 빈 칸으로 돌아온다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스페셜 모드는 무드등이 켜져 있을 때에만 작동이 유효하다. 무드등의 전원이 꺼져 있을 때 스페셜 모드를 작동하면 무드등에서 아무 일도 일어나지 않는다. 이런 상황을 예방하기 위해 앱에서 무드등의 전원이 꺼진 상태에서 스페셜 모드만 작동하는 일이 일어나지 않게 한다. 무드등의 전원이 꺼진 상태에서 스페셜 모드 버튼을 누르면, 앱은 무드등의 전원도 키고 스페셜 모드도 작동한다. 또한 스페셜 모드가 설정된 상태에서 무드등의 전원을 끄면 앱은 스페셜 모드를 기본 모드로 설정하고 무드등의 전원을 끈다. 만약 사용자가 레인보우 모드를 바로 사용하고 싶다면 전원 버튼을 누를 필요 없이 바로 스페셜 모드 버튼을 3회 누르면 된다. 전원 버튼을 누르는 단계를 거치지 않아도 되기 때문에 빠르고 간편하게 즉시 스페셜 모드를 이용할 수 있다. 결과적으로 무드등의 전원이 꺼진 상태에서 무드등의 모드는 항상 기본 모드로 설정되어 있고, 스페셜 모드가 켜진 상태면 무드등의 전원도 항상 켜진 상태가 된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 실외 모드&lt;br /&gt;
실외 모드에선 위치 정보 설정, 자외선 살균기의 전원 작동, 타이머 기능을 이용할 수 있다.&lt;br /&gt;
* 위치 정보 설정&lt;br /&gt;
: HOME LOCATION 버튼을 누르면 모바일 디바이스의 현재 위치 정보를 데이터베이스에 저장하여 집의 위치로 설정한다. 설정한 집의 위치 정보는 앱이 실행됐을 때 집과 사용자 사이의 거리를 계산하는 데 이용된다. 데이터베이스에 저장된 위치 정보는 사용자가 새롭게 설정하지 않는 한 항상 일정하게 유지된다. 그래서 앱을 재설치했을 때 집의 위치 정보 설정을 다시 할 필요가 없다. 상수의 변수명을 대문자 알파벳으로 선언하는 프로그래밍 관습을 응용해서 위치 정보 설정 버튼의 텍스트도 대문자 알파벳으로 하였다. &lt;br /&gt;
* 자외선 살균기&lt;br /&gt;
: 실외 모드에서 Power ON 버튼을 누르면 자외선 살균기의 전원이 켜진다. 전원 버튼 위의 spinner에서 타이머가 작동할 시간을 선택할 수 있으며 초기 타이머는 1시간으로 설정된 상태이다. 타이머는 시간 단위로 선택할 수 있고 최대 12시간까지 선택 가능하다. 자외선 살균기의 전원을 켜면 전원 버튼 아래에 textview가 표시되어 타이머의 남은 시간을 알려준다. 설정한 타이머가 종료되면 앱이 자동적으로 자외선 살균기의 전원을 끈다.&lt;br /&gt;
* 시간 Spinner&lt;br /&gt;
: APP의 실외모드에선 자외선 살균기를 제어한다. 사용자는 작동 시간을 spinner로 선택하여 자외선 살균기 전원 버튼을 눌러 작동시킨다. 그러면 APP은 스피너에 선택된 시간과 전원 데이터를 데이터베이스에 전송한다. APP에서 사용자가 직접 자외선 살균기의 전원을 끌 수 있지만 설정한 시간이 경과되면 APP은 전원을 끄는 데이터를 데이터베이스에 전송한다. 만약 사용자가 작동 시간을 연장시키고 싶다면 자외선 살균기를 끄고 다시 작동하면 된다. 그러면 남은 시간이 다시 세팅되어 작동 시간이 증가하게 된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Firebase&lt;br /&gt;
[[파일:99-1=0_파이어베이스스크린샷.jpg|300픽셀]]&lt;br /&gt;
[[파일:99-1=0_파이어베이스연동.jpg|500픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Firebase는 Google 사의 모바일 및 웹 개발 플랫폼이다. Firebase는 많은 유용한 기능을 지원하지만 여기선 실시간 데이터베이스를 사용한다. Firebase의 장점은 안드로이드 및 iOS 앱과 연동이 용이하다는 것이다. 또한, Firebase는 시간과 장소에 구애받지 않고, 데이터가 안전하게 보존되며, 데이터를 읽고 쓰는 것이 간편하다. 그래서 Firebase는 사용자의 집의 위치 정보를 저장하기에 적합하다. 이 데이터는 제 3자가 접근할 수 없으며 사용자가 새롭게 설정하지 않는 한 영구히 보존된다. 앱은 Firebase의 데이터베이스에 집의 위치 정보를 저장하기도 하지만 읽어오기도 한다. 앱은 실행되면 데이터베이스의 집의 위치 정보를 읽어와서 현재 사용자의 위치 사이의 거리를 계산하고 실내/실외를 판별한다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 작동할 때에도 Firebase를 이용한다. 사용자가 앱으로 작동한 무드등의 색상, 밝기, 전원, 모드 데이터와 자외선 살균기의 전원, 타이머 데이터는 Firebase의 데이터베이스에 저장된다. 앱은 무드등의 전원이 켜진 상태에서 색상이나 밝기가 변경되면 변경 사항을 즉시 데이터베이스에 반영한다. 자외선 살균기의 타이머는 데이터베이스에 저장되지만 남은 타이머 시간까지 일일이 저장되진 않는다. 앱에서 설정한 시간이 지나면 자외선 살균기의 전원을 끄는 데이터를 저장할 뿐이다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 직접 구동시키는 것은 라즈베리파이와 아두이노이다. 라즈베리파이는 데이터베이스에 저장된 무드등의 색상, 밝기, 전원, 모드 데이터를 읽어와 라즈베리파이에 직접 연결된 네오픽셀 LED의 작동을 지시한다. 또한 살균기의 전원 데이터를 읽어와 아두이노에 살균기를 들어올리는 모터의 동작과 살균기 LED의 동작을 지시한다. 아두이노에서는 라즈베리파이의 지시를 대기하다가 살균기의 ON/OFF 동작 신호를 받아 살균기를 켜고 끈다. 이 때 살균기가 부착된 구동부의 높이는 아두이노에 연결된 초음파센서로 거리를 측정하여 조절한다.&lt;br /&gt;
&lt;br /&gt;
===UV-LED 곰팡이 저해 실험===&lt;br /&gt;
&lt;br /&gt;
본 실험은 UV-A LED의 곰팡이 생육 저해 능력을 검증하기 위한 실험이다. 구체적인 저해율의 수치를 나타내기 위해 1차 살균 실험에 이어 2차 곰팡이로 실험을 하여 저해율을 나타낼 것이다.&lt;br /&gt;
&lt;br /&gt;
: 1. 실험 준비&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비1.png|200픽셀|ㅇ|왼쪽|Petri Dish, Sterile cork borer, 곰팡이]]    [[파일:99-1=0_세균실험준비2.png|173픽셀]]&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비3.png|200픽셀]]    [[파일:99-1=0_세균실험준비4.png|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 실험 과정&lt;br /&gt;
&lt;br /&gt;
:: 1) 먼저 3종의 곰팡이(Cladosporium sp, Fusarium sp, Aspergillus sp)를 키울 배지를 만들기 위해 Difo회사 제품인 PDA 12g과 Agar 10g을 비커에 넣은 후 500ml distilled water를 넣어준 후 autoclave에서 121℃에서 15분간 돌려준 뒤 petri dish에 부어 1~2일 동안 말린다.&lt;br /&gt;
&lt;br /&gt;
:: 2) 곰팡이가 PDA배지에서 충분히 성장했다면 이를 각각 Petri Dish에 접종한다. 이 때 일정한 양의 곰팡이를 접종하기 위해 그림에 있는 Sterile cork borer를 사용한다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정1.png|200픽셀]]    [[파일:99-1=0_세균실험과정2.png|182픽셀]]&lt;br /&gt;
: [[파일:99-1=0_세균실험과정3.png|200픽셀]]    [[파일:99-1=0_세균실험과정4.png|195픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 3) 3종의 곰팡이를 0.5m, 0.75m, 1m 간격으로 배치시키고, 다른 하나는 대조군으로 UV-A LED에 노출시키지 않은 채로 성장시킨다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정5.png|200픽셀]]    [[파일:99-1=0_세균실험과정6.png|172픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 4) Petri Dish 안에서 곰팡이의 성장률을 원의 반지름으로 근사시킵니다. 또한 생리식염수를 10마이크로미터 떨어트린 후, 소량의 곰팡이를 접종해준 뒤 커버 글라스를 붙이고 광학현미경으로 관찰한다.&lt;br /&gt;
&lt;br /&gt;
:: 5) 곰팡이가 자란 원의 반지름을 측정하고, 이를 저해율로 나타낸다. 저해율의 식은 다음과 같다. (저해율(%)= (대조군의 반지름 - 실험군의 반지름)/대조군의 반지름 * 100)&lt;br /&gt;
: [[파일:99-1=0_곰팡이_측정.jpg|400픽셀]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 3. 실험 결과&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 1) Cladosporium sp(벽지 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Cladosporium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 2) Fusarium sp(토양 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Fusarium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 3) Aspergillus sp(누룩 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Aspergillus_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
아래 표는 현미경을 통해 관측한 곰팡이의 반지름(cm)이고, 이를 위해 저해율식에 대입하여 곰팡이 저해율을 계산하였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:길이값.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:저해율.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:5시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:10시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 4. 결론&lt;br /&gt;
: : 최종 저해율로 나타낸 결과 UV-A LED에 5시간, 10시간에 따른 차이는 거의 없었지만 거리에 따른 저해율의 차이는 눈에 띄었다. 0.5m 거리에선 약 80%의 저해율을 보였고, 0.75m 에서는 Cladosporuim sp와 Fusarium sp은 약 50%의 저해율을 보였지만 Aspergillus sp는 30%의 저해율을 보였습니다. 1m 거리에선 저해율이 많이 떨어졌는데, Cladosporium sp는 39%, Fusarium sp는 25%, Aspergillus sp는 6%의 저해율을 보였습니다. 1m에서의 살균력은 많이 떨어졌고, 이를 보완하기 위해 기존 실험은 25cm UV-A LED 2개를 설치했는데, 제품에는 한 슬라이드 당 3개로 늘려 설치했다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic1.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/8/87/99-1%3D0_%EC%98%81%EC%83%81.avi]]  [[파일:99-1=0_videopic2.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/c/ce/99-1%3D0_%EC%98%81%EC%83%812.avi]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic3.png|300픽셀|link=https://capstone.uos.ac.kr/mie/images/8/82/99-1%3D0_%EC%98%81%EC%83%813.mp4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
'''UV-A LED Mode &amp;amp; Mood Light mode'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:UV-A LED.jpg|250픽셀]][[파일:99-1=0_무드등.jpg|250픽셀|link=https://capstone.uos.ac.kr/mie/images/99-1=0_영상.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_미구현내용.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_평가항목.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
[[파일:99-1=0_평가_결과.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
노*진: 프로젝트의 주제를 결정하기 전까지만 하더라도 라즈베리파이와 각종 모듈에 대한 지식이 없어 막막했습니다. 하지만 잘 모르는 만큼 팀원들과 더욱 조사를 열심히 하고, 구체적으로 설계를 하였습니다. 개념설계 발표 때까지 ‘과연 의도한대로 동작할까?’ 라는 의문점을 가졌지만 중간발표 준비를 하면서 원하는 기능들이 하나 둘씩 구현되고, 기구부의 Mechanism들이 완성되는 것을 보면서 재밌었고, 성취감을 느꼈습니다. 프로젝트를 진행하면서 전공 지식이 아닌 전기 회로도와 관련된 일들을 구글링과 여러 사이트에 물어보면서 해결을 했고, UV-LED의 살균력을 검증하기 위해 생명공학 전공인 지인의 도움을 받으면서 프로젝트의 문제들을 해결했습니다. 이 과정에서 잘 모르는 부분을 어떤 방법으로 해결할 지에 대한 능력을 기를 수 있었습니다. 마지막으로 내장형시스템 과목을 통해 기계정보공학과의 매력을 느낄 수 있었고, 앞으로 프로젝트를 수행할 때 자신감을 가질 수 있을 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
박*훈 : 기계정보공학을 전공한지 4학년 1학기에 처음으로 컴퓨터 관련 프로젝트를 진행했습니다. 그동안 기계 관련 CAD 프로젝트나 CAE 프로젝트는 다수 경험이 있었지만 컴퓨터 분야는 경험이 없었기 때문에 프로젝트 과정 중 가장 처음 과정인 주제 선정에서부터 엄청난 난관에 부딪혔습니다. 하지만 주어진 시간 안에 주어진 모듈로 할 수 있는 주제를 생각해냈고, 프로젝트를 시작했습니다. 프로젝트 진행 과정 중에 한계에 도달하는 일도 있었고 뜻처럼 진행되지 않는 일도 있었지만 팀원들과 함께 돌파구를 찾아가며 차근차근 프로젝트를 완성했습니다. 프로젝트를 진행하면서 기계정보공학의 융합적 전공의 취지에 맞게 기계적인 메커니즘과 임베디드 시스템을 융합한 완성품을 제작하기 위해 노력을 했고, 실제로 저는 기구부의 메커니즘 쪽을 맡아 CAD 프로그램인 CATIA를 통해 기어 및 랙 피니언 세트와 슬라이드 핀 등을 설계하고 3D 프린터를 이용하여 실제 구현을 완료했습니다. 기구부 제작 과정 중 값비싼 3D 프린터 제작의 비용과 3D 프린터 제작품의 과도한 무게 등의 문제가 있었지만, 팀원들과 협의한 끝에 해결 방안을 고안하여 이러한 문제를 해결했습니다. 이번 내장형 프로젝트의 경험을 통해 팀원들과의 소통 능력과 문제 해결 능력을 한층 성장시켰고, 앞으로 있을 프로젝트에 큰 도움이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
주*완: 이번 학기에 내장형시스템및실습을 수강하며 처음으로 주제 선정부터 완제품 제작까지 모든 것을 직접 경험했습니다. 주제 선정부터 시작해 모든 과정이 쉽지 않았고 걸림돌에 걸려서 결국 설계를 바꾸기도 했습니다. 하지만 한계를 극복할 방법을 고민하고 공부하고 질문하고 찾아 헤매면서 많은 것을 배웠습니다. 특히 제가 맡은 부분인 안드로이드 앱을 개발하기 위해 1학년 때 배웠지만 모두 잊어버린 java를 처음부터 다시 공부했고, 안드로이드를 아무것도 모르는 상태에서 인터넷 검색만으로 하나하나 공부하고 직접 코드를 짜서 완성까지 했습니다. 과정 중에 알 수 없는 이유때문에 앱이 원하는 대로 안 되거나 강제 종료 될 때가 매우 매우 많았는데, 스스로 계속 공부하고 원인을 찾고 고쳐 나가서 마침내 앱을 완성할 수 있었고, 스스로 문제를 해결하는 능력을 기를 수 있었습니다. 또한 기구부와 외형을 이루는 부분을 만들 때 자르고 다듬는 일이 많이 필요했는데 서로 필요한 부분이 있으면 도와주면서 협동하여 결과가 잘 나온 것 같아 만족스럽습니다. 프로젝트를 하며 값진 경험을 했고 앞으로 다른 프로젝트를 하게 된다면 이 경험이 소중한 밑바탕이 돼서 도움이 많이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
함*규: 임베디드 시스템을 처음 접해보기에 주제를 정하는 과정이 가장 어려웠던 것 같습니다. 처음에는 주어진 시간 내에 해낼 수 없는 비현실적인 주제에 대해 고민하며 기능적 한계에 부딪혀보기도 하고, 챗바퀴 도는 듯한 생각이 들었지만, 이 과정 속에서 전체적인 임베디드 시스템에 대한 이해와 할 수있는 것과 할 수 없는 것에 대해 구분할 수 있는 능력을 얻을 수 있었습니다. 이후에 직접 정한 주제를 바탕으로 프로젝트를 수행해나가면서, 초기 설계로는 해결할 수 없는 문제들을 팀원들과 함께 협동하여 풀어나가는 과정은 정말 힘들기도 했지만 되돌아 보면 책상에서는 배울 수 없는 많은 경험들을 배웠다고 생각합니다. 4-1학기를 떠올리면 많은 희노애락이 있었던 내장형 밖에 생각나지 않을 것 같은 데, 정말정말 재밌었고(농담이 아닙니다.) 후배들에게 꼭 추천해주고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
유*환: 내장형 시스템 프로젝트의 주제를 정하는 과정이 가장 어려웠습니다. 라즈베리파이에 센서나 모터, 또다른 보드를 연결하여 사용한 경험이 없어 주어진 시간 내에 완성할 수 있는 정도를 알기 어려웠습니다. 그래서 처음에는 학기 내에 완성이 불가능한 주제들을 생각했었습니다. 결국 마지막까지 주제를 정하지 못하다가 막바지에 급하게 살균 무드등을 만들기로 했습니다. 주제가 정해지고 자료조사를 마쳤을 때만 해도 힘든일이 끝나고 모든 일이 수월하게 진행될 것만 같았습니다. 그런데 출입 감지 방법, 음악 주파수 분석 등이 뜻대로 되지 않자 다시 조마조마해졌습니다. 하지만 적절한 대안을 찾고 해결해나가면서 완성되가는 프로젝트를 보며 뿌듯했습니다. 그리고 살균 무드등을 완성하고 영상까지 마무리 지었을 때는 그동안 밤을 새며 고생했던 시간들에 대해 보상받는 느낌이 들었습니다.&lt;br /&gt;
무엇보다 팀원 형들이 프로젝트에서 각자가 맡은 열할을 너무 잘 수행해주어서 감사했습니다. 유일하게 라즈베리파이 경험이 있는 저보다도 잘해주어서 미안하기도 하고 대단하다는 느낌이 들었습니다. 내장형 프로젝트는 과정은 정말 힘들었지만 팀원들과 살균 무드등을 만드는 것이 재밌었고 프로젝트 진행에 대한 경험치가 쌓여 좋은 기회가 되었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5916</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5916"/>
				<updated>2020-06-22T01:53:04Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 느낀점 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
[[파일:개발일정_착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
===영상인식 결과===&lt;br /&gt;
[[파일:영상인식.JPG|600픽셀|가운데|섬네일|그림9. 학습 결과 손실함수 및 IOU]]&lt;br /&gt;
===판별 정확도===&lt;br /&gt;
[[파일:정확도.JPG|600픽셀|가운데|섬네일|그림10. 판별정확도]]&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;br /&gt;
&lt;br /&gt;
윤*상 - RVM을 개발하면서 어느정도 수준의 하드웨어 제작이 필요한 시스템을 개발할 때에는 기본적인 하드웨어 시스템을 구현해놓고 소프트웨어를 개발하는 것이 효율적인 것을 느꼈습니다. 이번 프로젝트에선 대부분의 소프트웨어를 개발한 뒤 하드웨어를 제작하였는데 이 과정에서 예상치 못한 문제들이 발생하였습니다. 모터와 센서의 개수가 많아 전력 공급에서 문제가 생기고 배선과 같은 부분에서 제대로 작동하지 않아 오랜 시간이 걸렸습니다. 하지만 우리 팀원 모두 열심히 프로젝트에 임해주었고 소통도 활발히 되었으며 개개인의 능력 모두 뛰어났기 때문에 빠르게 문제를 해결하여 프로젝트를 마무리 할 수 있었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8E%9C%ED%83%80%EC%BD%94%EC%96%B4_-_%ED%97%88%EB%A6%AC%EC%97%85!_%EC%9E%90%EC%84%B8%EA%B5%90%EC%A0%95%EA%BD%83&amp;diff=5915</id>
		<title>펜타코어 - 허리업! 자세교정꽃</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8E%9C%ED%83%80%EC%BD%94%EC%96%B4_-_%ED%97%88%EB%A6%AC%EC%97%85!_%EC%9E%90%EC%84%B8%EA%B5%90%EC%A0%95%EA%BD%83&amp;diff=5915"/>
				<updated>2020-06-22T01:13:32Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 프로젝트 결과 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 허리업! 자세교정꽃&lt;br /&gt;
압력센서와 가속도계를 이용한 자발적 자세교정 시스템&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
 2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
 서울시립대학교 기계정보공학과 20154300** 표*근 (팀장)&amp;lt;br/&amp;gt; &lt;br /&gt;
 서울시립대학교 기계정보공학과 20154300** 고*균 &amp;lt;br/&amp;gt; &lt;br /&gt;
 서울시립대학교 기계정보공학과 20154300** 백*우 &amp;lt;br/&amp;gt;&lt;br /&gt;
 서울시립대학교 기계정보공학과 20154300** 이*현 &amp;lt;br/&amp;gt;&lt;br /&gt;
 서울시립대학교 기계정보공학과 20174300** 강*혁 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
 압력센서와 가속도 센서를 통한 자세 판단을 바탕으로 화분의 로봇 꽃을 제어해 사용자에게 피드백을 제공하는 자발적 자세 교정 시스템을 제작한다. 등받이에 부착된 가속도 센서를 통해 전체적인 자세를 판단하고, 사용자가 잘못된 자세를 취할 시 화분의 꽃 로봇이 잘못된 자세를 모방하는 움직임을 표현하여 사용자가 인지하도록 한다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경===&lt;br /&gt;
현대인들은 학업, 업무 등으로 인해 책상에 앉아 있는 시간이 점점 길어지고 있고, 이와 관련된 질환들로 내원하는 빈도가 빠르게 증가하고 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 척추 질환자 수.jpg|500픽셀|섬네일|가운데|그림 1. 20대 척추 질환자 수 추이]]&lt;br /&gt;
&lt;br /&gt;
이러한 통계를 뒷받침하듯 다양한 자세 교정 제품들이 시중에 출시되었으며, 이들의 공통적인 특징은 물리적으로 사용자의 행동을 제약하여 올바른 자세를 유도한다는 것이다. 그러나 많은 소비자 설문에 의하면 자발적인 자세 유지가 이 &lt;br /&gt;
루어지지 않을 시 사용자의 불편함을 초래할 수 있으며, 자세 교정을 위한 물리적인 피드백(진동, 움직임 제약 등)에 대한 부정적인 반응이 두드러지게 나타나는 경향을 보인다. 또한, 좋은 &lt;br /&gt;
자세는 어떤 기구에 의해 유지되어야 하는 것이 아니므로, 사용자의 자발적인 자세 교정을 유도하는 새로운 방식의 자세 교정 시스템이 좋은 자세 습관 형성에 기여할 것으로 예상한다.&lt;br /&gt;
[[파일:바르지 못한 자세 습관.png|500픽셀|섬네일|가운데|그림 2. 바르지 못한 자세습관]]&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
&lt;br /&gt;
본 시스템의 목적은 사용자의 자발적인 자세 교정과 좋은 자세 습관의 확립이며, 큰 불편함 없이 자세 교정기구를 사용할 수 있다는 장점이 있다. 또한, 애플리케이션의 인터페이스를 통해 자신의 자세 점수, 경향을 직관적으로 파악할 수 있어 간편한 자세 모니터링이 가능하다. 추후 누적된 데이터를 통해 사용자의 자세 습관을 파악하여 적절한 피드백을 제공하는 기능을 추가한다면 자세 습관 개선에 큰 도움이 될 것이다.&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:펜타코어_목적_계통도.PNG|800픽셀|섬네일|가운데|그림 3. 목적 계통도]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 ''' ㉠ 편의성'''&lt;br /&gt;
본 시스템은 강압적이지 않은 교정 방식으로 사용자의 올바른 자세 습관을 유도하므로, 보조 의자를 장시간 이용할 때 불편함이 있으면 안 된다. 또한, 실제 사용 시 별도의 조작 없이 간편하게 작동해야 하며 사용자에게 자세 정보를 적절한 때에 제공할 수 있어야 한다. 이상적인 설계에서 자세 정보를 수집하는 의자는 외부 전원공급 없이 작동할 수 있어야 하며, 다양한 환경에서 큰 제약 없이 사용이 가능하도록 한다. 그리고 안드로이드 OS 기반의 애플리케이션을 통해 수집된 데이터의 경향과 날짜, 시간별 자세 추이를 어디서나 손쉽게 확인할 수 있도록 시스템을 구성한다.&lt;br /&gt;
&lt;br /&gt;
 ''' ㉡ 정확성'''&lt;br /&gt;
올바른 자세라는 다소 추상적인 특성에서 구체적인 기준을 설정하고 사용자의 자세를 판단해야 하므로, 사용 전 기준 확립과 자세판단에서 높은 정확도가 요구된다. 이러한 점을 보완하기 위해 자이로 센서의 기울기 정보를 바탕으로 물리적 수평을 감지하며, 효율적인 로드셀 배치로 편향된 무게 분포를 파악할 수 있도록 한다. 그리고 전체 시스템의 유일한 구동부인 화분 모듈에서는 사용자가 잘못된 자세를 취할 때 이를 즉각적으로 명확하게 표현할 수 있어야 하므로 신속한 반응성이 서보 모터를 통해 구현되어야 한다. &lt;br /&gt;
&lt;br /&gt;
 ''' ㉢ 지속성'''&lt;br /&gt;
전체적인 완성품의 지속성은 크게 두 가지의 특성에 적용될 수 있다. 첫째는 H/W의 물리적 내구성을 의미하며, 의자에 가해지는 외부 충격이나 잠재적인 파손에 시스템을 보호할 수 있도록 안정성을 갖추는 설계를 지향한다. 두 번째 지속성은 사용자의 자세 데이터 수집과 관리의 측면에서 누적된 데이터를 체계적으로 정리, 분석하여 올바른 자세를 위해 사용자에게 적절한 피드백을 제공할 수 있어야 한다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:펜타코어_시나리오.png|800픽셀|섬네일|가운데|그림 4. 시스템 작동 시나리오]]&amp;lt;br/&amp;gt;&lt;br /&gt;
전체 시스템의 동작은 의자에서 변화하는 사용자 자세 데이터를 입력받아 저장하면 화분 모듈이 의자 모듈의 서버에서 데이터를 불러오고, 데이터에 기록된 자세 모드에 따라 애플리케이션 내의 Background Process로 꽃을 실시간으로 작동시킨다. 구체적인 시나리오로는 사용자가 의자와 화분 모듈에 전원을 인가하고, 화분의 디스플레이에 표시된 ‘측정하기’, ‘이어하기’ 두 버튼을 통해 자세 판단 및 피드백 기능을 실행할 수 있다. 사용자는 실시간으로 변화하는 자세에 대한 꽃의 피드백을 2초 단위로 받을 수 있으며, 앉아 있는 시간 동안 데이터는 계속해서 누적되어 의자 사용 시간, 자세 점수 등을 확인할 수 있다. 사용 종료를 위해서는 ‘오늘은 여기까지’ 버튼을 통해 시스템을 종료하여 자세 점수 측정을 중단할 수 있다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담===&lt;br /&gt;
	'''표*근 [팀장]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	프로젝트 진행 상황을 총괄하며 자재 관리 및 팀 구성원 간 역할분담과 조율 및 실험을 수행하였고, 회로도 구성 및 프로젝트 진행 과정에서 필요한 서식 문서들의 작성과 검토, 최종 승인까지 담당하여 팀의 중추를 담당하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
	'''고*균 [화분 제작 및 서보모터 제어 담당]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	허리 업! 시스템의 유일한 구동부인 서보모터를 통한 꽃 제어를 담당하였고, 다양한 메커니즘으로 꽃의 움직임을 실험, 관찰하여 시스템에 적합한 구조를 설계하였다. 또한, 블록을 이용해 화분을 제작하여 터치 디스플레이와 라즈베리파이, 서보모터가 모두 내장되며 심미성과 내구성을 갖춘 화분을 제작하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
	'''백*우 [서버 구축 및 통신 시스템 담당]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	의자 모듈의 라즈베리파이로 서버를 구축하여 화분 모듈이 실시간으로 데이터를 읽어와 꽃을 구동시킬 수 있도록 하였고, 추가적으로 압력 센서 배치를 위한 민감도 최적화를 수행하여 자세 판단 정확도를 크게 향상시켰다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
	'''이*현 [안드로이드 애플리케이션 개발]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	안드로이드 Things를 기반으로 작동할 수 있는 애플리케이션을 중점적으로 개발하였다. 애플리케이션의 UI 디자인과 통계 기능들을 효과적으로 수행할 수 있도록 프로그램을 설계하였으며, 사용자 편의성과 사용의 지속성을 위한 심미성을 확보하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
	'''강*혁 [실험 계획 및 하드웨어 제작 담당]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	초기 설계 단계에서 의자의 압력 분포를 확인하기 위한 실험 계획 및 수행, 데이터 해석과 의자 모듈 제작을 담당하였다. 자세 판단 코드를 작성하였으며 사용자의 정확한 자세를 판단하기위한 기준 체계를 확립하였다.&lt;br /&gt;
&lt;br /&gt;
===개발 일정===&lt;br /&gt;
[[파일:펜타코어_개발_일정.PNG|987픽셀|섬네일|가운데|그림 5. 개발 일정]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:펜타코어_전체_시스템_구성도.png|778픽셀|섬네일|가운데|그림 6. 전체 시스템 구성도]]&amp;lt;br/&amp;gt;&lt;br /&gt;
전체 시스템은 크게 의자 모듈과 화분 모듈로 구분할 수 있고, 구체적인 구성은 다음 그림과 같다. 각 모듈에 라즈베리파이가 내장되어 데이터 처리 및 통신, 제어 기능을 수행한다. 보조 의자에서는 사용자 환경에 맞게 설정된 기준에 따라 센서에서 입력된 데이터를 바탕으로 자세를 판단하며, 사용자의 자세가 바르지 않은 경우 화분 모듈의 꽃을 제어해 잘못된 자세를 모방함으로써 올바른 자세로 고칠 수 있도록 유도한다.&lt;br /&gt;
&lt;br /&gt;
===보조 의자 모듈 설계 및 회로 구현===&lt;br /&gt;
[[파일:펜타코어 의자 모듈 및 회로 구성.png|600픽셀|섬네일|가운데|그림 7. 의자 모듈 및 회로 구성]]&amp;lt;br/&amp;gt;&lt;br /&gt;
의자 모듈은 라즈베리파이4, 직경 12mm FSR 압력센서, 자이로 센서, 4채널 어레이 저항 브릿지, ADC칩과 20000mAh 용량의 보조배터리로 구성된다. 본 시스템에 사용되는 의자는 에이블루社의 Curble Chair로 여러 모델 중 프로젝트의 목적에 가장 잘 부합하는 제품인 커블 체어 와이더(Curble Chair Wider)를 사용하였다. 본 의자는 인체공학적으로 설계되어 사용자의 척추와 골반의 각도를 제한하여 척추 중립자세를 유지하도록 도와준다. 라즈베리파이를 통해 압력센서와 자이로센서로부터 얻은 데이터를 수집 및 처리하여 사용자의 자세를 판단한다. 또한, 서버를 열어 화분 모듈과의 통신을 가능하게 하며 사용자의 자세 모드를 전송하는 역할을 한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어_압력_센서_배치.png|500픽셀|섬네일|가운데|그림 8. FSR 압력 센서 부착 위치]]&amp;lt;br/&amp;gt;&lt;br /&gt;
4개의 압력센서는 사용자의 둔부와 허벅지 압력을 통해 의자 사용 여부 및 다리 꼼을 감지하는 역할을 한다. 나머지 한 개는 의자의 등 부분에 위치하며 사용자의 등이 굽은 자세를 감지한다. 이는 인체 해부학적으로 등을 굽었을 시 압력센서의 값이 증가하는 것에 착안하여 위치를 결정하였다. 5개의 센서는 4채널 저항 브릿지와 ADC 칩인 MCP-3208에 연결되어 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어_자이로_수평.png|450픽셀|섬네일|가운데|그림 9. 의자 등받이에 부착된 자이로센서]]&lt;br /&gt;
또한, 사용자의 전체적인 움직임은 등받이 뒤편에 위치한 자이로 센서로 파악한다. 등받이 뒤편에는 보조배터리가 부착되어 있으며 이는 라즈베리파이의 전원 공급을 담당한다. 콘센트로부터 외부전원이 입력된다면 사용자가 본 시스템을 이용하는 데 있어 제한사항이 발생하므로 이를 해결하기 위해 보조배터리를 부착하여 해결하였다. 보조배터리는 20,000mA의 대용량으로 라즈베리파이4의 정격 전원인 [5V,3A]를 충족함과 동시에 사용자에게 12시간 이상의 사용시간을 제공한다. 사용 후 충전을 통해 다음날 사용이 가능하다. &amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어_의자_모듈_케이싱.png|500픽셀|섬네일|가운데|그림 10. 만능기판으로 구성된 의자 모듈]]&amp;lt;br/&amp;gt;&lt;br /&gt;
의자 모듈 시스템의 회로는 만능기판을 통해 구성되어 있으며, 외부의 충격으로부터 회로를 보호하기 위해 블록으로 케이싱 되어있다.&lt;br /&gt;
&lt;br /&gt;
===화분 모듈 설계 및 회로 구현===&lt;br /&gt;
[[파일:펜타코어 화분 모듈 구성도.png|1200픽셀|섬네일|가운데|그림 11. 화분 모듈 구성도]]&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:화분내부.JPG|600픽셀|섬네일|가운데|그림 12. 화분 모듈 내부 모습]]&lt;br /&gt;
화분 모듈은 라즈베리파이3, 서보모터, 7인치 터치 디스플레이, 꽃 모형으로 구성되어 있다. 화분 모듈의 기능은 의자 모듈로부터 사용자의 자세 정보를 받아 사용자에게 피드백을 제공하는 것이다. 자세 문제에 대한 피드백은 터치 디스플레이와 꽃 모형을 통해 이루어진다. 라즈베리파이에는 Android Things를 활용한 어플리케이션이 구동되며 서보모터와 애플리케이션을 동작시킨다. 꽃 모형은 탄성체인 스프링과 와이어로 구성되어 있다. 서보모터의 제어를 통해 꽃 모형이 사용자의 자세 모드에 해당하는 움직임을 취하도록 구현하였으며 사용자는 이를 통해 자신의 잘못된 자세를 인지하게 된다. 화분의 디스플레이를 통해 실시간 자세 피드백을 받아 볼 수 있으며 애플리케이션의 다양한 기능을 활용할 수 있다. 전체적인 외형은 블록을 통해 구현되어 내부의 구성품들을 보호하며 심미성을 더해준다.&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
소프트웨어부는 자세 판단 알고리즘, 서버 구축 및 통신, 편리한 시스템 사용을 위한 애플리케이션으로 구분된다. &amp;lt;br/&amp;gt;&lt;br /&gt;
====자세 판단 알고리즘====&lt;br /&gt;
초기 설계 단계에서 자세 판단 방법을 확립하기 위해 다음과 같은 논의를 거쳤으며, 요약된 과정은 내용은 표와 같다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 자세 판단 방법 표.PNG|800픽셀|섬네일|가운데|표 1. 자세 판단 방법의 변화 ]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 롤 피치.png|500픽셀|섬네일|가운데|그림 13. 의자에 앉은 사용자의 Rolling과 Pitching]]&amp;lt;br/&amp;gt;&lt;br /&gt;
이후 확립된 자세 판단 코드는 의자 모듈의 라즈베리파이에서 동작하며 압력센서와 자이로 센서의 값을 통해 사용자의 자세를 판단한다. 압력센서는 사용자가 특정한 자세를 취할 때 변화하는 압력을 감지하며, 자이로 센서는 전체적인 사용자 몸의 기울기를 감지한다. 자세 판단에는 기본 자세 모드와 특정 자세 모드 두 가지로 나뉜다. 기본자세 모드는 등받이 뒤에 부착되어 있는 자이로 센서를 활용하여 사용자의 앉은 자세 기울기를 판별한다. 자이로 센서로부터 A_x, A_y, A_z 값을 읽을 수 있으며 이를 다음과 같이 계산하여 오일러 각(Euler angle)을 계산할 수 있다. 계산된 Pitch와 Roll 각도를 통해 사용자가 어떤 자세를 취하였는지 확인할 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 각 모드.png|1100픽셀|섬네일|가운데|그림 14. Rolling과 Pitching에 의한 자세 모드 구분(좌), 7가지의 기본 자세 모드]]&amp;lt;br/&amp;gt;&lt;br /&gt;
특정 자세 모드는 의자에 부착된 압력센서를 통해 판별되며 센서의 위치는 그림 6과 같다. 자세 점수는 ‘기본 자세 모드’와 ‘특정 자세 모드’의 조합을 통해 결정되며 초기 100점에서 각 자세별 감점이 적용되어 합산된 점수가 Sensing interval마다 DB에 저장된다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====서버 구축 및 통신 시스템====&lt;br /&gt;
[[파일:펜타코어 서버 표.PNG|900픽셀|섬네일|가운데|표 2. 서버 및 통신 시스템 구성도]]&amp;lt;br/&amp;gt;&lt;br /&gt;
위의 표는 의자 모듈의 프로그램 구성이다. 총 세 개의 프로그램으로 작동하며 각 프로그램은 데이터 수집, 서버 그리고 데이터베이스 역할을 한다. main.out은 데이터 수집 프로그램으로 GPIO를 통해 얻어온 데이터를 data.json 파일로 저장하는 역할을 한다. conv는 데이터베이스 프로그램으로 main.out의 결과물로 얻어진 data.json을 cron을 통해 일정 주기로 데이터베이스에 갱신하는 역할을 한다. 여기서 말하는 데이터베이스는 서버 컴퓨터(의자 모듈 내장 라즈베리파이)에 json file로 물리적으로 저장되어 있다. server는 npm express 모듈로 구성했으며 http 서버 구성을 따른다. REST API로 통신하며 클라이언트로부터 GET 요청만 받도록 설정되어있다. Route: /init은 conv를 실행하기 위한 키값을 가진 ok.json에 접근하여 GET 요청 시 conv를 활성화시킨다. Route: /db는 GET 요청 시 데이터베이스에 접근하여 해당 데이터를 전달한다. 그중 route:/db/end는 GET 요청 시 conv에서 갱신한 데이터들을 day.json과 weekdb.json에 정리하여 저장하며 ok.json에 접근하여 conv를 비활성화한다. 각 프로그램은 profile.d 서비스의 start*.sh에 의해 실행된다. AP mode를 수행하기 위해 hostapd와 dnsmasq를 사용했으며 dhcpcd와 rc.local를 수정하여 AP mode를 활성화한다.&lt;br /&gt;
&lt;br /&gt;
====애플리케이션====&lt;br /&gt;
라즈베리파이3에 Android Things를 설치하여 애플리케이션이 동작하도록 한다. IoT 개발에서 가장 중요한 부분은 연결성, 보안, 개발 환경이다. 우리는 안드로이드 씽스(Android Things)의 장점인 안드로이드 운영체제와 같은 맥락의 운영체제이며 기존 스마트 폰 앱처럼 IoT 앱을 개발할 수 있다는 점, 운영체제와 네트워크에 대한 별도의 개발이 필요없어 개발 난이도가 상대적으로 낮다는 점, 자바(JAVA)언어를 통해 프로그래밍이 가능하여 이식성이 좋다는 점에 착안하여 이를 선택하였다. 안드로이드 프레임워크를 적용한 임베디드 시스템을 만들 수 있기에 이를 적극적으로 활용하기로 하였다. 다음은 어플리케이션의 기본적인 기능 흐름도이다.&lt;br /&gt;
[[파일:펜타코어 애플리케이션 플로우차트.png|900픽셀|섬네일|가운데|그림 15. 애플리케이션 Flow Chart]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====클래스 내용=====&lt;br /&gt;
크게 시작, 메인, 달력, 통계1(주차 그래프), 통계2(실시간 그래프) 로 총 5개의 엑티비티로 이루어져있다. 또한, 클래스는 크게 4가지 기능으로 구분된다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''□ 통신 관련 클래스'''&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ MainActivity : 화분 모듈이 서버와 잘 통신되고 있는지 확인하고, 승인이 완료되면 메인화면으로 넘어갈 수 있다. 또한, 가장 초기화면으로써, 바른자세에 대한 안내, 이어서 의자 사용하기 등의 역할을 수행한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ Requestqueue : FIFO 구조로 json오로 요청하고 받는 것을 순차적으로 진행할 수 있게 처리한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ JsonObjectRequest,JsonArrayRequest : json파일을 array 또는 object 형식에 따라 요청한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''□ 모터제어 관련 클래스'''&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ mainscreen : 실시간으로 서버에서 받아오는 json파일을 파싱하여 알맞은 값들을 적재적소에 뿌려, 점수, 자세 등을 표출하며, 꽃을 모터제어를 통하여 제어한다. 제어할 때에는 json파일에 맞게 스레드를 생성하여 처리한다. &amp;lt;br/&amp;gt;&lt;br /&gt;
◎ servo : 안드로이드 씽스에서 사용 가능하며, 서보 모터를 인식할 수 있도록 처리한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''□ 달력 관련 클래스'''&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ calendars : 달력을 생성하여, 날짜를 클릭 시에 그 날짜를 읽어서 화면에 표출한다. 그 값을 통하여 json 파일의 key에 접근하여 해당하는 value, 즉, 그 당시 날짜의 앉은 시간의 비율, 바른자세, 점수 등을 불러온다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ MaterialCalendarView : 달력을 생성할 수 있도록 하는 클래스이다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''□ 통계 관련 클래스'''&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ stack : 그래프를 생성하여, week 데이터에 저장된 데이터를 받아와 상황에 맞게 표출한다. 위 그래프는 이전날짜 7일의 추세 그래프를 보여준다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ realtime : 그래프를 생성하여, min 데이터에 저장된 데이터를 받아와 상황에 맞게 표출한다. 위 그래프는 1분마다 실시간으로 업데이트되어 그래프를 보여준다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ LineChart : 그래프를 생성할 수 있도록 하는 클래스이다. &amp;lt;br/&amp;gt;&lt;br /&gt;
◎ glide : gif 이미지를 인식하여, 움직일 수 있는 이미지(움짤)를 사용할 수 있게 처리한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====클래스 세부 내용=====&lt;br /&gt;
◎ MainActivity : 화분 모듈이 서버와 잘 통신되고 있는지 확인하고, 승인이 완료되면 메인화면으로 넘어갈 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 앱표1.PNG|500픽셀|섬네일|가운데|애플리케이션 표1]]&amp;lt;br/&amp;gt;&lt;br /&gt;
-Requestqueue 클래스를 참조하여 mqueue1, 2를 생성한 뒤, 1은 측정시작 버튼을 클릭 시에 서버와 json 통신을 하기 위해 사용되며, 2는 이어하기 버튼을 클릭 시에 통신을 위해 사용된다.&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ mainscreen : 실시간으로 서버에서 받아오는 json파일을 파싱하여 알맞은 값들을 적재적소에 뿌려, 점수, 자세 등을 표출하며, 꽃을 모터제어를 통하여 제어한다. 제어할 때에는 json파일에 맞게 스레드를 생성하여 처리한다. 통신에 사용하는 클래스는 앞의 mainactivity와 동일하다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 앱표2.PNG|500픽셀|섬네일|가운데|애플리케이션 표2]]&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ calendars : 달력을 생성하여, 날짜를 클릭 시에 그 날짜를 읽어서 화면에 표출한다. &amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 앱표3.PNG|500픽셀|섬네일|가운데|애플리케이션 표3]]&amp;lt;br/&amp;gt;&lt;br /&gt;
-MaterialCalendarView 클래스를 통해 달력을 생성한다. 달력을 클릭 시에 setonDateChangedlistener를 통하여 클릭한 날짜를 받아온다. 날짜의 형식은  yyy.mm.dd 이며 date1에 string 형식으로 저장된다. date1 의 값으로 json의 key에 접근하여 값을 받아온다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
-calendardatescore의 jsonobject 형식의 변수 안의 value를 각각 score, percent, ptilt, prb, pcr 에 저장한다. score + percent + ptilt + prb + pcr = 100 % 이다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ stack : 그래프를 생성하여, week 데이터에 저장된 데이터를 받아와 상황에 맞게 표출한다. 위 그래프는 이전날짜 7일의 추세 그래프를 보여준다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 앱표4.PNG|500픽셀|섬네일|가운데|애플리케이션 표4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
-realtime 클래스는 실시간으로 그래프를 매 분마다 정보를 받아와서 그래프를 그려준다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====서버 통신=====&lt;br /&gt;
의자 모듈의 라즈베리파이3에 서버를 두고 서버에서 ap모드를 킬 시에, 모듈에서 와이파이를 잡아서 서로 통신이 되는 구조이다. 기본적으로 앞에서 얘기한 것처럼 REST API로 통신하며 클라이언트로부터 GET 요청만 받도록 설정되어있다. Route: /init은 conv를 실행하기위한 키 값을 가진 ok.json에 접근하여 GET 요청 시 conv룰 활성화 시킨다. 즉, 기본적인 통신 순서는 다음과 같다. &amp;lt;br/&amp;gt;&lt;br /&gt;
:1. 클라이언트에서 ok.json에 접근하여 conv(기록측정) 활성화&amp;lt;br/&amp;gt;&lt;br /&gt;
:2. jsonobjectrequest 클래스를 생성하여 요청에 맞는 key를 탐색하여 받아옴&amp;lt;br/&amp;gt;&lt;br /&gt;
:3. mQueue 클래스를 통하여 처음 요청한 값부터 선출&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====서보모터 구동=====&lt;br /&gt;
서보모터 구동은 mainscreen.java에서 일어난다. 서보모터는 jsonobjectrequest 문에서 score와 mode의 값을 서버에서 받아오는 것을 기다리고, 모드에 따라 해당하는 스레드를 생성한다. 구동 스레드는 총 6가지로, 정상자세, 왼쪽, 왼대각, 앞, 오른대각, 오른쪽이 존재한다. 이 서보모터의 구동은 다른 액티비티로 넘어가더라도 작동이 되어, 화분모듈이 작동하는 것을 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
'''1. 의자모듈'''&lt;br /&gt;
&lt;br /&gt;
의자모듈의 최종 외관은 다음과 같다.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
[[파일:의자전면.jpg|500픽셀|섬네일|가운데|그림16. 의자모듈의 앞면]]&lt;br /&gt;
[[파일:의자옆면.png|500픽셀|섬네일|가운데|그림17. 의자모듈의 옆면]]&lt;br /&gt;
[[파일:의자후면.jpg|500픽셀|섬네일|가운데|그림18. 의자모듈의 뒷변]]&lt;br /&gt;
&lt;br /&gt;
보조의자는 그림과 같이 사용자의 기존 의자위에 놓고 사용할 수 있다. 압력센서는 쿠션 밑부분에 위치하여 있어 외부로 보이지 않게 하였다. &lt;br /&gt;
&lt;br /&gt;
의자의 뒷면에는 회로와 구성품들을 보호하기위한 케이싱 작업이 되어있다. 최종적으로 아크릴판과 벨크로를 사용하여 케이스를 제작하였으며 탈부착이 용이하도록 하였다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''2. 화분모듈'''&lt;br /&gt;
&lt;br /&gt;
화분모듈은 터치스크린과 꽃 모형으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
화분모듈에서 사용하는 자세관리 애플리케이션은 다음과 같은 기능을 포함한다.&lt;br /&gt;
&lt;br /&gt;
[[파일:측정시작.jpg|500픽셀|섬네일|가운데|그림19. 애플리케이션 시작 화면]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:1) 실시간 피드벡&lt;br /&gt;
&lt;br /&gt;
:실시간 사용자의 자세 점수와 자세에 따른 꽃 모형의 모션을 통해 사용자에게 즉각적인 자세피드벡을 줄 수 있도록 하였다.&lt;br /&gt;
[[파일:꽃모형 작동.jpg|500픽셀|섬네일|가운데|그림20. 꽃 모형 작동 모습 ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:2) 통계 시스템 구축&lt;br /&gt;
&lt;br /&gt;
:사용자의 자세 점수에 대한 추이를 그래프로 표현하였으며 사용자는 이를 보고 분별, 날짜별 점수 추이를 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
[[파일:분별점수.jpg|500픽셀|섬네일|가운데|그림21. 분별 자세 점수 추이 ]]&lt;br /&gt;
[[파일:일별통계.jpg|500픽셀|섬네일|가운데|그림22. 일별 자세 점수 추이 ]] &lt;br /&gt;
&lt;br /&gt;
:3) 달력과 연계한 피드벡&lt;br /&gt;
&lt;br /&gt;
:사용자는 달력의 날짜를 터치함으로써 해당 일의 의자 사용시간, 일별 점수, 그날의 자세 문제에 대한 비율을 확인할 수 있다.&lt;br /&gt;
:자세 비율을 통해 하루 동안 어떤 자세문제가 얼마나 발생하였는지를 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
[[파일:달력.jpg|500픽셀|섬네일|가운데|그림23. 달력에 표시된 자세정보 ]]&lt;br /&gt;
&lt;br /&gt;
===발전 가능성===&lt;br /&gt;
'''㉠''' 애플리케이션 통계 기능의 그래픽 인터페이스에서 더 발전할 여지가 있으나, 시간적 제약에 의해 구현되지 못함&amp;lt;br/&amp;gt;&lt;br /&gt;
'''㉡''' 서보모터 구동 시 발생하는 소음에 의해 사용자가 불편함을 느낄 수 있어 소음에 대한 개선안이 필요&amp;lt;br/&amp;gt;&lt;br /&gt;
'''㉢''' 초기 설계 후 구매한 디스플레이의 크기에 의해 화분 전체의 크기가 다소 커진 경향이 있어 약간의 소형화가 요구됨&amp;lt;br/&amp;gt;&lt;br /&gt;
'''㉣''' 의자 모듈에 장착된 다른 기기들은 무게에 큰 영향을 미치지 않지만 함께 부착된 보조배터리로 인해 전체 중량이 증가하여 경량화가 요구될 수 있음&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:펜타코어 평가항목.PNG|900픽셀|섬네일|가운데|표 3. 프로젝트 평가 항목]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
 '''1. 자세 판단 정확도'''&lt;br /&gt;
:-&amp;gt; 총 100회의 자세 변화에서 97회의 자세 판단 성공&amp;lt;br/&amp;gt;&lt;br /&gt;
 '''2. 반응성'''&lt;br /&gt;
:-&amp;gt; 총 100회의 자세 변화에서 약 1.89s의 평균 꽃 응답 시간 (표준편차 = 0.17s)&amp;lt;br/&amp;gt;&lt;br /&gt;
 '''3. 설계 목표 달성도 [6/7]'''&lt;br /&gt;
:-&amp;gt; 데이터 누적을 통해 자세 습관을 분석하여 사용자에게 알맞은 피드백을 제공하는 기능은 구현하지 못함&amp;lt;br/&amp;gt;&lt;br /&gt;
 '''4. 배터리 절약'''&lt;br /&gt;
:-&amp;gt; 구체적인 소비 전력을 파악하여 배터리 사용 효율을 증대시키려 하였으나 넉넉한 배터리 용량 확보로 문제를 해결&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
'''표*근''' : 무언가를 만드는 과정은 그것이 쉽든 어렵든 간에 생각보다 더 많은 노력이 필요하다는 것을 깨달았다. 팀원들과 아이디어를 실제 제품까지 만드는 과정이 쉽지는 않았지만 재밌었다. &lt;br /&gt;
예상치 못한 문제들을 같이 해결해내는 과정에서 협업의 중요성을 배울 수 있었다.  &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''고*균''' : 이번 프로젝트를 통해 자세에 대해서 많이 연구하고 고민하면서 그동안 생각해보지 않았던 인체공학적 지식들을 많이 접할 수 있었다. 또한, 화분 모듈 하드웨어를 담당하면서 동작제어가 생각처럼 되지 않을 때가 많았고 케이싱 과정 중에도 디자인하고 제작하는 과정이 참 많이 까다로운 작업이라는 것을 느꼈다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''백*우''' : 소프트웨어를 통합하기 위해서는 소프트웨어에 사용되는 각 언어와 통신 등 전방위적인 특징과 개념을 알고 있어야 함을 알게 되었다. 프로젝트 개별적인 모듈을 만드는 것은 단순히 시작일 뿐이었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''이*현''' : 애플리케이션에 대해 개발하면서 통신 부분, 모터 제어 부분, 애플리케이션 부분을 통합하여 한 단에서 진행한다는 것이 얼마나 어려운지 알게 되었다. 또한 매우 흥미로워서 힘들지만 재밌었고 많은 것들을 얻어 갈 수 있었다. 이번 기회를 통하여 배운 지식을 통하여 더 많은 결과물을 만들어내고 싶다는 생각이 들었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''강*혁''' : 프로젝트를 진행하며 예기치 못한 상황에 발생하는 문제들로 인해 많이 좌절했지만, 단계적으로 문제들을 해결하며 극복하였다. 또한, 프로그램 작성 시 버전 관리의 중요성을 깨달았으며, 실제 프로그램 작동 시 발생할 수 있는 예외 상황들을 처리하는 것에서 많은 어려움이 있었다. 그리고 하드웨어 제작 과정에서는 많은 시행착오를 겪으며 항상 문제가 발생할 수 있다는 사실을 염두에 두고 작업에 임해야 한다는 것을 깨달았으며, 공학 문제 프로젝트를 협업을 통해 해결해나가는 귀중한 경험이었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8E%9C%ED%83%80%EC%BD%94%EC%96%B4_-_%ED%97%88%EB%A6%AC%EC%97%85!_%EC%9E%90%EC%84%B8%EA%B5%90%EC%A0%95%EA%BD%83&amp;diff=5914</id>
		<title>펜타코어 - 허리업! 자세교정꽃</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8E%9C%ED%83%80%EC%BD%94%EC%96%B4_-_%ED%97%88%EB%A6%AC%EC%97%85!_%EC%9E%90%EC%84%B8%EA%B5%90%EC%A0%95%EA%BD%83&amp;diff=5914"/>
				<updated>2020-06-22T01:12:47Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 허리업! 자세교정꽃&lt;br /&gt;
압력센서와 가속도계를 이용한 자발적 자세교정 시스템&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
 2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
 서울시립대학교 기계정보공학과 20154300** 표*근 (팀장)&amp;lt;br/&amp;gt; &lt;br /&gt;
 서울시립대학교 기계정보공학과 20154300** 고*균 &amp;lt;br/&amp;gt; &lt;br /&gt;
 서울시립대학교 기계정보공학과 20154300** 백*우 &amp;lt;br/&amp;gt;&lt;br /&gt;
 서울시립대학교 기계정보공학과 20154300** 이*현 &amp;lt;br/&amp;gt;&lt;br /&gt;
 서울시립대학교 기계정보공학과 20174300** 강*혁 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
 압력센서와 가속도 센서를 통한 자세 판단을 바탕으로 화분의 로봇 꽃을 제어해 사용자에게 피드백을 제공하는 자발적 자세 교정 시스템을 제작한다. 등받이에 부착된 가속도 센서를 통해 전체적인 자세를 판단하고, 사용자가 잘못된 자세를 취할 시 화분의 꽃 로봇이 잘못된 자세를 모방하는 움직임을 표현하여 사용자가 인지하도록 한다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경===&lt;br /&gt;
현대인들은 학업, 업무 등으로 인해 책상에 앉아 있는 시간이 점점 길어지고 있고, 이와 관련된 질환들로 내원하는 빈도가 빠르게 증가하고 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 척추 질환자 수.jpg|500픽셀|섬네일|가운데|그림 1. 20대 척추 질환자 수 추이]]&lt;br /&gt;
&lt;br /&gt;
이러한 통계를 뒷받침하듯 다양한 자세 교정 제품들이 시중에 출시되었으며, 이들의 공통적인 특징은 물리적으로 사용자의 행동을 제약하여 올바른 자세를 유도한다는 것이다. 그러나 많은 소비자 설문에 의하면 자발적인 자세 유지가 이 &lt;br /&gt;
루어지지 않을 시 사용자의 불편함을 초래할 수 있으며, 자세 교정을 위한 물리적인 피드백(진동, 움직임 제약 등)에 대한 부정적인 반응이 두드러지게 나타나는 경향을 보인다. 또한, 좋은 &lt;br /&gt;
자세는 어떤 기구에 의해 유지되어야 하는 것이 아니므로, 사용자의 자발적인 자세 교정을 유도하는 새로운 방식의 자세 교정 시스템이 좋은 자세 습관 형성에 기여할 것으로 예상한다.&lt;br /&gt;
[[파일:바르지 못한 자세 습관.png|500픽셀|섬네일|가운데|그림 2. 바르지 못한 자세습관]]&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
&lt;br /&gt;
본 시스템의 목적은 사용자의 자발적인 자세 교정과 좋은 자세 습관의 확립이며, 큰 불편함 없이 자세 교정기구를 사용할 수 있다는 장점이 있다. 또한, 애플리케이션의 인터페이스를 통해 자신의 자세 점수, 경향을 직관적으로 파악할 수 있어 간편한 자세 모니터링이 가능하다. 추후 누적된 데이터를 통해 사용자의 자세 습관을 파악하여 적절한 피드백을 제공하는 기능을 추가한다면 자세 습관 개선에 큰 도움이 될 것이다.&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:펜타코어_목적_계통도.PNG|800픽셀|섬네일|가운데|그림 3. 목적 계통도]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 ''' ㉠ 편의성'''&lt;br /&gt;
본 시스템은 강압적이지 않은 교정 방식으로 사용자의 올바른 자세 습관을 유도하므로, 보조 의자를 장시간 이용할 때 불편함이 있으면 안 된다. 또한, 실제 사용 시 별도의 조작 없이 간편하게 작동해야 하며 사용자에게 자세 정보를 적절한 때에 제공할 수 있어야 한다. 이상적인 설계에서 자세 정보를 수집하는 의자는 외부 전원공급 없이 작동할 수 있어야 하며, 다양한 환경에서 큰 제약 없이 사용이 가능하도록 한다. 그리고 안드로이드 OS 기반의 애플리케이션을 통해 수집된 데이터의 경향과 날짜, 시간별 자세 추이를 어디서나 손쉽게 확인할 수 있도록 시스템을 구성한다.&lt;br /&gt;
&lt;br /&gt;
 ''' ㉡ 정확성'''&lt;br /&gt;
올바른 자세라는 다소 추상적인 특성에서 구체적인 기준을 설정하고 사용자의 자세를 판단해야 하므로, 사용 전 기준 확립과 자세판단에서 높은 정확도가 요구된다. 이러한 점을 보완하기 위해 자이로 센서의 기울기 정보를 바탕으로 물리적 수평을 감지하며, 효율적인 로드셀 배치로 편향된 무게 분포를 파악할 수 있도록 한다. 그리고 전체 시스템의 유일한 구동부인 화분 모듈에서는 사용자가 잘못된 자세를 취할 때 이를 즉각적으로 명확하게 표현할 수 있어야 하므로 신속한 반응성이 서보 모터를 통해 구현되어야 한다. &lt;br /&gt;
&lt;br /&gt;
 ''' ㉢ 지속성'''&lt;br /&gt;
전체적인 완성품의 지속성은 크게 두 가지의 특성에 적용될 수 있다. 첫째는 H/W의 물리적 내구성을 의미하며, 의자에 가해지는 외부 충격이나 잠재적인 파손에 시스템을 보호할 수 있도록 안정성을 갖추는 설계를 지향한다. 두 번째 지속성은 사용자의 자세 데이터 수집과 관리의 측면에서 누적된 데이터를 체계적으로 정리, 분석하여 올바른 자세를 위해 사용자에게 적절한 피드백을 제공할 수 있어야 한다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:펜타코어_시나리오.png|800픽셀|섬네일|가운데|그림 4. 시스템 작동 시나리오]]&amp;lt;br/&amp;gt;&lt;br /&gt;
전체 시스템의 동작은 의자에서 변화하는 사용자 자세 데이터를 입력받아 저장하면 화분 모듈이 의자 모듈의 서버에서 데이터를 불러오고, 데이터에 기록된 자세 모드에 따라 애플리케이션 내의 Background Process로 꽃을 실시간으로 작동시킨다. 구체적인 시나리오로는 사용자가 의자와 화분 모듈에 전원을 인가하고, 화분의 디스플레이에 표시된 ‘측정하기’, ‘이어하기’ 두 버튼을 통해 자세 판단 및 피드백 기능을 실행할 수 있다. 사용자는 실시간으로 변화하는 자세에 대한 꽃의 피드백을 2초 단위로 받을 수 있으며, 앉아 있는 시간 동안 데이터는 계속해서 누적되어 의자 사용 시간, 자세 점수 등을 확인할 수 있다. 사용 종료를 위해서는 ‘오늘은 여기까지’ 버튼을 통해 시스템을 종료하여 자세 점수 측정을 중단할 수 있다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담===&lt;br /&gt;
	'''표*근 [팀장]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	프로젝트 진행 상황을 총괄하며 자재 관리 및 팀 구성원 간 역할분담과 조율 및 실험을 수행하였고, 회로도 구성 및 프로젝트 진행 과정에서 필요한 서식 문서들의 작성과 검토, 최종 승인까지 담당하여 팀의 중추를 담당하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
	'''고*균 [화분 제작 및 서보모터 제어 담당]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	허리 업! 시스템의 유일한 구동부인 서보모터를 통한 꽃 제어를 담당하였고, 다양한 메커니즘으로 꽃의 움직임을 실험, 관찰하여 시스템에 적합한 구조를 설계하였다. 또한, 블록을 이용해 화분을 제작하여 터치 디스플레이와 라즈베리파이, 서보모터가 모두 내장되며 심미성과 내구성을 갖춘 화분을 제작하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
	'''백*우 [서버 구축 및 통신 시스템 담당]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	의자 모듈의 라즈베리파이로 서버를 구축하여 화분 모듈이 실시간으로 데이터를 읽어와 꽃을 구동시킬 수 있도록 하였고, 추가적으로 압력 센서 배치를 위한 민감도 최적화를 수행하여 자세 판단 정확도를 크게 향상시켰다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
	'''이*현 [안드로이드 애플리케이션 개발]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	안드로이드 Things를 기반으로 작동할 수 있는 애플리케이션을 중점적으로 개발하였다. 애플리케이션의 UI 디자인과 통계 기능들을 효과적으로 수행할 수 있도록 프로그램을 설계하였으며, 사용자 편의성과 사용의 지속성을 위한 심미성을 확보하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
	'''강*혁 [실험 계획 및 하드웨어 제작 담당]''' &amp;lt;br/&amp;gt;&lt;br /&gt;
	초기 설계 단계에서 의자의 압력 분포를 확인하기 위한 실험 계획 및 수행, 데이터 해석과 의자 모듈 제작을 담당하였다. 자세 판단 코드를 작성하였으며 사용자의 정확한 자세를 판단하기위한 기준 체계를 확립하였다.&lt;br /&gt;
&lt;br /&gt;
===개발 일정===&lt;br /&gt;
[[파일:펜타코어_개발_일정.PNG|987픽셀|섬네일|가운데|그림 5. 개발 일정]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:펜타코어_전체_시스템_구성도.png|778픽셀|섬네일|가운데|그림 6. 전체 시스템 구성도]]&amp;lt;br/&amp;gt;&lt;br /&gt;
전체 시스템은 크게 의자 모듈과 화분 모듈로 구분할 수 있고, 구체적인 구성은 다음 그림과 같다. 각 모듈에 라즈베리파이가 내장되어 데이터 처리 및 통신, 제어 기능을 수행한다. 보조 의자에서는 사용자 환경에 맞게 설정된 기준에 따라 센서에서 입력된 데이터를 바탕으로 자세를 판단하며, 사용자의 자세가 바르지 않은 경우 화분 모듈의 꽃을 제어해 잘못된 자세를 모방함으로써 올바른 자세로 고칠 수 있도록 유도한다.&lt;br /&gt;
&lt;br /&gt;
===보조 의자 모듈 설계 및 회로 구현===&lt;br /&gt;
[[파일:펜타코어 의자 모듈 및 회로 구성.png|600픽셀|섬네일|가운데|그림 7. 의자 모듈 및 회로 구성]]&amp;lt;br/&amp;gt;&lt;br /&gt;
의자 모듈은 라즈베리파이4, 직경 12mm FSR 압력센서, 자이로 센서, 4채널 어레이 저항 브릿지, ADC칩과 20000mAh 용량의 보조배터리로 구성된다. 본 시스템에 사용되는 의자는 에이블루社의 Curble Chair로 여러 모델 중 프로젝트의 목적에 가장 잘 부합하는 제품인 커블 체어 와이더(Curble Chair Wider)를 사용하였다. 본 의자는 인체공학적으로 설계되어 사용자의 척추와 골반의 각도를 제한하여 척추 중립자세를 유지하도록 도와준다. 라즈베리파이를 통해 압력센서와 자이로센서로부터 얻은 데이터를 수집 및 처리하여 사용자의 자세를 판단한다. 또한, 서버를 열어 화분 모듈과의 통신을 가능하게 하며 사용자의 자세 모드를 전송하는 역할을 한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어_압력_센서_배치.png|500픽셀|섬네일|가운데|그림 8. FSR 압력 센서 부착 위치]]&amp;lt;br/&amp;gt;&lt;br /&gt;
4개의 압력센서는 사용자의 둔부와 허벅지 압력을 통해 의자 사용 여부 및 다리 꼼을 감지하는 역할을 한다. 나머지 한 개는 의자의 등 부분에 위치하며 사용자의 등이 굽은 자세를 감지한다. 이는 인체 해부학적으로 등을 굽었을 시 압력센서의 값이 증가하는 것에 착안하여 위치를 결정하였다. 5개의 센서는 4채널 저항 브릿지와 ADC 칩인 MCP-3208에 연결되어 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어_자이로_수평.png|450픽셀|섬네일|가운데|그림 9. 의자 등받이에 부착된 자이로센서]]&lt;br /&gt;
또한, 사용자의 전체적인 움직임은 등받이 뒤편에 위치한 자이로 센서로 파악한다. 등받이 뒤편에는 보조배터리가 부착되어 있으며 이는 라즈베리파이의 전원 공급을 담당한다. 콘센트로부터 외부전원이 입력된다면 사용자가 본 시스템을 이용하는 데 있어 제한사항이 발생하므로 이를 해결하기 위해 보조배터리를 부착하여 해결하였다. 보조배터리는 20,000mA의 대용량으로 라즈베리파이4의 정격 전원인 [5V,3A]를 충족함과 동시에 사용자에게 12시간 이상의 사용시간을 제공한다. 사용 후 충전을 통해 다음날 사용이 가능하다. &amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어_의자_모듈_케이싱.png|500픽셀|섬네일|가운데|그림 10. 만능기판으로 구성된 의자 모듈]]&amp;lt;br/&amp;gt;&lt;br /&gt;
의자 모듈 시스템의 회로는 만능기판을 통해 구성되어 있으며, 외부의 충격으로부터 회로를 보호하기 위해 블록으로 케이싱 되어있다.&lt;br /&gt;
&lt;br /&gt;
===화분 모듈 설계 및 회로 구현===&lt;br /&gt;
[[파일:펜타코어 화분 모듈 구성도.png|1200픽셀|섬네일|가운데|그림 11. 화분 모듈 구성도]]&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:화분내부.JPG|600픽셀|섬네일|가운데|그림 12. 화분 모듈 내부 모습]]&lt;br /&gt;
화분 모듈은 라즈베리파이3, 서보모터, 7인치 터치 디스플레이, 꽃 모형으로 구성되어 있다. 화분 모듈의 기능은 의자 모듈로부터 사용자의 자세 정보를 받아 사용자에게 피드백을 제공하는 것이다. 자세 문제에 대한 피드백은 터치 디스플레이와 꽃 모형을 통해 이루어진다. 라즈베리파이에는 Android Things를 활용한 어플리케이션이 구동되며 서보모터와 애플리케이션을 동작시킨다. 꽃 모형은 탄성체인 스프링과 와이어로 구성되어 있다. 서보모터의 제어를 통해 꽃 모형이 사용자의 자세 모드에 해당하는 움직임을 취하도록 구현하였으며 사용자는 이를 통해 자신의 잘못된 자세를 인지하게 된다. 화분의 디스플레이를 통해 실시간 자세 피드백을 받아 볼 수 있으며 애플리케이션의 다양한 기능을 활용할 수 있다. 전체적인 외형은 블록을 통해 구현되어 내부의 구성품들을 보호하며 심미성을 더해준다.&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
소프트웨어부는 자세 판단 알고리즘, 서버 구축 및 통신, 편리한 시스템 사용을 위한 애플리케이션으로 구분된다. &amp;lt;br/&amp;gt;&lt;br /&gt;
====자세 판단 알고리즘====&lt;br /&gt;
초기 설계 단계에서 자세 판단 방법을 확립하기 위해 다음과 같은 논의를 거쳤으며, 요약된 과정은 내용은 표와 같다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 자세 판단 방법 표.PNG|800픽셀|섬네일|가운데|표 1. 자세 판단 방법의 변화 ]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 롤 피치.png|500픽셀|섬네일|가운데|그림 13. 의자에 앉은 사용자의 Rolling과 Pitching]]&amp;lt;br/&amp;gt;&lt;br /&gt;
이후 확립된 자세 판단 코드는 의자 모듈의 라즈베리파이에서 동작하며 압력센서와 자이로 센서의 값을 통해 사용자의 자세를 판단한다. 압력센서는 사용자가 특정한 자세를 취할 때 변화하는 압력을 감지하며, 자이로 센서는 전체적인 사용자 몸의 기울기를 감지한다. 자세 판단에는 기본 자세 모드와 특정 자세 모드 두 가지로 나뉜다. 기본자세 모드는 등받이 뒤에 부착되어 있는 자이로 센서를 활용하여 사용자의 앉은 자세 기울기를 판별한다. 자이로 센서로부터 A_x, A_y, A_z 값을 읽을 수 있으며 이를 다음과 같이 계산하여 오일러 각(Euler angle)을 계산할 수 있다. 계산된 Pitch와 Roll 각도를 통해 사용자가 어떤 자세를 취하였는지 확인할 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 각 모드.png|1100픽셀|섬네일|가운데|그림 14. Rolling과 Pitching에 의한 자세 모드 구분(좌), 7가지의 기본 자세 모드]]&amp;lt;br/&amp;gt;&lt;br /&gt;
특정 자세 모드는 의자에 부착된 압력센서를 통해 판별되며 센서의 위치는 그림 6과 같다. 자세 점수는 ‘기본 자세 모드’와 ‘특정 자세 모드’의 조합을 통해 결정되며 초기 100점에서 각 자세별 감점이 적용되어 합산된 점수가 Sensing interval마다 DB에 저장된다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====서버 구축 및 통신 시스템====&lt;br /&gt;
[[파일:펜타코어 서버 표.PNG|900픽셀|섬네일|가운데|표 2. 서버 및 통신 시스템 구성도]]&amp;lt;br/&amp;gt;&lt;br /&gt;
위의 표는 의자 모듈의 프로그램 구성이다. 총 세 개의 프로그램으로 작동하며 각 프로그램은 데이터 수집, 서버 그리고 데이터베이스 역할을 한다. main.out은 데이터 수집 프로그램으로 GPIO를 통해 얻어온 데이터를 data.json 파일로 저장하는 역할을 한다. conv는 데이터베이스 프로그램으로 main.out의 결과물로 얻어진 data.json을 cron을 통해 일정 주기로 데이터베이스에 갱신하는 역할을 한다. 여기서 말하는 데이터베이스는 서버 컴퓨터(의자 모듈 내장 라즈베리파이)에 json file로 물리적으로 저장되어 있다. server는 npm express 모듈로 구성했으며 http 서버 구성을 따른다. REST API로 통신하며 클라이언트로부터 GET 요청만 받도록 설정되어있다. Route: /init은 conv를 실행하기 위한 키값을 가진 ok.json에 접근하여 GET 요청 시 conv를 활성화시킨다. Route: /db는 GET 요청 시 데이터베이스에 접근하여 해당 데이터를 전달한다. 그중 route:/db/end는 GET 요청 시 conv에서 갱신한 데이터들을 day.json과 weekdb.json에 정리하여 저장하며 ok.json에 접근하여 conv를 비활성화한다. 각 프로그램은 profile.d 서비스의 start*.sh에 의해 실행된다. AP mode를 수행하기 위해 hostapd와 dnsmasq를 사용했으며 dhcpcd와 rc.local를 수정하여 AP mode를 활성화한다.&lt;br /&gt;
&lt;br /&gt;
====애플리케이션====&lt;br /&gt;
라즈베리파이3에 Android Things를 설치하여 애플리케이션이 동작하도록 한다. IoT 개발에서 가장 중요한 부분은 연결성, 보안, 개발 환경이다. 우리는 안드로이드 씽스(Android Things)의 장점인 안드로이드 운영체제와 같은 맥락의 운영체제이며 기존 스마트 폰 앱처럼 IoT 앱을 개발할 수 있다는 점, 운영체제와 네트워크에 대한 별도의 개발이 필요없어 개발 난이도가 상대적으로 낮다는 점, 자바(JAVA)언어를 통해 프로그래밍이 가능하여 이식성이 좋다는 점에 착안하여 이를 선택하였다. 안드로이드 프레임워크를 적용한 임베디드 시스템을 만들 수 있기에 이를 적극적으로 활용하기로 하였다. 다음은 어플리케이션의 기본적인 기능 흐름도이다.&lt;br /&gt;
[[파일:펜타코어 애플리케이션 플로우차트.png|900픽셀|섬네일|가운데|그림 15. 애플리케이션 Flow Chart]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====클래스 내용=====&lt;br /&gt;
크게 시작, 메인, 달력, 통계1(주차 그래프), 통계2(실시간 그래프) 로 총 5개의 엑티비티로 이루어져있다. 또한, 클래스는 크게 4가지 기능으로 구분된다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''□ 통신 관련 클래스'''&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ MainActivity : 화분 모듈이 서버와 잘 통신되고 있는지 확인하고, 승인이 완료되면 메인화면으로 넘어갈 수 있다. 또한, 가장 초기화면으로써, 바른자세에 대한 안내, 이어서 의자 사용하기 등의 역할을 수행한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ Requestqueue : FIFO 구조로 json오로 요청하고 받는 것을 순차적으로 진행할 수 있게 처리한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ JsonObjectRequest,JsonArrayRequest : json파일을 array 또는 object 형식에 따라 요청한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''□ 모터제어 관련 클래스'''&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ mainscreen : 실시간으로 서버에서 받아오는 json파일을 파싱하여 알맞은 값들을 적재적소에 뿌려, 점수, 자세 등을 표출하며, 꽃을 모터제어를 통하여 제어한다. 제어할 때에는 json파일에 맞게 스레드를 생성하여 처리한다. &amp;lt;br/&amp;gt;&lt;br /&gt;
◎ servo : 안드로이드 씽스에서 사용 가능하며, 서보 모터를 인식할 수 있도록 처리한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''□ 달력 관련 클래스'''&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ calendars : 달력을 생성하여, 날짜를 클릭 시에 그 날짜를 읽어서 화면에 표출한다. 그 값을 통하여 json 파일의 key에 접근하여 해당하는 value, 즉, 그 당시 날짜의 앉은 시간의 비율, 바른자세, 점수 등을 불러온다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ MaterialCalendarView : 달력을 생성할 수 있도록 하는 클래스이다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''□ 통계 관련 클래스'''&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ stack : 그래프를 생성하여, week 데이터에 저장된 데이터를 받아와 상황에 맞게 표출한다. 위 그래프는 이전날짜 7일의 추세 그래프를 보여준다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ realtime : 그래프를 생성하여, min 데이터에 저장된 데이터를 받아와 상황에 맞게 표출한다. 위 그래프는 1분마다 실시간으로 업데이트되어 그래프를 보여준다.&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ LineChart : 그래프를 생성할 수 있도록 하는 클래스이다. &amp;lt;br/&amp;gt;&lt;br /&gt;
◎ glide : gif 이미지를 인식하여, 움직일 수 있는 이미지(움짤)를 사용할 수 있게 처리한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====클래스 세부 내용=====&lt;br /&gt;
◎ MainActivity : 화분 모듈이 서버와 잘 통신되고 있는지 확인하고, 승인이 완료되면 메인화면으로 넘어갈 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 앱표1.PNG|500픽셀|섬네일|가운데|애플리케이션 표1]]&amp;lt;br/&amp;gt;&lt;br /&gt;
-Requestqueue 클래스를 참조하여 mqueue1, 2를 생성한 뒤, 1은 측정시작 버튼을 클릭 시에 서버와 json 통신을 하기 위해 사용되며, 2는 이어하기 버튼을 클릭 시에 통신을 위해 사용된다.&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ mainscreen : 실시간으로 서버에서 받아오는 json파일을 파싱하여 알맞은 값들을 적재적소에 뿌려, 점수, 자세 등을 표출하며, 꽃을 모터제어를 통하여 제어한다. 제어할 때에는 json파일에 맞게 스레드를 생성하여 처리한다. 통신에 사용하는 클래스는 앞의 mainactivity와 동일하다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 앱표2.PNG|500픽셀|섬네일|가운데|애플리케이션 표2]]&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ calendars : 달력을 생성하여, 날짜를 클릭 시에 그 날짜를 읽어서 화면에 표출한다. &amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 앱표3.PNG|500픽셀|섬네일|가운데|애플리케이션 표3]]&amp;lt;br/&amp;gt;&lt;br /&gt;
-MaterialCalendarView 클래스를 통해 달력을 생성한다. 달력을 클릭 시에 setonDateChangedlistener를 통하여 클릭한 날짜를 받아온다. 날짜의 형식은  yyy.mm.dd 이며 date1에 string 형식으로 저장된다. date1 의 값으로 json의 key에 접근하여 값을 받아온다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
-calendardatescore의 jsonobject 형식의 변수 안의 value를 각각 score, percent, ptilt, prb, pcr 에 저장한다. score + percent + ptilt + prb + pcr = 100 % 이다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
◎ stack : 그래프를 생성하여, week 데이터에 저장된 데이터를 받아와 상황에 맞게 표출한다. 위 그래프는 이전날짜 7일의 추세 그래프를 보여준다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:펜타코어 앱표4.PNG|500픽셀|섬네일|가운데|애플리케이션 표4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
-realtime 클래스는 실시간으로 그래프를 매 분마다 정보를 받아와서 그래프를 그려준다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====서버 통신=====&lt;br /&gt;
의자 모듈의 라즈베리파이3에 서버를 두고 서버에서 ap모드를 킬 시에, 모듈에서 와이파이를 잡아서 서로 통신이 되는 구조이다. 기본적으로 앞에서 얘기한 것처럼 REST API로 통신하며 클라이언트로부터 GET 요청만 받도록 설정되어있다. Route: /init은 conv를 실행하기위한 키 값을 가진 ok.json에 접근하여 GET 요청 시 conv룰 활성화 시킨다. 즉, 기본적인 통신 순서는 다음과 같다. &amp;lt;br/&amp;gt;&lt;br /&gt;
:1. 클라이언트에서 ok.json에 접근하여 conv(기록측정) 활성화&amp;lt;br/&amp;gt;&lt;br /&gt;
:2. jsonobjectrequest 클래스를 생성하여 요청에 맞는 key를 탐색하여 받아옴&amp;lt;br/&amp;gt;&lt;br /&gt;
:3. mQueue 클래스를 통하여 처음 요청한 값부터 선출&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====서보모터 구동=====&lt;br /&gt;
서보모터 구동은 mainscreen.java에서 일어난다. 서보모터는 jsonobjectrequest 문에서 score와 mode의 값을 서버에서 받아오는 것을 기다리고, 모드에 따라 해당하는 스레드를 생성한다. 구동 스레드는 총 6가지로, 정상자세, 왼쪽, 왼대각, 앞, 오른대각, 오른쪽이 존재한다. 이 서보모터의 구동은 다른 액티비티로 넘어가더라도 작동이 되어, 화분모듈이 작동하는 것을 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
'''1. 의자모듈'''&lt;br /&gt;
&lt;br /&gt;
의자모듈의 최종 외관은 다음과 같다.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
[[파일:의자전면.jpg|500픽셀|섬네일|가운데|그림15. 의자모듈의 앞면]]&lt;br /&gt;
[[파일:의자옆면.png|500픽셀|섬네일|가운데|그림16. 의자모듈의 옆면]]&lt;br /&gt;
[[파일:의자후면.jpg|500픽셀|섬네일|가운데|그림17. 의자모듈의 뒷변]]&lt;br /&gt;
&lt;br /&gt;
보조의자는 그림과 같이 사용자의 기존 의자위에 놓고 사용할 수 있다. 압력센서는 쿠션 밑부분에 위치하여 있어 외부로 보이지 않게 하였다. &lt;br /&gt;
&lt;br /&gt;
의자의 뒷면에는 회로와 구성품들을 보호하기위한 케이싱 작업이 되어있다. 최종적으로 아크릴판과 벨크로를 사용하여 케이스를 제작하였으며 탈부착이 용이하도록 하였다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''2. 화분모듈'''&lt;br /&gt;
&lt;br /&gt;
화분모듈은 터치스크린과 꽃 모형으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
화분모듈에서 사용하는 자세관리 애플리케이션은 다음과 같은 기능을 포함한다.&lt;br /&gt;
&lt;br /&gt;
[[파일:측정시작.jpg|500픽셀|섬네일|가운데|그림18. 애플리케이션 시작 화면]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:1) 실시간 피드벡&lt;br /&gt;
&lt;br /&gt;
:실시간 사용자의 자세 점수와 자세에 따른 꽃 모형의 모션을 통해 사용자에게 즉각적인 자세피드벡을 줄 수 있도록 하였다.&lt;br /&gt;
[[파일:꽃모형 작동.jpg|500픽셀|섬네일|가운데|그림19. 꽃 모형 작동 모습 ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:2) 통계 시스템 구축&lt;br /&gt;
&lt;br /&gt;
:사용자의 자세 점수에 대한 추이를 그래프로 표현하였으며 사용자는 이를 보고 분별, 날짜별 점수 추이를 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
[[파일:분별점수.jpg|500픽셀|섬네일|가운데|그림20. 분별 자세 점수 추이 ]]&lt;br /&gt;
[[파일:일별통계.jpg|500픽셀|섬네일|가운데|그림21. 일별 자세 점수 추이 ]] &lt;br /&gt;
&lt;br /&gt;
:3) 달력과 연계한 피드벡&lt;br /&gt;
&lt;br /&gt;
:사용자는 달력의 날짜를 터치함으로써 해당 일의 의자 사용시간, 일별 점수, 그날의 자세 문제에 대한 비율을 확인할 수 있다.&lt;br /&gt;
:자세 비율을 통해 하루 동안 어떤 자세문제가 얼마나 발생하였는지를 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
[[파일:달력.jpg|500픽셀|섬네일|가운데|그림22. 달력에 표시된 자세정보 ]]&lt;br /&gt;
&lt;br /&gt;
===발전 가능성===&lt;br /&gt;
'''㉠''' 애플리케이션 통계 기능의 그래픽 인터페이스에서 더 발전할 여지가 있으나, 시간적 제약에 의해 구현되지 못함&amp;lt;br/&amp;gt;&lt;br /&gt;
'''㉡''' 서보모터 구동 시 발생하는 소음에 의해 사용자가 불편함을 느낄 수 있어 소음에 대한 개선안이 필요&amp;lt;br/&amp;gt;&lt;br /&gt;
'''㉢''' 초기 설계 후 구매한 디스플레이의 크기에 의해 화분 전체의 크기가 다소 커진 경향이 있어 약간의 소형화가 요구됨&amp;lt;br/&amp;gt;&lt;br /&gt;
'''㉣''' 의자 모듈에 장착된 다른 기기들은 무게에 큰 영향을 미치지 않지만 함께 부착된 보조배터리로 인해 전체 중량이 증가하여 경량화가 요구될 수 있음&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:펜타코어 평가항목.PNG|900픽셀|섬네일|가운데|표 3. 프로젝트 평가 항목]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
 '''1. 자세 판단 정확도'''&lt;br /&gt;
:-&amp;gt; 총 100회의 자세 변화에서 97회의 자세 판단 성공&amp;lt;br/&amp;gt;&lt;br /&gt;
 '''2. 반응성'''&lt;br /&gt;
:-&amp;gt; 총 100회의 자세 변화에서 약 1.89s의 평균 꽃 응답 시간 (표준편차 = 0.17s)&amp;lt;br/&amp;gt;&lt;br /&gt;
 '''3. 설계 목표 달성도 [6/7]'''&lt;br /&gt;
:-&amp;gt; 데이터 누적을 통해 자세 습관을 분석하여 사용자에게 알맞은 피드백을 제공하는 기능은 구현하지 못함&amp;lt;br/&amp;gt;&lt;br /&gt;
 '''4. 배터리 절약'''&lt;br /&gt;
:-&amp;gt; 구체적인 소비 전력을 파악하여 배터리 사용 효율을 증대시키려 하였으나 넉넉한 배터리 용량 확보로 문제를 해결&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
'''표*근''' : 무언가를 만드는 과정은 그것이 쉽든 어렵든 간에 생각보다 더 많은 노력이 필요하다는 것을 깨달았다. 팀원들과 아이디어를 실제 제품까지 만드는 과정이 쉽지는 않았지만 재밌었다. &lt;br /&gt;
예상치 못한 문제들을 같이 해결해내는 과정에서 협업의 중요성을 배울 수 있었다.  &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''고*균''' : 이번 프로젝트를 통해 자세에 대해서 많이 연구하고 고민하면서 그동안 생각해보지 않았던 인체공학적 지식들을 많이 접할 수 있었다. 또한, 화분 모듈 하드웨어를 담당하면서 동작제어가 생각처럼 되지 않을 때가 많았고 케이싱 과정 중에도 디자인하고 제작하는 과정이 참 많이 까다로운 작업이라는 것을 느꼈다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''백*우''' : 소프트웨어를 통합하기 위해서는 소프트웨어에 사용되는 각 언어와 통신 등 전방위적인 특징과 개념을 알고 있어야 함을 알게 되었다. 프로젝트 개별적인 모듈을 만드는 것은 단순히 시작일 뿐이었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''이*현''' : 애플리케이션에 대해 개발하면서 통신 부분, 모터 제어 부분, 애플리케이션 부분을 통합하여 한 단에서 진행한다는 것이 얼마나 어려운지 알게 되었다. 또한 매우 흥미로워서 힘들지만 재밌었고 많은 것들을 얻어 갈 수 있었다. 이번 기회를 통하여 배운 지식을 통하여 더 많은 결과물을 만들어내고 싶다는 생각이 들었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
'''강*혁''' : 프로젝트를 진행하며 예기치 못한 상황에 발생하는 문제들로 인해 많이 좌절했지만, 단계적으로 문제들을 해결하며 극복하였다. 또한, 프로그램 작성 시 버전 관리의 중요성을 깨달았으며, 실제 프로그램 작동 시 발생할 수 있는 예외 상황들을 처리하는 것에서 많은 어려움이 있었다. 그리고 하드웨어 제작 과정에서는 많은 시행착오를 겪으며 항상 문제가 발생할 수 있다는 사실을 염두에 두고 작업에 임해야 한다는 것을 깨달았으며, 공학 문제 프로젝트를 협업을 통해 해결해나가는 귀중한 경험이었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5913</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5913"/>
				<updated>2020-06-22T00:52:52Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 프로젝트 결과 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
[[파일:개발일정_착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
===영상인식 결과===&lt;br /&gt;
[[파일:영상인식.JPG|600픽셀|가운데|섬네일|그림9. 학습 결과 손실함수 및 IOU]]&lt;br /&gt;
===판별 정확도===&lt;br /&gt;
[[파일:정확도.JPG|600픽셀|가운데|섬네일|그림10. 판별정확도]]&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EC%A0%95%ED%99%95%EB%8F%84.JPG&amp;diff=5912</id>
		<title>파일:정확도.JPG</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EC%A0%95%ED%99%95%EB%8F%84.JPG&amp;diff=5912"/>
				<updated>2020-06-22T00:51:56Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5911</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5911"/>
				<updated>2020-06-22T00:51:24Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 프로젝트 결과 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
[[파일:개발일정_착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
===영상인식 결과===&lt;br /&gt;
[[파일:영상인식.JPG|가운데|섬네일|그림9. 학습 결과 손실함수 및 IOU]]&lt;br /&gt;
===판별 정확도===&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D.JPG&amp;diff=5910</id>
		<title>파일:영상인식.JPG</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D.JPG&amp;diff=5910"/>
				<updated>2020-06-22T00:49:55Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EC%98%81%EC%83%81%EA%B2%B0%EA%B3%BC.JPG&amp;diff=5909</id>
		<title>파일:영상결과.JPG</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EC%98%81%EC%83%81%EA%B2%B0%EA%B3%BC.JPG&amp;diff=5909"/>
				<updated>2020-06-22T00:49:29Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5908</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5908"/>
				<updated>2020-06-22T00:49:09Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 프로젝트 결과 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
[[파일:개발일정_착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
===영상인식 결과===&lt;br /&gt;
[[파일:영상인식.JPG|가운데]]&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5907</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5907"/>
				<updated>2020-06-22T00:46:21Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 개발일정표 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
[[파일:개발일정_착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5906</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5906"/>
				<updated>2020-06-22T00:46:04Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 개발일정표 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
[[파일:개발일정_착착착.JPG]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5905</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5905"/>
				<updated>2020-06-22T00:45:39Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 역할분담 및 추진체계 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG|가운데]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5904</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5904"/>
				<updated>2020-06-22T00:45:08Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 역할분담 및 추진체계 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
[[파일:구성원 착착착.JPG]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EA%B5%AC%EC%84%B1%EC%9B%90_%EC%B0%A9%EC%B0%A9%EC%B0%A9.JPG&amp;diff=5903</id>
		<title>파일:구성원 착착착.JPG</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EA%B5%AC%EC%84%B1%EC%9B%90_%EC%B0%A9%EC%B0%A9%EC%B0%A9.JPG&amp;diff=5903"/>
				<updated>2020-06-22T00:44:37Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A0%95_%EC%B0%A9%EC%B0%A9%EC%B0%A9.JPG&amp;diff=5902</id>
		<title>파일:개발일정 착착착.JPG</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A0%95_%EC%B0%A9%EC%B0%A9%EC%B0%A9.JPG&amp;diff=5902"/>
				<updated>2020-06-22T00:44:20Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5901</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5901"/>
				<updated>2020-06-22T00:37:16Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 구현 내용 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
===개발일정표===&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5900</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5900"/>
				<updated>2020-06-21T21:48:54Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 느낀점 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드웨어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5899</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5899"/>
				<updated>2020-06-21T21:47:18Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 미구현 내용 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
본 프로젝트를 진행하면서 Netson Nano에서 YOLOv3를 직접 실행시키는 것보다 Netson Nano에서 촬영한 사진 혹은 영상을 외부 PC로 전송하여 외부 PC에서 YOLOv3를 실행시키는 것이 좀 더 빠를 것이라고 생각되었다. 따라서 외부 기기로 영상을 손쉽게 송출하도록 도와주는 라이브러리인 [https://github.com/digitalpeer/raspberrypi-motion Motion]을 사용한다면 좀 더 빠른 시간 안에 물건 분류를 처리할 수 있을 거라고 생각한다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5898</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5898"/>
				<updated>2020-06-21T21:45:11Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 구현 내용 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%ED%8F%89%EA%B0%80%ED%95%AD%EB%AA%A9_%EC%B0%A9%EC%B0%A9%EC%B0%A9.JPG&amp;diff=5897</id>
		<title>파일:평가항목 착착착.JPG</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%ED%8F%89%EA%B0%80%ED%95%AD%EB%AA%A9_%EC%B0%A9%EC%B0%A9%EC%B0%A9.JPG&amp;diff=5897"/>
				<updated>2020-06-21T21:44:05Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%ED%8F%89%EA%B0%80%ED%95%AD%EB%AA%A9_%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80.JPG&amp;diff=5896</id>
		<title>파일:평가항목 일사분란.JPG</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%ED%8F%89%EA%B0%80%ED%95%AD%EB%AA%A9_%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80.JPG&amp;diff=5896"/>
				<updated>2020-06-21T21:40:52Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: 2020ese 1님이 파일:평가항목 일사분란.JPG의 새 판을 올렸습니다&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5895</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5895"/>
				<updated>2020-06-21T21:39:37Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 평가결과 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
(1) 판별 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
앞서 소개한 4가지 단계를 거쳐 최종적으로 10개의 페트와 10개의 캔에 대해서 테스트를 진행하였다. 결과로는 페트병에 대해서는 0.80, 캔에 대해서는 0.60의 정확도를 보였다. 학습을 하는 과정에서 크롤링한 데이터의 판별 정확도가 좋지 않아 직접 페트병과 캔을 촬영하여 학습을 진행하였다. 주로 분리수거장에서 페트와 캔을 확보하였는데 페트는 온전한 형태로 버려지는 경우가 많지만 캔은 부피를 줄여 버리는 경우가 많아 데이터 확보에 어려움이 있었고 이에 적은 종류의 캔으로 많은 데이터를 만들어 학습의 결과가 페트에 비해 좋지 않다고 판단이 된다. 페트의 데이터양에 비해 캔의 데이터양이 적은 경우 데이터 비중의 불균형으로 학습의 결과가 전체적으로 저하될 수 있다고 판단하여 자연스럽게 페트의 데이터양도 줄여서 학습을 진행하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(2) 모터 제어 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
판별부까지 30번 물체를 이동시킨 결과 총 27번 정확하게 이동시켰다. 이동에 실패한 3번의 경우 이동 자체는 성공했지만 페트나 캔이 가로로 돌아가버린 경우이기에 이는 모터 제어 정확도의 문제가 아닌 하드웨어적인 문제라 판단할 수 있을 것이다. 모터 제어는 성공적이라 판단하였다.&lt;br /&gt;
&lt;br /&gt;
(3) 모듈 동작 정확도 &amp;lt;br/&amp;gt;&lt;br /&gt;
라즈베리파이에 구축된 서버의 신호에 맞추어 각 모듈은 시나리오 순서대로 동작함을 알 수 있다. 모터 제어 정확도를 판별하기 위해 30번 물체를 이동시키며 동시에 모듈 동작의 정확도도 함께 측정하였다. 총 30번 중 30번 모두 시나리오에 맞추어 정확하게 동작하였다.&lt;br /&gt;
&lt;br /&gt;
(4) 경제성 &amp;lt;br/&amp;gt;&lt;br /&gt;
전체적으로 RVM 제작에 들어간 비용은 400,000이다. 물론 전체적으로 아크릴로 구성하고 압축기와 외형을 제작하지는 않았지만 기존의 RVM의 가격인 21,000,000원과 비교하면 충분히 경제성 면에서는 장점을 가진다 할 수 있다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(5) 직관성 &amp;lt;br/&amp;gt;&lt;br /&gt;
RVM의 상태는 모두 UI를 통해서 사용자에게 전달이 된다. 물체를 투입한 경우 현재 물체가 어느 단계를 거치고 있는지, 결과가 어떻게 나왔는지를 실시간으로 전달하여 직관성을 충분히 구성하였다. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(6) 신속성 &amp;lt;br/&amp;gt;&lt;br /&gt;
사전조사를 하면서 YOLOv3를 Jetson Nano 에서 사용하는 경우 전체적으로 판별에 필요한 시간이 3~5초임을 알 수 있었다. 이를 고려하여 한 물체에 대하여 전체적인 응답시간을 10초로 설정하였다. 실제로도 영상처리서버인 Jetson Nano에서 판별에 들어가는 시간은 약 3초이다. 하지만 이를 위해 가중치를 불러오는데 필요한 시간이 15~20초 정도이고 우리의 시스템은 판별시마다 가중치를 불러오기에 매번 응답시간에 가중치 로딩 시간이 들어가고 이에 전체적인 응답시간은 25~30초 정도로 측정이 된다.&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5894</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5894"/>
				<updated>2020-06-21T21:22:20Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 미구현 내용 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
미구현이라기 보다는 추가되면 좋은 사항으로는 물건 분류 방법의 추가가 있다. 현재에는 내용물이 들어 있거나 유리병같은 무거운 물건을 제외하기 위해 사용하는 무게 센서 이외에는 분류를 모두 YOLOv3의 영상처리에만 의존하게 된다. Netson Nano에서 YOLOv3를 돌리기에는 생각보다 부담스러웠던 점도 있고 몇가지 센서를 병행한다면 더 좋은 정확도를 얻을 수 있을 것이다. 예를들어 빛을 투과하는 페트병과 투과하지 못하는 캔의 성질을 이용하면 빛을 이용해서 좀 더 손쉽게 페트병과 캔의 분류에 도움을 줄 수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5893</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5893"/>
				<updated>2020-06-21T21:08:52Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 미구현 내용 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5892</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5892"/>
				<updated>2020-06-21T21:08:23Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 미구현 내용 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다. 추후에는 암실에서 LED플래시만의 조명으로 분류를위한 사진을 찍어서 물건 분류에 대한 변수를 줄일 생각이다.&lt;br /&gt;
&lt;br /&gt;
현재에는 모든 페트병과 캔을 분류하는 것이 아니고 일부분의 페트병과 캔만을 분류할 수 있습니다. 데이터 수집에 있어서 페트병과 캔의 종류를 늘리는데 시간상 부족하였다. 추후에 이 프로젝트를 개량하여 진행하게 된다면 더 많은 종류의 페트병과 캔의 데이터를 수집하여 학습시킬 계획이다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5891</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5891"/>
				<updated>2020-06-21T21:05:32Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 평가항목 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
[[파일:평가항목_일사분란.JPG]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%ED%8F%89%EA%B0%80%ED%95%AD%EB%AA%A9_%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80.JPG&amp;diff=5890</id>
		<title>파일:평가항목 일사분란.JPG</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%ED%8C%8C%EC%9D%BC:%ED%8F%89%EA%B0%80%ED%95%AD%EB%AA%A9_%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80.JPG&amp;diff=5890"/>
				<updated>2020-06-21T21:04:49Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5889</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5889"/>
				<updated>2020-06-21T21:00:36Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 느낀점 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 훌륭한 팀원들과 함께하며 배운것이 많았고 고맙습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5888</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5888"/>
				<updated>2020-06-21T20:59:46Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 느낀점 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;br /&gt;
&lt;br /&gt;
유*영 - 처음 주제 선정을 하는 단계부터 영상인식, 서버 구축, 전체적인 하드웨어 제작 이렇게 3가지 부분을 생각하며 정말 걱정이 많았습니다. 팀원들 모두가 정말 뛰어난 실력을 보여주어 이렇게 프로젝트를 잘 마무리할 수 있었습니다. 제작 과정에서 물품 구매와 제작 장소 선정에 여러 어려움이 있었습니다. 설계과정에서 적합하다 생각한 제작 재료도 제작을 하며 변경의 필요를 느꼈고 새로운 물품을 선정하는 과정이 쉽지 않았습니다. 또한 제작 환경이 보장되지 않아 힘든 부분도 있었습니다. 이런 힘든 과정 속에서도 아두이노와 모터제어를 처음 해보지만 정말 훌륭하게 제어를 구현한 강*찬형, 서버구축이라는 힘든 과정을 잘 해내준 윤*상, UI와 통신, 그리고 원포인트로 팀원들을 도와준 심*헌, 여러 프로젝트 경력과 영상인식을 잘해준 박*석 모두 너무나도 잘해주었습니다. 일사분란 착착착 화이팅!&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5887</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5887"/>
				<updated>2020-06-21T20:34:29Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 미구현 내용 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
현재 미구현이 된 것으로는 LED플래시가 있다. 기존 구상에는 물건 분류를 위한 사진을 찍을 때, 암실에서 LED 플래시를 켜고 찍는 것이였다. 하지만 시간상 하드웨어 완성과 YOLOv3 학습에 집중하느라 LED 플래시 대신 천장을 열어두는 것으로 대체하였다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5886</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5886"/>
				<updated>2020-06-21T20:29:59Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 영상인식 구현 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영 &amp;lt;br/&amp;gt;&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5885</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5885"/>
				<updated>2020-06-21T20:29:36Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 영상인식 구현 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다. &amp;lt;br/&amp;gt;&lt;br /&gt;
(1) 사진 촬영&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5884</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5884"/>
				<updated>2020-06-21T20:28:19Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 영상인식 구현 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
 &lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다.&lt;br /&gt;
(1) 사진 촬영&lt;br /&gt;
[https://developer.ridgerun.com/wiki/index.php?title=JetsonTX2/GStreamer/nvgstcapture-1.0 '''nvgstcapture'''] 라이브러리를 활용하여 Jetson Nano 보드에서 CSI 카메라를 사용할 수 있었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_capture = &amp;quot;rm -rf *.jpg &amp;amp;&amp;amp; nvgstcapture-1.0 --cus-prev-res=1920x1080 -A&amp;quot;&lt;br /&gt;
(2) 촬영 사진 전처리&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 전처리가 끝난 이미지를 불러와서 촬영 사진에 대한 검출을 실시한다. 검출 threshold는 0.4로 정확도가 0.4 아래인 물체는 물체가 아니라 판단을 하게 설정하였다. 검출 결과는 detect.txt 파일에 저장하도록 설정하였고 이를 후에 파싱하여 결과를 정리한다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data ./yolov3.cfg backup/pet_or_can.weights ./*.jpg -threshold 0.5 | tee detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.threshold 라는 변수를 두어 pet와 can의 정확도의 평균의 기준을 설정하였고 결과로 'pet', 'can', 'return'을 내어 이후 이 값을 라즈베리파이의 메인 서버로 전송한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def parse_result(file_name, threshold):&lt;br /&gt;
    with open(file_name, 'r') as f:&lt;br /&gt;
        lines = f.readlines()&lt;br /&gt;
    lines = lines[1:]&lt;br /&gt;
    can = []&lt;br /&gt;
    pet = []&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(lines)):&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;%&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        lines[i] = lines[i].replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
        each = lines[i].split()&lt;br /&gt;
        if(each[0] == 'pet'):&lt;br /&gt;
            pet.append(int(each[1]))&lt;br /&gt;
        else:&lt;br /&gt;
            can.append(int(each[1]))&lt;br /&gt;
&lt;br /&gt;
    if not pet:&lt;br /&gt;
        pet.append(0)&lt;br /&gt;
    if not can:&lt;br /&gt;
        can.append(0)&lt;br /&gt;
&lt;br /&gt;
    can_possibility = sum(pet)/len(pet)&lt;br /&gt;
    pet_possibility = sum(pet)/len(pet)&lt;br /&gt;
&lt;br /&gt;
    if (max(can_possibility, pet_possibility) &amp;lt; threshold or can_possibility==pet_possibility):&lt;br /&gt;
        return 'return'&lt;br /&gt;
    elif (can_possibility &amp;gt; pet_possibility):&lt;br /&gt;
        return 'pet'&lt;br /&gt;
    elif (can_possibility &amp;lt; pet_possibility):&lt;br /&gt;
        return 'can'&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5883</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5883"/>
				<updated>2020-06-21T18:31:45Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
&lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 그동안 학습시킨 가중치를 사용하여 전처리가 끝난 이미지를 불러와서 페트병과 캔이 검출이 되었는지 확인을 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data cfg/yolov3.cfg backup/yolov3_last.weights rvm/image/*.jpg &amp;gt;&amp;gt; detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True, timeout=10)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5882</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5882"/>
				<updated>2020-06-21T18:30:46Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
&lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 그동안 학습시킨 가중치를 사용하여 전처리가 끝난 이미지를 불러와서 페트병과 캔이 검출이 되었는지 확인을 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data cfg/yolov3.cfg backup/yolov3_last.weights rvm/image/*.jpg &amp;gt;&amp;gt; detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True, timeout=10)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
강*찬 - 프로젝트를 하면서 문제가 발생하면 소프트웨어 제작과 하드웨어 제작을 하는 과정에서 두 파트간에 많은 정보 공유가 필요하다는 것을 알 수 있었습니다. RVM이 오동작을 하는 경우 시스템의 문제인 경우도 있었지만 가장 크리티컬한 문제가 있었던 부분은 보드를 설치하고 배선을 연결하는 과정에서 있었던거 같습니다. 보드와 전선의 접촉 문제로 인한 오작동, 많은 수의 모터와 모터드라이버, 센서들로 인해 작동을 하다가 중간에 시스템이 다운 되는등 여러가지 문제점들이 있었고, 팀원들과 협력해 하나씩 해결해 나갈 수 있었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5881</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5881"/>
				<updated>2020-06-21T18:17:57Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
&lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 그동안 학습시킨 가중치를 사용하여 전처리가 끝난 이미지를 불러와서 페트병과 캔이 검출이 되었는지 확인을 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data cfg/yolov3.cfg backup/yolov3_last.weights rvm/image/*.jpg &amp;gt;&amp;gt; detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True, timeout=10)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5880</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5880"/>
				<updated>2020-06-21T18:17:03Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
여러 회사들의 기어박스 설계도면들을 찾아보고 필요한 구동을 위한 기어박스를 3D프린터기로 제작&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스1.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_1]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|300픽셀|섬네일|가운데|그림4.1 기어박스 카티아_2]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.2 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.3 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.4 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
&lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 그동안 학습시킨 가중치를 사용하여 전처리가 끝난 이미지를 불러와서 페트병과 캔이 검출이 되었는지 확인을 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data cfg/yolov3.cfg backup/yolov3_last.weights rvm/image/*.jpg &amp;gt;&amp;gt; detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True, timeout=10)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5879</id>
		<title>99-1=0 - Smart Mood Light with Sterilization</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5879"/>
				<updated>2020-06-21T18:16:25Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 느낀점 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 Smart Mood Light with Sterilization&lt;br /&gt;
 살균 기능을 탑재한 스마트 무드등&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 노*진 (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 박*훈 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 주*완 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 함*규 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20174300** 유*환 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
 원룸은 타 주거환경에 비해 채광과 통풍 능력이 떨어진다. 공기 청정기를 이용할 수 있지만, 채광 문제를 해결하지 못한다. 다행히 넓은 공간을 가진 주거환경에 비해 규모가 작아 상용 자외선을 통한 살균 효과는 비해 뛰어나지만, 살균기의 제어는 사용자가 원룸 내 없을 때 이루어 져야 한다. 위의 문제를 해결하기 위해 스마트폰을 이용하여 원격 제어를 실행한다. 또한 원격 제어를 하면서 사용자가 원룸 내 있을 때 활용할 수 없는 살균기가 공간을 비효율적으로 차지하게 되는 문제를 해결하기 위해 이를 무드등과 결합 한다. 결과적으로 사용자가 가장 많이 시간을 보내는 공간을 살균할 수 있으며 공간의 비효율성과 안전성, 더불어 무드등의 원격제어로 인한 편의성을 제공한다. 사용자는 외부에 있을 때, 스마트폰을 이용하여 살균기 동작을 제어할 수 있다. 사용자가 내부에 있을 때 별도의 조치 없이도 자외선 동작 제어는 비활성화 되며 무드등의 원격 제어가 가능하다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 1. 배경&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_최근4년간국내미세먼지수치.png|500픽셀]]&lt;br /&gt;
[[파일:99-1=0_최근전세계적바이러스주기.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
최근 국내 미세먼지 수치가 꾸준히 증가함에 따라 국민들의 미세먼지 정화에 관한 관심이 높아지고 있다. 또한, 전 세계적인 바이러스 발생 주기가 짧아짐에 따라 사람들의 살균 제품에 대한 관심도가 높아지고 있다. &lt;br /&gt;
&lt;br /&gt;
햇빛은 면역력 향상이나 기분 전환의 효과뿐 아니라 살균 기능을 가지고 있다. 원룸은 구조적으로 대부분 하나의 창문을 가지고 있어 충분한 빛을 받지 못하며 공기의 입/출구가 하나이기에 통풍이 원활하지 않다. 이러한 조건은 미생물이 자라나기 쉽고 미세먼지의 배출이 어렵다. 이들을 모두 정화하는 데 맞는 조건을 가진 것이 자외선이다. 자외선을 통해 공기 중에 살균이 가능하며 미세먼지 중금속을 분해해 공기를 정화할 수 있다. 하지만 자외선 살균기를 집안에 두고 사용하는 것에는 다음과 같은 문제점들이 있다.&lt;br /&gt;
&lt;br /&gt;
- 살균 자외선은 인간에게 유해하므로 사용자에게 직접적인 자외선이 가해지는 것은 위험하다. 사용자가 없는 곳에서 자외선 살균기가 작동해야 하며 사용자가 있을 때 확실한 방법으로 조작 가능성을 배제 시켜야 한다.&lt;br /&gt;
&lt;br /&gt;
- 사용자가 자주 쓰는 공간의 살균일수록 의미가 있으므로 자외선 살균기는 사용자가 자주 쓰는 공간에 배치되어야 한다. 그러나 위의 이유로 사용자가 실제 방에 있을 때 자외선 살균기를 사용하는 것은 위험하므로 사용자가 있을 때 별다른 기능 없이 공간만을 차지하게 된다.&lt;br /&gt;
&lt;br /&gt;
원룸 내 거주자가 가장 오래 머무르는 공간으로 예상되는 곳은 침대류 이다. 침대류 근처에 있는 물체에 살균기를 결합하여 제품을 만들 수 있다면 효율적으로 문제를 해결할 수 있다. 우리는 이 조건을 만족하는 물체가 무드등이라는 점에서 문제 해결을 시작했다. 또한, 첫 번째 문제 해결을 위해 임베디드 시스템을 설계하여 사용자가 없는 공간에서의 작동을 가능하게 하는 것뿐 아니라 무드등에 다양한 기능을 추가하여 편리성과 기능의 다양함을 추가하려고 한다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 기대효과&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_UV자외선을이용한살균 미세먼지정화과정.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
사용자는 집 밖에서 애플리케이션을 통해 자외선 모듈을 가동함으로써 안전한 방법으로 공간을 살균 및 미세먼지 분해 기능을 작동시킬 수 있다. &lt;br /&gt;
&lt;br /&gt;
또한, 사용자가 개발 제품과 같은 공간에 있다고 판단 시 애플리케이션 자동 스위칭 기능을 통해 자외선 컨트롤 기능이 아닌 무드등 컨트롤 기능을 제어하게 된다. 따로 조작 없이 방 안/밖이라는 공간의 변화에 따라 스위칭 되기에 조작에 신경 쓸 필요가 없다. 무드등 기능으로썬 조도 조정, 빛의 색상 선택, 주변 음악에 따른 색상 변화 기능 등이 있으며 사용자가 선택하는 옵션에 따라 기능을 수행할 수 있어 편리함과 다양성을 제공한다. 사용자의 유무에 상관없이 개발제품은 유용한 도구로써 사용되기에 공간 활용성 또한 확보할 수 있을 것으로 생각된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:99-1=0_목적계통도.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 안전성&lt;br /&gt;
자외선 활성/비활성화 기능은 가스 자동밸브 잠금과 비슷한 원리로 앱 구동 시 스마트폰 GPS 기능을 통해 사용자의 위치정보를 받아 주거 위치와 비교하여 앱 인터페이스 비활성화 여부를 결정한다. GPS 오작동으로 인한 비활성화가 적절히 이루어지지 않았을 때를 대비해 앱 내 수동 비활성화 버튼을 추가한다. 앱 인터페이스가 비활성화 되어 있어 사용자의 실수로 인한 신호 전달 가능성을 사전에 차단할 수 있다. 또한 회로의 긴급 정지 스위치를 통해 하드웨어 오동작 시 회로 자체의 전류를 제어할 수 있다. 또한 인체 및 환경에 유해한 정도를 판단하여 인체에 비교적 덜 유해한 UV-A의 자외선 영역을 사용했고, 수은으로 환경 파괴 문제를 일으키는 UV-램프를 대신하여 UV-LED를 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 실용성&lt;br /&gt;
먼저 자외선 영역의 성능을 비교하면 살균력 기준으로는 분 단위 살균력을 가진 UV-C가 시 단위 살균력을 가진 UV-A보다 월등히 강력하다. 하지만 살균 범위를 보면 UV-A는 공기 중 3-5m의 반경을 가진 것에 비해 UV-C는 공기 중 투과력이 매우 낮아 넓은 범위를 살균할 때에는 UV-A가 더 적합하다. 따라서 거주자 없이 비어있는 시간이 긴 원룸에 맞추어 UV-405nm 자외선을 이용했다. 이 때, 자외선 LED의 살균 방향을 15도 정도 아래쪽으로 내려주어 침구류 및 사용자가 자주 사용하는 물건도 더불어 살균하게 된다. 모터의 경우, 자외선 살균기의 상하 운동이 완료된 후에 해당 위치에서 고정이 되어야 하는데, 서보 모터는 고정 기능이 없고, 스탭 모터는 고정 기능을 사용할 때 소모 전력이 많다. 따라서 적은 소모 전력으로 고정 기능을 수행할 수 있는 웜 기어 모터를 사용했다.&lt;br /&gt;
공간 효율성을 기준으로는 제품 구성 시 얼마나 효율적인 공간 활용 여부를 비교했다. 따라서 각 제품의 특성에 맞추어 무드등 조명에는 필요 전선량이 많은 LED 전구 대신 네오 픽셀을 사용했고, 자외선 조명에는 1cm 이하의 조명 크기를 가지는 UV LED를 사용했다. 또한 사용자 위치를 판별하기 위해서 출입문 주변의 많은 공간을 차지하는 적외선 센서 활용 출/입 카운터 대신 외부 공간을 이용하지 않는 안드로이드 모바일 앱 내의 GPS 기능을 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 편의성&lt;br /&gt;
사용자 입장에서 제품을 이용할 때의 반응성을 기준으로 비교했다. 호스팅 제공 S/W에서는 외부의 데이터베이스를 이용하는 파이썬 애니웨어 대신 자체적으로 실시간 데이터베이스 기능을 제공하는 파이어 베이스를 사용했다. 또한 사용자 위치 판별법에서는 사용자의 출/입 여부를 얼마나 빠르게 판단할 수 있는지에 대한 비교를 했는데, 이는 적외선 센서 활용 카운터가 더 빠른 응답성을 보였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 경제성&lt;br /&gt;
제품에 대한 경제적인 측면은 제품을 구성하는데 사용하는 비용과 유지비로 생각할 수 있다. 먼저 제품 구성비용은 프로젝트의 해당 제품을 구매하는 비용이다. 따라서 보다 더 값이 싼 네오픽셀, UV-A, UV-램프가 더 적합했다. 또한 호스팅 기능 S/W에서는 무료로 이용할 수 있는 범위가 더 넓은 파이어 베이스가 적합했다. 또한 유지비는 제품의 수명이 얼마나 긴지, 배터리 소모량이 얼마나 적은지, 부품 교체 비용이 얼마나 큰지에 대해 비교했다. 따라서 부품 교체 비용이 더 저렴한 네오픽셀, 수명이 더 긴 UV-LED가 더 적합했다. 또한 항상 동작시키는 적외선 센서보다 필요시에만 동작시킬 수 있는 GPS 기능이 배터리 소모량이 거의 없어 더 적합했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 개발 용이성&lt;br /&gt;
개발제품 특성상 데이터베이스 및 호스팅 서비스가 필요하다. 개발자 독자적으로 데이터 베이스와 호스팅 서비스를 개발할 필요 없이 이러한 서비스를 제공하는 서버는 파이어 베이스이다. 개발 난이도는 물론 개발자의 서버 개발 시간을 최소화 하여 다른 분야의 성능 개발을 최대화 시킬 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_개발용이성.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Application과 라즈베리 파이의 통신 중간 다리로 파이어 베이스 서버를 이용한다. 물론 무드등 제어를 위한 라즈베리 파이 서버를 구축하여 통시에 이용하면 원룸 내 무드등 제어 동작 속도를 향상시킬 수도 있다. 그러나 하나의 서버를 이용하면 통신 오류나, 오류가 발생했을 때 오류를 제어하기 쉽다. 또한  추후에 기능 추가가 용이하다. 파이어 베이스는 서버 특성상 서버 사용자가 많아지게 되면 부하가 발생할 수 있다. 그러나 개발 상품의 경우 서버는 한명의 사용자만이 이용한다는 점을 고려하여 이러한 단점을 고려하지 않을 수 있다. 따라서 해당 프로젝트에 가장 적합하고, 통신 복잡도가 적은 파이어 베이스와 안드로이드 스튜디오를 사용했다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_동작시나리오.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_역할분담.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_세부개발일정표.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_시스템구성도.png|800픽셀]]&lt;br /&gt;
&lt;br /&gt;
===기구부 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_무드등모드3D설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_자외선살균모드3D.png|166픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
먼저 기구부에서는 최종적으로 제품의 형상과 베벨 기어로 사각 LED 기둥을 올리는 Mechanism과 자외선 LED가 부착되어 있는 Slide Pin Mechanism을 구현하였다. &lt;br /&gt;
제품의 형상의 경우에는 제품의 무드등 기능과 자외선 살균 기능을 정상적으로 구현할 수 있게 필요한 요소들을 적절히 배치할 수 있는 공간을 만들었고, 사용자의 안전성을 위해 Mood Light를 위한 NeoPixel과 UV-A LED를 구분하여 인체에 해로운 자외선 LED는 기능을 사용하지 않을 때 내부 공간에 위치하도록 구현하였다. &lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism은 첫 번째 자외선 LED가 설치된 사각기둥을 올리는 Motor Mechanism과 두 번째 자외선 LED가 적절한 각도로 펼쳐지는 슬라이드 핀 Mechanism이다. 첫 번째 Motor Mechanism은 모터와 베벨 기어 및 랙 피니언 시스템을 통해 모터의 회전 운동을 직선 운동으로 전환하여 구현하였다. 또한 두 번째 Slide Pin Mechanism은 슬라이드 핀을 이용하여 중력에 의해 자동으로 사각기둥 상승 시 펼쳐지고, 하강 시 접히도록 구현한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''기구부 설계'''&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 1. Motor Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_모터설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_모터구현.png|191픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism 중 자외선 LED가 설치된 사각 기둥을 올릴 베벨 기어와 랙 피니언을 사용한 Motor Mechanism 시스템 구현을 완료했다. CATIA CAD 프로그램을 이용하여 베벨 기어 및 랙 피니언 설계를 완료했고, 3D 프린터를 이용하여 규격에 맞추어 실제 제품을 만들었다. 기존에는 랙 피니언 한 쌍만을 직접 모터에 연결하여 사각기둥의 상하 운동을 구현하려고 했지만, 대칭성이 잘 맞지 않아 한쪽으로 쏠리는 현상이 발생하여, 추가적으로 베벨 기어 등을 사용하여 대칭성을 고려하여 양쪽에서 사각 기둥의 상하 운동을 구현했다.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. Slide Pin Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_슬라이드핀설계.png|200픽셀]]&lt;br /&gt;
[[파일:Slide.jpg|145픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Motor Mechanism을 통해 자외선 LED가 설치된 사각기둥이 상하운동을 하면, LED가 적절한 각도를 이루어 펼쳐지도록 구현하였다. 따라서 Slide Pin Mechanism을 이용하여 사각 기둥이 상승할 때 중력에 의해 자동으로 LED가 펼쳐지고, 하강할 때 자동으로 접힌다. CATIA CAD 프로그램을 이용하여 3D 설계를 하고, 3D 프린팅을 통해 실제로 외관을 만들었다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_LED각도설계.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LED가 펼쳐지는 적절한 각도로는 30도를 정했다. 각도를 정하는 과정에서 두 가지의 고려 사항을 정했는데, 첫 번째는 자외선 LED 끝이 직각으로 도달하는 지점의 거리가 원룸이라는 공간을 고려하여 1m 이상이 되고, 두 번째는 제품과 근접한 곳의 LED 사각지대가 제품의 끝으로부터 10cm 내가 되는 기준을 두었다. 따라서 CATIA CAD 프로그램의 2D 설계를 통해 적절한 각도 30도를 설정했다. (실제로 빛은 직각으로만 전달되는 것이 아니라 사방으로 퍼지기 때문에 실제 최대 도달 거리는 기준값인 1m 보다 더 커진다.)&lt;br /&gt;
&lt;br /&gt;
'''기구부 구현'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_사각기둥.jpg|250픽셀]]&lt;br /&gt;
[[파일:99-1=0_Mood.jpg|250픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_완성본.jpg|250픽셀]]&lt;br /&gt;
[[파일:위에서_본_모습.PNG|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
UV-A LED를 장착할 사각 기둥을 3D Printing을 통해 만들었다. 또한 Mood Light를 위한 NeoPixel은 사각 기둥 밖에 다른 기구에 물결 모양으로 48개를 달았다. 최종적으로 아크릴판에 불투명 시트를 붙여 등을 만들었다.&lt;br /&gt;
&lt;br /&gt;
'''최종 작품'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:파랑이.jpg|250픽셀]][[파일:UV-A_LED.jpg|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
최종으로 Mood Light에서 단색 모드로 파랑색 조명을 킬 때와, UV LED Mode에서 On버튼을 눌렀을 때 결과이다.&lt;br /&gt;
&lt;br /&gt;
===제어부 및 회로 구현===&lt;br /&gt;
 UV-A LED, Motor 회로도&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_제어회로도1.PNG|600픽셀]][[파일:99-1=0_내부회로도2.jpg|600픽셀]]&lt;br /&gt;
 &lt;br /&gt;
SMPS로부터 220V 정격 전압을 12V 정격 전압으로 나누어 아두이노와 Relay Switch를 통해 UV-LED의 전원 On/Off 구현이 가능하다. 모터와 UV LED는 아두이노로 제어하기 때문에 라즈베리파이에서 데이터베이스의 값을 실시간으로 확인하다가 UV LED 동작이 지시되면 아두이노에 신호를 보내준다. 데이터베이스의 값은 안드로이드 App을 통해 갱신되므로 결국은 안드로이드 App을 통해 UV-LED의 On/Off 제어가 가능하다. 라즈베리파이와 아누이노간의 데이터 통신은 USB 케이블을 이용한 시리얼 통신을 이용한다.(라즈베리파이와 아두이노 간의 거리가 가깝고 전송하는 데이터가 단순하기 때문에 간단한 통신 방법 선택) 구현 언어는 파이썬이며 사용한 파이썬 모듈은 serial이다. 이와 관련하여 사용한 함수들은 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
UV-LED 동작을 설정하는 값이 “PowerON”일 경우 USB 포트로 연결된 아두이노로 UTF-8로 인코딩된 문자 u를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 u를 받아 UV-LED On 동작을 수행한다. 만약 UV-LED 동작 설정값에 “PowerON” 외의 값이 저장되었을 경우 (“PowerOFF“의 경우가 일반적이지만 예외 처리를 위해 else로 조건 판단) 아두이노로 UTF-8로 인코딩된 문자 d를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 d를 받아 UV-LED Off 동작을 수행한다.&lt;br /&gt;
&lt;br /&gt;
 NeoPixel 회로도&lt;br /&gt;
[[파일:99-1=0_네오픽셀_회로도.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
Mood Light구현은 NeoPixel과 Raspberry PI를 통해 구현했다. 사용자가 Application을 통해 색을 바꾸면 Database의 값이 아래 표에 Data로 바뀌면서 Mood Light 색이 변한다. NeoPixel의 Digital input을 통해 값이 들어가고, 그에 맞는 색을 자유롭게 바꿀 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:색깔.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:App_insidemode_screenshot.jpg|200픽셀]] [[파일:App_outsidemode_screenshot.jpg|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
 앱&lt;br /&gt;
&lt;br /&gt;
앱을 처음 설치하고 실행하면 앱은 팝업 윈도우로 모바일 디바이스의 위치 정보 권한을 요청하며 사용자는 이를 최초 1회만 수락하면 된다. 앱은 실행되면 즉시 모바일 디바이스의 현재 위치 정보를 받고 데이터베이스에 저장된 집의 위치 정보와의 거리를 계산한다. 이 거리가 일정 거리 이상이면 앱은 실외 모드로 자동 설정되고, 일정 거리 미만이면 실내 모드로 자동 설정된다. 실내 모드와 실외 모드의 전환은 기본적으로 비활성화되어 있으나 사용자가 우측 하단의 스위치를 켜면 활성화되어 강제 모드 전환이 가능하게 된다. 그래서 앱을 실행했을 때 자동 모드 설정이 잘못되면 사용자가 직접 모드를 전환해서 원하는 기능을 사용할 수 있다.&lt;br /&gt;
&lt;br /&gt;
앱은 크게 실내 모드와 실외 모드로 구분된다. 각 모드는 Fragment로 정의됐고 MainActivity의 bottomnavigationview를 통해 replace해서 전환할 수 있다. bottomnavigationview는 각 모드에 배치된 스위치가 꺼져 있으면 disabled되고 스위치가 켜지면 enabled된다. 사용자는 실내 모드에서 무드등을 제어하고 실외 모드에서 자외선 살균기를 제어한다.&lt;br /&gt;
&lt;br /&gt;
 실내 모드&lt;br /&gt;
실내 모드에선 무드등의 색상 선택, 밝기 조절, 전원 작동, 스페셜 모드 기능을 이용할 수 있다.&lt;br /&gt;
* colorpicker&lt;br /&gt;
: colorpicker의 색상은 초기에 12시의 연두색으로 설정되어 있고 사용자가 버튼을 드래그해서 원하는 색상을 선택할 수 있다. colorpicker의 중앙에 있는 원은 왼쪽 반원에 이전 색상을 표시하고 오른쪽 반원에 현재 색상을 표시하여 전후 색상을 비교하기 좋게 만들어준다. 사용자가 무드등의 전원을 키면 이전 색상이 표시된 반원이 현재 색상으로 교체된다. 색상은 여섯 자리의 16진수 #nnnnnn으로 표현되며 정수형으로 저장된다. 사용자가 colorpicker로 다양한 색상을 연속적인 범위에서 선택할 수 있기 때문에 선택의 폭이 넓고 세밀한 색상 선택이 가능하다. 무드등의 전원이 켜져 있으면 색상의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 밝기&lt;br /&gt;
: 무드등의 밝기 조절은 실내 모드 중앙의 seekbar를 통해 할 수 있다. 밝기 값은 최소 0에서 최대 255까지 선택 가능하다. colorpicker와 같이 무드등의 전원이 켜져 있으면 밝기의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 전원&lt;br /&gt;
: Power ON 버튼을 누르면 무드등의 전원이 켜진다. 전원 버튼 하단의 얇은 띠가 전원이 꺼져있을 때 회색, 전원이 켜져있을 때 녹색을 띠면서 사용자가 무드등의 전원이 켜져있는지를 직관적으로 알 수 있게 해준다. 전원 작동 데이터가 데이터베이스에 성공적으로 전송되면 앱 화면 하단에 Toast 텍스트를 표시하여 사용자에게 전원의 작동을 알려준다. 전원 버튼의 Power ON/Power OFF 텍스트는 colorpicker에서 선택된 색상과 실시간으로 동일하게 적용되어 사용자에게 무드등이 어떤 색으로 켜질지 더욱 직관적으로 와닿게 만들어준다.&lt;br /&gt;
* 스페셜 모드&lt;br /&gt;
: 앱을 실행하면 무드등의 모드는 기본 모드로 설정되어 있는 상태이다. 스페셜 모드 버튼을 누르면 순차적으로 웨이브 모드 1, 웨이브 모드 2, 레인보우 모드 1, 레인보우 모드 2가 설정되며 그후 다시 기본 모드로 돌아온다. 현재 설정된 모드는 스페셜 모드 버튼의 텍스트로 나타나서 사용자가 이를 알 수 있다. 또한 버튼을 누를수록 단계적으로 버튼의 배경 색이 진해지며 버튼의 깊이감을 더해준다. 스페셜 모드 버튼의 아래엔 모드의 전체 개수와 현재 모드가 몇 번째인지를 시각적으로 쉽게 알게 해주는 공백 칸들이 있다. 이 칸들은 기본 모드에서 모두 비어있게 되고 스페셜 버튼이 눌릴 때마다 한 개씩 채워지며 모두 채워진 후엔 다시 모두 빈 칸으로 돌아온다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스페셜 모드는 무드등이 켜져 있을 때에만 작동이 유효하다. 무드등의 전원이 꺼져 있을 때 스페셜 모드를 작동하면 무드등에서 아무 일도 일어나지 않는다. 이런 상황을 예방하기 위해 앱에서 무드등의 전원이 꺼진 상태에서 스페셜 모드만 작동하는 일이 일어나지 않게 한다. 무드등의 전원이 꺼진 상태에서 스페셜 모드 버튼을 누르면, 앱은 무드등의 전원도 키고 스페셜 모드도 작동한다. 또한 스페셜 모드가 설정된 상태에서 무드등의 전원을 끄면 앱은 스페셜 모드를 기본 모드로 설정하고 무드등의 전원을 끈다. 만약 사용자가 레인보우 모드를 바로 사용하고 싶다면 전원 버튼을 누를 필요 없이 바로 스페셜 모드 버튼을 3회 누르면 된다. 전원 버튼을 누르는 단계를 거치지 않아도 되기 때문에 빠르고 간편하게 즉시 스페셜 모드를 이용할 수 있다. 결과적으로 무드등의 전원이 꺼진 상태에서 무드등의 모드는 항상 기본 모드로 설정되어 있고, 스페셜 모드가 켜진 상태면 무드등의 전원도 항상 켜진 상태가 된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 실외 모드&lt;br /&gt;
실외 모드에선 위치 정보 설정, 자외선 살균기의 전원 작동, 타이머 기능을 이용할 수 있다.&lt;br /&gt;
* 위치 정보 설정&lt;br /&gt;
: HOME LOCATION 버튼을 누르면 모바일 디바이스의 현재 위치 정보를 데이터베이스에 저장하여 집의 위치로 설정한다. 설정한 집의 위치 정보는 앱이 실행됐을 때 집과 사용자 사이의 거리를 계산하는 데 이용된다. 데이터베이스에 저장된 위치 정보는 사용자가 새롭게 설정하지 않는 한 항상 일정하게 유지된다. 그래서 앱을 재설치했을 때 집의 위치 정보 설정을 다시 할 필요가 없다. 상수의 변수명을 대문자 알파벳으로 선언하는 프로그래밍 관습을 응용해서 위치 정보 설정 버튼의 텍스트도 대문자 알파벳으로 하였다. &lt;br /&gt;
* 자외선 살균기&lt;br /&gt;
: 실외 모드에서 Power ON 버튼을 누르면 자외선 살균기의 전원이 켜진다. 전원 버튼 위의 spinner에서 타이머가 작동할 시간을 선택할 수 있으며 초기 타이머는 1시간으로 설정된 상태이다. 타이머는 시간 단위로 선택할 수 있고 최대 12시간까지 선택 가능하다. 자외선 살균기의 전원을 켜면 전원 버튼 아래에 textview가 표시되어 타이머의 남은 시간을 알려준다. 설정한 타이머가 종료되면 앱이 자동적으로 자외선 살균기의 전원을 끈다.&lt;br /&gt;
* 시간 Spinner&lt;br /&gt;
: APP의 실외모드에선 자외선 살균기를 제어한다. 사용자는 작동 시간을 spinner로 선택하여 자외선 살균기 전원 버튼을 눌러 작동시킨다. 그러면 APP은 스피너에 선택된 시간과 전원 데이터를 데이터베이스에 전송한다. APP에서 사용자가 직접 자외선 살균기의 전원을 끌 수 있지만 설정한 시간이 경과되면 APP은 전원을 끄는 데이터를 데이터베이스에 전송한다. 만약 사용자가 작동 시간을 연장시키고 싶다면 자외선 살균기를 끄고 다시 작동하면 된다. 그러면 남은 시간이 다시 세팅되어 작동 시간이 증가하게 된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Firebase&lt;br /&gt;
[[파일:99-1=0_파이어베이스스크린샷.jpg|300픽셀]]&lt;br /&gt;
[[파일:99-1=0_파이어베이스연동.jpg|500픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Firebase는 Google 사의 모바일 및 웹 개발 플랫폼이다. Firebase는 많은 유용한 기능을 지원하지만 여기선 실시간 데이터베이스를 사용한다. Firebase의 장점은 안드로이드 및 iOS 앱과 연동이 용이하다는 것이다. 또한, Firebase는 시간과 장소에 구애받지 않고, 데이터가 안전하게 보존되며, 데이터를 읽고 쓰는 것이 간편하다. 그래서 Firebase는 사용자의 집의 위치 정보를 저장하기에 적합하다. 이 데이터는 제 3자가 접근할 수 없으며 사용자가 새롭게 설정하지 않는 한 영구히 보존된다. 앱은 Firebase의 데이터베이스에 집의 위치 정보를 저장하기도 하지만 읽어오기도 한다. 앱은 실행되면 데이터베이스의 집의 위치 정보를 읽어와서 현재 사용자의 위치 사이의 거리를 계산하고 실내/실외를 판별한다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 작동할 때에도 Firebase를 이용한다. 사용자가 앱으로 작동한 무드등의 색상, 밝기, 전원, 모드 데이터와 자외선 살균기의 전원, 타이머 데이터는 Firebase의 데이터베이스에 저장된다. 앱은 무드등의 전원이 켜진 상태에서 색상이나 밝기가 변경되면 변경 사항을 즉시 데이터베이스에 반영한다. 자외선 살균기의 타이머는 데이터베이스에 저장되지만 남은 타이머 시간까지 일일이 저장되진 않는다. 앱에서 설정한 시간이 지나면 자외선 살균기의 전원을 끄는 데이터를 저장할 뿐이다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 직접 구동시키는 것은 라즈베리파이와 아두이노이다. 라즈베리파이는 데이터베이스에 저장된 무드등의 색상, 밝기, 전원, 모드 데이터를 읽어와 라즈베리파이에 직접 연결된 네오픽셀 LED의 작동을 지시한다. 또한 살균기의 전원 데이터를 읽어와 아두이노에 살균기를 들어올리는 모터의 동작과 살균기 LED의 동작을 지시한다. 아두이노에서는 라즈베리파이의 지시를 대기하다가 살균기의 ON/OFF 동작 신호를 받아 살균기를 켜고 끈다. 이 때 살균기가 부착된 구동부의 높이는 아두이노에 연결된 초음파센서로 거리를 측정하여 조절한다.&lt;br /&gt;
&lt;br /&gt;
===UV-LED 곰팡이 저해 실험===&lt;br /&gt;
&lt;br /&gt;
본 실험은 UV-A LED의 곰팡이 생육 저해 능력을 검증하기 위한 실험이다. 구체적인 저해율의 수치를 나타내기 위해 1차 살균 실험에 이어 2차 곰팡이로 실험을 하여 저해율을 나타낼 것이다.&lt;br /&gt;
&lt;br /&gt;
: 1. 실험 준비&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비1.png|200픽셀|ㅇ|왼쪽|Petri Dish, Sterile cork borer, 곰팡이]]    [[파일:99-1=0_세균실험준비2.png|173픽셀]]&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비3.png|200픽셀]]    [[파일:99-1=0_세균실험준비4.png|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 실험 과정&lt;br /&gt;
&lt;br /&gt;
:: 1) 먼저 3종의 곰팡이(Cladosporium sp, Fusarium sp, Aspergillus sp)를 키울 배지를 만들기 위해 Difo회사 제품인 PDA 12g과 Agar 10g을 비커에 넣은 후 500ml distilled water를 넣어준 후 autoclave에서 121℃에서 15분간 돌려준 뒤 petri dish에 부어 1~2일 동안 말린다.&lt;br /&gt;
&lt;br /&gt;
:: 2) 곰팡이가 PDA배지에서 충분히 성장했다면 이를 각각 Petri Dish에 접종한다. 이 때 일정한 양의 곰팡이를 접종하기 위해 그림에 있는 Sterile cork borer를 사용한다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정1.png|200픽셀]]    [[파일:99-1=0_세균실험과정2.png|182픽셀]]&lt;br /&gt;
: [[파일:99-1=0_세균실험과정3.png|200픽셀]]    [[파일:99-1=0_세균실험과정4.png|195픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 3) 3종의 곰팡이를 0.5m, 0.75m, 1m 간격으로 배치시키고, 다른 하나는 대조군으로 UV-A LED에 노출시키지 않은 채로 성장시킨다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정5.png|200픽셀]]    [[파일:99-1=0_세균실험과정6.png|172픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 4) Petri Dish 안에서 곰팡이의 성장률을 원의 반지름으로 근사시킵니다. 또한 생리식염수를 10마이크로미터 떨어트린 후, 소량의 곰팡이를 접종해준 뒤 커버 글라스를 붙이고 광학현미경으로 관찰한다.&lt;br /&gt;
&lt;br /&gt;
:: 5) 곰팡이가 자란 원의 반지름을 측정하고, 이를 저해율로 나타낸다. 저해율의 식은 다음과 같다. (저해율(%)= (대조군의 반지름 - 실험군의 반지름)/대조군의 반지름 * 100)&lt;br /&gt;
: [[파일:99-1=0_곰팡이_측정.jpg|400픽셀]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 3. 실험 결과&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 1) Cladosporium sp(벽지 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Cladosporium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 2) Fusarium sp(토양 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Fusarium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 3) Aspergillus sp(누룩 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Aspergillus_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
아래 표는 현미경을 통해 관측한 곰팡이의 반지름(cm)이고, 이를 위해 저해율식에 대입하여 곰팡이 저해율을 계산하였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:길이값.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:저해율.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:5시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:10시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 4. 결론&lt;br /&gt;
: : 최종 저해율로 나타낸 결과 UV-A LED에 5시간, 10시간에 따른 차이는 거의 없었지만 거리에 따른 저해율의 차이는 눈에 띄었다. 0.5m 거리에선 약 80%의 저해율을 보였고, 0.75m 에서는 Cladosporuim sp와 Fusarium sp은 약 50%의 저해율을 보였지만 Aspergillus sp는 30%의 저해율을 보였습니다. 1m 거리에선 저해율이 많이 떨어졌는데, Cladosporium sp는 39%, Fusarium sp는 25%, Aspergillus sp는 6%의 저해율을 보였습니다. 1m에서의 살균력은 많이 떨어졌고, 이를 보완하기 위해 기존 실험은 25cm UV-A LED 2개를 설치했는데, 제품에는 한 슬라이드 당 3개로 늘려 설치했다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic1.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/8/87/99-1%3D0_%EC%98%81%EC%83%81.avi]]  [[파일:99-1=0_videopic2.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/c/ce/99-1%3D0_%EC%98%81%EC%83%812.avi]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic3.png|300픽셀|link=https://capstone.uos.ac.kr/mie/images/8/82/99-1%3D0_%EC%98%81%EC%83%813.mp4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
'''UV-A LED Mode &amp;amp; Mood Light mode'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:UV-A LED.jpg|250픽셀]][[파일:99-1=0_무드등.jpg|250픽셀|link=https://capstone.uos.ac.kr/mie/images/99-1=0_영상.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_미구현내용.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_평가항목.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
[[파일:99-1=0_평가_결과.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
노*진: 프로젝트의 주제를 결정하기 전까지만 하더라도 라즈베리파이와 각종 모듈에 대한 지식이 없어 막막했습니다. 하지만 잘 모르는 만큼 팀원들과 더욱 조사를 열심히 하고, 구체적으로 설계를 하였습니다. 개념설계 발표 때까지 ‘과연 의도한대로 동작할까?’ 라는 의문점을 가졌지만 중간발표 준비를 하면서 원하는 기능들이 하나 둘씩 구현되고, 기구부의 Mechanism들이 완성되는 것을 보면서 재밌었고, 성취감을 느꼈습니다. 프로젝트를 진행하면서 전공 지식이 아닌 전기 회로도와 관련된 일들을 구글링과 여러 사이트에 물어보면서 해결을 했고, UV-LED의 살균력을 검증하기 위해 생명공학 전공인 지인의 도움을 받으면서 프로젝트의 문제들을 해결했습니다. 이 과정에서 잘 모르는 부분을 어떤 방법으로 해결할 지에 대한 능력을 기를 수 있었습니다. 마지막으로 내장형시스템 과목을 통해 기계정보공학과의 매력을 느낄 수 있었고, 앞으로 프로젝트를 수행할 때 자신감을 가질 수 있을 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
박*훈 : 기계정보공학을 전공한지 4학년 1학기에 처음으로 컴퓨터 관련 프로젝트를 진행했습니다. 그동안 기계 관련 CAD 프로젝트나 CAE 프로젝트는 다수 경험이 있었지만 컴퓨터 분야는 경험이 없었기 때문에 프로젝트 과정 중 가장 처음 과정인 주제 선정에서부터 엄청난 난관에 부딪혔습니다. 하지만 주어진 시간 안에 주어진 모듈로 할 수 있는 주제를 생각해냈고, 프로젝트를 시작했습니다. 프로젝트 진행 과정 중에 한계에 도달하는 일도 있었고 뜻처럼 진행되지 않는 일도 있었지만 팀원들과 함께 돌파구를 찾아가며 차근차근 프로젝트를 완성했습니다. 프로젝트를 진행하면서 기계정보공학의 융합적 전공의 취지에 맞게 기계적인 메커니즘과 임베디드 시스템을 융합한 완성품을 제작하기 위해 노력을 했고, 실제로 저는 기구부의 메커니즘 쪽을 맡아 CAD 프로그램인 CATIA를 통해 기어 및 랙 피니언 세트와 슬라이드 핀 등을 설계하고 3D 프린터를 이용하여 실제 구현을 완료했습니다. 기구부 제작 과정 중 값비싼 3D 프린터 제작의 비용과 3D 프린터 제작품의 과도한 무게 등의 문제가 있었지만, 팀원들과 협의한 끝에 해결 방안을 고안하여 이러한 문제를 해결했습니다. 이번 내장형 프로젝트의 경험을 통해 팀원들과의 소통 능력과 문제 해결 능력을 한층 성장시켰고, 앞으로 있을 프로젝트에 큰 도움이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
주*완: 이번 학기에 내장형시스템및실습을 수강하며 처음으로 주제 선정부터 완제품 제작까지 모든 것을 직접 경험했습니다. 주제 선정부터 시작해 모든 과정이 쉽지 않았고 걸림돌에 걸려서 결국 설계를 바꾸기도 했습니다. 하지만 한계를 극복할 방법을 고민하고 공부하고 질문하고 찾아 헤매면서 많은 것을 배웠습니다. 특히 제가 맡은 부분인 안드로이드 앱을 개발하기 위해 1학년 때 배웠지만 모두 잊어버린 java를 처음부터 다시 공부했고, 안드로이드를 아무것도 모르는 상태에서 인터넷 검색만으로 하나하나 공부하고 직접 코드를 짜서 완성까지 했습니다. 과정 중에 알 수 없는 이유때문에 앱이 원하는 대로 안 되거나 강제 종료 될 때가 매우 매우 많았는데, 스스로 계속 공부하고 원인을 찾고 고쳐 나가서 마침내 앱을 완성할 수 있었고, 스스로 문제를 해결하는 능력을 기를 수 있었습니다. 또한 기구부와 외형을 이루는 부분을 만들 때 자르고 다듬는 일이 많이 필요했는데 서로 필요한 부분이 있으면 도와주면서 협동하여 결과가 잘 나온 것 같아 만족스럽습니다. 프로젝트를 하며 값진 경험을 했고 앞으로 다른 프로젝트를 하게 된다면 이 경험이 소중한 밑바탕이 돼서 도움이 많이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
함*규: 임베디드 시스템을 처음 접해보기에 주제를 정하는 과정이 가장 어려웠던 것 같습니다. 처음에는 주어진 시간 내에 해낼 수 없는 비현실적인 주제에 대해 고민하며 기능적 한계에 부딪혀보기도 하고, 챗바퀴 도는 듯한 생각이 들었지만, 이 과정 속에서 전체적인 임베디드 시스템에 대한 이해와 할 수있는 것과 할 수 없는 것에 대해 구분할 수 있는 능력을 얻을 수 있었습니다. 이후에 직접 정한 주제를 바탕으로 프로젝트를 수행해나가면서, 초기 설계로는 해결할 수 없는 문제들을 팀원들과 함께 협동하여 풀어나가는 과정은 정말 힘들기도 했지만 되돌아 보면 책상에서는 배울 수 없는 많은 경험들을 배웠다고 생각합니다. 4-1학기를 떠올리면 많은 희노애락이 있었던 내장형 밖에 생각나지 않을 것 같은 데, 정말정말 재밌었고(농담이 아닙니다.) 후배들에게 꼭 추천해주고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
유*환: 내장형 시스템 프로젝트의 주제를 정하는 과정이 가장 어려웠습니다. 라즈베리파이에 센서나 모터, 또다른 보드를 연결하여 사용한 경험이 없어 주어진 시간 내에 완성할 수 있는 정도를 알기 어려웠습니다. 그래서 처음에는 학기 내에 완성이 불가능한 주제들을 생각했었습니다. 결국 마지막까지 주제를 정하지 못하다가 막바지에 급하게 살균 무드등을 만들기로 했습니다. 주제가 정해지고 자료조사를 마쳤을 때만 해도 힘든일이 끝나고 모든 일이 수월하게 진행될 것만 같았습니다. 그런데 출입 감지 방법, 음악 주파수 분석 등이 뜻대로 되지 않자 다시 조마조마해졌습니다. 하지만 적절한 대안을 찾고 해결해나가면서 완성되가는 프로젝트를 보며 뿌듯했습니다. 그리고 살균 무드등을 완성하고 영상까지 마무리 지었을 때는 그동안 밤을 새며 고생했던 시간들에 대해 보상받는 느낌이 들었습니다.&lt;br /&gt;
무엇보다 팀원 형들이 프로젝트에서 각자가 맡은 열할을 너무 잘 수행해주어서 감사했습니다. 유일하게 라즈베리파이 경험이 있는 저보다도 잘해주어서 미안하기도 하고 대단하다는 느낌이 들었습니다. 내장형 프로젝트는 과정은 정말 힘들었지만 팀원들과 살균 무드등을 만드는 것이 재밌었고 프로젝트 진행에 대한 경험치가 쌓여 좋은 기회가 되었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5878</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5878"/>
				<updated>2020-06-21T18:12:23Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.1 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.2 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.3 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
&lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 그동안 학습시킨 가중치를 사용하여 전처리가 끝난 이미지를 불러와서 페트병과 캔이 검출이 되었는지 확인을 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data cfg/yolov3.cfg backup/yolov3_last.weights rvm/image/*.jpg &amp;gt;&amp;gt; detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True, timeout=10)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5877</id>
		<title>일사분란착착착 - 영상인식을 통한 RVM 시스템 개발</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=%EC%9D%BC%EC%82%AC%EB%B6%84%EB%9E%80%EC%B0%A9%EC%B0%A9%EC%B0%A9_-_%EC%98%81%EC%83%81%EC%9D%B8%EC%8B%9D%EC%9D%84_%ED%86%B5%ED%95%9C_RVM_%EC%8B%9C%EC%8A%A4%ED%85%9C_%EA%B0%9C%EB%B0%9C&amp;diff=5877"/>
				<updated>2020-06-21T18:11:14Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
영상인식을 통한 RVM 시스템 개발 &amp;lt;br/&amp;gt;&lt;br /&gt;
Developing Reverse Vending Machine Using Image Detection&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3 ~ 2020.6&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (유*영) (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (강*찬) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (박*석) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (심*헌) &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 (20144300**) (윤*상) &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===소스코드===&lt;br /&gt;
https://github.com/YeonsangYoon/embedded-system-project&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
페트병은 재활용 자원 중에서도 재활용 가능성이 높고 또 재활용 후에도 품질이 크게 떨어지지 않는다. 하지만 라벨 및 뚜껑이 있는 경우 재활용 전처리 과정에서 비용과 시간이 크게 늘어가게 된다. 하지만 페트병의 라벨 및 뚜껑을 제거하여 버림이 올바른 방법임을 모르는 경우 많다. 이를 위해 올바른 재활용 방법 홍보가 필요하지만 실용적으로 이루어지지 않는 상태이다. 우리는 사람들에게 조금 더 흥미로운 방법을 통해서 재활용 방법을 홍보하기 위해 독일 덴마크 등에서 활용되는 RVM을 모티브로  삼았다. 또한 제도적으로 RVM이 확립되어 있지않은 상태에서 더 많은 페트와 캔을 수거하기 위해 영상인식 RVM을 생각하였다. RVM(reverse vending machine)은 일반 자판기와는 다르게 돈을 넣고 물건을 받는 것이 아닌, 물건은 넣으면 돈이 나오는 구조이다. 페트나 캔을 넣으면 소정의 보상을 받게되고 이 과정에서 자연스럽게 흥미가 생긴다. 동시에 올바른 재활용 방법을 익히는 교육적인 효과도 불러올 것이다. 우리는 기존의 RVM과 달리 임베디드 시스템을 활용하여 더 경제적이고 이동식인 시스템을 구축함을 목표로 했다. 기존의 RVM이 크기 및 무게 문제로 인해 설치 장소에 고정이 되어있는것과 달리 우리가 개발한 시스템은 여러장소(초등학교, 주거단지 등)에 유연하게 배치하여 소량의 기기로 큰 교육효과를 누릴수 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
[[파일:그림1.png|700픽셀|섬네일|가운데|그림 1. 사용자 시나리오]]&lt;br /&gt;
본 프로젝트에서 구현을 목표로 한 부분은 검은색 글씨로 표기하였다. 기존의 RVM 시스템은 사용자들의 적극적인 재활용쓰레기 수거를 위하여 투입한 쓰레기에 비례해 일정부분 현금이나 포인트로 보상을 주었다. 하지만 본 프로젝트에서 짧은 기간내 기본적인 RVM 기능 외 어플리케이션과 사용자 포인트 적립을 구현하는 것이 힘들다고 판단하여 부가기능은 추후에 개발하고 기본적인 RVM의 기능을 수행하는 시스템을 개발을 목표로 잡았다. 기본적인 사용자 시나리오는 다음과 같다. 사용자가 시작버튼을 누르면 내부적으로 기계의 상태가 ON으로 바뀌어 동작 시퀀스가 실행된다. 기본적으로 한번에 1개의 쓰레기를 처리하도록 동작을 하며 내부적으로 '''투입 -&amp;gt; 이동 -&amp;gt; 판별 -&amp;gt; 분류''' 의 순서로 동작한다. 사용자가 모든 재활용 쓰레기를 투입하여 종료버튼을 누르면 내부적으로 기계의 상태가 OFF로 바뀌어 동작 시퀀스가 완료된 후 idle 상태로 바뀌고 투입 결과가 UI에 표시된다.&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
[[파일:시스템구성도.JPG|500픽셀|가운데|섬네일|그림2. 시스템 구성도]]&lt;br /&gt;
*'''RVM Controller'''&lt;br /&gt;
*'''Image Processing Server'''&lt;br /&gt;
*'''Rail Controller'''&lt;br /&gt;
RVM 시스템은 크게 3개의 프로세스로 구동된다. RVM Controller는 UI를 통해 사용자 이벤트를 처리하고 Status와 Main Cycle을 통해 시스템 상태를 관리하고 기계를 동작시키는 역할을 한다. RVM Controller는 하나의 프로세스로서 라즈베리파이에서 작동하며 Main Cycle과 UI를 2개의 Thread로 나누어 동시에 실행된다. UI는 사용자의 Event를 받아 Machine Status를 바꾸고 Main Cycle은 현재 status에 따라 전체 시스템을 동작시킨다. Rail Controller는 모든 모터를 제어하여 쓰레기를 판별하는 위치까지 이동시키고 각 결과에 따라 분류하도록 하는 프로세스이다. RVM Controller에게 Serial 통신을 통해 명령을 전달받아 아두이노에서 작동한다. Rail Controller는 총 4가지 동작 Case를 처리한다. 마지막으로 Image Processing Server는 판별부까지 이동한 쓰레기의 사진을 찍어 영상처리를 통해 판별하는 프로세스이다. RVM Controller와 Htttp 통신을 하기 위해 Web Server를 구축하였다. RVM Controller에서 판별 request를 보내면 Image Server에서는 사진을 찍고 영상처리를 하여 결과를 다시 보내준다.&lt;br /&gt;
&lt;br /&gt;
===하드웨어 설계 및 구현===&lt;br /&gt;
RVM의 아두이노 메가 2560은 전반적인 모터제어 및  라즈베리파이와 시리얼 통신을 통해 동작 요청과 완료 신호를 송수신을 한다. &lt;br /&gt;
[[파일:그림2.png|300픽셀|가운데|섬네일|그림3. 하드웨어 연결도]]&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스2.PNG|400픽셀|섬네일|가운데]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:기어박스.PNG|400픽셀|섬네일|가운데|그림4.1 기어박스]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:레일.png|420픽셀|섬네일|가운데|그림4.2 레일 &amp;amp; 투입부]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:페트분류.png|600픽셀|섬네일|가운데|그림4.3 페트병 분류기]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
아두이노 메인루프 : 서버역할을 하는 라즈베리파이와 시리얼 통신을 하고 들어오는 신호에 따라 정해진 기구부를 작동 및 제어한 후 해당 동작을 완료하면 서버에 동작 완료 신호를 보냅니다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
    if(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      in_data = Serial.read();&lt;br /&gt;
      switch(in_data)&lt;br /&gt;
      {&lt;br /&gt;
    &lt;br /&gt;
          case '1' :  // 입구 동작&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            stepM(stepsPerRevolution*-1.3);&lt;br /&gt;
            M1_CW(225,1500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            stepM(stepsPerRevolution*1.3);&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          case '2' : // pet 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M3_CW(900);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CW(220,800);&lt;br /&gt;
            M3_CCW(900);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '3' :  // can 분류 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CW(5500,250);&lt;br /&gt;
            delay(100);&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M2_CCW(5400,250);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
  &lt;br /&gt;
          case '4':  // 반송 처리&lt;br /&gt;
            M2_duration = 0;&lt;br /&gt;
            M3_duration = 0;&lt;br /&gt;
            M1_CCW(255,2500);&lt;br /&gt;
            Serial.write('y');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
    &lt;br /&gt;
          default :&lt;br /&gt;
            Serial.write('E');&lt;br /&gt;
            Flush();&lt;br /&gt;
            break;&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Flush()    //Serial통신 버퍼 제거&lt;br /&gt;
{&lt;br /&gt;
    while(Serial.available() &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        Serial.read();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3개의 dc모터, 1개의 스탭모터, 2개의 모터 드라이버가 사용됐습니다. 각각의 모터의 속도, 방향 그리고 엔코더 값에따라 모터를 제어 할 수 있습니다.&lt;br /&gt;
4가지의 모터의 방향 및 속도를 제어, 두 개의 dc모터는 엔코더센서를 통해 회전 정도를 제어할 수 있습니다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
// DC Motor 1 : rail&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CW(int b, int c)&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1,HIGH);&lt;br /&gt;
    digitalWrite(in2,LOW);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M1_CCW(int b, int c) {&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, HIGH);&lt;br /&gt;
    analogWrite(analog, b);      &lt;br /&gt;
    delay(c);&lt;br /&gt;
&lt;br /&gt;
    M1_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M1_stop()&lt;br /&gt;
{&lt;br /&gt;
    digitalWrite(in1, LOW);&lt;br /&gt;
    digitalWrite(in2, LOW);&lt;br /&gt;
    delay(500);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 2&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CW(int a, int b) {&lt;br /&gt;
&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        digitalWrite(M2_in1,HIGH);&lt;br /&gt;
        digitalWrite(M2_in2,LOW);&lt;br /&gt;
        analogWrite(analog2, b);   &lt;br /&gt;
        &lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
          count++;&lt;br /&gt;
          if(count&amp;gt;150)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M2_CCW(int a, int b) &lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, HIGH);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M2_duration) &amp;lt;= a)&lt;br /&gt;
    {&lt;br /&gt;
        analogWrite(analog2, b);&lt;br /&gt;
        delay(5);&lt;br /&gt;
&lt;br /&gt;
        temp = M2_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;150)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        temp1 = M2_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M2_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M2_stop() {&lt;br /&gt;
    digitalWrite(M2_in1, LOW);&lt;br /&gt;
    digitalWrite(M2_in2, LOW);&lt;br /&gt;
    M2_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//DC Motor 3 : Exit&lt;br /&gt;
//a = enc &lt;br /&gt;
void M3_CW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,HIGH);&lt;br /&gt;
    digitalWrite(M3_in2,LOW);&lt;br /&gt;
    &lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//a = enc , b = speed (0~255)&lt;br /&gt;
void M3_CCW(int a)&lt;br /&gt;
{&lt;br /&gt;
    int temp = 0;&lt;br /&gt;
    int temp1 = 0;&lt;br /&gt;
    int count = 0;&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(M3_in1,LOW);&lt;br /&gt;
    digitalWrite(M3_in2,HIGH);&lt;br /&gt;
&lt;br /&gt;
    while(abs(M3_duration) &amp;lt;= a){&lt;br /&gt;
         //Serial.print(&amp;quot;M3_duration : &amp;quot;);&lt;br /&gt;
         //Serial.println(M3_duration);&lt;br /&gt;
         &lt;br /&gt;
        delay(5);&lt;br /&gt;
        temp = M3_duration;&lt;br /&gt;
        if(temp==temp1)&lt;br /&gt;
        {&lt;br /&gt;
            count++;&lt;br /&gt;
            if(count&amp;gt;100)&lt;br /&gt;
              break;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            count = 0;&lt;br /&gt;
        }&lt;br /&gt;
        temp1 = M3_duration;&lt;br /&gt;
    }&lt;br /&gt;
    M3_stop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void M3_stop() &lt;br /&gt;
{&lt;br /&gt;
  digitalWrite(M3_in1, LOW);&lt;br /&gt;
  digitalWrite(M3_in2, LOW);&lt;br /&gt;
  // Serial.println(&amp;quot;3 : stop&amp;quot;);&lt;br /&gt;
  // delay(500);&lt;br /&gt;
  M3_duration = 0;      &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Step Motor1 : Entrance&lt;br /&gt;
void stepM(int stepsPerRevolution)&lt;br /&gt;
{&lt;br /&gt;
  myStepper.step(stepsPerRevolution);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
RVM의 소프트웨어는 기본적으로 python, C, C++을 사용하여 프로그래밍하였다. 아래의 그림은 내부적으로 3개의 프로세스들이 동작하는 시퀀스를 나타낸다. 사용자가 시작 버튼을 누르면 기계 상태가 On으로 바뀌며 Main Cycle을 시작하게 된다. &lt;br /&gt;
[[파일:RVM 최소 시퀀스.png|500픽셀|가운데|섬네일|그림5. 내부 동작 시퀀스 다이어그램]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====RVMController====&lt;br /&gt;
RVM Controller는 라즈베리파이에서 작동하는 프로세스이다. 파이썬으로 작성되었으며 pyqt5 파이썬 GUI 라이브러리를 사용하여 기본 골격을 만들었다. 거기에 현재 기계의 상태를 나타내주는 RVM Status Class와 각 동작을 실행하게 하는 Main Cycle을 구현하였다. 각 프로세스들이 여러가지 방법을 통해 통신하기 때문에 RVM Controller는 처음 시작할 때 여러가지의 통신 인터페이스를 초기화하고 각 센서 측정을 위한 GPIO setting을 해야한다. 아래의 코드는 RVM Controller의 main thread로서 기계를 처음 킬 때 Status class 인스턴스를 생성하고 각종 인터페이스를 초기화하고 로드셀 영점을 조절한다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# stat class init&lt;br /&gt;
RVM_status = RVM_Stat() &lt;br /&gt;
&lt;br /&gt;
if not debug:&lt;br /&gt;
    # USB serial interface&lt;br /&gt;
    port = '/dev/ttyACM0'                           &lt;br /&gt;
    ser = serial.Serial(port, 9600, timeout = 2)&lt;br /&gt;
&lt;br /&gt;
    # Load Cell GPIO setting&lt;br /&gt;
    GPIO.setwarnings(False)&lt;br /&gt;
    hx711 = HX711(LC_DT_Pin, LC_SCK_Pin)&lt;br /&gt;
    hx711.reset()&lt;br /&gt;
&lt;br /&gt;
    w = []&lt;br /&gt;
    for i in range(20):&lt;br /&gt;
        w.append(hx711._read())&lt;br /&gt;
&lt;br /&gt;
        if False in w:&lt;br /&gt;
            w.remove(False)&lt;br /&gt;
        &lt;br /&gt;
    w.remove(max(w))&lt;br /&gt;
    w.remove(min(w))&lt;br /&gt;
    init_avg = sum(w) / len(w)&lt;br /&gt;
&lt;br /&gt;
    # IR Sensor GPIO setting&lt;br /&gt;
    GPIO.setup(IR_Pin1,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin2,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin3,GPIO.IN)&lt;br /&gt;
    GPIO.setup(IR_Pin4,GPIO.IN)&lt;br /&gt;
&lt;br /&gt;
# main Cycle init&lt;br /&gt;
t1 = threading.Thread(target = main_Cycle)&lt;br /&gt;
t1.daemon = False&lt;br /&gt;
t1.start()&lt;br /&gt;
&lt;br /&gt;
# 유저 인터페이스 init&lt;br /&gt;
app = QApplication(sys.argv) &lt;br /&gt;
phone_window = phoneWindow() &lt;br /&gt;
main_window = mainWindow()&lt;br /&gt;
main_window.showFullScreen()&lt;br /&gt;
app.exec_()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[파일:구성.png|600픽셀|가운데|섬네일|그림6. RVM Status &amp;amp; Main Cycle]]&lt;br /&gt;
RVM Controller는 전체 시스템을 관리하는 python 프로세스로서 UI, Main cycle, stat class의 인스턴스를 가지고 있다. Main cycle에서 단계별로 각 모듈을 함수 형태로 호출하며, 현재 stat을 관리한다. RVM Status는 기계의 온오프를 나타내는 Machine Status, 현재 실행 상태를 나타내는 Execute Status, 현재 투입된 재활용 쓰레기 정보인 Recycling Status, 에러 발생 여부를 나타내는 Error Status를 맴버로 가지고 있다. 또한 Main Cylce은 총 7단계의 실행 단계를 가지며 각 단계를 모두 실행해야 한개의 쓰레기가 처리된다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def main_Cycle():&lt;br /&gt;
    # main cycle&lt;br /&gt;
    while 1:&lt;br /&gt;
&lt;br /&gt;
        #0 machine stat check&lt;br /&gt;
        if RVM_status.machine_stat != RVM_STATE_ON:&lt;br /&gt;
            time.sleep(0.1)&lt;br /&gt;
        else :&lt;br /&gt;
            #1 Check IR sensor&lt;br /&gt;
            if checkObjectCond() &amp;lt; 0:&lt;br /&gt;
                if RVM_status.machine_stat == RVM_STATE_OFF :&lt;br /&gt;
                    resultD = 0;&lt;br /&gt;
                else : &lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            #2 Check Load cell &lt;br /&gt;
            elif checkLoadCell() &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #3 rail move command &lt;br /&gt;
            elif moveCommand('Dzone') &amp;lt; 0:&lt;br /&gt;
                errorExit()&lt;br /&gt;
&lt;br /&gt;
            #4 Request discrimination&lt;br /&gt;
            else :&lt;br /&gt;
                resultD = requestD()&lt;br /&gt;
                print(resultD)&lt;br /&gt;
&lt;br /&gt;
                #5 rail move command &lt;br /&gt;
                if moveCommand(resultD)&amp;lt;0:&lt;br /&gt;
                    errorExit()&lt;br /&gt;
&lt;br /&gt;
            if RVM_status.error_stat == retValOK:&lt;br /&gt;
                # Update Status&lt;br /&gt;
                RVM_status.updateStatus(resultD)&lt;br /&gt;
                main_window.can_pet()&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&lt;br /&gt;
            elif RVM_status.error_stat == Error :&lt;br /&gt;
                #debug msg&lt;br /&gt;
                while RVM_status.error_stat == Error :&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                printU(&amp;quot;Error fixed.. restart&amp;quot;)&lt;br /&gt;
                main_window.button_text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====RailController====&lt;br /&gt;
====Image Processing Server====&lt;br /&gt;
Image Processing Server는 판별부에 도착한 물체를 영상인식을 통해 판별하는 역할을 한다. RVM Controller는 python requests 모듈을 통해 간단하게 Http request를 보낼 수 있다. 따라서 RVM Controller로부터 Http request를 받아 판별을 하기 위해 간단한 웹서버를 구축하였다. 웹서버는 임베디드 보드에서 충분히 사용할 수 있는 Flask라는 웹 프레임워크를 사용하였다. 서버는 Http request를 받게되면 총 3가지 단계를 통해 판별을 수행한다. '''카메라 촬영 &amp;amp; 전처리 -&amp;gt; Yolo 영상인식 -&amp;gt; 결과 parsing'''의 순서로 동작하며 이에 대한 자세한 내용은 영상인식 파트에서 설명하겠다.&lt;br /&gt;
&lt;br /&gt;
===영상인식 구현===&lt;br /&gt;
본 프로젝트에서 투입 물건은 카메라로 촬영할 수 있는 장소에 페트병, 캔 두 종류가 오게된다. 페트병과 캔의 분류는 실시간 물체 감지 시스템인 [https://pjreddie.com/darknet/yolo/ '''YOLOv3''']를 사용한다. 물건의 분류는 크게 3가지 단계로 이루어진다. 사진촬영 및 전처리, YOLOv3 실행, 파싱을 통한 결과값 확인&lt;br /&gt;
&lt;br /&gt;
1단계 - 사진촬영 및 전처리&lt;br /&gt;
&lt;br /&gt;
우선 카메라를 통해 촬영되는 이미지를 그대로 사용하기에는 문제가 있었다. 카메라를 통해 보이는 물체는 분류를 하려는 물건만 있는 것이 아니고 모터와 기어박스 등 여러가지 물체들이 존재했다. 때문에 일말의 오류를 방지하고자 오픈소스인 [https://opencv.org/ '''OpenCV''']를 사용하여 이미지를 필요한 부분만 추출하는 전처리 과정을 넣었다.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
def imagepreprocess():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    확장자가 jpg인 파일을 찾아서 불러오고 원하는 크기로 이미지를 자른다.&lt;br /&gt;
    자른 이미지는 기존 이미지를 덮어씌워서 저장한다.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    path = &amp;quot;./&amp;quot;&lt;br /&gt;
    filenames = os.listdir(path)&lt;br /&gt;
    image_name = &amp;quot;&amp;quot;&lt;br /&gt;
    for filename in filenames:&lt;br /&gt;
        full_filename = os.path.join(path, filename)&lt;br /&gt;
        ext = os.path.splitext(full_filename)[-1]&lt;br /&gt;
        if ext == '.jpg': # find jpg&lt;br /&gt;
            image_name = full_filename[2:]&lt;br /&gt;
&lt;br /&gt;
    image = cv2.imread(image_name, cv2.IMREAD_COLOR) # image load&lt;br /&gt;
    image_cut = image[76:390, :, :] # image cut&lt;br /&gt;
    cv2.imwrite(image_name, image_cut)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2단계 - YOLOv3&lt;br /&gt;
&lt;br /&gt;
YOLOv3의 실행은 파이썬의 subprocess를 이용하였다. 그동안 학습시킨 가중치를 사용하여 전처리가 끝난 이미지를 불러와서 페트병과 캔이 검출이 되었는지 확인을 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
cmd_yolo = &amp;quot;./darknet detector test data/obj.data cfg/yolov3.cfg backup/yolov3_last.weights rvm/image/*.jpg &amp;gt;&amp;gt; detect.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Run Yolo&lt;br /&gt;
try:&lt;br /&gt;
    subprocess.call(cmd_yolo, shell=True, timeout=10)&lt;br /&gt;
except subprocess.TimeoutExpired: &lt;br /&gt;
    print(&amp;quot;Timeout during execution&amp;quot;)&lt;br /&gt;
    return -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3단계 - 파싱을 통한 결과값확인&lt;br /&gt;
&lt;br /&gt;
일정패턴으로 나오는 텍스트를 통해서 원하는 문자를 파싱하여 결과값을 확인하였다.&lt;br /&gt;
&lt;br /&gt;
===UI구현===&lt;br /&gt;
&lt;br /&gt;
RVM은 기계의 상태를 모니터링 하고 조작하기 위한 터치스크린 UI를 포함하고 있다.&lt;br /&gt;
라즈베리 파이에서도 안정적으로 동작하는 가벼운 프로그램으로 제작하기 위해 python pyqt5를 이용하여 제작하였다.&lt;br /&gt;
python으로 작성하여 메인 프로세스인 RVM_controller과 같은 프로세스에 동작하며 데이터 통신 비용을 낮추고 pyqt5를 이용하여 그래픽 부담이 낮은 UI를 구현하였다.&lt;br /&gt;
UI는 아래에 표시된 3개의 화면으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul class = &amp;quot;center&amp;quot;&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 1.PNG|400픽셀|섬네일|가운데|그림7.1 시작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 2.PNG|400픽셀|섬네일|가운데|그림7.2 동작화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[파일:RVM UI 3.PNG|400픽셀|섬네일|가운데|그림7.3 종료화면]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
왼쪽화면은 시작화면으로 RVM이 동작 중이지 않다는 것을 나타낸다. 시작 버튼으로 RVM을 동작 상태로 만들 수 있다.&lt;br /&gt;
가운데 화면은 RVM의 동작중 화면으로 RVM의 현재 동작 단계를 표시하는 메시지창과 투입된 페트 또는 캔의 개수를 표시하는 창으로 구성된다. 종료 버튼을 눌러 종료화면으로 넘아 갈 수 있다.&lt;br /&gt;
오른쪽 화면은 종료화면으로 시작부터 종료까지 넣은 페트병과 캔의 총 개수를 표시해주며 기계를 종료한다. 종료후에는 시작화면으로 돌아간다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
[[파일:완성본.png|800픽셀|가운데|섬네일|그림8. 최종 결과물]]&lt;br /&gt;
&lt;br /&gt;
===시연영상===&lt;br /&gt;
*[https://www.youtube.com/watch?v=aANCY5QO0gc 전체 시연영상]&lt;br /&gt;
&lt;br /&gt;
====페트병 처리====&lt;br /&gt;
[[파일:페트병.mp4]]&lt;br /&gt;
====캔====&lt;br /&gt;
[[파일:캔.mp4]]&lt;br /&gt;
====반송====&lt;br /&gt;
[[파일:반송.mp4]]&lt;br /&gt;
====무게초과====&lt;br /&gt;
[[파일:무게초과.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
박*석 - 이번 프로젝트를 하면서 하드에어 제작하는 것이 생각보다 어렵다는 것을 느꼈습니다. 아크릴, 풀리 등의 재료구매부터 각종 모터들과 센서들의 회로 구성까지 쉽지않았습니다. 팀원들과 같이 협력하면서 어려가지 문제들을 하나씩 해결하는 과정자체가 보람찼습니다. 페트병, 캔 분류에서는 YOLOv3 오픈소스를 사용하였는데 단순하게 데이터만 넣어서는 분류가 잘 되지않았습니다. 질 좋은 데이터들과 분류하려는 데이터들의 비율을 일정하게 맞추는 것이 물체 인식부분에 도움이 된다는 것을 새로 알았습니다. 이번 프로젝트 동안 같이한 팀원들에게 정말 고생 많았다고 말하고 싶습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5876</id>
		<title>99-1=0 - Smart Mood Light with Sterilization</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5876"/>
				<updated>2020-06-21T18:11:01Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 최종 결과물 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 Smart Mood Light with Sterilization&lt;br /&gt;
 살균 기능을 탑재한 스마트 무드등&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 노*진 (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 박*훈 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 주*완 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 함*규 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20174300** 유*환 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
 원룸은 타 주거환경에 비해 채광과 통풍 능력이 떨어진다. 공기 청정기를 이용할 수 있지만, 채광 문제를 해결하지 못한다. 다행히 넓은 공간을 가진 주거환경에 비해 규모가 작아 상용 자외선을 통한 살균 효과는 비해 뛰어나지만, 살균기의 제어는 사용자가 원룸 내 없을 때 이루어 져야 한다. 위의 문제를 해결하기 위해 스마트폰을 이용하여 원격 제어를 실행한다. 또한 원격 제어를 하면서 사용자가 원룸 내 있을 때 활용할 수 없는 살균기가 공간을 비효율적으로 차지하게 되는 문제를 해결하기 위해 이를 무드등과 결합 한다. 결과적으로 사용자가 가장 많이 시간을 보내는 공간을 살균할 수 있으며 공간의 비효율성과 안전성, 더불어 무드등의 원격제어로 인한 편의성을 제공한다. 사용자는 외부에 있을 때, 스마트폰을 이용하여 살균기 동작을 제어할 수 있다. 사용자가 내부에 있을 때 별도의 조치 없이도 자외선 동작 제어는 비활성화 되며 무드등의 원격 제어가 가능하다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 1. 배경&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_최근4년간국내미세먼지수치.png|500픽셀]]&lt;br /&gt;
[[파일:99-1=0_최근전세계적바이러스주기.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
최근 국내 미세먼지 수치가 꾸준히 증가함에 따라 국민들의 미세먼지 정화에 관한 관심이 높아지고 있다. 또한, 전 세계적인 바이러스 발생 주기가 짧아짐에 따라 사람들의 살균 제품에 대한 관심도가 높아지고 있다. &lt;br /&gt;
&lt;br /&gt;
햇빛은 면역력 향상이나 기분 전환의 효과뿐 아니라 살균 기능을 가지고 있다. 원룸은 구조적으로 대부분 하나의 창문을 가지고 있어 충분한 빛을 받지 못하며 공기의 입/출구가 하나이기에 통풍이 원활하지 않다. 이러한 조건은 미생물이 자라나기 쉽고 미세먼지의 배출이 어렵다. 이들을 모두 정화하는 데 맞는 조건을 가진 것이 자외선이다. 자외선을 통해 공기 중에 살균이 가능하며 미세먼지 중금속을 분해해 공기를 정화할 수 있다. 하지만 자외선 살균기를 집안에 두고 사용하는 것에는 다음과 같은 문제점들이 있다.&lt;br /&gt;
&lt;br /&gt;
- 살균 자외선은 인간에게 유해하므로 사용자에게 직접적인 자외선이 가해지는 것은 위험하다. 사용자가 없는 곳에서 자외선 살균기가 작동해야 하며 사용자가 있을 때 확실한 방법으로 조작 가능성을 배제 시켜야 한다.&lt;br /&gt;
&lt;br /&gt;
- 사용자가 자주 쓰는 공간의 살균일수록 의미가 있으므로 자외선 살균기는 사용자가 자주 쓰는 공간에 배치되어야 한다. 그러나 위의 이유로 사용자가 실제 방에 있을 때 자외선 살균기를 사용하는 것은 위험하므로 사용자가 있을 때 별다른 기능 없이 공간만을 차지하게 된다.&lt;br /&gt;
&lt;br /&gt;
원룸 내 거주자가 가장 오래 머무르는 공간으로 예상되는 곳은 침대류 이다. 침대류 근처에 있는 물체에 살균기를 결합하여 제품을 만들 수 있다면 효율적으로 문제를 해결할 수 있다. 우리는 이 조건을 만족하는 물체가 무드등이라는 점에서 문제 해결을 시작했다. 또한, 첫 번째 문제 해결을 위해 임베디드 시스템을 설계하여 사용자가 없는 공간에서의 작동을 가능하게 하는 것뿐 아니라 무드등에 다양한 기능을 추가하여 편리성과 기능의 다양함을 추가하려고 한다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 기대효과&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_UV자외선을이용한살균 미세먼지정화과정.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
사용자는 집 밖에서 애플리케이션을 통해 자외선 모듈을 가동함으로써 안전한 방법으로 공간을 살균 및 미세먼지 분해 기능을 작동시킬 수 있다. &lt;br /&gt;
&lt;br /&gt;
또한, 사용자가 개발 제품과 같은 공간에 있다고 판단 시 애플리케이션 자동 스위칭 기능을 통해 자외선 컨트롤 기능이 아닌 무드등 컨트롤 기능을 제어하게 된다. 따로 조작 없이 방 안/밖이라는 공간의 변화에 따라 스위칭 되기에 조작에 신경 쓸 필요가 없다. 무드등 기능으로썬 조도 조정, 빛의 색상 선택, 주변 음악에 따른 색상 변화 기능 등이 있으며 사용자가 선택하는 옵션에 따라 기능을 수행할 수 있어 편리함과 다양성을 제공한다. 사용자의 유무에 상관없이 개발제품은 유용한 도구로써 사용되기에 공간 활용성 또한 확보할 수 있을 것으로 생각된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:99-1=0_목적계통도.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 안전성&lt;br /&gt;
자외선 활성/비활성화 기능은 가스 자동밸브 잠금과 비슷한 원리로 앱 구동 시 스마트폰 GPS 기능을 통해 사용자의 위치정보를 받아 주거 위치와 비교하여 앱 인터페이스 비활성화 여부를 결정한다. GPS 오작동으로 인한 비활성화가 적절히 이루어지지 않았을 때를 대비해 앱 내 수동 비활성화 버튼을 추가한다. 앱 인터페이스가 비활성화 되어 있어 사용자의 실수로 인한 신호 전달 가능성을 사전에 차단할 수 있다. 또한 회로의 긴급 정지 스위치를 통해 하드웨어 오동작 시 회로 자체의 전류를 제어할 수 있다. 또한 인체 및 환경에 유해한 정도를 판단하여 인체에 비교적 덜 유해한 UV-A의 자외선 영역을 사용했고, 수은으로 환경 파괴 문제를 일으키는 UV-램프를 대신하여 UV-LED를 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 실용성&lt;br /&gt;
먼저 자외선 영역의 성능을 비교하면 살균력 기준으로는 분 단위 살균력을 가진 UV-C가 시 단위 살균력을 가진 UV-A보다 월등히 강력하다. 하지만 살균 범위를 보면 UV-A는 공기 중 3-5m의 반경을 가진 것에 비해 UV-C는 공기 중 투과력이 매우 낮아 넓은 범위를 살균할 때에는 UV-A가 더 적합하다. 따라서 거주자 없이 비어있는 시간이 긴 원룸에 맞추어 UV-405nm 자외선을 이용했다. 이 때, 자외선 LED의 살균 방향을 15도 정도 아래쪽으로 내려주어 침구류 및 사용자가 자주 사용하는 물건도 더불어 살균하게 된다. 모터의 경우, 자외선 살균기의 상하 운동이 완료된 후에 해당 위치에서 고정이 되어야 하는데, 서보 모터는 고정 기능이 없고, 스탭 모터는 고정 기능을 사용할 때 소모 전력이 많다. 따라서 적은 소모 전력으로 고정 기능을 수행할 수 있는 웜 기어 모터를 사용했다.&lt;br /&gt;
공간 효율성을 기준으로는 제품 구성 시 얼마나 효율적인 공간 활용 여부를 비교했다. 따라서 각 제품의 특성에 맞추어 무드등 조명에는 필요 전선량이 많은 LED 전구 대신 네오 픽셀을 사용했고, 자외선 조명에는 1cm 이하의 조명 크기를 가지는 UV LED를 사용했다. 또한 사용자 위치를 판별하기 위해서 출입문 주변의 많은 공간을 차지하는 적외선 센서 활용 출/입 카운터 대신 외부 공간을 이용하지 않는 안드로이드 모바일 앱 내의 GPS 기능을 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 편의성&lt;br /&gt;
사용자 입장에서 제품을 이용할 때의 반응성을 기준으로 비교했다. 호스팅 제공 S/W에서는 외부의 데이터베이스를 이용하는 파이썬 애니웨어 대신 자체적으로 실시간 데이터베이스 기능을 제공하는 파이어 베이스를 사용했다. 또한 사용자 위치 판별법에서는 사용자의 출/입 여부를 얼마나 빠르게 판단할 수 있는지에 대한 비교를 했는데, 이는 적외선 센서 활용 카운터가 더 빠른 응답성을 보였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 경제성&lt;br /&gt;
제품에 대한 경제적인 측면은 제품을 구성하는데 사용하는 비용과 유지비로 생각할 수 있다. 먼저 제품 구성비용은 프로젝트의 해당 제품을 구매하는 비용이다. 따라서 보다 더 값이 싼 네오픽셀, UV-A, UV-램프가 더 적합했다. 또한 호스팅 기능 S/W에서는 무료로 이용할 수 있는 범위가 더 넓은 파이어 베이스가 적합했다. 또한 유지비는 제품의 수명이 얼마나 긴지, 배터리 소모량이 얼마나 적은지, 부품 교체 비용이 얼마나 큰지에 대해 비교했다. 따라서 부품 교체 비용이 더 저렴한 네오픽셀, 수명이 더 긴 UV-LED가 더 적합했다. 또한 항상 동작시키는 적외선 센서보다 필요시에만 동작시킬 수 있는 GPS 기능이 배터리 소모량이 거의 없어 더 적합했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 개발 용이성&lt;br /&gt;
개발제품 특성상 데이터베이스 및 호스팅 서비스가 필요하다. 개발자 독자적으로 데이터 베이스와 호스팅 서비스를 개발할 필요 없이 이러한 서비스를 제공하는 서버는 파이어 베이스이다. 개발 난이도는 물론 개발자의 서버 개발 시간을 최소화 하여 다른 분야의 성능 개발을 최대화 시킬 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_개발용이성.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Application과 라즈베리 파이의 통신 중간 다리로 파이어 베이스 서버를 이용한다. 물론 무드등 제어를 위한 라즈베리 파이 서버를 구축하여 통시에 이용하면 원룸 내 무드등 제어 동작 속도를 향상시킬 수도 있다. 그러나 하나의 서버를 이용하면 통신 오류나, 오류가 발생했을 때 오류를 제어하기 쉽다. 또한  추후에 기능 추가가 용이하다. 파이어 베이스는 서버 특성상 서버 사용자가 많아지게 되면 부하가 발생할 수 있다. 그러나 개발 상품의 경우 서버는 한명의 사용자만이 이용한다는 점을 고려하여 이러한 단점을 고려하지 않을 수 있다. 따라서 해당 프로젝트에 가장 적합하고, 통신 복잡도가 적은 파이어 베이스와 안드로이드 스튜디오를 사용했다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_동작시나리오.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_역할분담.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_세부개발일정표.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_시스템구성도.png|800픽셀]]&lt;br /&gt;
&lt;br /&gt;
===기구부 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_무드등모드3D설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_자외선살균모드3D.png|166픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
먼저 기구부에서는 최종적으로 제품의 형상과 베벨 기어로 사각 LED 기둥을 올리는 Mechanism과 자외선 LED가 부착되어 있는 Slide Pin Mechanism을 구현하였다. &lt;br /&gt;
제품의 형상의 경우에는 제품의 무드등 기능과 자외선 살균 기능을 정상적으로 구현할 수 있게 필요한 요소들을 적절히 배치할 수 있는 공간을 만들었고, 사용자의 안전성을 위해 Mood Light를 위한 NeoPixel과 UV-A LED를 구분하여 인체에 해로운 자외선 LED는 기능을 사용하지 않을 때 내부 공간에 위치하도록 구현하였다. &lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism은 첫 번째 자외선 LED가 설치된 사각기둥을 올리는 Motor Mechanism과 두 번째 자외선 LED가 적절한 각도로 펼쳐지는 슬라이드 핀 Mechanism이다. 첫 번째 Motor Mechanism은 모터와 베벨 기어 및 랙 피니언 시스템을 통해 모터의 회전 운동을 직선 운동으로 전환하여 구현하였다. 또한 두 번째 Slide Pin Mechanism은 슬라이드 핀을 이용하여 중력에 의해 자동으로 사각기둥 상승 시 펼쳐지고, 하강 시 접히도록 구현한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''기구부 설계'''&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 1. Motor Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_모터설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_모터구현.png|191픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism 중 자외선 LED가 설치된 사각 기둥을 올릴 베벨 기어와 랙 피니언을 사용한 Motor Mechanism 시스템 구현을 완료했다. CATIA CAD 프로그램을 이용하여 베벨 기어 및 랙 피니언 설계를 완료했고, 3D 프린터를 이용하여 규격에 맞추어 실제 제품을 만들었다. 기존에는 랙 피니언 한 쌍만을 직접 모터에 연결하여 사각기둥의 상하 운동을 구현하려고 했지만, 대칭성이 잘 맞지 않아 한쪽으로 쏠리는 현상이 발생하여, 추가적으로 베벨 기어 등을 사용하여 대칭성을 고려하여 양쪽에서 사각 기둥의 상하 운동을 구현했다.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. Slide Pin Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_슬라이드핀설계.png|200픽셀]]&lt;br /&gt;
[[파일:Slide.jpg|145픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Motor Mechanism을 통해 자외선 LED가 설치된 사각기둥이 상하운동을 하면, LED가 적절한 각도를 이루어 펼쳐지도록 구현하였다. 따라서 Slide Pin Mechanism을 이용하여 사각 기둥이 상승할 때 중력에 의해 자동으로 LED가 펼쳐지고, 하강할 때 자동으로 접힌다. CATIA CAD 프로그램을 이용하여 3D 설계를 하고, 3D 프린팅을 통해 실제로 외관을 만들었다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_LED각도설계.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LED가 펼쳐지는 적절한 각도로는 30도를 정했다. 각도를 정하는 과정에서 두 가지의 고려 사항을 정했는데, 첫 번째는 자외선 LED 끝이 직각으로 도달하는 지점의 거리가 원룸이라는 공간을 고려하여 1m 이상이 되고, 두 번째는 제품과 근접한 곳의 LED 사각지대가 제품의 끝으로부터 10cm 내가 되는 기준을 두었다. 따라서 CATIA CAD 프로그램의 2D 설계를 통해 적절한 각도 30도를 설정했다. (실제로 빛은 직각으로만 전달되는 것이 아니라 사방으로 퍼지기 때문에 실제 최대 도달 거리는 기준값인 1m 보다 더 커진다.)&lt;br /&gt;
&lt;br /&gt;
'''기구부 구현'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_사각기둥.jpg|250픽셀]]&lt;br /&gt;
[[파일:99-1=0_Mood.jpg|250픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_완성본.jpg|250픽셀]]&lt;br /&gt;
[[파일:위에서_본_모습.PNG|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
UV-A LED를 장착할 사각 기둥을 3D Printing을 통해 만들었다. 또한 Mood Light를 위한 NeoPixel은 사각 기둥 밖에 다른 기구에 물결 모양으로 48개를 달았다. 최종적으로 아크릴판에 불투명 시트를 붙여 등을 만들었다.&lt;br /&gt;
&lt;br /&gt;
'''최종 작품'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:파랑이.jpg|250픽셀]][[파일:UV-A_LED.jpg|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
최종으로 Mood Light에서 단색 모드로 파랑색 조명을 킬 때와, UV LED Mode에서 On버튼을 눌렀을 때 결과이다.&lt;br /&gt;
&lt;br /&gt;
===제어부 및 회로 구현===&lt;br /&gt;
 UV-A LED, Motor 회로도&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_제어회로도1.PNG|600픽셀]][[파일:99-1=0_내부회로도2.jpg|600픽셀]]&lt;br /&gt;
 &lt;br /&gt;
SMPS로부터 220V 정격 전압을 12V 정격 전압으로 나누어 아두이노와 Relay Switch를 통해 UV-LED의 전원 On/Off 구현이 가능하다. 모터와 UV LED는 아두이노로 제어하기 때문에 라즈베리파이에서 데이터베이스의 값을 실시간으로 확인하다가 UV LED 동작이 지시되면 아두이노에 신호를 보내준다. 데이터베이스의 값은 안드로이드 App을 통해 갱신되므로 결국은 안드로이드 App을 통해 UV-LED의 On/Off 제어가 가능하다. 라즈베리파이와 아누이노간의 데이터 통신은 USB 케이블을 이용한 시리얼 통신을 이용한다.(라즈베리파이와 아두이노 간의 거리가 가깝고 전송하는 데이터가 단순하기 때문에 간단한 통신 방법 선택) 구현 언어는 파이썬이며 사용한 파이썬 모듈은 serial이다. 이와 관련하여 사용한 함수들은 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
UV-LED 동작을 설정하는 값이 “PowerON”일 경우 USB 포트로 연결된 아두이노로 UTF-8로 인코딩된 문자 u를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 u를 받아 UV-LED On 동작을 수행한다. 만약 UV-LED 동작 설정값에 “PowerON” 외의 값이 저장되었을 경우 (“PowerOFF“의 경우가 일반적이지만 예외 처리를 위해 else로 조건 판단) 아두이노로 UTF-8로 인코딩된 문자 d를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 d를 받아 UV-LED Off 동작을 수행한다.&lt;br /&gt;
&lt;br /&gt;
 NeoPixel 회로도&lt;br /&gt;
[[파일:99-1=0_네오픽셀_회로도.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
Mood Light구현은 NeoPixel과 Raspberry PI를 통해 구현했다. 사용자가 Application을 통해 색을 바꾸면 Database의 값이 아래 표에 Data로 바뀌면서 Mood Light 색이 변한다. NeoPixel의 Digital input을 통해 값이 들어가고, 그에 맞는 색을 자유롭게 바꿀 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:색깔.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:App_insidemode_screenshot.jpg|200픽셀]] [[파일:App_outsidemode_screenshot.jpg|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
 앱&lt;br /&gt;
&lt;br /&gt;
앱을 처음 설치하고 실행하면 앱은 팝업 윈도우로 모바일 디바이스의 위치 정보 권한을 요청하며 사용자는 이를 최초 1회만 수락하면 된다. 앱은 실행되면 즉시 모바일 디바이스의 현재 위치 정보를 받고 데이터베이스에 저장된 집의 위치 정보와의 거리를 계산한다. 이 거리가 일정 거리 이상이면 앱은 실외 모드로 자동 설정되고, 일정 거리 미만이면 실내 모드로 자동 설정된다. 실내 모드와 실외 모드의 전환은 기본적으로 비활성화되어 있으나 사용자가 우측 하단의 스위치를 켜면 활성화되어 강제 모드 전환이 가능하게 된다. 그래서 앱을 실행했을 때 자동 모드 설정이 잘못되면 사용자가 직접 모드를 전환해서 원하는 기능을 사용할 수 있다.&lt;br /&gt;
&lt;br /&gt;
앱은 크게 실내 모드와 실외 모드로 구분된다. 각 모드는 Fragment로 정의됐고 MainActivity의 bottomnavigationview를 통해 replace해서 전환할 수 있다. bottomnavigationview는 각 모드에 배치된 스위치가 꺼져 있으면 disabled되고 스위치가 켜지면 enabled된다. 사용자는 실내 모드에서 무드등을 제어하고 실외 모드에서 자외선 살균기를 제어한다.&lt;br /&gt;
&lt;br /&gt;
 실내 모드&lt;br /&gt;
실내 모드에선 무드등의 색상 선택, 밝기 조절, 전원 작동, 스페셜 모드 기능을 이용할 수 있다.&lt;br /&gt;
* colorpicker&lt;br /&gt;
: colorpicker의 색상은 초기에 12시의 연두색으로 설정되어 있고 사용자가 버튼을 드래그해서 원하는 색상을 선택할 수 있다. colorpicker의 중앙에 있는 원은 왼쪽 반원에 이전 색상을 표시하고 오른쪽 반원에 현재 색상을 표시하여 전후 색상을 비교하기 좋게 만들어준다. 사용자가 무드등의 전원을 키면 이전 색상이 표시된 반원이 현재 색상으로 교체된다. 색상은 여섯 자리의 16진수 #nnnnnn으로 표현되며 정수형으로 저장된다. 사용자가 colorpicker로 다양한 색상을 연속적인 범위에서 선택할 수 있기 때문에 선택의 폭이 넓고 세밀한 색상 선택이 가능하다. 무드등의 전원이 켜져 있으면 색상의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 밝기&lt;br /&gt;
: 무드등의 밝기 조절은 실내 모드 중앙의 seekbar를 통해 할 수 있다. 밝기 값은 최소 0에서 최대 255까지 선택 가능하다. colorpicker와 같이 무드등의 전원이 켜져 있으면 밝기의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 전원&lt;br /&gt;
: Power ON 버튼을 누르면 무드등의 전원이 켜진다. 전원 버튼 하단의 얇은 띠가 전원이 꺼져있을 때 회색, 전원이 켜져있을 때 녹색을 띠면서 사용자가 무드등의 전원이 켜져있는지를 직관적으로 알 수 있게 해준다. 전원 작동 데이터가 데이터베이스에 성공적으로 전송되면 앱 화면 하단에 Toast 텍스트를 표시하여 사용자에게 전원의 작동을 알려준다. 전원 버튼의 Power ON/Power OFF 텍스트는 colorpicker에서 선택된 색상과 실시간으로 동일하게 적용되어 사용자에게 무드등이 어떤 색으로 켜질지 더욱 직관적으로 와닿게 만들어준다.&lt;br /&gt;
* 스페셜 모드&lt;br /&gt;
: 앱을 실행하면 무드등의 모드는 기본 모드로 설정되어 있는 상태이다. 스페셜 모드 버튼을 누르면 순차적으로 웨이브 모드 1, 웨이브 모드 2, 레인보우 모드 1, 레인보우 모드 2가 설정되며 그후 다시 기본 모드로 돌아온다. 현재 설정된 모드는 스페셜 모드 버튼의 텍스트로 나타나서 사용자가 이를 알 수 있다. 또한 버튼을 누를수록 단계적으로 버튼의 배경 색이 진해지며 버튼의 깊이감을 더해준다. 스페셜 모드 버튼의 아래엔 모드의 전체 개수와 현재 모드가 몇 번째인지를 시각적으로 쉽게 알게 해주는 공백 칸들이 있다. 이 칸들은 기본 모드에서 모두 비어있게 되고 스페셜 버튼이 눌릴 때마다 한 개씩 채워지며 모두 채워진 후엔 다시 모두 빈 칸으로 돌아온다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스페셜 모드는 무드등이 켜져 있을 때에만 작동이 유효하다. 무드등의 전원이 꺼져 있을 때 스페셜 모드를 작동하면 무드등에서 아무 일도 일어나지 않는다. 이런 상황을 예방하기 위해 앱에서 무드등의 전원이 꺼진 상태에서 스페셜 모드만 작동하는 일이 일어나지 않게 한다. 무드등의 전원이 꺼진 상태에서 스페셜 모드 버튼을 누르면, 앱은 무드등의 전원도 키고 스페셜 모드도 작동한다. 또한 스페셜 모드가 설정된 상태에서 무드등의 전원을 끄면 앱은 스페셜 모드를 기본 모드로 설정하고 무드등의 전원을 끈다. 만약 사용자가 레인보우 모드를 바로 사용하고 싶다면 전원 버튼을 누를 필요 없이 바로 스페셜 모드 버튼을 3회 누르면 된다. 전원 버튼을 누르는 단계를 거치지 않아도 되기 때문에 빠르고 간편하게 즉시 스페셜 모드를 이용할 수 있다. 결과적으로 무드등의 전원이 꺼진 상태에서 무드등의 모드는 항상 기본 모드로 설정되어 있고, 스페셜 모드가 켜진 상태면 무드등의 전원도 항상 켜진 상태가 된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 실외 모드&lt;br /&gt;
실외 모드에선 위치 정보 설정, 자외선 살균기의 전원 작동, 타이머 기능을 이용할 수 있다.&lt;br /&gt;
* 위치 정보 설정&lt;br /&gt;
: HOME LOCATION 버튼을 누르면 모바일 디바이스의 현재 위치 정보를 데이터베이스에 저장하여 집의 위치로 설정한다. 설정한 집의 위치 정보는 앱이 실행됐을 때 집과 사용자 사이의 거리를 계산하는 데 이용된다. 데이터베이스에 저장된 위치 정보는 사용자가 새롭게 설정하지 않는 한 항상 일정하게 유지된다. 그래서 앱을 재설치했을 때 집의 위치 정보 설정을 다시 할 필요가 없다. 상수의 변수명을 대문자 알파벳으로 선언하는 프로그래밍 관습을 응용해서 위치 정보 설정 버튼의 텍스트도 대문자 알파벳으로 하였다. &lt;br /&gt;
* 자외선 살균기&lt;br /&gt;
: 실외 모드에서 Power ON 버튼을 누르면 자외선 살균기의 전원이 켜진다. 전원 버튼 위의 spinner에서 타이머가 작동할 시간을 선택할 수 있으며 초기 타이머는 1시간으로 설정된 상태이다. 타이머는 시간 단위로 선택할 수 있고 최대 12시간까지 선택 가능하다. 자외선 살균기의 전원을 켜면 전원 버튼 아래에 textview가 표시되어 타이머의 남은 시간을 알려준다. 설정한 타이머가 종료되면 앱이 자동적으로 자외선 살균기의 전원을 끈다.&lt;br /&gt;
* 시간 Spinner&lt;br /&gt;
: APP의 실외모드에선 자외선 살균기를 제어한다. 사용자는 작동 시간을 spinner로 선택하여 자외선 살균기 전원 버튼을 눌러 작동시킨다. 그러면 APP은 스피너에 선택된 시간과 전원 데이터를 데이터베이스에 전송한다. APP에서 사용자가 직접 자외선 살균기의 전원을 끌 수 있지만 설정한 시간이 경과되면 APP은 전원을 끄는 데이터를 데이터베이스에 전송한다. 만약 사용자가 작동 시간을 연장시키고 싶다면 자외선 살균기를 끄고 다시 작동하면 된다. 그러면 남은 시간이 다시 세팅되어 작동 시간이 증가하게 된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Firebase&lt;br /&gt;
[[파일:99-1=0_파이어베이스스크린샷.jpg|300픽셀]]&lt;br /&gt;
[[파일:99-1=0_파이어베이스연동.jpg|500픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Firebase는 Google 사의 모바일 및 웹 개발 플랫폼이다. Firebase는 많은 유용한 기능을 지원하지만 여기선 실시간 데이터베이스를 사용한다. Firebase의 장점은 안드로이드 및 iOS 앱과 연동이 용이하다는 것이다. 또한, Firebase는 시간과 장소에 구애받지 않고, 데이터가 안전하게 보존되며, 데이터를 읽고 쓰는 것이 간편하다. 그래서 Firebase는 사용자의 집의 위치 정보를 저장하기에 적합하다. 이 데이터는 제 3자가 접근할 수 없으며 사용자가 새롭게 설정하지 않는 한 영구히 보존된다. 앱은 Firebase의 데이터베이스에 집의 위치 정보를 저장하기도 하지만 읽어오기도 한다. 앱은 실행되면 데이터베이스의 집의 위치 정보를 읽어와서 현재 사용자의 위치 사이의 거리를 계산하고 실내/실외를 판별한다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 작동할 때에도 Firebase를 이용한다. 사용자가 앱으로 작동한 무드등의 색상, 밝기, 전원, 모드 데이터와 자외선 살균기의 전원, 타이머 데이터는 Firebase의 데이터베이스에 저장된다. 앱은 무드등의 전원이 켜진 상태에서 색상이나 밝기가 변경되면 변경 사항을 즉시 데이터베이스에 반영한다. 자외선 살균기의 타이머는 데이터베이스에 저장되지만 남은 타이머 시간까지 일일이 저장되진 않는다. 앱에서 설정한 시간이 지나면 자외선 살균기의 전원을 끄는 데이터를 저장할 뿐이다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 직접 구동시키는 것은 라즈베리파이와 아두이노이다. 라즈베리파이는 데이터베이스에 저장된 무드등의 색상, 밝기, 전원, 모드 데이터를 읽어와 라즈베리파이에 직접 연결된 네오픽셀 LED의 작동을 지시한다. 또한 살균기의 전원 데이터를 읽어와 아두이노에 살균기를 들어올리는 모터의 동작과 살균기 LED의 동작을 지시한다. 아두이노에서는 라즈베리파이의 지시를 대기하다가 살균기의 ON/OFF 동작 신호를 받아 살균기를 켜고 끈다. 이 때 살균기가 부착된 구동부의 높이는 아두이노에 연결된 초음파센서로 거리를 측정하여 조절한다.&lt;br /&gt;
&lt;br /&gt;
===UV-LED 곰팡이 저해 실험===&lt;br /&gt;
&lt;br /&gt;
본 실험은 UV-A LED의 곰팡이 생육 저해 능력을 검증하기 위한 실험이다. 구체적인 저해율의 수치를 나타내기 위해 1차 살균 실험에 이어 2차 곰팡이로 실험을 하여 저해율을 나타낼 것이다.&lt;br /&gt;
&lt;br /&gt;
: 1. 실험 준비&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비1.png|200픽셀|ㅇ|왼쪽|Petri Dish, Sterile cork borer, 곰팡이]]    [[파일:99-1=0_세균실험준비2.png|173픽셀]]&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비3.png|200픽셀]]    [[파일:99-1=0_세균실험준비4.png|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 실험 과정&lt;br /&gt;
&lt;br /&gt;
:: 1) 먼저 3종의 곰팡이(Cladosporium sp, Fusarium sp, Aspergillus sp)를 키울 배지를 만들기 위해 Difo회사 제품인 PDA 12g과 Agar 10g을 비커에 넣은 후 500ml distilled water를 넣어준 후 autoclave에서 121℃에서 15분간 돌려준 뒤 petri dish에 부어 1~2일 동안 말린다.&lt;br /&gt;
&lt;br /&gt;
:: 2) 곰팡이가 PDA배지에서 충분히 성장했다면 이를 각각 Petri Dish에 접종한다. 이 때 일정한 양의 곰팡이를 접종하기 위해 그림에 있는 Sterile cork borer를 사용한다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정1.png|200픽셀]]    [[파일:99-1=0_세균실험과정2.png|182픽셀]]&lt;br /&gt;
: [[파일:99-1=0_세균실험과정3.png|200픽셀]]    [[파일:99-1=0_세균실험과정4.png|195픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 3) 3종의 곰팡이를 0.5m, 0.75m, 1m 간격으로 배치시키고, 다른 하나는 대조군으로 UV-A LED에 노출시키지 않은 채로 성장시킨다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정5.png|200픽셀]]    [[파일:99-1=0_세균실험과정6.png|172픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 4) Petri Dish 안에서 곰팡이의 성장률을 원의 반지름으로 근사시킵니다. 또한 생리식염수를 10마이크로미터 떨어트린 후, 소량의 곰팡이를 접종해준 뒤 커버 글라스를 붙이고 광학현미경으로 관찰한다.&lt;br /&gt;
&lt;br /&gt;
:: 5) 곰팡이가 자란 원의 반지름을 측정하고, 이를 저해율로 나타낸다. 저해율의 식은 다음과 같다. (저해율(%)= (대조군의 반지름 - 실험군의 반지름)/대조군의 반지름 * 100)&lt;br /&gt;
: [[파일:99-1=0_곰팡이_측정.jpg|400픽셀]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 3. 실험 결과&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 1) Cladosporium sp(벽지 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Cladosporium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 2) Fusarium sp(토양 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Fusarium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 3) Aspergillus sp(누룩 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Aspergillus_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
아래 표는 현미경을 통해 관측한 곰팡이의 반지름(cm)이고, 이를 위해 저해율식에 대입하여 곰팡이 저해율을 계산하였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:길이값.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:저해율.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:5시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:10시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 4. 결론&lt;br /&gt;
: : 최종 저해율로 나타낸 결과 UV-A LED에 5시간, 10시간에 따른 차이는 거의 없었지만 거리에 따른 저해율의 차이는 눈에 띄었다. 0.5m 거리에선 약 80%의 저해율을 보였고, 0.75m 에서는 Cladosporuim sp와 Fusarium sp은 약 50%의 저해율을 보였지만 Aspergillus sp는 30%의 저해율을 보였습니다. 1m 거리에선 저해율이 많이 떨어졌는데, Cladosporium sp는 39%, Fusarium sp는 25%, Aspergillus sp는 6%의 저해율을 보였습니다. 1m에서의 살균력은 많이 떨어졌고, 이를 보완하기 위해 기존 실험은 25cm UV-A LED 2개를 설치했는데, 제품에는 한 슬라이드 당 3개로 늘려 설치했다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic1.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/8/87/99-1%3D0_%EC%98%81%EC%83%81.avi]]  [[파일:99-1=0_videopic2.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/c/ce/99-1%3D0_%EC%98%81%EC%83%812.avi]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic3.png|300픽셀|link=https://capstone.uos.ac.kr/mie/images/8/82/99-1%3D0_%EC%98%81%EC%83%813.mp4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
'''UV-A LED Mode &amp;amp; Mood Light mode'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:UV-A LED.jpg|250픽셀]][[파일:99-1=0_무드등.jpg|250픽셀|link=https://capstone.uos.ac.kr/mie/images/99-1=0_영상.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_미구현내용.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_평가항목.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
[[파일:99-1=0_평가_결과.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
노*진: 프로젝트의 주제를 결정하기 전까지만 하더라도 라즈베리파이와 각종 모듈에 대한 지식이 없어 막막했습니다. 하지만 잘 모르는 만큼 팀원들과 더욱 조사를 열심히 하고, 구체적으로 설계를 하였습니다. 개념설계 발표 때까지 ‘과연 의도한대로 동작할까?’ 라는 의문점을 가졌지만 중간발표 준비를 하면서 원하는 기능들이 하나 둘씩 구현되고, 기구부의 Mechanism들이 완성되는 것을 보면서 재밌었고, 성취감을 느꼈습니다. 프로젝트를 진행하면서 전공 지식이 아닌 전기 회로도와 관련된 일들을 구글링과 여러 사이트에 물어보면서 해결을 했고, UV-LED의 살균력을 검증하기 위해 생명공학 전공인 지인의 도움을 받으면서 프로젝트의 문제들을 해결했습니다. 이 과정에서 잘 모르는 부분을 어떤 방법으로 해결할 지에 대한 능력을 기를 수 있었습니다. 마지막으로 내장형시스템 과목을 통해 기계정보공학과의 매력을 느낄 수 있었고, 앞으로 프로젝트를 수행할 때 자신감을 가질 수 있을 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
박*훈 : 기계정보공학을 전공한지 4학년 1학기에 처음으로 컴퓨터 관련 프로젝트를 진행했습니다. 그동안 기계 관련 CAD 프로젝트나 CAE 프로젝트는 다수 경험이 있었지만 컴퓨터 분야는 경험이 없었기 때문에 프로젝트 과정 중 가장 처음 과정인 주제 선정에서부터 엄청난 난관에 부딪혔습니다. 하지만 주어진 시간 안에 주어진 모듈로 할 수 있는 주제를 생각해냈고, 프로젝트를 시작했습니다. 프로젝트 진행 과정 중에 한계에 도달하는 일도 있었고 뜻처럼 진행되지 않는 일도 있었지만 팀원들과 함께 돌파구를 찾아가며 차근차근 프로젝트를 완성했습니다. 프로젝트를 진행하면서 기계정보공학의 융합적 전공의 취지에 맞게 기계적인 메커니즘과 임베디드 시스템을 융합한 완성품을 제작하기 위해 노력을 했고, 실제로 저는 기구부의 메커니즘 쪽을 맡아 CAD 프로그램인 CATIA를 통해 기어 및 랙 피니언 세트와 슬라이드 핀 등을 설계하고 3D 프린터를 이용하여 실제 구현을 완료했습니다. 기구부 제작 과정 중 값비싼 3D 프린터 제작의 비용과 3D 프린터 제작품의 과도한 무게 등의 문제가 있었지만, 팀원들과 협의한 끝에 해결 방안을 고안하여 이러한 문제를 해결했습니다. 이번 내장형 프로젝트의 경험을 통해 팀원들과의 소통 능력과 문제 해결 능력을 한층 성장시켰고, 앞으로 있을 프로젝트에 큰 도움이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
주*완: 이번 학기에 내장형시스템및실습을 수강하며 처음으로 주제 선정부터 완제품 제작까지 모든 것을 직접 경험했습니다. 주제 선정부터 시작해 모든 과정이 쉽지 않았고 걸림돌에 걸려서 결국 설계를 바꾸기도 했습니다. 하지만 한계를 극복할 방법을 고민하고 공부하고 질문하고 찾아 헤매면서 많은 것을 배웠습니다. 특히 제가 맡은 부분인 안드로이드 앱을 개발하기 위해 1학년 때 배웠지만 모두 잊어버린 java를 처음부터 다시 공부했고, 안드로이드를 아무것도 모르는 상태에서 인터넷 검색만으로 하나하나 공부하고 직접 코드를 짜서 완성까지 했습니다. 과정 중에 알 수 없는 이유때문에 앱이 원하는 대로 안 되거나 강제 종료 될 때가 매우 매우 많았는데, 스스로 계속 공부하고 원인을 찾고 고쳐 나가서 마침내 앱을 완성할 수 있었고, 스스로 문제를 해결할 수 있는 능력을 기를 수 있었습니다. 또한 기구부와 외형을 이루는 부분을 만들 때 자르고 다듬는 일이 많이 필요했는데 서로 필요한 부분이 있으면 도와주면서 협동하여 결과가 잘 나온 것 같아 만족스럽습니다. 프로젝트를 하며 값진 경험을 했고 앞으로 다른 프로젝트를 하게 된다면 이 경험이 소중한 밑바탕이 돼서 도움이 많이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
함*규: 임베디드 시스템을 처음 접해보기에 주제를 정하는 과정이 가장 어려웠던 것 같습니다. 처음에는 주어진 시간 내에 해낼 수 없는 비현실적인 주제에 대해 고민하며 기능적 한계에 부딪혀보기도 하고, 챗바퀴 도는 듯한 생각이 들었지만, 이 과정 속에서 전체적인 임베디드 시스템에 대한 이해와 할 수있는 것과 할 수 없는 것에 대해 구분할 수 있는 능력을 얻을 수 있었습니다. 이후에 직접 정한 주제를 바탕으로 프로젝트를 수행해나가면서, 초기 설계로는 해결할 수 없는 문제들을 팀원들과 함께 협동하여 풀어나가는 과정은 정말 힘들기도 했지만 되돌아 보면 책상에서는 배울 수 없는 많은 경험들을 배웠다고 생각합니다. 4-1학기를 떠올리면 많은 희노애락이 있었던 내장형 밖에 생각나지 않을 것 같은 데, 정말정말 재밌었고(농담이 아닙니다.) 후배들에게 꼭 추천해주고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
유*환: 내장형 시스템 프로젝트의 주제를 정하는 과정이 가장 어려웠습니다. 라즈베리파이에 센서나 모터, 또다른 보드를 연결하여 사용한 경험이 없어 주어진 시간 내에 완성할 수 있는 정도를 알기 어려웠습니다. 그래서 처음에는 학기 내에 완성이 불가능한 주제들을 생각했었습니다. 결국 마지막까지 주제를 정하지 못하다가 막바지에 급하게 살균 무드등을 만들기로 했습니다. 주제가 정해지고 자료조사를 마쳤을 때만 해도 힘든일이 끝나고 모든 일이 수월하게 진행될 것만 같았습니다. 그런데 출입 감지 방법, 음악 주파수 분석 등이 뜻대로 되지 않자 다시 조마조마해졌습니다. 하지만 적절한 대안을 찾고 해결해나가면서 완성되가는 프로젝트를 보며 뿌듯했습니다. 그리고 살균 무드등을 완성하고 영상까지 마무리 지었을 때는 그동안 밤을 새며 고생했던 시간들에 대해 보상받는 느낌이 들었습니다.&lt;br /&gt;
무엇보다 팀원 형들이 프로젝트에서 각자가 맡은 열할을 너무 잘 수행해주어서 감사했습니다. 유일하게 라즈베리파이 경험이 있는 저보다도 잘해주어서 미안하기도 하고 대단하다는 느낌이 들었습니다. 내장형 프로젝트는 과정은 정말 힘들었지만 팀원들과 살균 무드등을 만드는 것이 재밌었고 프로젝트 진행에 대한 경험치가 쌓여 좋은 기회가 되었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	<entry>
		<id>http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5875</id>
		<title>99-1=0 - Smart Mood Light with Sterilization</title>
		<link rel="alternate" type="text/html" href="http://capstone.uos.ac.kr/mie/index.php?title=99-1%3D0_-_Smart_Mood_Light_with_Sterilization&amp;diff=5875"/>
				<updated>2020-06-21T18:10:48Z</updated>
		
		<summary type="html">&lt;p&gt;2012430017: /* 최종 결과물 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==프로젝트 소개==&lt;br /&gt;
===프로젝트 명===&lt;br /&gt;
 Smart Mood Light with Sterilization&lt;br /&gt;
 살균 기능을 탑재한 스마트 무드등&lt;br /&gt;
&lt;br /&gt;
===프로젝트 기간===&lt;br /&gt;
2020.3.16~2020.6.22&lt;br /&gt;
&lt;br /&gt;
===팀 소개===&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 노*진 (팀장)&amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 박*훈 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 주*완 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20154300** 함*규 &amp;lt;br/&amp;gt;&lt;br /&gt;
서울시립대학교 기계정보공학과 20174300** 유*환 &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==프로젝트 개요==&lt;br /&gt;
&lt;br /&gt;
===프로젝트 요약===&lt;br /&gt;
&lt;br /&gt;
 원룸은 타 주거환경에 비해 채광과 통풍 능력이 떨어진다. 공기 청정기를 이용할 수 있지만, 채광 문제를 해결하지 못한다. 다행히 넓은 공간을 가진 주거환경에 비해 규모가 작아 상용 자외선을 통한 살균 효과는 비해 뛰어나지만, 살균기의 제어는 사용자가 원룸 내 없을 때 이루어 져야 한다. 위의 문제를 해결하기 위해 스마트폰을 이용하여 원격 제어를 실행한다. 또한 원격 제어를 하면서 사용자가 원룸 내 있을 때 활용할 수 없는 살균기가 공간을 비효율적으로 차지하게 되는 문제를 해결하기 위해 이를 무드등과 결합 한다. 결과적으로 사용자가 가장 많이 시간을 보내는 공간을 살균할 수 있으며 공간의 비효율성과 안전성, 더불어 무드등의 원격제어로 인한 편의성을 제공한다. 사용자는 외부에 있을 때, 스마트폰을 이용하여 살균기 동작을 제어할 수 있다. 사용자가 내부에 있을 때 별도의 조치 없이도 자외선 동작 제어는 비활성화 되며 무드등의 원격 제어가 가능하다.&lt;br /&gt;
&lt;br /&gt;
===프로젝트의 배경 및 기대효과===&lt;br /&gt;
: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 1. 배경&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_최근4년간국내미세먼지수치.png|500픽셀]]&lt;br /&gt;
[[파일:99-1=0_최근전세계적바이러스주기.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
최근 국내 미세먼지 수치가 꾸준히 증가함에 따라 국민들의 미세먼지 정화에 관한 관심이 높아지고 있다. 또한, 전 세계적인 바이러스 발생 주기가 짧아짐에 따라 사람들의 살균 제품에 대한 관심도가 높아지고 있다. &lt;br /&gt;
&lt;br /&gt;
햇빛은 면역력 향상이나 기분 전환의 효과뿐 아니라 살균 기능을 가지고 있다. 원룸은 구조적으로 대부분 하나의 창문을 가지고 있어 충분한 빛을 받지 못하며 공기의 입/출구가 하나이기에 통풍이 원활하지 않다. 이러한 조건은 미생물이 자라나기 쉽고 미세먼지의 배출이 어렵다. 이들을 모두 정화하는 데 맞는 조건을 가진 것이 자외선이다. 자외선을 통해 공기 중에 살균이 가능하며 미세먼지 중금속을 분해해 공기를 정화할 수 있다. 하지만 자외선 살균기를 집안에 두고 사용하는 것에는 다음과 같은 문제점들이 있다.&lt;br /&gt;
&lt;br /&gt;
- 살균 자외선은 인간에게 유해하므로 사용자에게 직접적인 자외선이 가해지는 것은 위험하다. 사용자가 없는 곳에서 자외선 살균기가 작동해야 하며 사용자가 있을 때 확실한 방법으로 조작 가능성을 배제 시켜야 한다.&lt;br /&gt;
&lt;br /&gt;
- 사용자가 자주 쓰는 공간의 살균일수록 의미가 있으므로 자외선 살균기는 사용자가 자주 쓰는 공간에 배치되어야 한다. 그러나 위의 이유로 사용자가 실제 방에 있을 때 자외선 살균기를 사용하는 것은 위험하므로 사용자가 있을 때 별다른 기능 없이 공간만을 차지하게 된다.&lt;br /&gt;
&lt;br /&gt;
원룸 내 거주자가 가장 오래 머무르는 공간으로 예상되는 곳은 침대류 이다. 침대류 근처에 있는 물체에 살균기를 결합하여 제품을 만들 수 있다면 효율적으로 문제를 해결할 수 있다. 우리는 이 조건을 만족하는 물체가 무드등이라는 점에서 문제 해결을 시작했다. 또한, 첫 번째 문제 해결을 위해 임베디드 시스템을 설계하여 사용자가 없는 공간에서의 작동을 가능하게 하는 것뿐 아니라 무드등에 다양한 기능을 추가하여 편리성과 기능의 다양함을 추가하려고 한다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 기대효과&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_UV자외선을이용한살균 미세먼지정화과정.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
사용자는 집 밖에서 애플리케이션을 통해 자외선 모듈을 가동함으로써 안전한 방법으로 공간을 살균 및 미세먼지 분해 기능을 작동시킬 수 있다. &lt;br /&gt;
&lt;br /&gt;
또한, 사용자가 개발 제품과 같은 공간에 있다고 판단 시 애플리케이션 자동 스위칭 기능을 통해 자외선 컨트롤 기능이 아닌 무드등 컨트롤 기능을 제어하게 된다. 따로 조작 없이 방 안/밖이라는 공간의 변화에 따라 스위칭 되기에 조작에 신경 쓸 필요가 없다. 무드등 기능으로썬 조도 조정, 빛의 색상 선택, 주변 음악에 따른 색상 변화 기능 등이 있으며 사용자가 선택하는 옵션에 따라 기능을 수행할 수 있어 편리함과 다양성을 제공한다. 사용자의 유무에 상관없이 개발제품은 유용한 도구로써 사용되기에 공간 활용성 또한 확보할 수 있을 것으로 생각된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===목적계통도===&lt;br /&gt;
[[파일:99-1=0_목적계통도.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
 안전성&lt;br /&gt;
자외선 활성/비활성화 기능은 가스 자동밸브 잠금과 비슷한 원리로 앱 구동 시 스마트폰 GPS 기능을 통해 사용자의 위치정보를 받아 주거 위치와 비교하여 앱 인터페이스 비활성화 여부를 결정한다. GPS 오작동으로 인한 비활성화가 적절히 이루어지지 않았을 때를 대비해 앱 내 수동 비활성화 버튼을 추가한다. 앱 인터페이스가 비활성화 되어 있어 사용자의 실수로 인한 신호 전달 가능성을 사전에 차단할 수 있다. 또한 회로의 긴급 정지 스위치를 통해 하드웨어 오동작 시 회로 자체의 전류를 제어할 수 있다. 또한 인체 및 환경에 유해한 정도를 판단하여 인체에 비교적 덜 유해한 UV-A의 자외선 영역을 사용했고, 수은으로 환경 파괴 문제를 일으키는 UV-램프를 대신하여 UV-LED를 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 실용성&lt;br /&gt;
먼저 자외선 영역의 성능을 비교하면 살균력 기준으로는 분 단위 살균력을 가진 UV-C가 시 단위 살균력을 가진 UV-A보다 월등히 강력하다. 하지만 살균 범위를 보면 UV-A는 공기 중 3-5m의 반경을 가진 것에 비해 UV-C는 공기 중 투과력이 매우 낮아 넓은 범위를 살균할 때에는 UV-A가 더 적합하다. 따라서 거주자 없이 비어있는 시간이 긴 원룸에 맞추어 UV-405nm 자외선을 이용했다. 이 때, 자외선 LED의 살균 방향을 15도 정도 아래쪽으로 내려주어 침구류 및 사용자가 자주 사용하는 물건도 더불어 살균하게 된다. 모터의 경우, 자외선 살균기의 상하 운동이 완료된 후에 해당 위치에서 고정이 되어야 하는데, 서보 모터는 고정 기능이 없고, 스탭 모터는 고정 기능을 사용할 때 소모 전력이 많다. 따라서 적은 소모 전력으로 고정 기능을 수행할 수 있는 웜 기어 모터를 사용했다.&lt;br /&gt;
공간 효율성을 기준으로는 제품 구성 시 얼마나 효율적인 공간 활용 여부를 비교했다. 따라서 각 제품의 특성에 맞추어 무드등 조명에는 필요 전선량이 많은 LED 전구 대신 네오 픽셀을 사용했고, 자외선 조명에는 1cm 이하의 조명 크기를 가지는 UV LED를 사용했다. 또한 사용자 위치를 판별하기 위해서 출입문 주변의 많은 공간을 차지하는 적외선 센서 활용 출/입 카운터 대신 외부 공간을 이용하지 않는 안드로이드 모바일 앱 내의 GPS 기능을 사용했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 편의성&lt;br /&gt;
사용자 입장에서 제품을 이용할 때의 반응성을 기준으로 비교했다. 호스팅 제공 S/W에서는 외부의 데이터베이스를 이용하는 파이썬 애니웨어 대신 자체적으로 실시간 데이터베이스 기능을 제공하는 파이어 베이스를 사용했다. 또한 사용자 위치 판별법에서는 사용자의 출/입 여부를 얼마나 빠르게 판단할 수 있는지에 대한 비교를 했는데, 이는 적외선 센서 활용 카운터가 더 빠른 응답성을 보였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 경제성&lt;br /&gt;
제품에 대한 경제적인 측면은 제품을 구성하는데 사용하는 비용과 유지비로 생각할 수 있다. 먼저 제품 구성비용은 프로젝트의 해당 제품을 구매하는 비용이다. 따라서 보다 더 값이 싼 네오픽셀, UV-A, UV-램프가 더 적합했다. 또한 호스팅 기능 S/W에서는 무료로 이용할 수 있는 범위가 더 넓은 파이어 베이스가 적합했다. 또한 유지비는 제품의 수명이 얼마나 긴지, 배터리 소모량이 얼마나 적은지, 부품 교체 비용이 얼마나 큰지에 대해 비교했다. 따라서 부품 교체 비용이 더 저렴한 네오픽셀, 수명이 더 긴 UV-LED가 더 적합했다. 또한 항상 동작시키는 적외선 센서보다 필요시에만 동작시킬 수 있는 GPS 기능이 배터리 소모량이 거의 없어 더 적합했다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
 개발 용이성&lt;br /&gt;
개발제품 특성상 데이터베이스 및 호스팅 서비스가 필요하다. 개발자 독자적으로 데이터 베이스와 호스팅 서비스를 개발할 필요 없이 이러한 서비스를 제공하는 서버는 파이어 베이스이다. 개발 난이도는 물론 개발자의 서버 개발 시간을 최소화 하여 다른 분야의 성능 개발을 최대화 시킬 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_개발용이성.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Application과 라즈베리 파이의 통신 중간 다리로 파이어 베이스 서버를 이용한다. 물론 무드등 제어를 위한 라즈베리 파이 서버를 구축하여 통시에 이용하면 원룸 내 무드등 제어 동작 속도를 향상시킬 수도 있다. 그러나 하나의 서버를 이용하면 통신 오류나, 오류가 발생했을 때 오류를 제어하기 쉽다. 또한  추후에 기능 추가가 용이하다. 파이어 베이스는 서버 특성상 서버 사용자가 많아지게 되면 부하가 발생할 수 있다. 그러나 개발 상품의 경우 서버는 한명의 사용자만이 이용한다는 점을 고려하여 이러한 단점을 고려하지 않을 수 있다. 따라서 해당 프로젝트에 가장 적합하고, 통신 복잡도가 적은 파이어 베이스와 안드로이드 스튜디오를 사용했다.&lt;br /&gt;
&lt;br /&gt;
==동작 시나리오==&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_동작시나리오.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==구현 내용==&lt;br /&gt;
&lt;br /&gt;
===역할분담 및 추진체계===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_역할분담.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===개발일정표===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_세부개발일정표.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===시스템 구성===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_시스템구성도.png|800픽셀]]&lt;br /&gt;
&lt;br /&gt;
===기구부 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_무드등모드3D설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_자외선살균모드3D.png|166픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
먼저 기구부에서는 최종적으로 제품의 형상과 베벨 기어로 사각 LED 기둥을 올리는 Mechanism과 자외선 LED가 부착되어 있는 Slide Pin Mechanism을 구현하였다. &lt;br /&gt;
제품의 형상의 경우에는 제품의 무드등 기능과 자외선 살균 기능을 정상적으로 구현할 수 있게 필요한 요소들을 적절히 배치할 수 있는 공간을 만들었고, 사용자의 안전성을 위해 Mood Light를 위한 NeoPixel과 UV-A LED를 구분하여 인체에 해로운 자외선 LED는 기능을 사용하지 않을 때 내부 공간에 위치하도록 구현하였다. &lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism은 첫 번째 자외선 LED가 설치된 사각기둥을 올리는 Motor Mechanism과 두 번째 자외선 LED가 적절한 각도로 펼쳐지는 슬라이드 핀 Mechanism이다. 첫 번째 Motor Mechanism은 모터와 베벨 기어 및 랙 피니언 시스템을 통해 모터의 회전 운동을 직선 운동으로 전환하여 구현하였다. 또한 두 번째 Slide Pin Mechanism은 슬라이드 핀을 이용하여 중력에 의해 자동으로 사각기둥 상승 시 펼쳐지고, 하강 시 접히도록 구현한다.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''기구부 설계'''&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 1. Motor Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_모터설계.png|200픽셀]]&lt;br /&gt;
[[파일:99-1=0_모터구현.png|191픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 가지 Mechanism 중 자외선 LED가 설치된 사각 기둥을 올릴 베벨 기어와 랙 피니언을 사용한 Motor Mechanism 시스템 구현을 완료했다. CATIA CAD 프로그램을 이용하여 베벨 기어 및 랙 피니언 설계를 완료했고, 3D 프린터를 이용하여 규격에 맞추어 실제 제품을 만들었다. 기존에는 랙 피니언 한 쌍만을 직접 모터에 연결하여 사각기둥의 상하 운동을 구현하려고 했지만, 대칭성이 잘 맞지 않아 한쪽으로 쏠리는 현상이 발생하여, 추가적으로 베벨 기어 등을 사용하여 대칭성을 고려하여 양쪽에서 사각 기둥의 상하 운동을 구현했다.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. Slide Pin Mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_슬라이드핀설계.png|200픽셀]]&lt;br /&gt;
[[파일:Slide.jpg|145픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Motor Mechanism을 통해 자외선 LED가 설치된 사각기둥이 상하운동을 하면, LED가 적절한 각도를 이루어 펼쳐지도록 구현하였다. 따라서 Slide Pin Mechanism을 이용하여 사각 기둥이 상승할 때 중력에 의해 자동으로 LED가 펼쳐지고, 하강할 때 자동으로 접힌다. CATIA CAD 프로그램을 이용하여 3D 설계를 하고, 3D 프린팅을 통해 실제로 외관을 만들었다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_LED각도설계.png|500픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LED가 펼쳐지는 적절한 각도로는 30도를 정했다. 각도를 정하는 과정에서 두 가지의 고려 사항을 정했는데, 첫 번째는 자외선 LED 끝이 직각으로 도달하는 지점의 거리가 원룸이라는 공간을 고려하여 1m 이상이 되고, 두 번째는 제품과 근접한 곳의 LED 사각지대가 제품의 끝으로부터 10cm 내가 되는 기준을 두었다. 따라서 CATIA CAD 프로그램의 2D 설계를 통해 적절한 각도 30도를 설정했다. (실제로 빛은 직각으로만 전달되는 것이 아니라 사방으로 퍼지기 때문에 실제 최대 도달 거리는 기준값인 1m 보다 더 커진다.)&lt;br /&gt;
&lt;br /&gt;
'''기구부 구현'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_사각기둥.jpg|250픽셀]]&lt;br /&gt;
[[파일:99-1=0_Mood.jpg|250픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_완성본.jpg|250픽셀]]&lt;br /&gt;
[[파일:위에서_본_모습.PNG|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
UV-A LED를 장착할 사각 기둥을 3D Printing을 통해 만들었다. 또한 Mood Light를 위한 NeoPixel은 사각 기둥 밖에 다른 기구에 물결 모양으로 48개를 달았다. 최종적으로 아크릴판에 불투명 시트를 붙여 등을 만들었다.&lt;br /&gt;
&lt;br /&gt;
'''최종 작품'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:파랑이.jpg|250픽셀]][[파일:UV-A_LED.jpg|250픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
최종으로 Mood Light에서 단색 모드로 파랑색 조명을 킬 때와, UV LED Mode에서 On버튼을 눌렀을 때 결과이다.&lt;br /&gt;
&lt;br /&gt;
===제어부 및 회로 구현===&lt;br /&gt;
 UV-A LED, Motor 회로도&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_제어회로도1.PNG|600픽셀]][[파일:99-1=0_내부회로도2.jpg|600픽셀]]&lt;br /&gt;
 &lt;br /&gt;
SMPS로부터 220V 정격 전압을 12V 정격 전압으로 나누어 아두이노와 Relay Switch를 통해 UV-LED의 전원 On/Off 구현이 가능하다. 모터와 UV LED는 아두이노로 제어하기 때문에 라즈베리파이에서 데이터베이스의 값을 실시간으로 확인하다가 UV LED 동작이 지시되면 아두이노에 신호를 보내준다. 데이터베이스의 값은 안드로이드 App을 통해 갱신되므로 결국은 안드로이드 App을 통해 UV-LED의 On/Off 제어가 가능하다. 라즈베리파이와 아누이노간의 데이터 통신은 USB 케이블을 이용한 시리얼 통신을 이용한다.(라즈베리파이와 아두이노 간의 거리가 가깝고 전송하는 데이터가 단순하기 때문에 간단한 통신 방법 선택) 구현 언어는 파이썬이며 사용한 파이썬 모듈은 serial이다. 이와 관련하여 사용한 함수들은 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
UV-LED 동작을 설정하는 값이 “PowerON”일 경우 USB 포트로 연결된 아두이노로 UTF-8로 인코딩된 문자 u를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 u를 받아 UV-LED On 동작을 수행한다. 만약 UV-LED 동작 설정값에 “PowerON” 외의 값이 저장되었을 경우 (“PowerOFF“의 경우가 일반적이지만 예외 처리를 위해 else로 조건 판단) 아두이노로 UTF-8로 인코딩된 문자 d를 시리얼 통신을 통해 전송한다. 아두이노에서는 문자 d를 받아 UV-LED Off 동작을 수행한다.&lt;br /&gt;
&lt;br /&gt;
 NeoPixel 회로도&lt;br /&gt;
[[파일:99-1=0_네오픽셀_회로도.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
Mood Light구현은 NeoPixel과 Raspberry PI를 통해 구현했다. 사용자가 Application을 통해 색을 바꾸면 Database의 값이 아래 표에 Data로 바뀌면서 Mood Light 색이 변한다. NeoPixel의 Digital input을 통해 값이 들어가고, 그에 맞는 색을 자유롭게 바꿀 수 있다.&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:색깔.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===소프트웨어 설계 및 구현===&lt;br /&gt;
&lt;br /&gt;
[[파일:App_insidemode_screenshot.jpg|200픽셀]] [[파일:App_outsidemode_screenshot.jpg|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
 앱&lt;br /&gt;
&lt;br /&gt;
앱을 처음 설치하고 실행하면 앱은 팝업 윈도우로 모바일 디바이스의 위치 정보 권한을 요청하며 사용자는 이를 최초 1회만 수락하면 된다. 앱은 실행되면 즉시 모바일 디바이스의 현재 위치 정보를 받고 데이터베이스에 저장된 집의 위치 정보와의 거리를 계산한다. 이 거리가 일정 거리 이상이면 앱은 실외 모드로 자동 설정되고, 일정 거리 미만이면 실내 모드로 자동 설정된다. 실내 모드와 실외 모드의 전환은 기본적으로 비활성화되어 있으나 사용자가 우측 하단의 스위치를 켜면 활성화되어 강제 모드 전환이 가능하게 된다. 그래서 앱을 실행했을 때 자동 모드 설정이 잘못되면 사용자가 직접 모드를 전환해서 원하는 기능을 사용할 수 있다.&lt;br /&gt;
&lt;br /&gt;
앱은 크게 실내 모드와 실외 모드로 구분된다. 각 모드는 Fragment로 정의됐고 MainActivity의 bottomnavigationview를 통해 replace해서 전환할 수 있다. bottomnavigationview는 각 모드에 배치된 스위치가 꺼져 있으면 disabled되고 스위치가 켜지면 enabled된다. 사용자는 실내 모드에서 무드등을 제어하고 실외 모드에서 자외선 살균기를 제어한다.&lt;br /&gt;
&lt;br /&gt;
 실내 모드&lt;br /&gt;
실내 모드에선 무드등의 색상 선택, 밝기 조절, 전원 작동, 스페셜 모드 기능을 이용할 수 있다.&lt;br /&gt;
* colorpicker&lt;br /&gt;
: colorpicker의 색상은 초기에 12시의 연두색으로 설정되어 있고 사용자가 버튼을 드래그해서 원하는 색상을 선택할 수 있다. colorpicker의 중앙에 있는 원은 왼쪽 반원에 이전 색상을 표시하고 오른쪽 반원에 현재 색상을 표시하여 전후 색상을 비교하기 좋게 만들어준다. 사용자가 무드등의 전원을 키면 이전 색상이 표시된 반원이 현재 색상으로 교체된다. 색상은 여섯 자리의 16진수 #nnnnnn으로 표현되며 정수형으로 저장된다. 사용자가 colorpicker로 다양한 색상을 연속적인 범위에서 선택할 수 있기 때문에 선택의 폭이 넓고 세밀한 색상 선택이 가능하다. 무드등의 전원이 켜져 있으면 색상의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 밝기&lt;br /&gt;
: 무드등의 밝기 조절은 실내 모드 중앙의 seekbar를 통해 할 수 있다. 밝기 값은 최소 0에서 최대 255까지 선택 가능하다. colorpicker와 같이 무드등의 전원이 켜져 있으면 밝기의 변화를 실시간으로 무드등에 반영한다.&lt;br /&gt;
* 전원&lt;br /&gt;
: Power ON 버튼을 누르면 무드등의 전원이 켜진다. 전원 버튼 하단의 얇은 띠가 전원이 꺼져있을 때 회색, 전원이 켜져있을 때 녹색을 띠면서 사용자가 무드등의 전원이 켜져있는지를 직관적으로 알 수 있게 해준다. 전원 작동 데이터가 데이터베이스에 성공적으로 전송되면 앱 화면 하단에 Toast 텍스트를 표시하여 사용자에게 전원의 작동을 알려준다. 전원 버튼의 Power ON/Power OFF 텍스트는 colorpicker에서 선택된 색상과 실시간으로 동일하게 적용되어 사용자에게 무드등이 어떤 색으로 켜질지 더욱 직관적으로 와닿게 만들어준다.&lt;br /&gt;
* 스페셜 모드&lt;br /&gt;
: 앱을 실행하면 무드등의 모드는 기본 모드로 설정되어 있는 상태이다. 스페셜 모드 버튼을 누르면 순차적으로 웨이브 모드 1, 웨이브 모드 2, 레인보우 모드 1, 레인보우 모드 2가 설정되며 그후 다시 기본 모드로 돌아온다. 현재 설정된 모드는 스페셜 모드 버튼의 텍스트로 나타나서 사용자가 이를 알 수 있다. 또한 버튼을 누를수록 단계적으로 버튼의 배경 색이 진해지며 버튼의 깊이감을 더해준다. 스페셜 모드 버튼의 아래엔 모드의 전체 개수와 현재 모드가 몇 번째인지를 시각적으로 쉽게 알게 해주는 공백 칸들이 있다. 이 칸들은 기본 모드에서 모두 비어있게 되고 스페셜 버튼이 눌릴 때마다 한 개씩 채워지며 모두 채워진 후엔 다시 모두 빈 칸으로 돌아온다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스페셜 모드는 무드등이 켜져 있을 때에만 작동이 유효하다. 무드등의 전원이 꺼져 있을 때 스페셜 모드를 작동하면 무드등에서 아무 일도 일어나지 않는다. 이런 상황을 예방하기 위해 앱에서 무드등의 전원이 꺼진 상태에서 스페셜 모드만 작동하는 일이 일어나지 않게 한다. 무드등의 전원이 꺼진 상태에서 스페셜 모드 버튼을 누르면, 앱은 무드등의 전원도 키고 스페셜 모드도 작동한다. 또한 스페셜 모드가 설정된 상태에서 무드등의 전원을 끄면 앱은 스페셜 모드를 기본 모드로 설정하고 무드등의 전원을 끈다. 만약 사용자가 레인보우 모드를 바로 사용하고 싶다면 전원 버튼을 누를 필요 없이 바로 스페셜 모드 버튼을 3회 누르면 된다. 전원 버튼을 누르는 단계를 거치지 않아도 되기 때문에 빠르고 간편하게 즉시 스페셜 모드를 이용할 수 있다. 결과적으로 무드등의 전원이 꺼진 상태에서 무드등의 모드는 항상 기본 모드로 설정되어 있고, 스페셜 모드가 켜진 상태면 무드등의 전원도 항상 켜진 상태가 된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 실외 모드&lt;br /&gt;
실외 모드에선 위치 정보 설정, 자외선 살균기의 전원 작동, 타이머 기능을 이용할 수 있다.&lt;br /&gt;
* 위치 정보 설정&lt;br /&gt;
: HOME LOCATION 버튼을 누르면 모바일 디바이스의 현재 위치 정보를 데이터베이스에 저장하여 집의 위치로 설정한다. 설정한 집의 위치 정보는 앱이 실행됐을 때 집과 사용자 사이의 거리를 계산하는 데 이용된다. 데이터베이스에 저장된 위치 정보는 사용자가 새롭게 설정하지 않는 한 항상 일정하게 유지된다. 그래서 앱을 재설치했을 때 집의 위치 정보 설정을 다시 할 필요가 없다. 상수의 변수명을 대문자 알파벳으로 선언하는 프로그래밍 관습을 응용해서 위치 정보 설정 버튼의 텍스트도 대문자 알파벳으로 하였다. &lt;br /&gt;
* 자외선 살균기&lt;br /&gt;
: 실외 모드에서 Power ON 버튼을 누르면 자외선 살균기의 전원이 켜진다. 전원 버튼 위의 spinner에서 타이머가 작동할 시간을 선택할 수 있으며 초기 타이머는 1시간으로 설정된 상태이다. 타이머는 시간 단위로 선택할 수 있고 최대 12시간까지 선택 가능하다. 자외선 살균기의 전원을 켜면 전원 버튼 아래에 textview가 표시되어 타이머의 남은 시간을 알려준다. 설정한 타이머가 종료되면 앱이 자동적으로 자외선 살균기의 전원을 끈다.&lt;br /&gt;
* 시간 Spinner&lt;br /&gt;
: APP의 실외모드에선 자외선 살균기를 제어한다. 사용자는 작동 시간을 spinner로 선택하여 자외선 살균기 전원 버튼을 눌러 작동시킨다. 그러면 APP은 스피너에 선택된 시간과 전원 데이터를 데이터베이스에 전송한다. APP에서 사용자가 직접 자외선 살균기의 전원을 끌 수 있지만 설정한 시간이 경과되면 APP은 전원을 끄는 데이터를 데이터베이스에 전송한다. 만약 사용자가 작동 시간을 연장시키고 싶다면 자외선 살균기를 끄고 다시 작동하면 된다. 그러면 남은 시간이 다시 세팅되어 작동 시간이 증가하게 된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Firebase&lt;br /&gt;
[[파일:99-1=0_파이어베이스스크린샷.jpg|300픽셀]]&lt;br /&gt;
[[파일:99-1=0_파이어베이스연동.jpg|500픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Firebase는 Google 사의 모바일 및 웹 개발 플랫폼이다. Firebase는 많은 유용한 기능을 지원하지만 여기선 실시간 데이터베이스를 사용한다. Firebase의 장점은 안드로이드 및 iOS 앱과 연동이 용이하다는 것이다. 또한, Firebase는 시간과 장소에 구애받지 않고, 데이터가 안전하게 보존되며, 데이터를 읽고 쓰는 것이 간편하다. 그래서 Firebase는 사용자의 집의 위치 정보를 저장하기에 적합하다. 이 데이터는 제 3자가 접근할 수 없으며 사용자가 새롭게 설정하지 않는 한 영구히 보존된다. 앱은 Firebase의 데이터베이스에 집의 위치 정보를 저장하기도 하지만 읽어오기도 한다. 앱은 실행되면 데이터베이스의 집의 위치 정보를 읽어와서 현재 사용자의 위치 사이의 거리를 계산하고 실내/실외를 판별한다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 작동할 때에도 Firebase를 이용한다. 사용자가 앱으로 작동한 무드등의 색상, 밝기, 전원, 모드 데이터와 자외선 살균기의 전원, 타이머 데이터는 Firebase의 데이터베이스에 저장된다. 앱은 무드등의 전원이 켜진 상태에서 색상이나 밝기가 변경되면 변경 사항을 즉시 데이터베이스에 반영한다. 자외선 살균기의 타이머는 데이터베이스에 저장되지만 남은 타이머 시간까지 일일이 저장되진 않는다. 앱에서 설정한 시간이 지나면 자외선 살균기의 전원을 끄는 데이터를 저장할 뿐이다.&lt;br /&gt;
&lt;br /&gt;
무드등과 자외선 살균기를 직접 구동시키는 것은 라즈베리파이와 아두이노이다. 라즈베리파이는 데이터베이스에 저장된 무드등의 색상, 밝기, 전원, 모드 데이터를 읽어와 라즈베리파이에 직접 연결된 네오픽셀 LED의 작동을 지시한다. 또한 살균기의 전원 데이터를 읽어와 아두이노에 살균기를 들어올리는 모터의 동작과 살균기 LED의 동작을 지시한다. 아두이노에서는 라즈베리파이의 지시를 대기하다가 살균기의 ON/OFF 동작 신호를 받아 살균기를 켜고 끈다. 이 때 살균기가 부착된 구동부의 높이는 아두이노에 연결된 초음파센서로 거리를 측정하여 조절한다.&lt;br /&gt;
&lt;br /&gt;
===UV-LED 곰팡이 저해 실험===&lt;br /&gt;
&lt;br /&gt;
본 실험은 UV-A LED의 곰팡이 생육 저해 능력을 검증하기 위한 실험이다. 구체적인 저해율의 수치를 나타내기 위해 1차 살균 실험에 이어 2차 곰팡이로 실험을 하여 저해율을 나타낼 것이다.&lt;br /&gt;
&lt;br /&gt;
: 1. 실험 준비&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비1.png|200픽셀|ㅇ|왼쪽|Petri Dish, Sterile cork borer, 곰팡이]]    [[파일:99-1=0_세균실험준비2.png|173픽셀]]&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험준비3.png|200픽셀]]    [[파일:99-1=0_세균실험준비4.png|200픽셀]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: 2. 실험 과정&lt;br /&gt;
&lt;br /&gt;
:: 1) 먼저 3종의 곰팡이(Cladosporium sp, Fusarium sp, Aspergillus sp)를 키울 배지를 만들기 위해 Difo회사 제품인 PDA 12g과 Agar 10g을 비커에 넣은 후 500ml distilled water를 넣어준 후 autoclave에서 121℃에서 15분간 돌려준 뒤 petri dish에 부어 1~2일 동안 말린다.&lt;br /&gt;
&lt;br /&gt;
:: 2) 곰팡이가 PDA배지에서 충분히 성장했다면 이를 각각 Petri Dish에 접종한다. 이 때 일정한 양의 곰팡이를 접종하기 위해 그림에 있는 Sterile cork borer를 사용한다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정1.png|200픽셀]]    [[파일:99-1=0_세균실험과정2.png|182픽셀]]&lt;br /&gt;
: [[파일:99-1=0_세균실험과정3.png|200픽셀]]    [[파일:99-1=0_세균실험과정4.png|195픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 3) 3종의 곰팡이를 0.5m, 0.75m, 1m 간격으로 배치시키고, 다른 하나는 대조군으로 UV-A LED에 노출시키지 않은 채로 성장시킨다.&lt;br /&gt;
&lt;br /&gt;
: [[파일:99-1=0_세균실험과정5.png|200픽셀]]    [[파일:99-1=0_세균실험과정6.png|172픽셀]]&lt;br /&gt;
&lt;br /&gt;
:: 4) Petri Dish 안에서 곰팡이의 성장률을 원의 반지름으로 근사시킵니다. 또한 생리식염수를 10마이크로미터 떨어트린 후, 소량의 곰팡이를 접종해준 뒤 커버 글라스를 붙이고 광학현미경으로 관찰한다.&lt;br /&gt;
&lt;br /&gt;
:: 5) 곰팡이가 자란 원의 반지름을 측정하고, 이를 저해율로 나타낸다. 저해율의 식은 다음과 같다. (저해율(%)= (대조군의 반지름 - 실험군의 반지름)/대조군의 반지름 * 100)&lt;br /&gt;
: [[파일:99-1=0_곰팡이_측정.jpg|400픽셀]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 3. 실험 결과&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 1) Cladosporium sp(벽지 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Cladosporium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 2) Fusarium sp(토양 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Fusarium_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: : 3) Aspergillus sp(누룩 곰팡이)&lt;br /&gt;
: [[파일:99-1=0_Aspergillus_sp.PNG|800픽셀]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
아래 표는 현미경을 통해 관측한 곰팡이의 반지름(cm)이고, 이를 위해 저해율식에 대입하여 곰팡이 저해율을 계산하였다.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:길이값.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:저해율.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:5시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: [[파일:10시간_기준.PNG|600픽셀]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
: 4. 결론&lt;br /&gt;
: : 최종 저해율로 나타낸 결과 UV-A LED에 5시간, 10시간에 따른 차이는 거의 없었지만 거리에 따른 저해율의 차이는 눈에 띄었다. 0.5m 거리에선 약 80%의 저해율을 보였고, 0.75m 에서는 Cladosporuim sp와 Fusarium sp은 약 50%의 저해율을 보였지만 Aspergillus sp는 30%의 저해율을 보였습니다. 1m 거리에선 저해율이 많이 떨어졌는데, Cladosporium sp는 39%, Fusarium sp는 25%, Aspergillus sp는 6%의 저해율을 보였습니다. 1m에서의 살균력은 많이 떨어졌고, 이를 보완하기 위해 기존 실험은 25cm UV-A LED 2개를 설치했는데, 제품에는 한 슬라이드 당 3개로 늘려 설치했다.&lt;br /&gt;
&lt;br /&gt;
==프로젝트 결과==&lt;br /&gt;
&lt;br /&gt;
===최종 결과물===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_videopic1.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/8/87/99-1%3D0_%EC%98%81%EC%83%81.avi]]  [[파일:99-1=0_videopic2.png|200픽셀|link=https://capstone.uos.ac.kr/mie/images/c/ce/99-1%3D0_%EC%98%81%EC%83%812.avi]]&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:99-1=0_videopic3.png|300픽셀|link=https://capstone.uos.ac.kr/mie/images/8/82/99-1%3D0_%EC%98%81%EC%83%813.mp4]]&amp;lt;br/&amp;gt;&lt;br /&gt;
'''UV-A LED Mode &amp;amp; Mood Light mode'''&amp;lt;br/&amp;gt;&lt;br /&gt;
[[파일:UV-A LED.jpg|250픽셀]][[파일:99-1=0_무드등.jpg|250픽셀|link=https://capstone.uos.ac.kr/mie/images/99-1=0_영상.mp4]]&lt;br /&gt;
&lt;br /&gt;
===미구현 내용===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_미구현내용.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==프로젝트 평가==&lt;br /&gt;
&lt;br /&gt;
===평가항목===&lt;br /&gt;
&lt;br /&gt;
[[파일:99-1=0_평가항목.png|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
===평가결과===&lt;br /&gt;
[[파일:99-1=0_평가_결과.PNG|600픽셀]]&lt;br /&gt;
&lt;br /&gt;
==느낀점==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
노*진: 프로젝트의 주제를 결정하기 전까지만 하더라도 라즈베리파이와 각종 모듈에 대한 지식이 없어 막막했습니다. 하지만 잘 모르는 만큼 팀원들과 더욱 조사를 열심히 하고, 구체적으로 설계를 하였습니다. 개념설계 발표 때까지 ‘과연 의도한대로 동작할까?’ 라는 의문점을 가졌지만 중간발표 준비를 하면서 원하는 기능들이 하나 둘씩 구현되고, 기구부의 Mechanism들이 완성되는 것을 보면서 재밌었고, 성취감을 느꼈습니다. 프로젝트를 진행하면서 전공 지식이 아닌 전기 회로도와 관련된 일들을 구글링과 여러 사이트에 물어보면서 해결을 했고, UV-LED의 살균력을 검증하기 위해 생명공학 전공인 지인의 도움을 받으면서 프로젝트의 문제들을 해결했습니다. 이 과정에서 잘 모르는 부분을 어떤 방법으로 해결할 지에 대한 능력을 기를 수 있었습니다. 마지막으로 내장형시스템 과목을 통해 기계정보공학과의 매력을 느낄 수 있었고, 앞으로 프로젝트를 수행할 때 자신감을 가질 수 있을 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
박*훈 : 기계정보공학을 전공한지 4학년 1학기에 처음으로 컴퓨터 관련 프로젝트를 진행했습니다. 그동안 기계 관련 CAD 프로젝트나 CAE 프로젝트는 다수 경험이 있었지만 컴퓨터 분야는 경험이 없었기 때문에 프로젝트 과정 중 가장 처음 과정인 주제 선정에서부터 엄청난 난관에 부딪혔습니다. 하지만 주어진 시간 안에 주어진 모듈로 할 수 있는 주제를 생각해냈고, 프로젝트를 시작했습니다. 프로젝트 진행 과정 중에 한계에 도달하는 일도 있었고 뜻처럼 진행되지 않는 일도 있었지만 팀원들과 함께 돌파구를 찾아가며 차근차근 프로젝트를 완성했습니다. 프로젝트를 진행하면서 기계정보공학의 융합적 전공의 취지에 맞게 기계적인 메커니즘과 임베디드 시스템을 융합한 완성품을 제작하기 위해 노력을 했고, 실제로 저는 기구부의 메커니즘 쪽을 맡아 CAD 프로그램인 CATIA를 통해 기어 및 랙 피니언 세트와 슬라이드 핀 등을 설계하고 3D 프린터를 이용하여 실제 구현을 완료했습니다. 기구부 제작 과정 중 값비싼 3D 프린터 제작의 비용과 3D 프린터 제작품의 과도한 무게 등의 문제가 있었지만, 팀원들과 협의한 끝에 해결 방안을 고안하여 이러한 문제를 해결했습니다. 이번 내장형 프로젝트의 경험을 통해 팀원들과의 소통 능력과 문제 해결 능력을 한층 성장시켰고, 앞으로 있을 프로젝트에 큰 도움이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
주*완: 이번 학기에 내장형시스템및실습을 수강하며 처음으로 주제 선정부터 완제품 제작까지 모든 것을 직접 경험했습니다. 주제 선정부터 시작해 모든 과정이 쉽지 않았고 걸림돌에 걸려서 결국 설계를 바꾸기도 했습니다. 하지만 한계를 극복할 방법을 고민하고 공부하고 질문하고 찾아 헤매면서 많은 것을 배웠습니다. 특히 제가 맡은 부분인 안드로이드 앱을 개발하기 위해 1학년 때 배웠지만 모두 잊어버린 java를 처음부터 다시 공부했고, 안드로이드를 아무것도 모르는 상태에서 인터넷 검색만으로 하나하나 공부하고 직접 코드를 짜서 완성까지 했습니다. 과정 중에 알 수 없는 이유때문에 앱이 원하는 대로 안 되거나 강제 종료 될 때가 매우 매우 많았는데, 스스로 계속 공부하고 원인을 찾고 고쳐 나가서 마침내 앱을 완성할 수 있었고, 스스로 문제를 해결할 수 있는 능력을 기를 수 있었습니다. 또한 기구부와 외형을 이루는 부분을 만들 때 자르고 다듬는 일이 많이 필요했는데 서로 필요한 부분이 있으면 도와주면서 협동하여 결과가 잘 나온 것 같아 만족스럽습니다. 프로젝트를 하며 값진 경험을 했고 앞으로 다른 프로젝트를 하게 된다면 이 경험이 소중한 밑바탕이 돼서 도움이 많이 될 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
함*규: 임베디드 시스템을 처음 접해보기에 주제를 정하는 과정이 가장 어려웠던 것 같습니다. 처음에는 주어진 시간 내에 해낼 수 없는 비현실적인 주제에 대해 고민하며 기능적 한계에 부딪혀보기도 하고, 챗바퀴 도는 듯한 생각이 들었지만, 이 과정 속에서 전체적인 임베디드 시스템에 대한 이해와 할 수있는 것과 할 수 없는 것에 대해 구분할 수 있는 능력을 얻을 수 있었습니다. 이후에 직접 정한 주제를 바탕으로 프로젝트를 수행해나가면서, 초기 설계로는 해결할 수 없는 문제들을 팀원들과 함께 협동하여 풀어나가는 과정은 정말 힘들기도 했지만 되돌아 보면 책상에서는 배울 수 없는 많은 경험들을 배웠다고 생각합니다. 4-1학기를 떠올리면 많은 희노애락이 있었던 내장형 밖에 생각나지 않을 것 같은 데, 정말정말 재밌었고(농담이 아닙니다.) 후배들에게 꼭 추천해주고 싶습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
유*환: 내장형 시스템 프로젝트의 주제를 정하는 과정이 가장 어려웠습니다. 라즈베리파이에 센서나 모터, 또다른 보드를 연결하여 사용한 경험이 없어 주어진 시간 내에 완성할 수 있는 정도를 알기 어려웠습니다. 그래서 처음에는 학기 내에 완성이 불가능한 주제들을 생각했었습니다. 결국 마지막까지 주제를 정하지 못하다가 막바지에 급하게 살균 무드등을 만들기로 했습니다. 주제가 정해지고 자료조사를 마쳤을 때만 해도 힘든일이 끝나고 모든 일이 수월하게 진행될 것만 같았습니다. 그런데 출입 감지 방법, 음악 주파수 분석 등이 뜻대로 되지 않자 다시 조마조마해졌습니다. 하지만 적절한 대안을 찾고 해결해나가면서 완성되가는 프로젝트를 보며 뿌듯했습니다. 그리고 살균 무드등을 완성하고 영상까지 마무리 지었을 때는 그동안 밤을 새며 고생했던 시간들에 대해 보상받는 느낌이 들었습니다.&lt;br /&gt;
무엇보다 팀원 형들이 프로젝트에서 각자가 맡은 열할을 너무 잘 수행해주어서 감사했습니다. 유일하게 라즈베리파이 경험이 있는 저보다도 잘해주어서 미안하기도 하고 대단하다는 느낌이 들었습니다. 내장형 프로젝트는 과정은 정말 힘들었지만 팀원들과 살균 무드등을 만드는 것이 재밌었고 프로젝트 진행에 대한 경험치가 쌓여 좋은 기회가 되었습니다.&lt;/div&gt;</summary>
		<author><name>2012430017</name></author>	</entry>

	</feed>