[63호]씀(SSM, Smart Spice machine)
2020 ICT 융합 프로젝트 공모전 장려상
씀(SSM, Smart Spice machine)
글 | 인하대학교 이은성, 강태욱, 남성훈, 이태민, 이홍찬, 정원욱
1. 심사평
칩센 재미있는 작품입니다. 여러가지 레시피를 DB로 등록하고, 사용자가 레시피를 따라 하면서 양념을 준비해야하는 시퀀스와 맞추어 동작하게 되면 매우 재미있을듯 합니다. 시제품이라 사이즈가 크고, 각 조미료 재료 마다 각각의 추출기가 있어야 한다는 것과, 추출기 내부 스크류에 잔류 조미료가 남아있게 된다는 점은 아쉬움이 남습니다. 추출기 내부에 무게를 측정하는 형태로 하여 정량만큼만 추출되는 형태로 하면 더 좋을듯 합니다.
펌테크 실생활에 활용될 수 있는 아이디어와 실용성을 지닌 작품이라고 생각합니다. 기획 의도에 맞게 시스템을 안정적이고 완성도 높게 구현하였고 제출된 보고서 구성 내용도 명확하고, 충실했다고 생각이 듭니다. 전체적으로 기획 의도, 기술 구현도, 완성도 등에서 우수한 작품으로 생각됩니다.
위드로봇 아이디어가 재미있습니다. 배출하고 남은 양념의 처리에 대한 고민이 더 추가되면 좋겠습니다.
2. 작품 개요
보통 요리를 자주 하지 않는 일반인이나 자취하는 학생들은 인터넷이나 책에 있는 음식 레시피를 참고해 요리한다. 요리를 어려워하는 사람들을 위해 요즘 레시피는 정확한 수치보다는 스푼이나 종이컵 등 생활에서 쉽게 구할 수 있는 도구로 계량을 한다. 하지만 이 또한 요리를 잘 하지 않는 사람들에게는 어렵게 느껴진다. 그리고 넣어야 하는 조미료가 많아질수록 도구도 많이 사용하게 되어 번거로움을 느낀다. 이런 사람들을 위해 ‘씀’은 조미료를 자동으로 계량하고 내보낸다. 사용자는 씀과 연동된 애플리케이션을 통해 계량 수치를 입력할 수 있고, 자주 사용하는 레시피는 저장하여 실행할 수도 있다.
본 제품은 애플리케이션을 통해 사용자가 원하는 값을 입력하면, 시스템 내에서 조미료를 분출시키는 동시에 계량을 하기 때문에 입력된 만큼만 분출하여 위생적이다. 또한 거치대와 조미료를 담은 통을 분리할 수 있게끔 설계하여, 조미료를 다 쓴 뒤에는 세척이 가능하다. 기존에 상용화된 조미료통보다 쉽게 사용자가 사용할 수 있게 하고, 위생적인 측면을 극대화하여 가정에서 씀을 사용함으로 좀 더 쉽게 요리를 할 수 있게끔 하였다.
더하여 가정에서뿐만 아니라 카페나 제과점, 프랜차이즈 음식 업계 등 정확한 계량이 필수인 분야에서 필요한 가루를 정확히 계량하는 데에 사용할 수 있어 다양한 분야에서 활용도가 높을 것으로 예상한다.
3. 작품 설명
씀(SSM)은 설계, 제어, 앱, 통신 이렇게 4가지 시스템으로 구성되어 있다.
3.1. 기계
가루나 파우더 형태의 조미료를 투입 후 애플리케이션으로 원하는 만큼의 양을 설정하면 자동으로 원하는 만큼의 양을 배출해준다.
조미료를 통의 상단에 투입하면 아래의 스크류 형태의 회전체가 회전하면서 작품의 앞쪽으로 배출해주며, 배출된 분체는 아래 접시에서 계량된다. 계량이 완료된 경우 스크류가 정지하여 분체 공급을 정지하게 되는 구조이다.
흔히 조미료로 사용하는 설탕, 소금의 경우 습기에 의해 분체간에 엉키거나 덩어리져 원활한 이동이 어려운 경우가 생긴다. 이때 그림의 A 와같이 분체를 섞어주며 덩어리진 분체를 분쇄해주는 역할을 해주는 회전 Paddle을 추가하였다. 또한 작품 내부에서 분체의 고착을 막아 B 로의 전달이 용이하도록 하는 역할도 함께 수행한다.
또한, 그림4과 같이 내부에 경사가 지도록 설계되어 주입된 모든 분체가 중력에 의해 화살표 방향으로 아래로 원활히 이동하도록 하였다.
음식에 사용되는 조미료를 이용하는 작품 특성상 위생에 대한 부분을 고려하여 분리 가능한 형태로 제작하였다. 조미료가 직접 닿는 부분은 본체와 분리하여 물로 세척이 가능하다. 모터가 사용되는 구동부와 분체를 내보내는 부분을 모듈식으로 해서 탈착이 가능하도록 하였는데, 육각형태의 어뎁터를 통해 동력 전달을 하고 있다.
3.2. 제어
아두이노를 통해 시스템을 실행시킨 후 통신이 연결되면 저울 위에 받침대를 놓고 영점 스위치를 누른다. 그 후 안드로이드 앱에서 원하는 무게를 입력하면 NodeMCU가 서버에서 사용자가 입력한 데이터를 가져오고 시리얼 통신을 통해 아두이노 보드로 값을 전달한다. 보드에서 값을 받으면 모터가 동작한다.
3.2.1. 모터
통의 내부에는 2개의 모터가 있다. A의 모터는 조미료가 습기로 인해 굳어지면 배출구를 통과하지 못하는 것을 방지하기 위하여 분쇄하는 역할을 담당한다. B의 모터는 나선형으로 된 스크류(screw)를 돌리면서 조미료가 적당량 나오게 한다. 조미료가 나오면서 무게를 측정하고 목표한 무게에 도달하면 모터가 멈춘다. 이 때 분출구와 저울 사이의 높이차로 인해 목표한 무게와 실제 무게에 차이가 있게 된다. 따라서 모터의 속도를 일정하게 하지 않고 실제 무게가 목표한 무게의 절반 이상이 되었을 때부터 모터의 속도를 감소시켜 오차를 줄이고 섬세하게 무게 조정을 할 수 있도록 하였다.
3.2.2. 스위치
또 서버에서 값을 보내자마자 바로 모터가 작동하여 조미료가 흩뿌려지는 것을 방지하기 위하여 물리적 스위치를 사용했다. 사용자가 준비가 끝난 후 스위치를 누르면 영점이 맞춰지고 모터가 작동된다.
3.3. 사용 부품
3.3.1. 로드셀
외력에 의해 비례적으로 변하는 탄성체에 휘스톤 브릿지 회로로 구성된 스트레인 게이지를 케이스에 내장시킨 센서이다. 힘이나 하중과 같은 물리량을 측정할 수 있다. 로드셀은 무게 값을 파운드 단위로 나타낸다. 상업용 전자거울 등 산업분야의 공장제어, 자동화 분야에 사용되고 있다. 로드셀이 무게를 측정하는 원리에는 로드셀의 표면에 붙어 있는 스트레인 게이지(Strain gauge)가 관여한다. 스트레인 게이지 내부에는 금속 호일이 지그재그로 설치되어 있고 힘을 받아 금속 호일의 길이가 변하면 저항 값이 변화하게 된다. 즉, 형태가 변화하는 만큼 전기신호의 값이 강해지고 이 값으로 무게를 측정할 수 있게 된다.
저항변화에 의한 작은 전압변화를 측정하는 방법으로 로드셀에서 휘트스톤 브리지 회로를 사용했다. 휘트스톤 브리지는 4개의 저항이 다이아몬드 모양으로 연결된다. 4개의 저항이 모두 같으면 중간의 측정단에서는 전압이 0으로 측정된다. 로드셀이 외부로부터 압력을 받으면 내부의 금속 호일이 길어지는 스트레인 게이지는 전류가 지나는 길이가 좁아지고 경로가 길어져 저항이 커진다. 반대로 수축하는 스트레인 게이지는 전류가 지나는 길이 넓어지고 경로는 짧아져 저항이 작아진다. 이렇게 저항이 변하게 되면 각 저항이 달라져 전압차가 발생한다.
3.3.2. HX711 앰프(증폭기)
로드셀에서 받아 들인 값을 아두이노에서 더 정밀하게 사용하기 위해 증폭장치인 hx711을 사용한다.
3.3.3. 모터드라이버(L9110)
하나의 보드에서 2개 이상의 기어 모터를 사용하기 위해 모터드라이버를 사용했다. 이 모듈은 pwm신호를 통해 5~12v에 해당하는 모터의 방향과 속도를 제어 할 수 있다.
3.4. 아두이노 보드 주요 알고리즘
3.4.1. 최초 calibration factor 설정
로드셀로 만든 저울은 만든 구조와 상황에 따라 Calibration Factor의 값이 달라진다. 때문에 저울을 제작할 때마다 Factor 값을 측정하는 코드를 최초 1회 실행해야 한다. 먼저 무게를 정확히 알고 있는 물체를 로드셀 위에 올려 놓고 그 무게를 아두이노에 입력해주면 자동으로 Calibration Factor(힘의 변화에 대한 출력 값의 변화 비율)를 설정해 출력해준다. 그 결과를 실제 무게 측정에 사용될 코드에 추가한다.
저울위에 에어팟(46g)을 올렸을 때 로드셀이 측정한 최초 무게는 41g이다. 이후 시리얼 창에 실제 무게인 46g을 입력하였다. 이후 Calibration Factor값이 1000씩 증감하며 측정 무게 42g을 실제 무게 46g에 맞게 조정한다. 42g으로부터 Calibration Factor값을 1000씩 감소시키며 실제 무게 46g에 자동으로 맞추었다.
3.4.2. 영점 조절과 무게 측정
앞의 Calibration Factor 설정 코드로 맞춘 Calibration Factor값을 적용하여 무게를 측정하였다. 분출되는 조미료의 양을 측정하기 위해서는 그릇을 올린 후 함수 scale.tare()를 이용하여 영점 조정을 해야 한다. 로드셀은 측정한 무게 값을 파운드 단위로 나타내기 때문에 453,492를 곱하여 우리에게 익숙한 g 단위로 변환하였다.
서버에서 목표 무게 값(Target)을 전송하면 NodeMCU를 통하여 값이 전달된다.
모터가 작동하기 시작하면 그릇에 분출된 조미료 양이 증가하는 것이 시리얼 모니터에 위와 같이 나타난다.
3.4.3. 모터 속도 제어
조미료가 떨어지면서 Weight 무게가 총 무게의 50%에 도달하기 전까지는 모터 속도는 최대 속도인 200으로 작동한다. Weight 무게가 Target무게의 50%이상이 되면 일정 간격으로 모터가 멈추었다가 다시 작동하도록 코딩을 하여 조미료가 계속 분출되는 것이 아니라 조금씩 분출되도록 하였다. 시연을 통해 분출된 조미료의 무게가 목표 무게에 도달한 순간에도 떨어지고 있는 잔여 조미료가 있어서 목표 무게보다 조미료가 조금 더 나오는 것을 확인했다. 이를 방지하여 목표 무게에 최대한 정확하게 맞추기 위하여 모터가 목표 무게에 가까워지면 분출되는 양을 적게하여 정확성을 높였다.
3.4.4. NodeMCU 주요 알고리즘
3.4.4.1. HTTP GET 요청
inoExectueData.php에 씀의 id 값을 GET 방식으로 HTTP 요청하여 사용자가 실행할 데이터를 받아 온다.
3.4.4.2. 데이터 받기
서버에서 혼동을 없애기 위해 보낼 값 앞에 ‘*’ 표시를 붙여서 보낸다. ‘*’가 들어오면 그 이후의 값을 읽는다. 그 후 아두이노 보드로 값을 전송한다.
서버로부터 20g의 값이 입력되면 NdeMCU시리얼창에는 c 변수로 2,0값을 받아 str로는 20이라는 값을 받는 것을 시리얼창을 통해 알 수 있다.
4. 애플리케이션
사용자는 자신이 자주 사용하는 레시피의 조미료 양을 저장하고 조미료통으로 실행할 수 있다. 혹은 레시피에 저장하지 않아도 실시간으로 필요한 조미료의 양을 애플리케이션을 통해 실행할 수 있다.
4.1. 애플리케이션 플로우 차트
4.2. 기능
1) 조미료 이름 저장 및 수정 : 애플리케이션을 처음 실행 시 조미료 이름을 저장하게 되고, 메인 레이아웃의 조미료 변경 버튼을 클릭해 변경할 수 있다. SharedPreferences를 사용하여 조미료의 이름을 서버가 아닌 내부적으로 저장 및 수정하게 하였다.
2) 커스텀 레시피의 저장, 수정 및 삭제 : 애플리케이션의 메인 레이아웃에서 우측 하단의 적색 플로팅 액션버튼을 클릭해 커스텀 레시피를 구성할 수 있게 하였다. 사용자가 입력한 값을 서버 데이터 베이스에 저장하여 수정 및 삭제할 수 있도록 하였다. 또한 메인 레이아웃에서 커스텀 레시피를 클릭하면 NodeMCU에게 값을 전송하여 조미료통을 실행한다.
3) 값을 입력해 바로 실행 : 커스텀 레시피를 저장해서 실행해야 하는 것이 아닌, 메인 레이아웃의 바로실행 버튼을 클릭해 조미료의 값만 입력하도록 구성하였고, 그 데이터를 NodeMCU로 바로 전송할 수 있도록 하였다.
4) 도움말 : 사용자가 사용법을 모를 경우 클릭하여 애플리케이션에 대한 전반적인 도움을 줄 수 있도록 실행 사진과 글을 넣어 설명하였다.
5) 애플리케이션 설명
① 처음 애플리케이션의 화면사용자가 사용할 조미료의 이름을 정한다.
②애플리케이션의 메인 레이아웃
숫자에 해당하는 버튼을 클릭 시 다음과 같은 이벤트를 취한다.
1) 앱 종료 (알림 창 팝업)
2) 도움말 액티비티 띄움
3) 레시피를 추가
4) 처음에 설정한 조미료를 변경
5) 조미료 값을 입력해 저장 없이 바로 실행한다. 또한, 휴대폰 자체의 취소버튼 두 번을 연속하면 종료된다.
③ 3번 버튼을 클릭해 동작한 커스텀 레시피 저장 레이아웃. 레시피의 이름, 조미료의 양을 입력 후 확인을 눌러 저장한다.
④ 새로운 커스텀 레시피가 생성된다. 추가된 레시피를 짧게 클릭할 시 선택된 레시피의 값을 서버로 전송한다. 레시피를 길게 클릭할 시 수정 및 삭제를 선택하는 알림창이나 팝업창을 띄운다.
4.3. Android studio의 구성 Activity와 Layout
4.4. 세부 class 설명
4.4.1. Data
· 데이터베이스에 사용하는 데이터들을 관리하는 Class이다.
· 레시피를 구분하는 int형 변수 id, 레시피의 이름을 저장하는 String형 변수 Title, 레시피의 조미료 1, 2의 이름을 저장하는 String형 변수 condiment0, 1, 레시피의 조미료 1, 2의 양을 저장하는 int형 변수 gram0, 1의 생성자와 Getter와 Setter로 이루어져 있다.
4.4.2. GetData
· Http 통신을 통해 서버에 POST 방식으로 데이터를 JSON 형식으로 수신한다.
· AsyncTask를 사용하여 서버 연결에 분리된 스레드를 사용하였다.
4.4.3. InsertData
· Http 통신을 통해 서버에 POST 방식으로 데이터를 송신한다.
· AsyncTask를 사용하여 서버 연결에 분리된 스레드를 사용하였다.
4.4.4. listAdapter
· 데이터와 list_card.xml를 ListView와 연결하기 위해 사용하는 다Class이다.
4.4.5. Preference Manager
· 조미료의 이름을 서버에 저장하는 DB가 아닌 애플리케이션이 설치된 기기 안에서 저장하기 위해 사용한 Class이다.
4.5. 세부Activity 설명
4.5.1. InitialActivity
· 조미료의 이름을 설정하는 Activity, 확인버튼을 클릭하면 PerFerenceManeger Class를 통해 기기 내부에 조미료 이름을 저장한다. 도움말 버튼을 클릭하면 QuestionActivity를 실행한다.
4.5.2. MainActivity
· 이 프로젝트의 애플리케이션에서 기본화면을 담당하고 있는 Activity이다.
· 우측 하단의 적색 플로팅 액션버튼을 클릭하면 AddActivity 실행하고, 우측 상단의 ? 버튼을 클릭하면 QuestionActivity가 실행된다.
· 조미료변경 버튼을 클릭하면 PreferenceManager에서 저장했던 파일을 지우고 InitalActivity을 실행한다.
· 바로실행 버튼을 클릭하면 PlayActivity 실행한다.
· 좌측상단의 X 버튼을 클릭하면 애플리케이션의 종료여부를 묻는 알림창을 실행한다.
· 커스텀 레시피를 추가하여 서버에 데이터베이스가 생성되었다면 그림과 같이 ListView에 데이터가 생긴다.
· 데이터를 짧게 클릭하게 되면 실행여부를 묻는 알림창 생성되며, 데이터를 길게 클릭하게 되면 수정 및 삭제 여부를 묻는 알림창이 생성된다.
· 기기 자체의 뒤로 가기 버튼이 두 번 연속 입력되면 앱이 종료된다.
4.5.3. AddActivity
· 커스텀 레시피를 추가하는 기능을 가진 Activity. 제목과 그램수를 입력하고 확인 버튼을 클릭하게 되면 변수 title, gram0, gram1에 EditText의 값을 불러오고 InsertData 클래스를 통해 데이터베이스에 저장한다. 좌측상단의 X버튼을 누르면 Activity가 닫힌다.
4.5.4. PlayActivity
· 바로 값을 입력하여 PHP를 통해 데이터를 전송시키는 Activity. EditText에 입력된 값을 불러오고 InsertData 클래스를 통해 데이터 전송한다. 좌측상단의 X버튼을 누르면 Activity가 닫힌다.
4.5.5. QuestionActivity
· 사용자에게 도움을 주기위한 Activity. 좌측상단의 X버튼을 누르면 Activity가 닫힌다.
5. 통신
서버와 2개의 클라이언트(애플리케이션, 아두이노)가 통신한다. 서버에는 사용자가 저장한 레시피가 데이터베이스에 저장되어 있다. DB에는 Recipe와 Ino 테이블이 있다.
5.1. recipe 테이블
사용자가 설정한 레시피에 대한 정보가 담겨 있다.
5.1.1. 구조
5.1.2. 필드 설명
5.2. ino 테이블 구조
아두이노에서 실행해야 하는 정보가 담겨 있다.
5.2.1. 구조
5.2.2. 필드 설명
5.2.3. PHP 설명
6. 개발 환경
설계 : NX 10.0, 제어 : Arduino, 애플리케이션 : Android Studio, 통신 : MySQL, Bitnami
7. 단계별 제작 과정
7.1. 작품 설계
분체형태의 조미료를 정확한 양으로 일정하게 배출하는 방법을 고안해 내기까지 설계에 있어 시행착오가 있었다.
설계 초기에는 그림과 같이 중력으로 인해 떨어지는 분체를 여닫는 장치를 사용하여 정량을 배출해내는 방식을 고안했었으나, 분체간의 마찰로 인해 낙하하는 분체의 양이 저장되어 있는 분체의 무게와 분체간의 마찰에 영향을 받아 시간에 따라 일정하지 않게 될 염려가 있었다. 또한, 분체의 이동 중에 즉각적인 차단이 어렵고, 작동 중에 분체가 구동부에 끼어 정확한 동작에 방해가 될수 있다고 판단하였다.
이를 보완하기 위해 최종적으로, 그림과 같이 스크류를 통해 분체를 지면에 수직방향이 아닌 평행한 방향으로 배출하는 방법을 고안하였다. 스크류의 회전에 따라 일정한 양의 분체가 공급되며, 회전이 멈추면 차단되도록 회전수를 조정하였다. 스크류의 경우 모터의 속도와 회전당 공급할수 있는 분체량을 고려하여 회전수를 조정하였다.
7.2. 소재 및 가공방법 선정
소재 선정에 가공 편의성과 정확성, 가공비용 등을 고려했을 때 3D프린터를 통해 가공하는 것이 가장 적절하다고 생각하였다. 특히, 스크류 형태의 부품을 제작함에 있어 절삭등의 방법을 사용하기에는 비용면에서 감당이 불가능하기에 이에 중점을 두고 고려하였다. 필라멘트는 작품이 식품에 사용되는 조미료를 사용함에 따라 안정성 확보를 위해 PLA 필라멘트를 사용하였다.
스크류의 경우 수직으로 길게 가공하는 도중에 3D프린터의 적층 오류로 인해 오차가 발생하였고, 이것이 회전에 있어서 간섭을 발생 시켰다. 이를 선반을 이용해 재가공하여 발생한 오차로 인한 편심을 바로잡도록 하였다.
모든 가공 이후 부품들을 조림하고 테스트를 하였을 때, 굵은 결정체인 설탕이나 소금의 경우 스크류와 본체 벽 간의 공간에 끼어 스크류의 회전이 멈추는 경우가 있었다. 처음 설계한 스크류는 회전시 스크류의 옆면과 벽이 면으로 밀착하게 하여 분체가 흘러들어가는 경우를 막고자 하였으나, 공차가 존재함에 따라 분체가 벽 사이에 끼어드는 경우가 생길수 밖에 없었다. 또한 마찰면적이 넓음에 따라 선정했던 모터의 출력만으로는 정상적인 작동이 어려워지는 문제가 발생하였다. 따라서 스크류의 옆면을 선 형태로 설계하고, 스크류 내부에 가진부분에 부드러운 형상을 가지도록 처리하여 분체를 더 잘 내보낼수 있도록 설계하였다.
초기에 설계한 프레임과 달리 크기가 비교적 크고, 가공 중에 정밀성 요구의 정도가 비교적 적다고 판단하여 프레임은 제작 편의상 5T 포맥스를 사용하여 제작하기로 결정하였고, 위와 같이 설계를 수정 후 제작하였다.
8. 회로도
[63호]초 광 시야각 스마트 안경(Super Wide Field Of View Smart Glasses)
2020 ICT 융합 프로젝트 공모전 장려상
초 광 시야각 스마트 안경(Super Wide Field Of View Smart Glasses)
글 | 이문기
1. 심사평
칩센 제작 목표가 쉽게 평가를 하기 어려운 부분으로 생각됩니다. 현재 시중에 출시된 솔루션에 대한 기본적인 약점에 대한 분석이 분명히 이루어진듯 하지만, 그 약점의 극복을 위하여 제시한 방안이 근본적인 해결책이 될지에 대하여는 의문점이 듭니다. 다만 하드웨어 설계의 고도화가 이루어지고 실제 안경에 적용될 경우 흥미로운 아이템임에는 분명해 보입니다.
펌테크 스마트 안경의 현재 단점을 보완한 아이디어, 창의성이 돋보이며 실생활과도 밀접한 작품이라고 생각합니다. 현재 구성된 하드웨어 구성을 가볍고, 컴팩트화 시킨다면 상업적인 가치를 갖기에 손색이 없는 작품이라고 생각됩니다.
위드로봇 해상도 높은 LCD로 추가 작업을 진행하면 더 좋은 결과가 있을 것으로 기대됩니다.
2. 작품 개요
초 광 시야각 스마트 안경(super wide field of view smart glasses)은 기존의 스마트 안경의 좁은 시야각의 불편함을 개선한 스마트 안경 이다.
3. 작품 설명
3.1. 개발동기
정보가 폭증함에 따라 빠르고 효율적인 정보처리를 위해 스마트폰은 스마트 글래스로 진화할 것이다.
스마트폰은 항상 사용하는 장치이므로 스마트 글래스가 스마트폰을 대체하려면 스마트 글래스를 항상 착용할 수 있어야 한다. 또한 항상 착용하려면 가볍고 부피가 작아야 한다. (가볍고 부피가 작으려면 광학계가 간단해야 한다.)
가상현실용 HMD와 증강현실용 스마트 안경은 하나로 융합될 것이다. 사용자가 가상현실용 HMD와 증강현실용 스마트 안경을 각각 구매하는 것은 부담스러울 것이다. 또한 스마트 안경이 가상현실도 구현하려면 시야각이 넓어야 하고 해상도가 높아야 한다.
3.2. 필요성
항상 착용할 수 있는 고해상도 광시야각 스마트 안경은 없을까? 가상현실용 HMD는 광시야각이고 고해상도이지만 부피가 크고 무거워서 항상 착용하기엔 부담스럽다.
기존 스마트 안경은 일상생활을 하면서 항상 착용할 수 있지만 시야각이 작고 해상도가 낮다.
3.3. 주요 동작 및 특징
어떻게 광시야각 스마트 안경을 구현할 수 있는가?
눈 바로 앞에 각 픽셀마다 그 픽셀의 광원의 색의 보색 필터가 눈을 향하는 면에 부착된 투명 디스플레이와 오목한 반거울을 설치한다.
보색 칼라 필터는 투명 디스플레이의 칼라 픽셀의 빛을 선택적으로 차단해서 눈부심을 방지한다.
파랑색 픽셀과 눈 사이에는 파란색 차단필터(노랑색 필터)가 설치된다.
빨강색 픽셀과 눈 사이에는 빨강색 차단 필터(시안색 필터)가 설치된다.
녹색 픽셀과 눈 사이에는 녹색 차단 필터(마젠타색 필터)가 설치된다. 이러한 보색 필터는 각 픽셀에서 방출된 빛 중에서 직접 눈을 향하는 빛은 차단하고 오목 반거울에 반사된 빛만 통과시킨다.
3.3.1. 시제품 외관 및 특성
· 투명 디스플레이가 커서 해상도가 크다
· 투명 디스플레이가 눈 바로 앞에 있어서 시야각이 매우 넓다.
· 광학 모듈이 간단하고 가볍다.
· 기존 안경과 유사한 디자인의 설계가 유리하다.
3.4. 전체 시스템 구성
3.4.1. 시제품 외관 및 구성
3.4.2. 시제품 구성요소
3.5. 개발 환경(개발 언어, Tool, 사용 시스템 등)
아두이노 나노를 안경 다리에 부착하고 프로그램을 아두이노 나노에 아두이노 IDE에서 업로드하여 Spi 통신으로 좌우측 눈 앞의 투명 Oled를 구동한다.
3.5.1. 사용 부품
· 128×56 Transparent OLED Display
· Fpc Ffc 케이블 커넥터 24 핀 0.5mm 어댑터 24 핀
· 12V Dc To Dc Voltage Regulator
· 아두이노 나노 호환보드 FT232RL
4. 단계별 제작 과정
안경 렌즈를 제거한 안경테에 투명 Oled를 부착한다.
아두이노 나노와 투명 Oled인터페이스 회로를 제작한다.
제작한 인터페이스 회로를 안경 다리에 부착한다.
투명한 빨강 Pvc 필름을 레이저로 재단해서 칼라필터를 제작한다.
칼라필터를 투명 Oled의 눈을 향하는 면에 부착한다.
회로들 사이의 배선을 한다.
아두이노 나노에 투명 Oled구동 프로그램을 업로드해 투명 Oled에 테스트 영상을 출력한다.
안경을 착용해서 테스트 영상이 보이는지 확인한다.
아래 사진은 스마트 안경을 착용하기 직전과 착용 했을 때 보이는 영상 (스마트 안경에 출력된 수직선과 수평 점선 너머로 스마트폰이 보인다.)
5. 기타
인터페이스회로 외관
아두이노 나노와 인터페이스회로
하나의 아두이노의 Chip Sel R과 Chip Sel L 핀으로 좌우측 투명 Oled를 번갈아 선택해 Spi 통신으로 각각 구동한다.
회로에 각종 주변 장치와 센서를 추가하고 아두아노 나노 구동 프로그램에 자신만의 코드를 추가하여 스마트 안경을 구동할 수 있다.
투명 Oled를 직접 제작해 고해상도 영상도 출력할 수 있다.
아두이노를 더 고성능 칩으로 대체해 스마트폰과 데스크탑 수준의 컴퓨터 기능도 구현 할 수 있다.
[63호]이룽이의 방역세트
2020 ICT 융합 프로젝트 공모전 장려상
이룽이의 방역세트
글 | 한양대학교 (ERICA) 김민서, 김동연, 정우진, 이성준, 이도걸, 김경현
1. 심사평
칩센 특정한 주제에 대하여 지원자(팀)께서 속한 로봇 학술 동아리에 걸맞는 재미있는 작품을 만들어 낸듯 합니다. 어쩌면 누구나 생각할 수 있는 것들로 기능이 구성되었습니다만, 최종 작품의 형태나 외관을 보았을 때 지금 속해 있는 학교나 학과에서는 유일무이한 관심과 흥미를 유발할 수 있을 것으로 보입니다. 적용 목표가 정해진 작품(또는 제품)은 스스로의 가치보다 훨씬 좋은 면이 부각되기 마련입니다. 구성기능 중 소독을 위해 구현한 분무 기능의 경우 상부의 한방향보다는 옆면으로의 분출도 이루어졌더라면 하는 아쉬움이 있습니다.
펌테크 실생활과 밀접한 아이디어와 실용성이 우수한 작품이라고 생각합니다. 기획 의도에 맞게 전체 시스템을 안정적이고 완성도 높게 구현되었다고 판단되며 전체적으로 기획의도, 기술 구현도, 완성도 등에서 우수한 작품으로 생각됩니다.
위드로봇 손 소독제를 움직이면서 살포한다는 아이디어는 참신합니다.
2. 작품 개요
2.1. 개발배경 및 필요성
처음 아이디어를 기획할 때 수동 조작이 가능한 쓰레기통을 만들고자 했다. 그러나 코로나19 사태가 일어나고, 사회적으로 큰 문제가 되자 이것을 예방할 수 있는 방법을 의논하다 실내 방역을 할 수 있는 것을 만들기로 하였다.
실제로 매드아카이브에 공개된 바이러스 titer 수치 통계(그림.1)에 따르면 HCoV-19 즉, 코로나 바이러스는 SARS-Cov-1, 우리가 흔히 알고 있는 사스 바이러스와 유사한 생존 시간을 보여준다.
코로나 바이러스나 사스 바이러스 모두 Aerosols, 공기 중에선 바이러스의 생존시간과 비례하는 titer의 수치가 몇 시간 만에 줄어드는 것을 볼 수 있다. 그러나 두 바이러스는 모두 주변에서 볼 수 있는 물체들인 철, 플라스틱에서 몇 시간을 넘어 하루 정도가 지나야 급격히 줄어드는 것을 알 수 있다. 즉, 실내 주변에서 흔히 만질 수 있는 물체에선 바이러스가 장시간 머물기 때문에 실내 방역이 필요하다.
과학 저널 사이언스에 Emory 대학교의 환경 보건 과학자인 Juan Leon 교수에 따르면 코로나 바이러스는 대부분의 바이러스와 마찬가지로 살균제를 사용했을 때 비활성화 된다고 말했으나 교수의 실험실에 있는 Julia Silva Sobolik 박사는 2019년 10월 JAMANetworkOpen에서 정기적으로 살균제를 사용하여 청소한 간호사들이 만성 폐기 병에 걸릴 위험이 더 높다는 사실을 발견했다고 한다.
위 자료들을 통해 이룽이는 지속적인 방역은 좋지 못하므로 적절한 시간에 한 번씩 실내 방역을 하고, 우리가 물체를 만질 때 주로 사용하는 손에 코로나 바이러스가 머물러 접촉될 수 있다는 것을 고려하여 손 소독을 할 수 있는 것을 만들고자 했다. 또한 우리가 만들고자 하는 것이 실내 방역에 실제로 도움이 되어 코로나를 예방할 수 있기를 바라는 마음으로 이룽이의 방역세트를 완성시키는 것이 이룽이의 목표이다.
3. 작품 설명
3.1. 주요 동작 및 특징
3.1.1. 동작 설명 : 소프트웨어 구현
■ 학사모 위로 소독약 분사
가습기 모듈을 이용하여 학사모에서 일정시간에 한 번씩 소독약을 분사하여 실내 방역을 한다. : 가습기 모듈은 소독약이 분사되는 것을 표현하기 위해 사용
■ 사람 손에 손 소독제 분사
센서가 사람의 손을 감지하면 손 소독제를 한 번 분사하고, 다시 손을 뗐다가 센서에 갖다대면 다시 손 소독제가 한 번 분사된다. : 액체형태의 손 소독제 사용
■ 손 소독제 부족 시 표시
잔량이 부족하다는 것을 IR센서가 감지하면 이룽이 얼굴 중 볼 부분에 연결된 LED가 빛나도록 하였다.
소독제를 다시 채우면 LED가 자동으로 꺼진다. : 청각은 실내에서 소음이 될 수 있다고 판단하여 시각적으로 알아볼 수 있게 함
■ 검은색 라인을 따라 이룽이의 방역세트 작동
블루투스 연결 후 APP Inventor로 START를 누르면 로봇이 라인을 따라 움직이기 시작한다.
STOP를 누르면 DC 모터가 동작을 멈춘다.
START 상태에서 라인을 따라 움직이다 초음파 센서가 사람을 감지하면 멈추고, 사람이 센서의 감지 범위에서 멀어지면 다시 라인을 따라 움직인다. : 라인을 따라 움직이게 한 이유: 선을 만들어 그 선에서 벗어나지 않고 원하는 부분에서만 움직일 수 있게 하였고, 반복적인 움직임을 통하여 전 방향 방역이 가능하다고 판단
■ PID를 이용한 모터제어
이룽이의 방역세트(모바일 로봇)이 라인을 따라서 움직이고, 원하는 값을 입력하여 움직이게 하기 위하여 PID제어를 사용하였다. : PID제어 뿐만아니라 Interrupt와 Timer5도 같이 사용
3.1.2. 순서도
3.2. 전체 시스템 구성
3.2.1. 전체 구성
3.2.2. 파트별 구성
■ 학사모 파트
가습기 센서를 통해 물 나오는 곳으로 뚜껑 부분과 몸통 부분으로 나누어 분리될 수 있도록 제작하였다. 학사모가 밑의 기둥과 분리될 수 있게 제작하여 통 안에 액체가 얼마나 남았는지 확인 가능하다.
코튼 필터 고정하는 것으로 나사를 풀면 코튼 필터 교체 가능하다. 가습기를 작동시키기 위하여 코튼 필터를 사용한다.
시계 방향 회전 시 분사통이 고정되며, 반시계 방향 회전 시 분사통이 분리된다. 분사 통 안에 물이 부족하면 다시 채워 넣을 수 있도록 제작되었다.
■ 바디 파트
판끼리 고정하기 위해 3D프린터를 이용하여 너트와 볼트가 고정될 수 있도록 연결 부품을 솔리드웍스로 설계하여 육각기둥 형태의 바디를 만들었다.
1. ~ 4. 옆판
5. 윗판 : 학사모가 들어가는 부분을 50mmX50mm으로 하고, 선을 밑으로 뺄 수 있는 부분을 25mmX10mm로 하였다.
6. 밑판 : 육각기둥과 밑의 판을 서포터로 고정을 할 수 있게 판에 구멍을 3.4mm로 가공하였으며, 모터와 관련된 선들을 육각기둥으로 넣기 위해 50mmX50mm로 선이 지나다닐 수 있는 공간을 만들었다.
■ 손 소속제 파트
1. 2. 분사통을 분리할 수 있게 제작하여 손 소독제를 쉽게 충전할 수 있다.
3. 잔량을 측정해주는 IR 센서를 고정할 수 있도록 한다.
4. 손 소독제를 분사할 때 서보모터가 고정 되어야 하기 때문에 고정할 수 있는 것을 제작한다.
■ 하냥이 파트
1. 손 소독제 교체 시 편리하게 자석으로 육각기둥에서 얼굴이 탈 부착될 수 있게 한다.
2. 손 소독제 부족 시 시각적으로 알리기 위해 볼 쪽에 구멍을 뚫어 LED가 들어갈 수 있게 제작한다.
3. 입을 통해 손 소독제가 나온다.
■ 하부 바디 파트
1. Caster Wheel M3볼트로 고정가능하다.
2. 50mm 서포터 연결을 위한 구멍 가공한다.
3. IR 센서 고정 가능하도록 제작한다.
4. DC 모터 연결하기 위해 3.4mm로 구멍 가공한다.
3.3. 개발환경
3.3.1. 아두이노 프로그램 IDE
■ Arduino 1.8.9
대학교 2학년의 관점에서 센서, 모듈 및 모터를 제어하기 위해 이룽이의 지식 기반인 Arduino를 활용하였다.
APP Inventor와 DC Motor의 통신으로 이동하는 이룽이의 방역세트를 제작 하였고, 이 과정에서 IR Sensor와 DC Motor를 이용하여 Line Tracing을 하였다.
Aduino로 Humidifier module과 Arduino Relay Module을 제어하여 일정 시간에 한 번씩 자동 방역을 할 수 있고, IR Sensor와 Ultrasonic Sensor를 이용 하여 손 인식 및 손 소독제를 분사 하는 방역세트를 만들게 되었다.
모터를 제어하는 과정에서 Arduino Mega Board가 정상 작동함에도 업로드가 되지 않아 아두이노 1.8.10 버전에서 1.8.9 버전으로 낮추어 사용하였더니 정상적으로 업로드 되는 것을 확인하고, 1.8.9 버전에서 소스코드를 작성하였다.
3.3.2. 하드웨어 설계 툴
■ SolidWorks 2018
이룽이의 방역 세트의 하드웨어 제작을 위해 SoildWorks를 전반적으로 사용 했다.
SoildWorks를 통해 이룽이 방역세트의 메인 하드웨어인 얼굴, 육각모양의 바디의 몸체와 학사모를 제작하였고, 내부 센서, 모듈과 모터를 고정하기 위한 부품들도 SoildWorks를 통해 만들었다.
각각의 파트들을 어셈블리를 하여 서로 간섭 없는 견고한 하드웨어를 제작할 수 있었다.
3.3.3. 회로 설계 툴
■ OrCAD
이룽이 방역 세트의 내부 회로 중 Humidifier module과 Relay Module을 활용한 자동 방역을 제어하는 과정에서 회로적인 이해와 원리를 파악하기 위해 OrCAD를 사용했다. OrCAD를 통해 Relay Module의 내부 구조를 파악하고, 핀 연결에 따른 Humidifier module 제어의 원리를 알맞게 파악할 수 있었다.
3.3.4. 실험 환경
이룽이의 방역세트에 사용된 DC모터가 107 rpm(0.38 m/s)출력되기 때문에 바닥이 일정하지 않은 곳보다는 평평한 바닥이 있는 곳에서 사용 해야한다. Line Tracing을 하기 위해 검은색 전기 테이프가 필요하다. 손 소독제 기능은 이룽이의 방역 세트 자체의 높이가 낮아 사용자가 자세를 낮추어 사용해야 한다.
4. 단계별 제작과정
4.1. 시행착오 – SW
4.1.1. 앱인벤터 모터 제어
앱인벤터로 모터를 제어하고, 라인트레이싱을 하려면 블루투스 셋팅, 블루투스와 앱인벤터 통신확인, 모터와 엔코더 고장여부 확인하고 블루투스 값과 IR Sensor 값에 따라 모터가 잘 움직이는지 확인해야한다.
블루투스 셋팅은 블루투스에 맞는 보드레이트를 맞춰주고 이름과 비밀번호를 설정한다.
블루투스 통신은 Serial.begin(9600);에서 9600과 같이 블루투스에 맞는 보드레이트를 사용해야 한다. 여기서 이룽이는 보드레이트 115200을 사용한다.
모터와 엔코더 고장 여부를 확인하는 과정에서 한 쪽 엔코더의 값을 받아오지 못하고 계속 0으로 출력되는 것을 볼 수 있었다. 교체를 해보아도 똑같은 현상이 발생하여 점프선을 교체하였더니 정상적으로 값이 출력되는 것을 확인할 수 있었다.
IR Sensor를 총 2개 사용하여 라인을 인식하도록 하였으나 두 센서 모두 흰색일 경우 100보다 작거나 같은 값을, 검은 색의 경우 900보다 작거나 같은 값을 출력하였다. IR 센서의 기준값을 정하고, 블루투스와 센서 값에 따라 모터가 잘 작동하는지 확인해 보았는데 Motor2(오른쪽)의 속도제어가 되지않고 계속해서 최고 속도를 출력하여 바퀴가 빠르게 계속 회전하는 것을 확인할 수 있었다.
원인을 찾아보니 PID의 목표 값은 양수지만 Motor2(오른쪽)의 값이 계속 음수로 커짐에 따라 ERROR값 또한 계속 커져 최고 속도인 255로 출력되어 바퀴가 계속 굴러갔던 것이다.
해결방법은 두 가지가 있다. 1) go 함수 안에서 ref_speedB = -spdB;에서 (-)를 삭제하여 spdB로 하는 것 2) controlspeedB함수 코드에서 에러 값에 절댓값을 붙이는 것이다. 결과적으로 (-)를 지우는 방법을 선택하였다.
모든 것이 해결된 후 PID 값을 계속해서 변경하며 모터에 적합한 값을 찾았다. 최종적으로 왼쪽 P값 20, I값 0.1, D값 4, 오른쪽 P값 12, I값 0.1, D값 4로 하였다.
float mA_gain_speed[] = {20, 0.1, 4};
float mB_gain_speed[] = {12, 0.1, 4};
마지막으로 Motor1(왼쪽)의 P값이 갑자기 튕기는 일이 발생 하였다. 이 문제는 블루투스와 앱인벤터가 통신하기 전에 바퀴가 이상한 값을 받아 굴러가는 것 이였다. 그래서 코드 상으로 loop()안에서 char input;를 static char input = ”;로 수정하여 통신이 되기 전에 쓰레기 값을 받아 통신 신호처럼 보내주는 값을 0으로 만들어 모터가 굴러가지 않게 하여 해결했다.
4.1.2. 사람 손 인식 및 손 소독제 분사
사람의 손을 감지하여 손 소독제를 분사하는 기능에서 초음파 센서가 사람의 손을 감지하여야 하는데 왼쪽 그래프에서 보이듯이 중간에 이상한 값들이 출력되어 서보모터가 비정상적으로 작동하는 현상이 발생했다. 이 문제를 해결하기 위해 코드 상에서 데이터를 필터 처리할 수 있도록 해주었다.
4.1.3. 잔량 부족 표시
손 소독제가 부족하지 않음에도 구하고 LED가 켜져 이를 감지하는 IR 센서가 인식하는 값을 시리얼 모니터로 확인한 후 물을 감지하는 기준값을 500에서 200으로 수정했다. 검은색은 500보다 큰 값이면 LED가 꺼질 수 있도록 했다.
4.2. 시행착오 : HW
5. 기타
5.1. 앱인벤터 디자인 및 블록코딩
BluetithList
이름 그대로 블루투스 연결 가능한 목록들을 보여준다.
연결필요!
블루투스와 앱인벤터가 연결이 되어 있지 않을 때에 나타나는 문구이다.
방역시작
방역시작을 누르면 모터가 라인을 따라 움직이기 시작한다.
방역 끝
라인을 따라 움직이던 모터가 정지한다.
블루투스 리스트에서 셋팅 했던 블루투스를 찾아 연결하면 하늘색으로 연결 완료! 라고 핸드폰 화면에 나타난다. 만약 방역시작을 누르면 앱인벤터에서 ‘1’을 보내고, 라인을 따라 모터가 움직이게 되고, 방역 끝을 누르면 앱인벤터에서 ‘0’을 보내어 모터가 정지하게 된다.
5.2. 회로도 및 이론
가습기 모듈을 통해 실내 방역을 구현해내는 과정에서 가습기 모듈은 일정 시간의 텀을 가지고 작동을 해야 한다. 이를 위해 이룽이는 릴레이 모듈을 가지고 구현이 가능한지 파악해보았다.
실제로 릴레이 모듈을 사용하기 전, 릴레이 모듈의 내부 구조와 원리를 파악하기 위해 릴레이 모듈의 내부 회로도를 찾고 Orcad를 통해 릴레이 모듈을 활용한 아두이노 기반 회로를 작성했다.
릴레이 내부에는 전자석 즉, 코일이 있어 전류가 통하게 되면 자석이 된다. 이를 활용하여 전원을 공급하게 되면 릴레이 내부에 전자석이 자석이 되어 옆에 있던 철판을 끌어당겨 스위칭 동작을 해준다. 릴레이의 COM 핀은 공통 단자로 릴레이가 동작했을 땐 NO 핀으로 동작하지 않았을 땐 NC 핀으로 전류가 흐르게 된다. SIG는 제어 신호 핀으로 아두이노 핀으로 할당이 된다. 릴레이 모듈을 활용하여 해당 제어 신호 핀을 통해 가습기 모듈이 신호로 제어가 된다.
이를 바탕으로 thinker cad를 통해 가상 회로를 구상 후 시뮬레이션으로 릴레이를 활용하여 일정 시간의 텀을 주는 작동이 가능하다는 것을 확인할 수 있었다. 시뮬레이션을 토대로 실제 회로를 구현해본 결과 가습기 모듈이 일정 시간의 텀을 가지고 작동을 하였다.
5.3. 하드웨어 디자인
5.3.1. 레이저 컷팅
CNC 레이저 커터로 재단중인 아크릴 판 : 아크릴판은 포맥스 등 다른 재료에 비해 단단하기 때문에 칼을 이용한 재단이 어렵다. 또한 아크릴판은 정육각기둥을 구성하는 부분이므로 정밀한 재단이 필요하다. 따라서 CNC 레이저 커터를 이용하여 재단을 진행하였다.
5.3.2. 3D 프린터
출력 중인 3D 프린터 : 크기가 작고, 정밀하게 제작되어야 하는 부분이나 곡선이 들어가는 부분은 3D 프린팅을 통해 제작하였다. 따라서 아크릴판을 연결하는 부분과 센서 및 모터를 장착하는 부분, 그리고 곡선이 들어가는 학사모와 하냥이의 손 부분은 3D 프린팅으로 제작하였다. 3D 프린팅의 재료로는 PLA 필라멘트를 사용하였는데, PLA 필라멘트의 경우 ABS 필라멘트보다 비교적 수축이 적기 때문에 정확한 치수를 맞추기에 유리하다.
5.3.3. 도색
도색 후 건조 중인 아크릴판과 출력물 : 학사모를 제작한 3D 프린터의 필라멘트가 흰색이었기 때문에 검은색인 학사모를 표현하기 위해 무광 검은색으로 도색을 진행하였다. 동시에 몸통 부분의 육각기둥을 구성하는 아크릴판도 무광 검은색으로 도색하여 외관을 더욱 깔끔하게 하였다. 3D 프린터로 출력한 학사모의 경우 글라인더, 폴리퍼티, 서페이서를 이용하여 표면처리를 마친 뒤 도색을 진행하였다.
5.4. 사용부품
5.5. 참고문헌 및 출처
· 만들게 된 계기 목적에서의 통계 자료 : https://www.medrxiv.org/content/10.1101/2020.03.09.20033217v2.full.pdf+html
· https://www.sciencemag.org/news/2020/03/does-disinfecting-surfaces-really-prevent-spread-coronavirus
· 하냥이 규정을 맞추어 이룽이의 얼굴을 제작하였고, 대외협력팀의 허가 받고 사용
· 알고리즘 순서도 작성함. https://app.diagrams.net/
· 회로 참고 자료 : http://scipia.co.kr/blog/242, http://makeshare.org/bbs/board.php?bo_table=Parts&wr_id=9
· http://scipia.co.kr/blog/242,
· http://makeshare.org/bbs/board.php?bo_table=Parts&wr_id=9)
[63호]2020 로보월드
로봇 관련 전시회 중 올해 전 세계 최초로 개최된 전시회
2020 로보월드
글 | 박진아 기자 jin@ntrex.co.kr
지난 10월 4일간 킨텍스 제1전시장에서 2020 로보월드가 개최되었다. 산업통상자원부 주최, 한국로봇산업협회, 한국로봇 산업진흥원, 제어로봇 시스템학회의 공동 주관으로 진행된 2020 로보월드는 총 400여 부스의 국내외 로봇 관련 기업 150여 곳이 참가하였다.
제조, 물류, 서비스, 드론, 스마트 응용 SW 등 다양한 주제로 구성되어 각 분야에 맞는 최신 제품, 기술 등을 만나 볼 수 있었다. 특히 이번 전시는 온라인과 오프라인이 결합한 하이브리드 전시회로 온라인을 통해서도 해당 전시회에 출품된 제품들을 쉽게 만날 수 있었으며, 다채로운 부대행사를 통해 재미와 볼거리가 가득한 전시회를 만들었다.
로보월드를 방문하자마자 좌, 우로 열을 맞춰 움직이는 로봇암들의 모습에 발길을 멈추게 되었다. 이곳은 제조용 로봇 전문 기업 로보스타의 부스로 산업용 로봇 RA004, RA007가 관람객을 대상으로 움직임을 시연하고 있었다. 초소형 다관절 로봇인 두 로봇은 고성능, 고기능 범용 로봇으로 조작이 간편하고, 사용이 용이하며, 완벽한 제어 능력으로 높은 제품 신뢰성이 특징이다.
가반 하중이 4, 7kg인 두 로봇에는 충격감지 및 힘 제어 등 협동 로봇 기능이 탑재돼 있으며, 기존 산업용 로봇과 달리 펜스 없이 사용할 수 있도록 설계됐다. 해당 로봇은 현재 각종 안정 인증 취득을 위한 절차를 밟고 있으며, 향후 2년 내 정식으로 출시할 예정이라고 한다.
기자의 눈길을 끌은 또 다른 곳은 로봇암을 이용하여 촬영을 진행하고 있던 레인보우 로보틱스 부스였다. 레인보우 로보틱스는 카이스트 휴머노이드 로봇연구센터-휴보랩에서 창업된 벤처기업으로, 대한민국 최초의 이족보행 휴머노이드 로봇인 HUBO에 대한 모든 기술과 상표권을 갖고 있는 로봇 개발 회사이다. 특히 레인보우는 감속기를 제외한 핵심 부품을 자체적으로 개발 및 생산하는 기술 기반 회사로 다른 협동 로봇 제조사에 비해 높은 기술력을 갖춘 기업으로 인식되고 있다.
전시된 제품은 작업자와 함께하는 협동 로봇 RB 시리즈로 기존 제품과 달리 부품의 내재화를 통해 가격경쟁력을 갖췄고, 고객 요구 사항에 따라 커스터마이징이 가능하며 기반 하중은 5Kg, 10kg, 3kg으로 작업반경, 중량에 따라 필요 로봇을 선택할 수 있다.
현장에서는 스튜디오 촬영을 시연하고 있었는데 기존에 생각했던 로봇의 움직임보다 훨씬 안정적이고 섬세했으면 다양한 촬영 기법을 사용하는 모습을 보며 발전한 로봇 기술력에 감탄하였다. 또한, 서비스 시장에서 협동 로봇을 활성화하기 위해 제작된 음료 제조 로봇 믹스의 업그레이드 버전을 함께 전시하였으며, 이 믹스 플래폼은 이미 여러 장소에서 활용되고 있다.
반도체와 디스플레이, 신재생에너지, 제조 장비 공급업체인 제우스에서는 다관절 로봇인 제로, 스카라 로봇, 직교 로봇, 델타 로봇 등을 전시하였다. 주요 모델인 제로(ZERO)는 2019년도에 제우스 최초의 6축 다관절 산업용 로봇으로 가반 중량 5kg로 Pick -and-place, PCB 조립 및 비전 검사를 포함한 다양한 응용 분야를 위해 설계되었다. 산업용 로봇으로 높은 수준의 ±0.02mm 반복 정밀도를 구현하며, 모듈식 설계로 유지 보수 비용을 줄이고, 낮은 소비전력(250W), 사용성 좋은 PC 기반 프로그래밍 등 산업용 로봇 장점을 극대화한 제품이다.
이 밖에 새로운 주력 제품인 스카라 로봇은 가반 하중 4Kg, 무게 16kg으로 중공축 모터 적용으로 컴팩트한 구조를 구현, 공간 효율성을 높였다. 10마이크론 즉 0.01mm의 정밀도를 제공하며 모터 및 감속기 등이 모듈화된 제품이다. 또한, 이미지에 나온 제품은 델타로봇으로 27Kg의 가벼운 무게로 기존 공정에서 라인을 멈추거나 개조할 필요 없이 즉시 설치가 가능하고, 무급유 베어링 기술을 채택, 기존 스프링 방식의 마모 등 여러가지 문제를 예방할 수 있는 특징이 있다.
산업 현장에서 사용되는 로봇의 다양한 분야에서 최근 빠르게 활용도가 높아지는 분야는 물류 분야라고 할 수 있다. 이에 따라 전시장에서 가장 많은 눈길은 끈 것도 물류, 배송, 서빙 로봇이었다.
먼저 전시회장 곳곳을 누비며 신문이나 손소독제를 전달하는 트위니의 따르고 로봇을 만날 수 있었다. 따르고는 사람을 따라다니는 추종 로봇으로 대상의 색깔, 크기, 위치, 모양 정보 등을 기반으로 대상을 따르는 로봇이다.
다음 나르고는 자율 주행 로봇과 추종 로봇을 결합한 형태의 기차로봇으로 선두의 자율주행 로봇이 이동하면, 추종로봇들이 그 뒤를 차례대로 따라 이동한다. 나르고60은 QR코드, 비컨, UWB 등 인프라가 필요없는 완전 자율주행 로봇으로 좁고 복잡한 실내 공간 어디에도 적합한 운송로봇이다. 최대 2시간 충전으로 8시간 사용이 가능하며, 1.2m/s 속도를 가진 나르고 60은 대형 사무실, 택배, 우편, 물류창고 등에서 사용이 가능하다고 한다.
로봇 시스템과 자율 주행 로봇을 개발 및 공급하는 시스콘에서는 물류 이송 로봇 SR 시리즈 및 IM을 선보였다.
시스콘의 자율 주행 로봇은 360도 라이더 센서를 이용하여 주변 감지, 장애물 감지를 통해 안전한 자율주행이 가능하며 고정된 경로 또는 최적의 경로를 운영자가 선택할 수 있어 작업의 유연성, 효율성 극대화의 효과를 거둘 수 있는 특징을 갖는다.
SR 시리즈는 자율 주행 이송 로봇으로 컨베이어, 팔레트타입, 모바일 매니퓰레이터와 같이 협업할 수 있고, 딜리버리 서비스 로봇인 IM시리즈는 안내, 서빙, 관리 등의 기능을 갖고 있는 서비스 모듈 장치를 결합할 수 있다. 즉 다양한 목적에 따라 상부 모듈 및 소프트웨어 변경이 가능하다.
특히 IM 시리즈는 창의적이고 혁신기술을 인정받아 2020 로보월드 어워드 우수 제품으로 선정되는 영광을 얻었다.
LG 유플러스 부스에서는 물류 창고를 옮겨 놓은 듯한 대형 부스에서 다양한 이동식 물류 로봇 등이 쉴새 없이 움직이며 관람객의 발걸음을 멈추게 하였다. 이동통신사 중 유일하게 참여한 LG유플러스는 글로벌 지게차 업체 클라크, 물류 자동화 업체 케이엔, 인공지능 기반 물류 솔루션 업체 무샤이니 등과 5G 기반 무인 지게차-물류 로봇 등을 선보였다.
5G 무인 지게차는 관리자 없이 무인으로 이용할 수 있는 솔루션으로 10mm의 정밀한 위치 측위는 물론 한 명의 작업자가 최대 50대까지 동시 제어할 수 있다. 5G 물류 로봇은 공장이나 물류 센터 내 보관을 위한 운반 업무를 로봇이 대체하여 물류 처리 시간을 줄여 빠른 입출고 관리가 가능하다.
로봇용 충돌, 근접 감지 안전센서 개발 전문 업체인 에이딘 로보틱스에서는 4족 보행 로봇 에이딘을 시연 및 전시하였다. 에이딘은 로봇의 다리가 지면에 닿을 때의 힘을 정확히 측정하는 다축 힘 토크 센서가 있기 때문에 자연스럽게 네발로 다닐 수 있으며, 바퀴형 로봇이 갈 수 없는 곳까지도 이동이 가능한 장점이 있다. 에이딘 로보틱스의 힘 토크 센서는 외부 충격에 강인하여 내구성이 보장되며, 3축 힘과 3축 모멘트 모두 측정이 가능한 6축 힘 토크 센서이다. 힘 토크 센서를 통해 엘리베이터의 버튼을 누르거나, 짐을 옮기거나, 차를 끄는 등 다른 사물들과 상호 작용이 가능하며 다양한 측정 장비를 탑재해 영상 촬영 또는 데이터 수집 목적으로도 사용이 가능하다.
또한, 함께 선보인 듀얼모드 근접 촉각 센서는 필드 센싱 기술인 메소 스케일의 변화를 감지해 자동화 공정 라인에서 작업자와 로봇의 출동을 사전에 방지해 준다.
자율 주행 솔루션 업체 코가플렉스는 인식-제어 기술과 실내 자율주행 기술을 결합한 코나 시스템이 탑재된 서빙고 로봇을 전시했다.
서빙고는 키스트, 우리 로봇과 협력해 개발됐으며, 카메라와 위치센서로 공간을 파악하며 미리 입력된 알고리즘에 따라 행동한다.
일반 서빙 로봇이 식당 천장이나 테이블, 벽 등에 주행로봇이 인식할 수 있는 표식을 붙여야만 실내 주행이 가능했던 것과 달리, 표식 없이도 자율 주행으로 길을 찾을 수 있는 차별점이 있다.
또한, 이번 전시회에서는 로봇암, 이동식 로봇 외에도 사람의 건강관리와 정서 안정을 지원하는 로봇들도 한자리에서 확인할 수 있었다. 앞으로 발전가능성이 무궁무진한 헬스케어 로봇 중 먼저 로보 케어 부스의 실벗 제품을 확인할 수 있었다.
로보케어는 KIST 1호 출자 회사로 설립된 기업으로 이번 전시회에서 치매예방 로봇 인지훈련 시스템 실벗과 일대일 개인형 인지훈련 로봇 보미를 소개하였다.
실벗은 그룹형 인지훈련 시스템으로 고령자 및 치매 위험이 있는 어르신을 대상으로 로봇을 이용한 두뇌향상 콘텐츠를 제공해 뇌 기능 활성 및 치매예방에 도움을 준다. 최대 12인이 동시에 훈련이 가능하며, 치매, 우울 검사 수행 및 학습 수준에 따라 난이도 조절이 가능하다는 장점이 있다. 보미는 I, II 2종으로 개인형 로봇과 탁상형 로봇 선택이 가능하고, 탁상형 로봇은 이동형 인지 훈련 데일리 케어 로봇이다. 이 역시 난이도 조절이 가능한 다양한 두뇌 콘텐츠를 통해 인지훈련이 가능하며, 응급 콜 서비스 및 복용약 알림 기능을 제공한다. 이중 실벗은 이번 전시 우수 제품으로 선정되었다.
실벗과 보미가 인지훈련을 도와줬다면 티로보틱스의 Healbot-G는 KIST, 아산병원과 협력하여 마비 환자, 뇌졸중 환자의 보행 재활에 전문적인 도움을 준다.
티로보틱스는 진공로봇, 자율 주행 이송로봇뿐만 아니라 재활 및 근력 보조 용도의 외골격 로봇을 개발하여 공급하고 있으며 재활로봇은 두 가지로 Healbot-T와 Healbot-G가 있다.
Healbot-T는 뇌졸중 환자의 초기 보행운동 능력 회복을 위한 Treadmill 타입의 보행 보조 로봇으로 실제적인 보행운동 구현을 위한 14축 자유도가 지원되는 외골격 로봇이다. 사진 속 제품은 Healbot-G로 편마비 환자의 보행 행태를 지원하기 위한 Overground 타입의 외골격 보행보조 로봇으로 단계별 근력 보조 조절이 가능한 4축 외골격 로봇이다. 간편하게 조절 착용이 가능한 프리 피팅(Free-Fitting) 구조로 STS(Sit to Stand), 균형 및 보행 연습 등에 적용할 수 있다.
다이나믹케어서는 최대 근력 측정 및 개인 맞춤형 운동 프로그램을 지원하는 파워 로그 제품을 직접 시연하였다.
파워 로그는 사용자가 발휘하는 힘에 대해 3/100SEC에 한 번씩 능동적으로 반응함으로써 최대 근력(1RM)을 끌어낼 수 있다. 또한, 측정된 부위별 1RM은 DB에 저장되며, 이를 바탕으로 개인 맞춤형 운동 프로그램을 자동으로 계산 및 제공된다.
APP으로 제공된 운동 프로그램은 키오스크에 자동 전송되어, 별도의 입력 없이 개인 맞춤형 운동을 POWER LOG를 통해 실행할 수 있으며 모든 데이터는 저장되어 운동 스케줄 관리를 도와준다.
이 제품은 트레이너의 전문성과 신뢰도를 향상시킬 수도 있고, 5단계 안전장치로 스스로 운동할 때 바벨, 덤델 등을 통해 다치는 것을 방지해 주는 특징이 있다. 이 제품은 재활센터, 운동센터 등에 설치하여 사용하거나 개인 운동기기로 사용이 가능하다.
이런 헬스 케어 로봇은 고령화에 따라 관리 인력의 문제를 해결하고, 코로나 장기화에 따른 비대면 의료, 개인 건강 관리에 도움을 줄 수 있기 때문에 앞으로의 기술발전과 실제 적용이 더욱더 기대되는 분야이다.
코로나로 인해 이번 전시회 방문이 조심스러웠으나, 입구에서부터 열 체크, 신원확인, 음식물 섭취 금지, KF-80 이상의 마스크 착용도 감염 예방 수칙이 잘 마련되어 있어 안전하게 관람할 수 있었다. 하지만 갑작스럽게 취소되는 전시도 간혹 있으니 구독자분들이 전시회를 방문할 계획이라면 일정과 감염 예방을 위한 수칙 등을 사전에 확인해보고 방문하기 바란다.
올해 세계 처음으로 열린 2020 로봇 전시회는 많은 관람 제약과 시기가 좋지 않았음에도 불구하고, 전문 기업들이 다양한 제품을 선보여 관람 내내 볼거리가 많았으며 산업, 의료 분야의 전문 로봇뿐만 아니라 교육용 코딩 로봇, 라이다, 드론, 산업용 모터 등 완제품 및 부품 모두를 한자리에서 볼 수 있다.
정부에서도 앞으로 로봇 분야의 규제를 완화하고, 지원을 아끼지 않는다고 하니 더욱더 발전하는 로봇 산업, 로보월드 전시회를 기대하며 2020 로보월드 관람기를 마친다.
[63호]WhoRU
2020 ICT 융합 프로젝트 공모전 장려상
WhoRU
글 | 국민대학교 홍희정, 서울시립대학교 박진석, 서울과학기술대학교 윤승한
1. 심사평
칩센 최근 미자격자(미성년)의 공유차량의 불법 운행으로 인하여 여러 가지 문제가 발생하고 있는데 시기적으로 적절한 주제를 선정하였습니다. DB에 등록된 데이터와 실제 얼굴 인식을 통한 매칭 후 차량의 운행이 가능하도록 하겠다는 목표에 대하여는 충분히 공감합니다. DB 등록시에 유효한 사용자인지에 대한 부분은 우선, 이 주제에서 해당되지 않는듯하여 배제하고 본다면, 목표는 어느정도 달성한 것으로 보입니다. 다만, 기존 차량에 개발 작품을 추가하고자 할 경우 보고서에 있듯이 OBD 또는 차량 내부에 존재하는 시스템과 연동하여야 하는데, 이 부분에 대한 추가적인 고민은 필요할 것으로 보입니다.
펌테크 세심한 관찰력이 반영된 실생활과 밀접한 아이디어와 실용성이 우수한 작품이라고 생각합니다. 기획의도에 맞게 전체 시스템을 안정적이고 완성도 높게 구현하였다고 판단되며. 전체적으로 기획의도, 기술 구현도, 완성도 등에서 상당히 뛰어나며 상업적으로 활용이 가능한 우수한 작품으로 생각됩니다.
위드로봇 얼굴 인식을 차량 공유 시스템에 적용하는 아이디어는 재미있으나, 얼굴 인식률을 높이기 위한 고민이 보이지 않습니다.
2. 작품 개요
2.1. 개발 배경
다가오는 모빌리티 시장에는 퍼스널 모빌리티 뿐만 아니라 차량 공유 서비스 (카쉐어링 서비스)도 진출했다. 차량 공유 서비스는 필요할 때에 원하는 시간만큼만 차량을 쓰는 서비스이다. 최근 스마트폰 어플을 통하여 계정 등록만 하면 간편하게 차량을 예약하여 사용할 수 있는 차량 공유 서비스 시장이 커지면서, 미성년자 등 무면허 운전이 급증하였다. 이는 기존의 렌터카와는 달리 차량을 인수하고 반납하는 과정을 모두 무인으로 운영하여 계정의 회원 정보와 실제 사용자가 일치하는지 알 수 없는 취약점 때문이다. 그래서 우리는 차량 공유 서비스에서의 무면허 및 명의도용 방지를 막기위해 ‘얼굴인식 본인인증 절차’를 추가하고, 차량마다 이 인증 장치를 통하여 Start Button(시동 버튼)을 제어하는 ‘WhoRU’를 제시한다.
2.2. 기대효과
WhoRU 제품을 기존의 차량에 쉽게 장착할 수 있고, 별도의 전원 없이 제어할 수 있게 발전시킬 수 있다. 차량의 OBD 단자와 연결하고 차량의 시동을 제어할 수도 있다. 이러한 방식으로 기존에 있는 공유 차량에 적용하며 WhoRU를 상업화한다면 얼굴인식을 통해 무면허/미성년자들의 무면허 운전을 방지할 수 있다.
WhoRU를 이용한다면 카 셰어링 업체 측에서는 어플을 통해 차량을 예약한 사람의 정보와 실제 운전자가 일치하는지 항상 확인할 수 있다. 이로써 업체는 기존의 어플을 통해 편리한 무인 차량 대여 서비스를 유지하고 위험부담은 줄일 수 있다. 즉, 공유 차량 서비스의 최고의 장점인 편리함을 유지할 수 있게 된다.
마지막으로 카 셰어링 업체에서는 무면허 운전자뿐만 아니라 현재 이슈 되고 있는 무보험 운전자들로 인한 차량 사고 방지 대책으로 ‘WhoRU 설치’를 내세울 수 있다. 이렇게 위험 부담이 줄어들게 된다면 WhoRU를 설치한다는 것은 차량을 대여할 때의 보험료가 인하되는 요소로 작용할 것이라고 예상된다. 이로써 사용자 또한 ‘보험료 인하’라는 혜택을 누릴 수 있을 것이라고 기대된다.
3. 작품 설명
3.1. 주요 동작 및 특징
공유 차량에 대해 WhoRU-Device를 장착하고 Server와 연동하여 Start Button(시동 버튼)을 제어하도록 설계하였다. 사용자가 공유차량을 예약할 때 면허증 사진을 등록한다. 이렇게 저장된 사진은 Storage에 저장되고, 사용자가 공유차량을 이용할 때 WhoRU-Device의 버튼을 눌러주면, 카메라를 통해 사용자의 얼굴을 실시간으로 받아온 뒤 Storage에 저장된 사진과 얼굴 이미지를 비교한다. WhoRU-Server에서 비교를 마친 후 사용자 인증이 성공적으로 이루어지면 파란색 LED가 켜지며 Start Button을 막고 있던 잠금 장치가 열리게 된다. 사용자가 공유 차량 이용을 마친 후 버튼을 다시 눌러주면 LED가 꺼지며 잠금 장치는 다시 닫히게 된다.
3.2. 전체 시스템 구성
3.3. 개발 환경
3.3.1. 서버
Tool :Firebase, OS : Ubuntu 18.04 (Linux), 개발 언어 : python
3.3.2. H/W
Devices : Raspberry pi 4, Camera (savitmicro VIJE Q-800), Servo motor, LED, button, 가상 계기판, Tool : Pycharm, soildworks, 개발 언어 : python
4. 단계별 제작 과정
4.1. Overview
제작과정은 크게 WhoRU-Device와 WhoRU-Server로 나눠진다. 차량에 장착되는 WhoRU-Device는 Raspberry pi를 메인 보드로 사용하고 Camera, Servo-Motor, Led 등을 연결하여 차량 고유의 원격 장치를 구성하였다. WhoRU-Server는 face recognition 작업을 진행하는 부분으로 우리는 GPU를 사용하는 PC를 Server로 하고 프로젝트를 진행했다.
4.2. 외관 Hardware 구성
WhoRU 작동 방향을 보여주기 위해 가상 계기판을 제작하였다. 아래의 그림은 솔리드웍스 툴을 이용하여 가상 계기판을 설계한 그림이다. 좌측에 계기판이 있고 우측 공간 안에는 라즈베리파이가 들어간다. 상단에는 led가 연결되고, 하단에는 서보모터를 이용한 Start Button(시동 버튼) 잠금 장치가 연결된다. WhoRU 시작 버튼도 상자 밖에서 누를 수 있도록 구성하였다.
위와 같이 우드락으로 계기판 모델을 제작하여 카메라의 적합한 위치 파악과 완성된 시스템의 프로토타입을 만들었다.
4.3. Device (Raspberry pi)
4.3.1. Firebase
4.3.1.1. Setting
라즈베리파이에서 Firebase를 사용하기 위해 관련 패키지들을 설치해준다. 라즈베리파이에서 python으로 작업하기 때문에 Firebase-admin을 추가로 설치해 주어야 한다.
pip install firebase
pip install firebase-admin
4.3.1.2. Thread
라즈베리파이는 지속해서 Firebase의 데이터베이스에서 확인을 해야 한다. 데이터베이스에서 데이터를 읽는데 걸리는 시간이 1초 정도로 느리다. 하나의 프로세스에서 작업할 경우, 원활한 진행에 문제가 발생하였기 때문에 데이터베이스 읽은 과정은 따로 스레드를 만들어서 처리해주었다.
4.3.1.3. wifi
라즈베리파이를 무선네트워크 연결을 하는 데 연결이 자꾸 끊기는 현상이 발생했다. 라즈베리파이를 부팅한 후 다음 명령어를 입력하면 이 현상을 방지할 수 있다.
4.3.2. Camera
4.3.2.1. Motion
Motion 패키지는 카메라 모듈과 라즈베리파이를 통하여 IP 카메라 기반의 CCTV를 만들어 주는 패키지이다. Motion 패키지의 일부 기능 중 하나인 카메라에서 받은 실시간 영상을 HTML을 통하여 영상을 전송하는 기능을 사용하였다. 그리고 Motion 패키지는 라즈베리파이의 전원이 켜지는 즉시 백그라운드에서 자동으로 실행이 된다. 여럿 카메라 패키지 중 Motion을 쓴 이유는 사용자가 차량에 탑승하는 즉시 차량에서 자동으로 카메라를 활성화를 시켜야 하므로 이 패키지를 쓰게 되었다.
4.3.2.2. Setting
/etc/default 디렉토리 안에 motion이라는 파일을 위의 내용대로 수정하여 준다.
/etc/motion 디렉토리 안에 motion.conf 파일을 위와 같이 수정하여 준다. 위의 작업은 라즈베리파이가 켜지면 자동으로 백그라운드에 motion이 실행되게 해주는 기능이다.
4.3.3. Devices
4.3.3.1. Servo motor
얼굴인식을 통해 사용자 인증에 성공하면 Start Button(시동 버튼)의 잠금 장치를 서보 모터를 통해 열어준다. 즉, Database의 ‘Approved’ 값이 1로 바뀌면 작동하게 되며, 한번 더 눌러주면 시스템이 초기화되며 잠금 장치가 닫히게 된다.
4.3.3.2. Led
UI/UX 관점으로 사용자에게 얼굴 인증이 진행되고 있음을 알려준다. 사용자가 WhoRU-Device 버튼을 눌러주면 노란색 불이 들어오며 얼굴인식이 진행된다. 이 후 인증이 완료되면 파란 불을 켜준다. 공유 차량 이용 후 버튼을 다시 눌러주면 모든 불이 꺼지게 된다.
LED 제어는 Firebase의 Flag 값 기반으로 제어한다.
4.4. Server
4.4.1. Firebase
4.4.1.1. Database
Firebase의 Database Tree는 다음과 같다. Carlist 아래에 공유 차량의 번호를 적어 두고, 각 차량 번호 아래에는 ‘Approved’, ‘Request’, ‘Username’이 있다. 사용자가 Whoru-Device의 버튼을 누르면, 해당 디바이스와 연결된 차량번호 하위 항목의 ‘Request’값이 1로 바뀐다. Whoru-Server에서 얼굴인식이 성공적으로 이루어지면 ‘Approved’값이 1로 바뀐다. 공유 차량 이용 후 Whoru-Device의 버튼을 다시 눌러주면 ‘Approved’와 ‘Request’ 값이 0으로 바뀐다.
4.4.1.2. Storage
Firebase의 Storage에는 사용자의 운전면허 사진이 등록된다. 저장된 사진은 WhoRU-Server가 작동되면서 해당 사용자의 이름과 일치하는 이미지를 불러와 실시간 촬영되는 사용자의 이미지와 비교하여 얼굴을 인식한다.
Storage에 저장된 사진은 url형태로 저장되며 이를 Server에서 jpg파일로 변환시켜 불러온다. 이를 위해 아래의 패키지를 설치해준다.
pip install image
4.4.2. Face Recognition
4.4.2.1. face_recognition
Face_recognition 패키지는 얼굴 인식에 KNN (k-nearest-neighbors) 알고리즘을 사용한다. 저장된 인물 사진을 기반으로 Deep metric learning을 활용하여 판별해야 하는 사람의 얼굴을 인식하고 그 사람의 이름을 알려준다. 우리는 Adam Geitgey가 작성한 Face_recognition 패키지에서 Ip-camera를 통해 받은 실시간 화면과의 얼굴 인식을 하는 기능을 활용하였다.
4.4.2.2. Deep metric learning
Deep metric learning은 학습을 할 때 ‘분류’를 하는 것이 아니라, 같다/다르다를 나타내는 Distance를 계산하는 Metric을 사용하는 방식으로 문제를 바꾼다. 이 학습법은 단순히 ‘맞다/틀리다’가 아닌 입력 값이 다수인 얼굴 인식에 많이 쓰인다.
4.4.2.3. Setting
Face recognition을 실행하기 위하여 Ubuntu 환경에 scikit-learn, opencv, numpy 패키지를 설치해준다.
pip3 install scikit-learn
pip3 install numpy
pip3 install opencv-contrib-python
또 라즈베리파이에서와 마찬가지로 Firebase와 연동하기 위해 관련 패키지를 설치해준다.
pip install google-cloud-storage
pip install firebase
pip install firebase-admin
4.4.2.4. 얼굴인식 민감도 조절
Tolerance 값은 민감도에 영향을 주는 변수이다. 이 값이 낮을수록 얼굴인식이 민감해지고 커질수록 둔감해진다. 단, 값이 너무 낮아지면 조도, 얼굴의 각도 등 여러 변수들이 얼굴인식에 영향을 미쳐 얼굴인식이 잘 안될 수 있다. 기본 값은 0.45이다.
우리는 여러 인물들의 사진을 기반으로 테스트 해본 결과 0.4정도의 값이 적절하다고 판단되어 0.4로 설정하였다.
/face_recognition/api.py 라는 파이썬 파일에 위와 같이tolerance 값을 변경할 수 있다.
그와 동시에 ‘/face_recognition/face_recognition_cli.py’ 에도 tolerance 값을 같이 변경해주어야 한다.
5. 기타
5.1. 회로도
5.2. 소스코드
5.2.1. WhoRU_Device.py (Raspberry pi)
버튼이 눌리면 firebase database에 있는 ‘request’ 값을 1로 바꿈
2020.03.22.
‘approved’값이 1이 되면 servo motor 제어
”’
import threading, requests
import firebase_admin
from firebase_admin import credentials
from firebase_admin import db
import RPi.GPIO as GPIO
import time
pin_servo_motor = 18 # GPIO.BCM
pin_switch = 21 # GPIO.BCM
pin_led_yellow = 19 # GPIO.BCM
pin_led_blue = 26 # GPIO.BCM
# red_led = port 12
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin_led_yellow, GPIO.OUT)
GPIO.setup(pin_led_blue, GPIO.OUT)
GPIO.setup(pin_switch, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(pin_servo_motor, GPIO.OUT)
db_url = ‘https://whoru-ed991.firebaseio.com/’
cred = credentials.Certificate(“myKey.json”)
db_app = firebase_admin.initialize_app(cred, {‘databaseURL’: db_url})
ref = db.reference()
#pin_servo_motor = 12 # GPIO.BOARD
#p = GPIO.PWM(pin_servo_motor, 50)
p = GPIO.PWM(pin_servo_motor, 50)
p.start(0)
cnt = 0
pwm = 0
switch = 0
flag = 0
request = 0
input_state = 0
input_state_pre = 0
isrequest = False
GPIO.output(pin_led_yellow, GPIO.LOW)
GPIO.output(pin_led_blue, GPIO.LOW)
class get_database (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print(“[Thread] get database start”)
global flag
global request
while True:
flag = ref.child(“carlist/06수 8850/approved”).get()
request = ref.child(“carlist/06수 8850/request”).get()
print(“[Thread] flag\t: {}”.format(flag))
print(“[Thread] request\t: {}”.format(request))
if __name__ == “__main__”:
try:
_time = 0
thread_database = get_database()
thread_database.start()
p.ChangeDutyCycle(1)
while True:
input_state_pre = input_state
input_state = GPIO.input(pin_switch)
# APPRROVED
if flag == ’1′ and request == ’1′:
print(“flag 1″)
# led off
GPIO.output(pin_led_yellow, GPIO.LOW)
GPIO.output(pin_led_blue, GPIO.HIGH)
# motor on
p.ChangeDutyCycle(10)
print(“angle : {}”.format(pwm))
if (input_state != input_state_pre): # switch edge detecting
print(“edge!”)
if input_state == 0:
switch += 1
else:
pass
if (switch % 2 == 1) and (isrequest == False):
print(“request 1″)
_time =time.time()
ref.child(“carlist/06수 8850″).update({‘request’: ’1′})
GPIO.output(pin_led_yellow, GPIO.HIGH)
GPIO.output(pin_led_blue, GPIO.LOW)
isrequest = True
if (switch % 2 == 0) and (isrequest == True):
print(“request 0″)
_time = 0
GPIO.output(pin_led_yellow, GPIO.LOW)
GPIO.output(pin_led_blue, GPIO.LOW)
p.ChangeDutyCycle(1)
ref.child(“carlist/06수 8850″).update({‘request’: ’0′})
ref.child(“carlist/06수 8850″).update({‘approved’: ’0′})
isrequest = False
time.sleep(1)
”’
if time.time() – _time > 15: # time out
switch += 1
”’
except KeyboardInterrupt:
p.stop()
GPIO.cleanup()
# -*- coding: utf-8 -*-
import cv2
import math
from sklearn import neighbors
import os
import os.path
import pickle
from PIL import Image, ImageDraw
import face_recognition
from face_recognition.face_recognition_cli import image_files_in_folder
import numpy as np
# FOR STORAGE
try:
from google.cloud import storage
except ImportError:
raise ImportError(‘Failed to import the Cloud Storage library for Python. Make sure ”to install the “google-cloud-storage” module.’)
from firebase_admin import storage
import datetime
import urllib.request
# FOR DATABASE
import firebase_admin
from firebase_admin import credentials
from firebase_admin import db
import time
####### Global Variable ######
cnt_face = 0
CARNUMBER = None
USERNAME = None
###############################
########### firebase import name ###############
db_url = ‘https://whoru-ed991.firebaseio.com/’
cred = credentials.Certificate(“myKey.json”)
db_app = firebase_admin.initialize_app(cred, {‘databaseURL’: db_url})
alldata = db.reference()
sr_buck = ‘whoru-ed991.appspot.com’
sr_app = firebase_admin.initialize_app(cred, {‘storageBucket’: sr_buck, }, name=’storage’)
############################################
ALLOWED_EXTENSIONS = {‘png’, ‘jpg’, ‘jpeg’, ‘JPG’}
def train(train_dir, model_save_path=None, n_neighbors=None, knn_algo=’ball_tree’, verbose=False):
X = []
y = []
for class_dir in os.listdir(train_dir):
if not os.path.isdir(os.path.join(train_dir, class_dir)):
continue
for img_path in image_files_in_folder(os.path.join(train_dir, class_dir)):
image = face_recognition.load_image_file(img_path)
face_bounding_boxes = face_recognition.face_locations(image)
if len(face_bounding_boxes) != 1:
if verbose:
print(“Image {} not suitable for training: {}”.format(img_path, “Didn’t find a face” if len(face_bounding_boxes) < 1 else “Found more than one face”))
else:
X.append(face_recognition.face_encodings(image, known_face_locations=face_bounding_boxes)[0])
y.append(class_dir)
if n_neighbors is None:
n_neighbors = int(round(math.sqrt(len(X))))
if verbose:
print(“Chose n_neighbors automatically:”, n_neighbors)
knn_clf = neighbors.KNeighborsClassifier(n_neighbors=n_neighbors, algorithm=knn_algo, weights=’distance’)
knn_clf.fit(X, y)
if model_save_path is not None:
with open(model_save_path, ‘wb’) as f:
pickle.dump(knn_clf, f)
return knn_clf
def predict(X_frame, knn_clf=None, model_path=None, distance_threshold=0.5):
if knn_clf is None and model_path is None:
raise Exception(“Must supply knn classifier either thourgh knn_clf or model_path”)
if knn_clf is None:
with open(model_path, ‘rb’) as f:
knn_clf = pickle.load(f)
X_face_locations = face_recognition.face_locations(X_frame)
if len(X_face_locations) == 0:
return []
faces_encodings = face_recognition.face_encodings(X_frame, known_face_locations=X_face_locations)
closest_distances = knn_clf.kneighbors(faces_encodings, n_neighbors=1)
are_matches = [closest_distances[0][i][0] <= distance_threshold for i in range(len(X_face_locations))]
return [(pred, loc) if rec else ("unknown", loc) for pred, loc, rec in
zip(knn_clf.predict(faces_encodings), X_face_locations, are_matches)]
def show_prediction_labels_on_image(frame, predictions):
global cnt_face
pil_image = Image.fromarray(frame)
draw = ImageDraw.Draw(pil_image)
for name, (top, right, bottom, left) in predictions:
top *= 2
right *= 2
bottom *= 2
left *= 2
draw.rectangle(((left, top), (right, bottom)), outline=(0, 0, 255))
name = name.encode(“UTF-8″)
names = name.decode()
############## name compare #################
if cnt_face < 40:
# LED = Orange
if names == USERNAME:
print(“true”)
cnt_face += 1
else:
print(“false”)
cnt_face = 0
if cnt_face == 40: db.reference(‘carlist’).child(“{}”.format(CARNUMBER)).update({‘approved’: ’1′})
print(“Recognition successfully!”)
# LED = Green
elif cnt_face > 40:
cap1.release()
cv2.destroyAllWindows()
# LED = OFF
exit(0)
now = time.time()
if now > start_time + 15:
db.reference(‘carlist’).child(“{}”.format(CARNUMBER)).update({‘request’: ’0′})
print(“Disapproved. Try Again.”)
############################################
text_width, text_height = draw.textsize(name)
draw.rectangle(((left, bottom – text_height – 10), (right, bottom)), fill=(0, 0, 255), outline=(0, 0, 255))
draw.text((left + 6, bottom – text_height – 5), name, fill=(255, 255, 255, 255))
del draw
opencvimage = np.array(pil_image)
return opencvimage
if __name__ == “__main__”:
try:
while True:
############### Get Car Number ###############
print(“####\tGet Car Number……..\t####”)
carlist = db.reference(‘carlist’).get()
# print(carlist.items())
for carNumber, val in carlist.items():
Request = db.reference(‘carlist’).child(‘{}/request’.format(carNumber)).get()
if Request == ’1′:
CARNUMBER = carNumber
print(“Request : {} \t carNumber : {}”.format(Request, carNumber))
print(“####\tGet Car Number Complete!!\t####”)
################# Call Image #################
print(“####\tGet User Image……….\t####”)
USERNAME = db.reference(‘carlist’).child(‘{}/username’.format(CARNUMBER)).get()
print(“username : {}”.format(USERNAME))
bucket = storage.bucket(app=sr_app)
blob = bucket.blob(“WhoRU_target/{}.jpg”.format(USERNAME))
user_path = “./knn_examples/train/{}”.format(USERNAME)
if not os.path.isdir(user_path):
os.mkdir(user_path)
img_url = blob.generate_signed_url(datetime.timedelta(seconds=300), method=’GET’)
urllib.request.urlretrieve(img_url, ‘{0}/{1}.jpg’.format(user_path, USERNAME))
print(“####\tGet User Image Comoplete!!\t####”)
###########################################
print(“####\tTraining KNN classifier…\t####”)
classifier = train(“knn_examples/train”, model_save_path=”trained_knn_model.clf”, n_neighbors=2)
print(“####\tTraining complete!\t####”)
# process one frame in every 30 frames for speed
process_this_frame = 29
print(‘####\tSetting cameras up…\t####’)
# multiple cameras can be used with the format url = ‘http://username:password@camera_ip:port’
url = ‘http://192.168.43.78:8081/’
cap = cv2.VideoCapture(url)
# 시작 시간 기록
start_time = time.time()
while 1 > 0:
ret, frame = cap.read()
if ret:
img = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)
process_this_frame = process_this_frame + 1
if process_this_frame % 30 == 0:
predictions = predict(img, model_path=”trained_knn_model.clf”)
frame = show_prediction_labels_on_image(frame, predictions)
cv2.imshow(‘camera’, frame)
if ord(‘q’) == cv2.waitKey(10):
cap1.release()
cv2.destroyAllWindows()
exit(0)
else:
pass
time.sleep(1)
except KeyboardInterrupt:
print(“KeyboardInterrupt!!”)
5.3. 참고문헌
· Firebase setting 참조 : https://medium.com/@97preveenraj/image-upload-to-firebase-storage-with-python-ebf18c615f34
· 사진 불러오기참조 : https://firebase.google.com/docs/reference/admin/python/firebase_admin.db
· Firebase Storage : https://cloud.google.com/storage/docs/downloading-objects?hl=ko#storage-download-object-python
· https://cloud.google.com/storage/docs/reference/libraries?hl=ko
· Firebase-admin github : https://github.com/firebase/firebase-admin-python/blob/master/firebase_admin/storage.py
· 얼굴인식 관련 참고 : https://github.com/ageitgey/face_recognition
· motion 관련 참고 : https://motion-project.github.io/