신발정리해조 - 자동 수납 신발장
목차
프로젝트 소개
프로젝트 명
자동 수납 신발장
프로젝트 기간
2023.3.6~2023.6.20
팀 소개
서울시립대학교 기계정보공학과 20184300** 김*완 (팀장)
서울시립대학교 기계정보공학과 20184300** 박*영
서울시립대학교 기계정보공학과 20184300** 최*
서울시립대학교 기계정보공학과 20184300** 허*
프로젝트 개요
프로젝트 요약
본 프로젝트에서는 집에 들어갈 때 편리하게 신발을 자동으로 수납해주는 자동 정리 신발장 제작을 목표로 한다. 현관이 협소한 1인 가구 거주자를 대상으로 하며, 정해진 수납 위치에 신발을 넣으면 자동으로 정리한 후, 집에서 나갈 때는 사용자가 원하는 신발을 출납 위치에서 꺼내갈 수 있도록 한다. 스마트폰 어플리케이션을 통해 나갈 때 신을 신발을 미리 선택하고, 신발을 관리할 수 있는 기능을 제공한다. 자취방의 현관은 공간이 협소해 신발을 정리하기에 어려움이 있다. 정리를 안하다보면 신발이 섞이고 원하는 신발을 찾기가 어려워진다. 자동 정리 신발장은 이런 고초를 겪는 자취생들의 고민을 해결해줄 것으로 예상된다. 정해진 수납 위치에만 넣으면 자동으로 정리해준다. 또한 앱을 통해 신발을 관리하고 출납하여 보다 쉽게 신발을 찾을 수 있도록 도와준다.
프로젝트의 배경 및 기대효과
배경
자취생들은 자취방에 들어올 때 신발을 현관 바닥에 그냥 벗어 놓을 때가 많다. 현관의 공간이 넓을 경우 큰 문제가 되지 않을 수 있지만, 대부분의 자취방은 공간이 협소하여 벗어놓은 신발이 계속 쌓일 경우 현관에 발 딛을 틈조차 사라질 정도이다. 물론 바닥에 놓인 신발을 손으로 잡아 신발장에 올려놓으면 되지만, 이러한 작업을 기계가 스스로 해 준다면 훨씬 편리해질 것이다. 이번 프로젝트에서는 이러한 편리함에 초점을 맞춰, 사용자가 집에 들어올 때 신발을 단순히 지정된 위치에 벗어놓기만 한다면 자동으로 수납해 주고, 나갈 때는 원하는 신발을 미리 선택하여 정해진 위치에 놓인 신발을 꺼내가는 새로운 형태의 신발장을 제작할 것이다.
기대 효과
자동 정리 신발장은 평소 신발 정리에 부담을 가지던 사람이나, 일상의 편리함을 원했던 사람들의 니즈를 효과적으로 충족시킬 수 있을 것이다. 사람이 할 일을 대신 해주는 기기는 그 기능에 따라 전국적으로 집집마다 필수적인 가전기구가 되기도 한다.
집에 들어올 때 손에 집을 가득 들고 있는 경우에는 손을 이용하여 신발을 수납하기 힘들다. 또한 다리 또는 허리를 다친 사람의 경우, 바닥에서 신발을 집는 행동이 매우 불편하다. 이 기계 장치는 신발 수납 과정을 자동화하여 이러한 사람들에게 편의를 제공할 것이다. 이번 프로젝트에서는 여러 사람이 들어올 경우를 고려하지 않아 자취방에서 사용하는 것으로만 한정했지만, 향후 연구개발을 통해 처리 속도를 향상시킨다면 식당이나 헬스장 같은 공동 이용 시설에서도 활용이 가능할 것으로 생각된다.
프로젝트 개발 목표
실용성
자동정리 신발장이 실용적이기 위해서는 하드웨어 동작이 정확하고 수납/출납에 대한 시나리오가 빠르게 동작해야 한다. 스텝모터를 사용하기 때문에 발판의 움직임을 정확하게 제어할 수 있다. 발판 사이의 간격은 하드웨어가 동작할 때 충돌하지 않도록 정하였다. 간격이 너무 멀면 동작이 느릴 수 있으므로 이러한 부분 또한 고려하여 간격을 정하였다. 신발이 발판 위에 불안정하게 놓여있으면 떨어질 염려가 있다. 불안정하게 놓여있는 신발을 제대로 놓여있을 때까지 하드웨어의 동작은 시작하지 않는다. 이처럼 시나리오가 지연되는 것을 막기 위해 사용자에게 스피커로 신발이 제대로 놓여있지 않음을 알린다.
편의성
자동 정리 신발장은 사용자에게 편리함을 제공한다. 수납하는 발판의 위치를 아래쪽으로 두어 사용자가 손을 이용하지 않고 발로 신발을 넣을 수 있게 한다. 발판을 3면이 닫힌 구조로 제작하여 신발이 떨어지지 않게 도와주어 안정적으로 신발을 발로 넣을 수 있다. 또한, 출납위치를 위쪽에 둠으로써 출납할 신발과 사용자의 손이 최대한 가깝게 한다. 사용자는 스마트폰의 앱을 이용함으로써 간단히 신발의 리스트를 확인하고 관리할 수 있다. 라즈베리파이와 앱은 신발의 데이터를 firebase를 통해 처리하기 때문에 사용자가 어떠한 IP주소이든 간에 산발 데이터에 대한 접근과 수정을 할 수 있다. Classification을 활용해 수납한 신발을 분류하여 자동으로 리스트에 신발이 업데이트된다. 또한, 새로운 종류/색깔의 신발을 인식했을 경우 앱을 통해 새로운 신발을 리스트에 등록할 수 있다.
경제성
주요 타겟이 자취방에 사는 자취생들이기 때문에 실용성과 편의성을 최대한 보장하는 선에서 재료비를 최소화한다.
동작 시나리오
신발장의 작동 시나리오는 사용자가 신발을 보관하는 수납 시나리오와 APP 내에서 신발을 꺼내도록 요청한 후 작동하는 출납 시나리오로 이뤄져 있다.
수납 시나리오
사용자가 신발을 수납부에 넣으면 옆에 있는 적외선 센서가 이를 감지한다. 아두이노가 정보를 받아 라즈베리 파이와 연결된 카메라가 수납부의 사진을 찍고 이를 분석한다. Detection 과정에서 신발이 잘 올라갔는지 opencv 이미지처리를 사용하여 확인한다. 노란색 스티커 10개가 바닥에 붙어져 있는데, 이것을 전부 가려야 신발이 정상적으로 올라간 것으로, 일부만 올라가면 잘못 올라간 것으로 판단하여 아두이노와 연결된 스피커에서 에러 효과음을 출력한다. 스티커가 하나도 가려져 있지 않으면 센서가 오작동한 것으로 판단하여 아무 반응을 하지 않는다. 정상적으로 신발이 올라가 있는 것이 확인되면, 신발 종류와 색상을 파악한다. 인공지능 모델을 사용하여 신발 종류를 크게 구두, 슬리퍼, 운동화로 구분한다. 신발 색상은 opencv 이미지처리로 masking을 이용하여 아래 5가지 색 중 사진에서 가장 많은 부분을 차지하는 색을 신발의 색으로 판단한다. 그 후 판단한 결과가 기존 리스트에 있는 신발이라면 별도의 푸시알림 없이 수납 시나리오를 진행한다. 리스트에 없는 경우, 앱에게 신발의 사진과 함께 새로운 신발인지 묻는 푸시알림을 보낸다. 푸시알림을 보내는 이유는 인공지능 판단의 오작동을 방지하기 위해 사용자가 2차 검증을 할 수 있도록 하기 위해서 알림 기능을 추가하였다. 선택 후 기기는 신발을 보관하고 새로운 수납 발판을 제공하기 위해 모터를 작동한다. 불필요한 작동과 에너지 소모를 줄이기 위해 최단 거리에 있는 빈 발판을 수납부에 위치시킨다. 그 후 발판별 신발의 정보를 갱신하고 수납 시나리오를 종료한다.
출납 시나리오
사용자는 사용을 원하는 신발을 어플 내에서 출납 버튼을 눌러 출납을 요청하면 파이어베이스에서 신발에 해당하는 문서 이름으로 필드가 변경되고, 라즈베리파이는 그 필드값을 불러와 해당 신발의 발판 정보를 데이터베이스에서 검색하여 가져온다. 라즈베리파이에서 계산한 출납부까지 도달하기 위한 모터 회전각을 아두이노가 전달받아 모터를 돌려 요청한 신발을 출납부에 위치시킨다. 사용자가 신발을 꺼내면 센서가 이를 감지하고, 수납부에 위치한 발판이 비었는지 판별하고 비어있지 않으면 빈 발판을 제공하기 위해 모터가 계산한 회전각만큼 회전한다.
역할분담 및 개발일정
역할 분담
김*완
[전체 진행 및 데이터베이스 관리, 신발 종류 및 색상 구분 알고리즘 제작]
박*영
[부품 주문 및 제작, 시제품 제작, 학습 데이터 정제]
최*
[캐드 모델링, 시제품 제작, 안드로이드 어플리케이션 제작]
허*
[아두이노 코드 제작, 회로 연결, 안드로이드 어플리케이션 제작]
개발 일정
구현 내용
시스템 구성
제품 형태 및 시스템 개략도는 위와 같다. 전체적인 시스템을 자동 신발 수납장(제어부), 데이터베이스, 스마트폰 어플리케이션으로 나눌 수 있다. 자동 신발 수납장은 아두이노를 중심으로 한 입출력 장치와, 아두이노에서 받은 제어 신호를 기반으로 신발 상태에 대한 판단을 수행하는 라즈베리파이로 구성된다. 어플리케이션은 사용자가 신발 상태를 확인하고 신발장에 대한 조작 명령을 받아 처리한다. 데이터베이스는 신발 정보를 저장하여 어플리케이션과 수납장 사이에서 정보를 송수신하는 매개체로 사용된다.
기구부 설계 및 구현
자동 수납 신발장의 작동 방식은 체인-스프로킷 구조를 이용한 수직 순환 방식이다. 체인 스프로킷 구조를 이용할 때, 체인 구동 중 스프로킷을 지지하고 있는 지지부가 휘어지면 이에 따라 체인의 장력이 약해져, 구동이 원하는 대로 되지 않을 수 있다. 따라서, 이를 방지하기 위해 알루미늄 프로파일을 이용하여, 튼튼한 지지부를 만든 뒤 스프로킷을 결합하여, 처짐에 의한 장력 감소 현상을 최대한 막고자 하였다.
구동 메커니즘
기구부 구동을 위한 모터 및 체인 시스텀 구성은 다음과 같다. 우선, 그림 (a)와 같이 공간활용성을 높이기 위해서 스텝 모터를 프레임 내부에 두고, 스텝모터와 기어를 커플링을 통해 연결한 뒤, 이를 구동축(발판이 부착된 체인을 직접적으로 돌리는 회전축. 이하 구동축) 과 기어 연결하여 동력을 전달한다. 구동축의 경우 발판과 충돌할 우려가 있기 때문에, 그림 (b)와 같이 발판의 이동과 영향이 가장 적은 기구부 중심에 배치하였다(추가적으로 가운데 배치시 구동축을 양 옆 스프로킷에 비해 살짝 아래에 위치시켜 체인이 텐션이 느슨해 지는 현상을 최대한 막고자 함(그림(c) 참조)). 모터에서 토크가 발생하면, 이에 따라 구동축이 움직여 구동축의 토크를 바탕으로 체인을 구동시켜 발판을 수/출납부로 이동시킨다.
그림 (d)는 모터가 구동축에 토크를 전달하는 상황을 나타낸 그림이다. 이때 기구학적인 관점에서 각 성분에 가해지는 회전 마찰, 토크, 관성 모멘트 등의 물리적 요소들을 고려하여 체인과 모터 회전축에 가해질 부하를 계산하여 이에 맞는 요구 스펙의 모터를 구매하였다.
구동축 스프로킷에 가장 많은 힘이 요구되는 상황은 그림 (e)와 같이 연속되는 발판 두개에 신발이 몰려있어, 신발의 무게를 이겨내며 체인 구동을 해야하는 상황이다. 신발과 발판을 포함한 질량이 최대 2kg 라고 가정하면, (신발 최대 질량은 전투화 무게 기준 – 약1.5kg) 2kg * 9.8m/s2 * 2 = 39.2N 이상의 힘을 체인에 전달해야한다. 구동축과 연결된 스프로킷의 피치원지름은 8.521cm 로, 구동축의 필요 토크 T = F * r = 39.2 * (8.521/2) = 167 N·cm =1.67N·m 이다.
다음으로 신발자의 발판이 원하는 속도로 작동하기 위해 RPM을 고려하였다. 스텝모터의 rpm이 증가하면 그에 따라 모터 인덕턴스와 모터 구동 전압 간의 관계에 영향을 받아, 속도가 증가함에 따라 토크가 감소한다. 사용하고자 하는 모터의 적합성을 따지기 위해서 RPM을 구하였다. 목표 수/출납 속도는 5초 이내이고, 모터를 양방향 제어를 한다는 점을 고려하면, 수/출납부에서 가장 멀리 떨어진 지점까지의 체인 상에서의 거리는 800cm 이다. 이를 바탕으로 RPM을 구하면 35.87rev/min(분당 35.87회)이다.
구매하고자 하는 모터의 유지토크(holding torque) = 1.89N·m 이고, 모터 사양 견적서에서 확인한 결과 100 RPM 이하에서는 1.8 N ·m 이상의 토크를 가지고 있었다. 필요한 모터의 토크는 1.67N·m 이고, RPM = 35.87 이므로, 모터가 체인을 구동시키기에 적합한 사양을 갖고 있음을 확인할 수 있다.
기구부 구현
주문한 재료를 초안 설계에 맞게 가공 및 조립하여 제작하였다. 알루미늄 프로파일로 기본 틀을 만들고, 프로파일과 호환되는 베어링을 통해 모터 구동축 및 스프로킷 회전축을 구현하였다. 조립과정에서 미세하게 규격이 맞지 않는 부품들은 밀링과 그라인딩으로 가공하여 조립에 이상이 없도록 하였다. 구입한 어태치먼트 체인의 경우, 길이가 1524mm로 개념설계 때 목표로 한 1600mm 보다 길이가 짧기 때문에, 어태치먼트 체인을 하나 더 구입하고 이를 분리한 후 나머지 두 체인에 이어 목표 체인 길이를 만족시킬 수 있었다.
기구부의 경우 스프로킷이 쇠 막대를 통해 한쪽 베어링에 외팔보와 같은 형태로 연결되어있는 형태이기 때문에, 처짐이 나타나 체인의 장력이 저하될 우려가 있다. 따라서 처짐 완화를 위해 경량화가 중요하다고 판단했고, 이를 위해 스프로킷의 형번 선택시 결합이 용이하다는 장점을 가진 B형 대신 보다 가벼운 A형을 선택하였고, 이를 축에 직접 용접해서 사용하였다.
모터 중심축의 처짐을 방지하기 위해 베어링 홀더를 사용하려하였으나, 일반적인 베어링 홀더의 중심축 높이는 모터의 중심축 높이와 일치하지 않는다는 문제점이 있었다. 이를 해결하기 위해 5T 금속 판을 가공하여 ㄷ자 형태의 금속 브라켓을 만들고, 이를 모터 지지부 프로파일에 장착함으로써 베어링 홀더의 높이를 높여 회전축을 지지할 수 있게 하였다.
프로파일끼리 수직으로 결합하기 위해서는 일반적으로 L자 브라켓을 이용하여 볼트 결합을 한다. 하지만 L자 브라켓은 그 자체의 부피 때문에 한 프로파일에 수직으로 연결된 두 프로파일 사이의 간격을 매우 가깝게 하기가 어렵다는 단점이 있다. 제작할 기구부의 경우 평기어를 통하여 모터로부터 체인 구동축으로 동력전달을 하려했기 때문에, 프로파일간 간격이 가까워야했다. 따라서 아래 그림과 같이 5t 금속판을 가공하여 T자 지지 브라켓을 제작하여 프로파일간의 간격을 좁힐수가 있었다.
신발을 보관하는 수납 발판의 경우 아크릴을 레이저 가공기로 절단하여 조립 제작하였다. 조립의 용이성과 발판의 견고함을 위해 그림 8-(b) 와 같이 절단면에 홈을 넣어 조립 시 비틀림과 압력에 강한 구조로 제작할 수 있도록 하였다.
베어링의 축방향이 일정하게 고정될 것이라고 예상했던 것과 달리, 베어링의 내륜이 체인의 장력에 의해 기울어져 회전축이 지면과 수평을 이루지 않는 일이 발생하였다. 회전축이 기울어질 경우 체인의 장력이 저하되고, 회전축의 기울어짐으로 인하여 4개의 스프로킷이 평행하지 않게 된다면 체인 구동에 대한 저항력도 강화된다. 이를 해결하기 위해 내륜이 기울어지지 않는 새로운 베어링을 구매하여 문제를 해결하였다.
체인과 모터를 기구부에 체결하고 동작을 확인했을 때, 체인 구동축의 스프로킷이 체인을 제대로 물고 회전하지 못하고 공회전을 하는 현상이 나타났다. 기존 설계 과정에서 체인 구동축을 양끝 스프로킷 회전축보다 살짝만 아래에 위치시켰는데, 이에 따라 구동축 스프로킷에 접촉하는 체인이 일직선(180도)에 가까워 회전 시 스프로킷이 무는 체인 링크의 개수가 적어 이러한 현상이 발생한 것이다. 이를 해결하기 위해 체인 구동축을 아래로 더 내려서 체인과의 접촉 면적을 더 넓혀 보다 더 많은 체인 링크를 스프로킷의 물 수 있도록 하였다, 구동축 위치 변경 후 다시 모터를 연결하여 테스트한 결과, 체인이 원활하게 잘 구동이 되는 것을 확인하였다. 하지만 체인 구동축이 내려감에 따라, 신발 수납부를 위한 공간이 줄어들었기 때문에, 신발장의 높이를 더 늘리는(10cm 정도) 방향으로 기구부를 수정하여 제작하였다.
위와 같은 설계안 수정을 통해 최종적으로 위와 같이 기구부를 완성하였다. 프로파일을 통해 기초적인 외골격을 제작하고, 사각 너트를 사용하여 원하는 위치에 구동부를 장착하였다. 볼트-너트를 통해 고정하기 어려운 센서, 라즈베리파이 등은 접착제, 케이블 타이 등을 통해 안정적으로 골격에 장착하였다. 신발 종류와 색상 분석을 카메라로 찍은 수납부 사진을 이용하여 진행하기 때문에, 외부 빛에 대한 영향을 최소화하기 위해 외장재를 어두운 아크릴로 선택하여 장착하였다.
제어부 및 회로 구현
회로도 구현
위 사진은 전원은 생략한 아두이노 회로도이다. 모터드라이버는 사진과 실제가 약간 다르나 사용하는 핀의 역할은 모두 동일하다. 각 센서의 역할은 다음과 같다.
아두이노 나노는 각 센서들에서 물리적인 신호를 감지하고 이에 맞는 동작을 수행하기 위한 하드웨어 제어 신호를 모터 드라이버로 송신한다. 아두이노는 라즈베리파이와 시리얼 통신을 하기 때문에 물리적 상황을 라즈베리파이 또한 알 수 있게 하였다. 수납부 적외선 센서는 사용자가 신발을 넣는 행위를 인지하기 위하여 사용된다. 출납부 적외선 센서는 사용자가 출납위치에서 신발을 빼는 행위를 인지하기 위해 사용된다. 네오픽셀 LED는 수납 시나리오가 진행될 때, 신발 종류와 색상 구분을 할 사진이 밝게 나오게 하기 위해 사용된다. 스피커는 신발이 한 짝만 들어가거나, 제대로 신발이 발판 위에 위치하지 않았을 때 사용자에게 경고음을 보내기 위해 사용된다. 모터 드라이버의 경우 PUL과 DIR이 아두이노 디지털 핀에 연결되어 있다. 아두이노는 PUL 핀의 ON/OFF 주기를 조절하여 모터의 속도와 바퀴수를 제어한다. 또한 DIR 핀의 ON/OFF를 설정하여 모터가 움직일 방향을 결정한다. 다이오드 센서는 모터를 정확히 원하는 만큼 돌리기 위해 사용된다. 자세한 설명은 아래에서 하도록 한다.
모터부-모터동작방식
수납/출납 발판 위치를 사용자가 넣고 빼기 쉬운 위치에 정확하게 맞추기 위하여 스텝모터를 정확한 수치만큼 돌려야 한다. 이를 구현하기 위해 각 발판이 연결된 체인에 검은색 테이프를 붙이 고 해당 테이프가 다이오드 센서에 인식될 때까지 스텝모터가 동작하도록 하였다. 발판 사이의 거리를 한 칸이라 할 때, 필요에 따라 한 칸 또는 두 칸을 돌려야 한다. 만약 검은 테이프를 인식할 때까지만 모터를 돌리도록 할 경우, 두 칸을 가야할 때 한 칸만 갈 수가 있다. 이와 같은 상황을 방지하기 위해 한 칸을 가야 하는 경우 0.5칸을 우선적으로 돌리고, 다음 검은 테이프가 인식될 때까지 모터가 돌도록 하였다. 두 칸을 가야할 경우 1.5칸을 우선적으로 돌리고, 다음 검은 테이프가 인식될 때까지 모터를 돌리도록 동작을 제어하였다.
라즈베리파이-아두이노 간 시리얼 통신 과정
[아두이노 main loop]
void loop() {
int val_IR=1;
val_IR = digitalRead(insert_IRsensor);
if (val_IR==LOW) {
if (isScenarioRunning == false){
insert_scenario();
}
}
//RPi가 아두이노로부터 데이터를 받아들이는 함수는
//데이터를 받아들일 때까지 대기함
//RPi의 코드를 돌리기 위해 trivial 값을 보냄
Serial.println(".\n");
if (Serial.available()) {
Data = Serial.readStringUntil('\n'); // 개행문자까지 읽어드림
Data.trim(); //개행문자 제거
if (Data == "remove_scenario" && isScenarioRunning == false){
remove_scenario();
}
}
delay(1000);
}
[라즈베리파이 main loop]
def send_data(data_to_arduino):
ser.write(data_to_arduino_encode())
# 이 루프를 계속 반복하면서 입력, 출력 시나리오 실행 시 정지
while True:
#ready to recieve data from Arduino
print('ready to get data')
# 출납 시나리오가 실행 중인 동안 입력 시나리오가 실행되지 않도록 함.
while(firebase.remove == True):
continue
#아두이노에서 데이터 받음
data_from_arduino=scenario.ser.readline().decode().strip()
#Check insert_scenario
if data_from_arduino=="Insert" :
# 앱에서 버튼 눌러도 출납 시나리오가 실행되지 않도록 함
firebase.doc_watch_request.unsubscribe()
# 수납 시나리오 실행
scenario.insert_scenario()
# 수납 시나리오 실행 후 다시 출납 시나리오 실행될 수 있는 상태로 바꿈.
firebase.doc_watch_request=firebase.doc_ref.on_snapshot(firebase.on_snapshot_request)
time.sleep(0.5)
아두이노와 라즈베리파이가 시리얼 통신을 이용하여 데이터를 주고 받는 과정은 다음과 같다. <#아두이노에서 데이터 받음> 부분에서, 아두이노로부터 데이터가 올 때까지 대기하고 데이터가 오면 그 데이터 값을 리턴하는 명령어이다. 라즈베리파이의 main loop이 항상 돌아가야하기 때문에 아두이노 쪽에서는 별다른 이벤트(수납부 적외선 센서 인식)이 없다면 trivial한 데이터를 보내 라즈베리파이의 loop을 돌린다. 만약 수납부 적외선 센서가 인식되었을 시, 이때 아두이노는 trivial한 값이 아닌 수납시나리오의 시작을 알리는 데이터를 라즈베리파이로 보낸다. 이 값을 읽고 Rpi가 시나리오에 대한 동작하게 된다. 라즈베리파이 또한 아두이노로 데이터를 전송할 수 있어야 한다. 라즈베리파이 코드 위쪽에 정의된 send_data가 아두이노로 데이터를 전송하는 함수이다. send_data가 실행되었다면 아두이노 쪽에서는 라즈베리파이가 보낸 데이터를 받아들일 수 있다.
소프트웨어 설계 및 구현
신발 종류 및 색상 구분
앱에서 사용자가 신발의 목록을 관리하고, 새 신발인지 구분하기 위해 수납 시 기기가 기존 신발에 해당하는지 확인하는 과정이 필요하다. 이번 프로젝트에서는 신발의 종류와 색상으로 모든 신발을 구분할 수 있다고 가정한다. 신발의 종류는 구두, 슬리퍼, 스니커즈 3종류로 하며 신발의 색상은 빨강, 초록, 파랑, 하양, 검정 5가지로 하여 총 15종류로 신발을 분류한다.
신발 종류 구분
이번 프로젝트에서 여러 개의 신발 종류를 구분해내야 하므로 class가 여러 개인 Multi-class classification을 수행한다. 데이터셋으로는 kaggle의 Large Shoe Dataset의 /ut-zap50k-images 폴더 내의 이미지를 활용하여 우리가 분류하고자 하는 구두, 스니커즈, 슬리퍼로 다시 나누었다. 구두의 경우 /Shoes의 Loafers, Oxfords 폴더에서, 스니커즈의 경우 /Shoes의 /Sneakers and Athletic Shoes 폴더에서, 슬리퍼의 경우 /Sandals의 Flat 폴더와 /Slippers의 Slipper Flats 폴더에서 이미지를 가져왔다.
앞에서 설명한 폴더 내의 이미지를 확인한 결과, 위 그림과 같이 우리가 예상한 신발 모양과는 달라 학습에 방해가 될 것으로 예상되는 이미지를 선별하는 작업을 수작업으로 진행하였다. 선별한 데이터셋을 train:valid:test = 6:2:2로 나누고, 베이스라인 모델로는 3층의 CNN 모델을 사용했다. 모델의 구조는 다음과 같다.
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 102, 136, 32) 2432
conv2d_1 (Conv2D) (None, 102, 136, 32) 25632
max_pooling2d (MaxPooling2D (None, 51, 68, 32) 0
)
dropout (Dropout) (None, 51, 68, 32) 0
conv2d_2 (Conv2D) (None, 51, 68, 64) 18496
conv2d_3 (Conv2D) (None, 51, 68, 64) 36928
max_pooling2d_1 (MaxPooling (None, 25, 34, 64) 0
2D)
dropout_1 (Dropout) (None, 25, 34, 64) 0
flatten (Flatten) (None, 54400) 0
dense (Dense) (None, 256) 13926656
dropout_2 (Dropout) (None, 256) 0
dense_1 (Dense) (None, 3) 771
=================================================================
Total params: 14,010,915
Trainable params: 14,010,915
Non-trainable params: 0
_________________________________________________________________
딥러닝 서버에서 모델을 학습시키고, TFLite 경량 모델로 변환하여 라즈베리파이에서 추론이 가능하도록 하였다. 이 모델로 중간 테스트 결과, 정확도가 충분치 않아 기구부 완성 이후 실제 환경에서 촬영한 사진을 사용하여 transfer learning을 추가로 진행하였다. 동일한 신발을 여러 번 반복 촬영하여 구두 33장, 슬리퍼 29장, 스니커즈 87장의 이미지를 얻었으며, train:test를 8:2 비율로 분할한 후에 이미지 회전 및 뒤집기를 통해 train data를 증강하였다. 기존 모델에서 flatten 층까지 83488개의 가중치를 동결시킨 이후에 0.0001의 학습률로 epoch 2000으로 하여 학습하였다. test accuracy는 96.8%로 높았지만 이외의 신발의 경우 정확도가 목표에 미치지 못했다.
신발 색상 구분
신발 색상 검출의 경우 <신발 개수 파악>에서 촬영한 이미지를 활용하고, 각 색상에 대한 mask를 생성하여 넓이를 비교하여 이 신발을 가장 잘 나타낼 수 있는 색상 하나를 선택한다. mask 생성 예시는그림 (a)와 같다. 신발장에 넣는 신발은 누구나 보기에도 한 가지 색상으로 명확히 드러나는 신발로 한정한다. RGB(빨간색, 초록색, 파란색)을 사용한 신발의 경우 회색, 검은색을 같이 사용하는 경우가 많기 때문에, 회색/검은색이 더 넓은 범위를 차지하더라도 RGB을 우선시하기로 하였다. RGB색 중 가장 많은 면적을 갖는 색을 선택하고, 만약 3가지 RGB색 모두 기준치에 미치지 못할 경우 회색/검은색 중 한 가지로 판별한다. 알고리즘은 그림 (b)와 같다. 인식을 쉽게 하기 위해 발판의 색을 흰색으로 제작한다.
신발 개수 파악
신발의 개수를 파악하기 위해 발판에 10개의 노란 색 스티커를 부착하고, 카메라로 촬영한 이미지에서 노란색 스티커의 개수를 감지하여 신발이 몇 개 올라가 있는지 파악한다. 감지 방법은 노란색의 범위를 지정하여 opencv 알고리즘을 통해 노란색 부분의 mask를 추출해 contour를 생성하고, contour의 개수를 리턴한다. 신발이 없을 경우는 그림 (a)에서 10개의 스티커가 모두 감지되고, 신발이 1짝일 경우는 그림 (b)에서 5개의 스티커가 감지된다. 그리고 신발이 2짝으로 올바르게 올라갈 경우 그림 (c)에서 모든 스티커가 가려 0개가 감지된다.
신발에 노란색 무늬가 존재할 경우 이것을 스티커로 인식하여 오동작을 일으킬 위험성이 있다. 이를 방지하기 위해 색상뿐만 아니라 contour의 넓이와 모양 또한 고려하기로 하였다. 똑같은 크기와 모양의 스티커를 바닥에 부착하고, 사전에 스티커의 크기를 계산하여 범위 내에 있는 경우만 인식한다. 모양의 경우 두 contour의 모양 일치 정도를 계산하는 cv2.matchShapes 함수를 사용한다. 사전에 바닥에 부착된 스티커 1장에서 contour를 뽑아내어 촬영한 신발 사진의 contour와의 모양 불일치 정도(distance)를 계산하여 임계값보다 낮을 경우만 카운트한다.
데이터베이스
데이터베이스로는 Google Firebase를 이용해 신발장의 작동에 필요한 신발, 발판의 정보와 신발 이미지를 한번에 관리한다. Firestore Database의 경우 collection, document, field로 이루어져 있다.
- init collection
document | field | 설명 |
---|---|---|
value | difference | shelf_location - shelf_num 값 저장하여 수납부, 출납부에 위치한 shelf_location 값 파악 |
init 이름의 collection을 만들어 현재 shelf_num과 shelf_location 값의 차이를 저장한다.
- shoe collection
field 이름 | 유형 | 설명 | 가능한 값 |
---|---|---|---|
shoe_name | string | 사용자가 지정한 신발의 이름 | - |
shoe_type | string | 신발의 종류 | shoes, slippers, sneakers |
shoe_color | string | 신발의 색상 | white, black, red, green, blue |
shelf_num | number | 신발이 저장된 발판 번호 | 1, 2, 3, 4 |
shelf_location | number | 신발의 발판이 신발장의 어떤 위치에 해당하는지 사전 정의된 숫자 |
1, 2, 3, 4 |
shelf_status | boolean | 신발이 저장되어 있는지 유무 | True, False |
order | number | 어플리케이션 화면에 신발 정보를 띄울 순서 |
1, 2, ... , 저장된 신발 개수 |
remain | boolean | 신발 정보를 삭제할지를 판단하기 위한 변수 |
True, False |
신발 및 발판 정보를 저장할 collection의 이름은 shoes로 하고, document의 이름은 신발의 이미지 이름으로 한다. 이미지 이름은 촬영한 시점의 날짜-시간.jpg로 하여 유일하므로 document에 사용 가능하다. 각 document에 저장할 field의 정보는 위 표와 같다. 수납부의 shelf_location은 3, 출납부의 shelf_location은 1로 한다.
shelf_location의 갱신을 그림으로 나타내면 위와 같다. 그림에서 회색 상자는 shelf_location을 의미하고, 빨강/노랑/초록/파랑 상자는 shelf_num을 의미한다. 초기에 발판이 모두 비어있는 경우에는 발판이 1칸(1번 발판이 3->2 위치로) 이동한다. shelf_location 2, 4 위치에 신발이 차 있는 경우에는 발판이 2칸(1번 발판이 3->1 위치로) 이동한다.
- signal collection
document | field | 설명 |
---|---|---|
From_app_to_RPi | DocName | 사용자가 출납을 원하는 신발의 이름을 RPi에게 전달하기 위해 사용 |
Fcm_token | FCM을 사용하기 위한 토큰 | |
Request | 출납 요청을 보낼 때 사용, 출납이 완료되기까지 앱의 동작을 막기 위함 | |
Sign_question | Sign_new | 새로운 신발을 인식했을 때 RPi에서 사용자 응답을 기다리기 위해 사용 |
Which_shoe | 기존 신발이 어떤 것인지를 RPi 가 알게끔 하는 용도 |
'signal’ collection은 앱과 Rpi가 데이터를 주고 받기 위해서 사용된다. 해당 collection의 'from_app_to_RPi’ document에는 4가지 field가 있다. ‘request’ field는 앱에서 Rpi로 출납 요청을 보내기 위해 사용된다. 앱에서 출납 버튼을 누르면 해당 field 값이 ‘false’에서 ‘true’로 바뀌고 Rpi가 이를 읽어 출납 시나리오를 진행하는 방식이다. ‘DocName’ field는 사용자가 앱에서 선택한 신발(문서이름)을 저장하게 된다. Rpi는 해당 field를 읽어 사용자가 어떤 신발을 출납하려 하는지 알 수 있게 된다. ‘fcm_token’ field는 사용자 기기에 푸시알림인 FCM(Firebase Cloud Messaging)을 보내기 위해 사용된다. Rpi에서 앱으로 푸시알림을 보내기 위해서는 사용자 기기(스마트폰)에 생성되는 token 정보를 알아야 한다. 앱이 실행되면 ‘fcm_token’ field의 값을 생성된 token 값으로 변경하고 Rpi가 이를 읽고 앱으로 FCM을 보낼 수 있게 된다.
‘signal’ collection에 ‘sign_question’ document에는 2가지 field가 있다. ‘sign_new’라는 field는 평소에는 ‘No’로 저장되어 있다가 FCM에 대한 사용자의 응답을 저장한다. FCM은 classification의 결과가 새로운 신발로 인식했을 때 보내지게 되는데 사용자는 이 신발이 정말 새로운 신발인지, 아니면 기존에 있던 신발인지를 선택하게 된다. 이에 대한 응답에 따라 ‘sign_new’에는 ‘yes’ 또는 ‘exist’이라는 값이 저장되게 된다. 만약 사용자가 기존 신발이라고 선택했을 경우, 신발 리스트에서 어떤 신발이었는지 다시 선택하게 되는데, 이 결과가 ‘which_shoe’ field에 저장되고 Rpi는 해당 field를 읽어 이에 대한 수납 처리를 진행할 수 있도록 한다.
Firestore Database에는 이미지를 업로드할 수 없기에, storage에 이미지를 저장하고 Firestore Database에는 이미지의 주소를 저장한다.
스마트폰 어플리케이션
어플리케이션 수납 시나리오
신발 수납 수행시, 기존 신발을 넣을 경우에는 자동으로 신발 보관상태가 업데이트가 되고, 새 신발을 넣을 경우에는 이를 등록하는 과정을 어플에서 수행할 수 있도록 하였다. 아직 신발 분류 과정의 정확도가 높지 않기 때문에, 예외 처리를 어플에서 할 수 있도록 하였다 . Rpi의 신발 분류 결과가 기존 신발 리스트에 없는 종류일 경우, Rpi는 앱에게 푸시알림을 보내게 된다. 푸시 알림을 누르면 새롭게 인식된 신발의 사진과 두가지 선택사항이 있는 화면을 띄우게 된다. 버튼을 누르게 되면 'signal’ collection에 'sign_question’ document의 'sign_new’ field 값을 바꾸게 된다. '네 등록할래요' 버튼을 누르게 되면 해당 field를 ‘yes’로 바꾸게 되고 Rpi가 이 field를 읽고 수납 시나리오를 진행하고 앱은 넣은 신발이 추가된 메인화면으로 돌아가게 된다. 만약 신발 분류의 결과가 잘못되어 기존 신발을 새로운 신발로 인식할 경우, 사용자는 ‘기존신발이에요’ 버튼을 누르면 된다. 이 버튼을 누르면 사용자에게 이 신발이 어떤 신발이었는지 선택할 수 있는 화면으로 넘어가게 된다. 이 화면에 뜨는 신발은 보관상태가 ‘미보관'인 신발들만 나타나게 된다. 사용자가 넣은 신발이 어떤 신발이었는지 선택하게 되면 Rpi는 그 신발에 대한 수납을 진행하고 앱은 선택한 신발의 상태가 ‘보관중’으로 바뀐 메인화면으로 돌아가게 된다.
어플리케이션 출납 시나리오
어플리케이션의 메인 화면은 firebase에 저장된 사용자의 신발 리스트 정보를 제공한다. 사용자가 정의한 신발의 이름, 신발 사진, 보관상태를 제공한다. 이를 통해 사용자는 신발장을 열어보지 않고도 어떤 신발이 보관되어 있는지 알 수 있다. 각 신발 인터페이스에는 '출납'이라는 버튼이 있다. '미보관’ 상태의 신발의 ‘출납’ 버튼을 누르면 앱은 아무런 동작을 하지 않고 오직 토스트 메시지를 띄운다. 만약 ‘보관중’인 신발의 ‘출납’ 버튼을 누를 시, Rpi로 출납요청을 보내게 된다. ‘출납’ 버튼을 누를 시 firestore에 저장된 'signal’ collection에 'from_app_to_RPi’ document의 ‘request’ field를 true 상태로 바꾸게 되고 Rpi가 이를 인지하면 출납 시나리오가 시작된다. Filed를 바꾸고 앱은 로딩창을 띄우게 된다. 이는 출납이 완료될 때까지 앱의 행동을 막기 위함이다. 이후 Rpi가 신발의 정보를 수정하고 출납 동작을 마치게 되면 앱은 로딩창에서 메인화면으로 넘어가게 된다. 이 메인 화면에는 출납버튼을 누른 신발 상태 정보가 ‘미보관’으로 업데이트 되어 나타난다.
어플리케이션 사용자 편의 기능
어플리케이션을 사용하는 사용자의 편의를 증대시키기 위해서, 신발 목록을 편집할 수 있는 화면을 구현하였다. 어플의 메인화면 우측 하단에 있는 편집 버튼을 클릭하면, 신발 정보 수정 화면으로 넘어가게 된다. 수정 화면에서 할 수 있는 일은 신발 이름 변경, 신발 목록 순서 변경, 그리고 데이터 베이스에서 특정 신발 정보가 담겨져 있는 문서를 삭제하는 일이다. 위의 신발 분류 과정을 통해, 수납된 신발이 기존 신발과 매칭이 잘 된다면, 이러한 리스트 수정 과정은 사용자에게 큰 편의성을 가져다 줄 수 있다. 신발 순서 변경, 삭제 등의 기능은 드래그나 스와이프로 구현하는 것이 직관적이기 때문에, 드래그 앤 드랍을 할 시 리스트 순서 변경을, 그리고 왼쪽으로 밀면 신발 문서가 삭제되도록 하였다. 이는 Kotlin 내의 ItemTouchHelper이라는 클래스를 활용하여 구현할 수 있다. 혹시나 사용자가 순서 변경, 삭제 등의 기능에 대해서 알기 힘들 수 있으므로, 신발 리스트를 터치했을 때 안내 문구가 나오도록 하였다.
프로젝트 결과
최종 결과물
결과물 사진
프로젝트 동안 설계한 내용을 바탕으로 위와 같이 신발장을 제작하였다.
발표 포스터
시연영상
파일:새신발등록.mp4
위 영상은 새로운 신발을 등록할 때의 시연 영상이다. 새 신발을 수납부에 넣으면, 신발을 찍은 사진과 함께 새 신발이 맞는지 묻는 푸시 알림이 사용자 어플리케이션으로 간다.
파일:기존새신발.mp4
위 영상은 리스트에 있는 기존 신발을 새로운 신발로 인식한 경우의 시연 영상이다. 사용자는 어플리케이션에서 기존 신발임을 선택하고, 어떤 신발인지 리스트에서 선택하여 저장 스테이터스를 변경한다.
파일:신발정상X.mp4
위 영상은 신발을 발판 위에 제대로 놓지 않았을 경우의 시연 영상이다. 발판의 노란 스티커가 다 안가려질 경우, 스피커를 통해 경고 메세지를 출력하며 제대로 올려놓으면 정상적으로 다음 시나리오를 진행한다.
파일:어플.mp4
위 영상은 어플리케이션의 세부 기능에 대한 시연 영상이다. 어플리케이션 내에서 신발 이름 수정, 신발 리스트 순서 변경 및 삭제가 가능하다.
파일:기존신발수납.mp4
위 영상은 기존 리스트에 있는 신발을 수납하는 경우의 시연 영상이다. 이미 리스트에서 종류 판별이 된 신발이므로 별다른 알림 없이 수납을 진행한다.
파일:출납.mp4
위 영상은 리스트에서 사용자가 출납할 신발을 정하고 출납하는 경우의 시연 영상이다.
파일:신발정리해조 최종영상.mp4
위 영상은 각 경우의 상황을 모두 합쳐 짧은 영상으로 프로젝트의 기능을 시연하는 영상이다.
미구현 내용
- 신발 종류 파악 정확도 개선
- 현재 60%인 모델 성능을 85% 이상으로 개선
- 여러 사용자 동시 이용 가능
- Firebase 구글 인증 과정을 추가하여 여러 사용자를 등록하고, 토큰 관리 방법 탐색
- 예외 처리
- 신발 이외 의도치 않은 물건이 들어가는 경우 추가
프로젝트 평가
평가항목 및 결과
평가 항목 | 평가 방법 | 적용 기준 | 개발 목표치 | 비중 | 평가 결과 | 달성도 |
---|---|---|---|---|---|---|
발판 위치 정확성 |
10회씩 수납을 수행하며 발판 위치의 허용 오차 범위가 1cm 이내에 위치하는지 여부를 카운트 |
Accuracy | 90% 이상 | 35% | 100% | 100% |
신속성 | 10회의 출납 과정을 통해 사용자가 APP에서 신발을 고른 후 신발이 출납부에 위치할 때까지 걸린 시간 |
Time | 10회 평균 5sec 이내 |
20% | 4.88sec | 100% |
신발 개수 감지 정확도 |
정상인 경우, 이상이 있는 경우 각각 10회 측정하여 제대로 판단하는지 카운트 |
Accuracy | 90% 이상 | 20% | 100% | 100% |
신발 종류 파악 정확도 |
20개의 신발 수납 과정에서 신발 종류 및 색상을 모두 정확히 판단하는지 카운트 |
Accuracy | 85% 이상 | 15% | 60% | 80% |
APP 연동성 | 수납/출납 후 신발 정보가 APP에 정확히 갱신되는지 수납/출납 각각 10회 진행하여 카운트 |
Accuracy | 90% 이상 | 10% | 100% | 100% |
느낀점
김*완
지금까지 배운 지식들을 활용해서 실제 제품을 만드는 경험을 처음 해 봤는데, 힘들었지만 얻어갈 것이 많았던 수업이었다. 하나의 제품을 완성해나가는 데 재료 주문부터 제작, 소프트웨어와의 통합 등 고려해야 할 것이 많았다. 신발 종류를 구분하는 데 사용되는 인공지능 모델의 경우 주어진 시간 내에 많은 신발을 확보할 수 없었고, 관련된 기초 지식도 부족하여 정확도를 확보하는 데 어려움이 있었다. 처음에 생각했던 목표치에 달성하지는 못했지만, 남은 기간 동안 최대한의 결과를 내기 위하여 사용자가 판단하게 하는 대안을 적용하여 잘 대처한 것 같다. 처음 생각할 때부터 가능성에 대해 많은 고민을 해야 하고, 중간에 잘 안되는 부분이 있으면 현재 상황에서 최선의 방향을 계속 찾아나가면 프로젝트를 성공적으로 마칠 수 있다는 경험을 하게 되었다. 또한 팀원들과의 많은 소통이 프로젝트의 완성에 가장 중요하다는 것을 느꼈다. 팀원들 모두 프로젝트에 대해 많은 고민을 하고 열심히 참여해 주어서 고마웠다. 조교님들과 교수님께도 많은 도움을 주셔서 감사의 말을 드리고 싶다.
박*영
기계공학과에 들어온 후 진행한 프로젝트 중 가장 난이도가 높고, 또 그만큼 노력과 능력이 많이 필요했던 프로젝트였던 것 같다. 주제의 발안자로써 더 많은 책임감을 가지고 프로젝트의 방향성, 돌발적인 문제에 대한 대처 방안, 세부적인 설계 조정 등의 상황에 대처해야 했으며 하드웨어와 소프트웨어가 공존하는 임베디드 시스템을 완성도 높게 구현해야 했다.
기구부 제작을 메인으로 담당하였는데, 시중에 있는 재료들로 정확하게 동작하는 기구를 만드는 것에서 많은 현실적인 문제를 겪었으며, 학교에서 책으로 배우는 것과 달리 수많은 변수들이 존재하는 실제 설계에서 정밀하고 강건한 설계가 무엇보다 중요하다는 것을 배웠다.
또한 소프트웨어에서 OpenCV를 사용한 이미지 처리와 인공지능을 이용한 신발 종류 파악 알고리즘 구현에 참여하였다. 그 과정에서 처음으로 실제로 찍은 이미지에 대한 분석을 진행하며 낮은 해상도의 이미지를 분석하는 것에 대한 어려움도 겪었으며, 이에 대안을 고심하기도 하였다. 그 과정에서 상대적으로 약한 부분이었던 소프트웨어에 대한 문제해결 능력을 향상시켰다.
처음 컨설팅 때부터 고난이도의 주제임을 알았고, 프로젝트를 진행하면서도 여러번 프로젝트의 완성이 보이지 않을 정도의 좌절의 상황에 당착했다. 그러나 정말 우수한 팀원들과 함께 각자의 장점을 살려 문제들을 해결해 나갔고, 끊임없이 의견을 나누며 성공적으로 완성품 제작까지 달성하였다. 어려웠던 만큼 배운 것도 많았고, 큰 성장을 이룰 수 있어 임베디드 수업을 선택한 것에 대한 후회가 전혀 남지 않았다. 나의 부족한 부분을 채워준 팀원들에게 감사하며, 프로젝트가 성공할 수 있도록 열과 성을 다해 인도해준 교수님과 조교님들에게도 감사하며 이번 프로젝트에 대한 느낀점을 마치겠다.
최*
학교 수업에서 진행하는 프로젝트 중 제품 기획 부터 설계, 실제작까지 수행하는 프로젝트는 처음이었기 때문에, 한 학기 내에 임베디드 시스템을 적용한 제품을 만드는 것이 어려웠다. 또한, 본 프로젝트에서 제작한 기구부의 경우 단순한 구조가 아니었기 떄문에 설계에 시간이 많이 들었고, 또한 기어/ 체인 등의 우리가 실제로 다루어 본적이 별로 없는 기계 부품들을 직접 활용하는 부분이 생각과 많이 달랐기 때문에, 많은 시행 착오를 겪었었다. 그리고 이번 프로젝트에서 어플리케이션을 일부 담당하면서, 새로운 언어를 배우고, 파이어베이스와 연동하고 이에 대한 기능을 구현하는 것이 처음이었기 때문에 소프트웨어 측면에서도 어려움을 겪었었다. 이렇듯 힘든 부분도 많았던 프로젝트이지만, 새로운 경험을 많이 해보면서 설계에 대한 안목을 늘릴 수 있었고, 팀원들과 지속적으로 소통하는 능력을 기르고, 이에 따라 문제해결 능력도 기를 수 있었다. 다양한 요소들이 유기적으로 연결되어 있는 프로젝트였기 떄문에 이전까지 했었던 프로젝트와 달리 서로에 대한 의존성이 많이 큰 프로젝트였고, 따라서 제품 기획 및 생산 과정 내의 팀원과의 소통이 중요했기 때문에 MicroSoft Teams, Github와 같은 프로젝트 관리 툴을 사용하면서 협업 능력을 기를 수가 있었다. 또한, 해결하지 못할 것 같은 문제들도 이러한 협업 과정에서 아이디어를 얻어 해결한, 소중한 경험도 있었다. 이렇듯 힘든 만큼 얻어갈 것이 많은 수업이었다. 굉장히 어려운 프로젝트였지만 열심히 해준 팀원들에게 감사의 말을 남긴다.
허*
이번 한 학기 동안 아이디어에서 실제작까지 과정을 모두 마쳤다. 이번 학기 많은 시간을 이 프로젝트에 할애한 것 같다. 주제를 선정하는데 5주의 시간이 걸렸고 주제를 정하고 나서부터는 원하는 기능을 어떻게 구현할지 항상 고민했다. 그렇기 때문에 설계 과정에서 발생하는 문제에 대한 다양한 해결 방법이 나왔던 것 같다. 처음 계획한 설계안대로 프로젝트를 진행하다가 많은 문제를 직면했다. 그럴때마다 기존의 설계안을 최대한 유지하면서 약간의 변화를 주어 문제들을 해결했다. 그렇게 한 스텝 씩 원하는 구현에 가까워졌다. 비록 처음 계획했던 기능에 완전히 도달하지는 못했지만 주어진 시간 내에서 최선의 결과를 뽑을 수 있었다. 이번 프로젝트를 통해 처음 설정한 목표치에 도달하면 베스트이겠지만 주어진 시간 내에 최선의 결과를 얻어내는 것도 설계자가 갖추어야할 덕목임을 알았다. 이번 프로젝트에서 제어부와 어플과 라즈베리파이의 통신을 담당했다. 모두 처음해보는 일이였지만 어떻게든 마무리하겠다는 다짐으로 해결방법을 찾았다. 일이 안풀릴 때도 많았지만 날밤을 새어 하나 씩 진행해갈 때 뿌뜻함을 느꼈다. 최종 결과물을 위해 각자의 파트들을 융합하는 과정에서 협업의 중요성을 다시 한번 느꼈다. 전체적인 설계 과정을 거치며 많은 것을 얻어갔다. 이번 임베디드 과목은 설계과정의 전반적인 이해와 설계자에게 요구되는 자세를 알게 해준 과목이었다.