쌈@뽕 - 스마트 바텐더(Smart Bartender)

MIE capstone
EmSys2024B (토론 | 기여)님의 2024년 6월 22일 (토) 05:01 판 (최종 시연 영상)
이동: 둘러보기, 검색
Smart Bartender
쌈@뽕
AI.png
학교 서울시립대학교
학과 기계정보공학과
학번 및 성명 20194300** 이*헌[1]
20214300** 김*랑
20194300** 유*원
20194300** 정 *
20194300** 조*빈


프로젝트 개요

프로젝트 요약

  • 완성된 이미지
완성된 이미지

스마트 바텐더(Smart Bartender)는 안드로이드 앱을 통해 사용자의 취향과 선호에 맞춰 개인화된 칵테일을 제조하는 시스템이다. 사용자는 칵테일에 대한 배경 지식이 없어도 AI 기반 추천 시스템과 챗봇 기능을 통해 맞춤형 칵테일을 추천받고 제조할 수 있다. 이 시스템은 칵테일 바에서의 사용자 맞춤 서비스를 향상시키며, 기존의 자동화된 바텐딩 기기의 한계를 넘어 사용자의 취향과 기분을 파악해 새로운 칵테일을 제안한다.

  • 시스템 개요도
시스템 개요도

프로젝트 배경 및 기대효과

  • 배경
칵테일은 다양한 재료와 복잡한 레시피로 인해 초보자가 쉽게 접근하기 어려운 음료이다. 기존의 자동화된 바텐딩 기기들은 사용자 맞춤형 서비스를 제공하는 데 한계가 있었다. 스마트 바텐더는 이러한 문제를 해결하기 위해 개발되었다. 이 시스템은 사용자가 칵테일에 대한 배경 지식이 없어도 AI 기반 추천 시스템과 챗봇 기능을 통해 맞춤형 칵테일을 추천받고 제조할 수 있도록 설계되었다. 이를 통해 사용자들은 자신만의 맞춤형 칵테일을 쉽게 만들 수 있으며, 칵테일 바에서는 더욱 개인화된 서비스를 제공할 수 있다.
  • 기대 효과
스마트 바텐더는 사용자의 취향과 기분을 파악해 새로운 칵테일을 제안함으로써 사용자 경험을 극대화하고 칵테일의 진입 장벽을 낮추는 효과를 기대할 수 있다. 이 시스템은 칵테일 바에서의 사용자 맞춤 서비스를 향상시키며, 기존의 자동화된 바텐딩 기기의 한계를 넘어 더욱 개인화된 서비스를 제공한다. 또한, 실시간 재고 관리 시스템을 통해 효율적인 재고 관리를 가능하게 하여 운영 효율성을 높일 수 있다.

프로젝트 목표

목적 계통도
  • 성능
(1) 정확성
스마트 바텐더는 기본 제공되는 레시피와 사용자가 직접 커스텀한 레시피 두 가지 유형에 대해 정확한 비율로 재료를 혼합하여 칵테일을 제조해야 한다. 또한, 추천하는 칵테일은 현재 스마트 바텐더가 만들 수 있는 칵테일이어야 한다.
(2) 신속성
앱에서 칵테일을 선택하면 소켓 통신을 활용하여 서버로 데이터를 신속하게 전송한다. 또한 전송받은 데이터를 기반으로 스테핑 모터가 원형 디스크를 회전시켜 칵테일 제조 속도를 높인다.
  • 편의성
(1) 접근성
칵테일은 종류가 다양하고 레시피도 복잡해 초보자가 쉽게 접근하기 어려운 음료다. 스마트 바텐더는 현재 보유한 재료로 제조 가능한 칵테일 목록을 보여줄 수 있어야 한다. 또한, 사용자가 각 재료의 배합 비율을 조정하여 자신만의 맞춤형 칵테일을 만들 수 있게 한다. 구체적인 요구사항이 없는 사용자에게는 챗봇 서비스를 제공하여 기분과 분위기에 맞는 칵테일을 추천한다. 이를 통해 사용자의 칵테일 접근성을 높일 수 있다.
(2) 직관성
사용자는 제조 가능한 칵테일 목록을 확인하고, 원할 경우 해당 칵테일의 상세 정보와 이미지를 볼 수 있다. 칵테일 추천은 대부분의 사용자에게 익숙한 채팅 시스템을 활용한다. 또한, 커스텀 시에는 +, - 버튼으로 재료를 간단하고 직관적으로 조절할 수 있어 스마트 바텐더의 각 기능을 쉽게 제어할 수 있다.
(3) 개인화
사용자는 자신이 즐겨 마시는 칵테일을 즐겨찾기에 추가할 수 있다. 또한, 재료를 커스텀하여 자신만의 칵테일을 만든 후에도 이를 즐겨찾기에 저장할 수 있다.
  • 지속성
(1) 유지보수성
모든 하드웨어 모듈이 쉽게 분리될 수 있도록 설계되어 있어, 부품 고장 발생 시 신속한 대처가 가능하게 해야 한다. 또한 디스펜서를 간단히 분리할 수 있어 정기적인 세척과 칵테일 재료 보충이 용이해야 한다.

동작 시나리오

동작 시나리오
  • 칵테일 고르기
사용자가 칵테일 고르기를 선택하면, 현재 보유한 재료를 기반으로 사전에 정의된 칵테일 목록을 화면에 표시한다. 사용자가 원하는 칵테일을 선택하면, 해당 칵테일의 상세 정보가 표시된다. 사용자는 칵테일 정보를 확인한 후 선택 버튼을 눌러 칵테일을 제조한다.
  • 칵테일 추천받기
사용자가 칵테일 추천받기를 선택하면, 채팅 화면으로 이동하여 자신의 맛이나 분위기를 설명한다. 앱은 Chat GPT API를 사용하여 사용자 요구에 맞는 칵테일을 추천한다.
  • 칵테일 커스텀하기
사용자가 칵테일 커스텀하기를 선택하면, 현재 보유한 재료 목록을 보여준다. 각 재료는 "+"와 "-" 버튼을 통해 사용자가 원하는 양을 조절할 수 있다. 사용자가 원하는 재료를 조합한 후, 제조 방법(stirring 또는 build)을 선택하여 칵테일을 제조한다.
  • 개발자 모드
칵테일 기기의 음료를 변경하거나 양을 채울 때, 개발자 모드를 선택한다. 원하는 재료의 수량 혹은 종류를 갱신하고, 선택을 마치면 재료의 잔량이 업데이트된다.


구현 내용

재료 선정

  • MNED
학교 근처 칵테일 바인 MNED에 방문하여 10년 이상 경력의 바텐더님께 8가지 칵테일 재료와 스터링 메커니즘에 대해 자문하였다. 결과적으로, 스터링 메커니즘도 기존의 휘젓는 방식에서 컵을 받치는 판을 돌리는 형태로 변경하였으며 8가지의 칵테일 재료를 확정할 수 있었다. 해당 문서를 빌려 다시 한 번 감사의 말씀을 전한다.
MNED 내부 사진
자문 과정
단체샷


  • 조주 가능 칵테일 목록
칵테일 재료 및 제조 방법
이름 사용 재료 제조방법
보드카 크랜베리 자몽 오렌지 레몬 원액 트리플 섹
씨 브리즈 0 0 0 빌드
베이 브리즈 0 0 0 0
플랜터즈 펀치 0 0 0 0 0 0
스크루드라이버 0 0
허리케인 0 0 0 0 0
그레이하운드 0 0 0 스터링
코스모폴리탄 0 0 0 0
레드 데빌 0 0 0 0
화이트 레이디 0 0 0 0
롱 비치 아이스티 0 0 0 0 0 0 0
레몬 드롭 마티니 0 0 0 0

기구부 설계

모델링 이미지

매커니즘 설계

  1. 컵 운반 매커니즘 : 컵이 디스크 위에 올려져 디스크가 회전하여 원하는 디스펜서로 이동하는 메커니즘을 고안하였다. 초기 설계에서는 서보모터를 사용하여 디스크를 회전시켰으나, 서보모터는 조절 각에 한계가 있었으므로 스테핑 모터를 도입하기로 결정하였다. 스테핑 모터는 바닥면에 위치하여 외접 기어를 통해 디스크를 회전시키는 구조로 매커니즘을 설계하였다.
    컵 회전 매커니즘
  2. 음료 추출 매커니즘 : 초기 설계에서는 전자석 혹은 서보모터를 사용하여 디스펜서를 작동시키는 매커니즘을 사용하여 했으나 선정한 디스펜서가 세 다리를 동시에 눌러야 음료가 추출되는 구조였다. 따라서 리니어 액추에이터를 사용하여 세 다리를 동시에 누를 수 있도록 하였다.
    음료 추출 매커니즘
  3. 스터링 매커니즘 : 초기 설계에서는 전동 거품기가 내려와 음료와 얼음을 섞는 매커니즘을 고안하였다. 다만, 해당 방식은 컵 내의 얼음의 위치가 불확실하므로 예측 불가능한 동작이 발생할 수 있고 매커니즘 자체도 복잡하였다. MNED(칵테일 바) 바텐더분께 조언을 구한 결과 컵을 회전시키는 것만으로도 음료와 얼음의 열교환이 충분할 것이라고 말씀하였다. 따라서 DC모터를 이용하여 컵을 회전시키는 방식으로 스터링을 구현하였다.

부품 설계

  1. 기어 설계 : 스테핑 모터의 동력을 디스크로 전달하기 위해 평기어를 외접시키는 방식을 사용하며 빨간색 기어를 피니언기어(스테핑 모터 축), 초록색 기어를 스퍼기어(디스크 축)으로 하여 기어를 설계하였다. 프로젝트에 사용하는 스테핑 모터의 스텝각이 7.2이므로 다음 디스펜서로 향하는 스텝이 정확하게 나누어떨어지게 하는 것을 고려하며 전달되는 토크를 고려한 결과 1:8의 기어비가 적합하다는 결론을 내렸다. 해당 기어비를 사용하면 스테핑 모터가 한 바퀴 회전할 때 다음 디스펜서로 이동하기 때문에 직관적이며 후술할 soft start-stop을 사용하기에도 적합하다. 1:8이라는 높은 기어비를 좁은 공간 내에 적용하기 위해서는 기어의 모듈(Module)을 작게 해야한다. 다만, 기어를 3D 프린터를 사용하여 출력하므로 해상도가 떨어지고 모듈을 너무 작게 할 시 원활하게 맞물리지 않게 된다. 따라서 기어의 이높이가 3.6mm가 되도록 모듈을 1.6으로 선정하고 백래시(backlash)를 0.5mm로 두어 오차를 보정하였다. 해당 기어에는 고하중이 작용하지 않으므로 압력각(pressure angle)을 20로 선정하였다. 기어의 부피를 줄이기 위하여 잇수를 최소화하도록 하였다. 다만, 잇수의 실질적 최솟값이 12개이므로 모터축 기어의 잇수를 12개로 하고 디스크축 기어의 잇수를 96개로 하여 1:8의 기어비를 구현하였다. 기어 설계에는 Autodesk사의 Fusion 360을 사용하였다.
    기어 모델링 이미지(빨간색과 초록색)
  2. 강판 및 아크릴판 도면 설계 : 디스펜서가 장착되는 기구부의 위쪽은 디스펜서와 음료병으로 인한 큰 자중을 견뎌야 한다. 보수적인 설계를 위하여 금속 소재를 사용하는 것이 적합하다고 판단하였으며, 용접 결합을 위해 용접성이 좋은 탄소강을 사용하였다. 또한, 컵과 리니어 액추에이터가 올려지는 디스크는 편평하고 가벼워야 하므로 아크릴을 사용하였다. 위의 소재들은 저렴하면서도 정밀도가 높은 레이져컷팅 방식을 사용하여 성형하였다.해당 방식은 2D 도면을 필요로 하므로 모델링 파일은 2차원 형상으로 이상화하고 AutoDesk사의 AutoCAD를 이용하여 2D도면으로 재설계하였다.
    아크릴판 2D도면
  3. 3D프린터 활용 부품 설계 : 큰 하중이 작용하지는 않으나 복잡한 형상을 띄는 부품들은 3D프린터를 활용하도록 설계하였다. 베어링 힌지, 스페이서, 모터 마운트 들이 이에 해당하며 레이저컷팅으로 잘라진 강판의 홈에 맞도록 설계하였다. PLA소재의 열수축을 고려하여 공차를 적용하였고, 구조물에 작용하는 하중에 따라 형상과 채움률을 다르게 설계하였다.
    3D프린터를 활용한 부품들(노란색 부품에 해당)

가공 및 제작

  1. 부품 가공 : 강판과 아크릴판은 레이저컷팅을 이용해 재단하였다. 아크릴판은 교내 "메이커스스튜디오"의 레이저컷팅기를 사용해 재단하였으나 해당 기기는 금속류의 가공을 지원하지 않아 강판은 사설 업체에 외주를 맡겨 진행하였다. 3D프린터를 활용하는 부품은 요구하는 정밀도에 따라 다른 3D프린터를 사용하여 제작하였다. 일반적인 부품들은 매우 정밀한 수준의 가공이 필요하지 않아 일반 3D프린터를 사용했으나, 기어의 경우 이(teeth)의 크기가 3mm이내로 작았으므로 최적설계연구실의 3D프린터를 이용하여 정밀가공하였다.
    레이저컷팅된 강판 확인과정
    3D 프린터 활용 부품들
  2. 기구부 조립 : 가공이 완료된 부품들로 기구부를 조립하였다. 바닥판과 기둥은 볼트체결하였고 상단의 두 강판은 용접을 통해 결합하였다. 아크릴로 가공된 디스크에는 재단된 홈에 3D 프린터 출력물이 결합될 수 있도록 순간접착제를 사용하였다. 리니어 액추에이터와 DC모터는 디스크와 함께 회전하므로 전선이 꼬이지 않도록 기둥에 구멍을 뚫어 배선할 수 있도록 하였다. 조립된 기구부에 스테핑모터, 리니어 액추에이터, DC모터를 볼트체결하여 결합시켰다. 배선부는 바닥면 아래에 위치하도록 하였으며 아크릴판을 덧대어 강철판으로 인한 쇼트를 방지하였다.
    조립된 이미지
  3. 구조 보강 : 조립 과정은 원할하게 진행되었으나 리니어 액추에이터에 작용하는 반력으로 인해 모멘트가 발생하여 구조적 변형이 발생하고 음료가 제대로 추출되지 않는 상황이 발생하였다. 따라서 리니에 액추에이터의 감속기를 교체하여 추력을 높였고 추가적인 3D프린터 구조물을 설치하여 디스크의 처짐과 봉의 기울어짐을 방지하였다. 또한, 변형이 발생하는 부분을 에폭시와 순간접착제를 이용해 고정하여 구조적 건전성을 확보하였다.

회로

전체 회로 구상도

회로는 전원부, 제어부, 구동부로 구성된다. 전원부는 SMPS, 브레드 보드, 가변 컨버터, 라즈베리파이, 아두이노 우노, 모터드라이버로 구성된다. 제어부는 라즈베리파이가 담당하며, 안드로이드로부터 정보를 가공하여 아두이노 우노와 제어 정보를 송수신하고 네오픽셀을 작동시킨다. 구동부는 아두이노 우노가 스테핑 모터, 리니어 모터, DC 모터를 제어하여 칵테일을 제조하는 역할을 한다.


전체 회로 구상도

전원부

전원부는 SMPS, 브레드 보드, 가변 컨버터, 라즈베리파이, 아두이노 우노, 모터드라이버 1과 2로 구성된다. SMPS는 12V, 12.5A의 전원을 공급하며, 이를 통해 전체 시스템에 전원을 공급한다. 브레드 보드는 가변 컨버터와 모터드라이버 1, 2에 전원을 분배한다. 가변 컨버터는 12V 전원을 5.2V로 변환하여 라즈베리파이에 안정적으로 전원을 공급한다. 이 과정은 가변 컨버터의 USB-A 포트를 통해 이루어지며, USB A to C 타입 케이블을 사용하여 라즈베리파이의 C타입 전원 공급 포트로 전원이 전달된다. 라즈베리파이는 시리얼 케이블을 통해 아두이노 우노에 전원을 공급한다. 모터드라이버 1과 2는 각각 브레드 보드로부터 받은 +와 - 전원을 모터에 공급해준다. 모터와 모터드라이버 모두 12V를 필요로 하기 때문에 이 방식으로 전력을 공급받는다. 실 제작품은 브레드보드를 PCB 기판으로 대체하여 완성하였다.


전원부 회로

제어부

라즈베리파이는 시스템에서 세 가지 주요 역할을 수행한다. 첫째, 안드로이드로부터 수신한 정보를 가공한다. 둘째, 아두이노 우노와 제어 정보를 송수신한다. 셋째, 네오픽셀을 동작시킨다.


제어부 회로


안드로이드로부터 수신한 정보 가공 블루투스 통신을 통해 안드로이드로부터 받은 정보의 종류는 다음과 같다:

1. 칵테일의 유형 :
스마트 바텐더는 두 가지 유형의 칵테일 제조를 지원한다. 하나는 build 형식이고, 다른 하나는 stir 형식이다. Build 형식의 칵테일을 만들기 위해서는 재료를 넣는 순서가 중요하다. 따라서 재료의 위치에 따라 build 순서대로 스테핑 모터를 움직여야 하며, 각 재료의 양에 따라 해당 위치에 컵이 이동하면 리니어 모터를 작동시켜 디스펜서로부터 해당 재료를 추출해야 한다. Stir 형식의 칵테일은 재료를 넣는 순서가 상관없다. 따라서 가장 빠른 경로로 각 재료의 위치에 따라 스테핑 모터를 움직이고, 해당 재료의 양에 따라 리니어 모터로 음료를 추출하며, 마지막에는 DC 모터를 작동시켜야 한다.
  • Build 형식: Build 형식의 칵테일을 만들기 위해서는 재료를 넣는 순서가 중요하다. 따라서 재료의 위치에 따라 build 순서대로 스테핑 모터를 움직여야 하며, 각 재료의 양에 따라 해당 위치에 컵이 이동하면 리니어 모터를 작동시켜 디스펜서로부터 해당 재료를 추출해야 한다.
  • Stir 형식: Stir 형식의 칵테일은 재료를 넣는 순서가 상관없다. 따라서 가장 빠른 경로로 각 재료의 위치에 따라 스테핑 모터를 움직이고, 해당 재료의 양에 따라 리니어 모터로 음료를 추출하며, 마지막에는 DC 모터를 작동시켜야 한다.
2. 칵테일 재료 :
스마트 바텐더는 8가지 재료로 다양한 칵테일을 제조할 수 있다. 컵의 초기 위치는 보드카 아래에 있으며, 디스크는 반시계 방향으로 회전한다.
재료의 순서는 다음과 같다:
칵테일 재료 순서
순서 1 (초기위치) 2 3 4 5 6 7 8
재료 보드카 트리플 섹 희석된 레몬 원액 오렌지 주스 자몽 주스 크랜베리 주스
안드로이드 앱으로부터 수신하는 재료에 대한 리스트는 다음과 같다:
  • Build 형식: 예를 들어 씨 브리즈(Sea Breeze)를 제조할 때 필요한 재료는 보드카, 크랜베리 주스, 자몽 주스이다. Build 순서는 보드카를 먼저 넣고, 크랜베리 주스를 넣은 다음 자몽 주스를 넣는다. 따라서 material_list = {1,0,0,0,0,0,3,2}로 받는다.
  • Stir 형식: 예를 들어 코스모폴리탄(Cosmopolitan)을 제조할 때 필요한 재료는 보드카, 크랜베리 주스, 트리플 섹이다. Stir 형식에서는 최단 경로로 재료들을 컵에 담는다. 따라서 material_list = {1,0,0,2,0,0,0,3}으로 받는다.
3. 각 재료의 양 :
칵테일은 재료뿐만 아니라 재료의 양에 따라서도 구분된다. 따라서 재료를 얼마나 넣어야 할지에 대한 정보도 필요하다. 안드로이드 앱으로부터 수신하는 재료의 양에 대한 리스트는 다음과 같다:
  • Build 형식: 예를 들어 씨 브리즈(Sea Breeze)를 제조할 때 필요한 재료는 보드카 60ml, 크랜베리 주스 120ml, 자몽 주스 30ml이다. 디스펜서는 한 번에 30ml씩 추출하므로 각각 2번, 4번, 1번 추출하면 된다. 따라서 quantity_list = {2,0,0,0,0,0,1,4}이다.
  • Stir 형식: 예를 들어 코스모폴리탄(Cosmopolitan)을 제조할 때 필요한 재료는 보드카 60ml, 크랜베리 주스 30ml, 트리플 섹 30ml이다. 디스펜서는 한 번에 30ml씩 추출하므로 각각 2번, 1번, 1번 추출하면 된다. 따라서 quantity_list = {2,0,0,1,0,0,0,1}이다.
4. 정보 가공 :
수신을 완료하였다면 수신한 자료들을 가공해야 한다. 아두이노에는 모터를 구동시키는 코드만 프로그래밍되어 있기 때문에, 라즈베리파이는 아두이노로 스테핑 모터 회전, 리니어 모터 작동, DC 모터 작동에 대한 신호를 송신해야 한다.
아두이노는 스테핑 모터를 작동시키기 위해 1이라는 신호를 받으면, 1바퀴를 회전한다. 예를 들어, 라즈베리파이가 {1}이라는 신호를 시리얼 통신으로 송신하면, 아두이노는 {1}이라는 신호를 받고 이를 통해 step_motor(1)을 실행시켜 스테핑 모터를 반시계 방향으로 한 바퀴 회전시킨다. 스테핑 모터가 돌지 않게 하려면 0을 보내면 되고, 4바퀴를 돌게 하려면 4를 보내면 된다.
앞서 말했던 씨 브리즈(Sea Breeze)의 material_list와 quantity_list는 다음과 같다:
씨 브리즈(Sea Breeze) 재료 목록
순서 1 (초기위치) 2 3 4 5 6 7 8
재료 보드카 트리플 섹 희석된 레몬 원액 오렌지 주스 자몽 주스 크랜베리 주스
Material_list 1 0 0 0 0 0 3 2
Quantity_list 2 0 0 0 0 0 1 4
따라서 원위치에서 보드카를 2번 추출하고, 반시계방향으로 7회전해서 크랜베리 주스를 4번 추출하며, 시계방향으로 1번 회전해서 자몽 주스를 1번 추출해야 한다. 또한, 다시 초기 위치로 돌아와서 사용자에게 컵을 다시 주어야 하기 때문에 반시계방향으로 6번 회전해서 초기 위치로 돌아가야 한다. 씨 브리즈는 build 형식이기 때문에 DC 모터를 작동할 필요가 없다. 따라서 안드로이드로부터 수신한 정보를 가공한 결과는 다음과 같다:
씨 브리즈(Sea Breeze) 작업 목록
Dc_motor_state 0
순서 1 2 3 4
재료 보드카 크랜 베리 주스 자몽주스 초기위치
Disk_rotate_list 0 7 -1 -6
Dispenser_push_list 2 4 1 0


DC 모터를 사용하지 않으면 int 타입으로 0을 아두이노로 송신하고, 스테핑 모터를 돌릴 횟수 리스트 disk_rotate_list = {0, 7, -1, -6}을 송신하면, 반시계 방향으로 7번 회전, 시계방향으로 1번 회전, 시계방향으로 6번 회전한다. 양수 값을 입력하면 반시계 방향으로 돌고, 음수 값을 입력하면 시계방향으로 돌며, 0을 입력하면 이동하지 않는다. 또한, 리니어 모터를 작동시킬 횟수 리스트 dispenser_push_list = {2, 4, 1, 0}을 송신하면, 리니어 모터는 처음 위치에서 2번, 두 번째 위치에서 4번, 세 번째 위치에서 1번 재료를 디스펜서로부터 추출한다.

아두이노 우노로 제어 정보 송수신

위에서 가공한 dc_motor_state, disk_rotate_list, dispenser_push_list 세 가지를 콤마(,)로 연결하여 하나의 긴 문자열로 만들어 아두이노로 정보를 송신한다. 예를 들어 씨 브리즈(Sea Breeze)의 가공된 정보를 다음과 같이 송신한다: Sent_data=0,{0,7,-1,-6},{2,4,1} 아두이노는 이를 콤마(,)로 구분하여 각각을 다시 dc_motor_state, disk_rotate_list, dispenser_push_list로 재할당한다.

또한, 한 사용자의 요청으로 칵테일을 제조 중일 때, 다른 사용자는 칵테일을 제조할 수 없다. 따라서 제조할 수 없다는 정보를 사용자에게 제공해야 한다. 이를 위해 아두이노가 칵테일 제조를 완료하고 원위치로 돌아오면, 아두이노는 라즈베리파이에 제조 완료 신호를 다시 송신한다. 라즈베리파이가 이 신호를 받기 전까지 안드로이드 앱에서 아직 제조 중이므로 기다린 뒤에 재주문해달라는 메시지를 표시하여 동시 주문에 대한 문제를 해결한다.

네오픽셀 동작

라즈베리파이는 네오픽셀을 동작시키는데, 네오픽셀은 기기의 전원이 켜질 때, 칵테일 제조를 시작할 때, 칵테일 제조가 끝났을 때 사용자에게 가시적으로 보여주기 위한 역할을 한다. 이를 위해 rpi_ws281x, adafruit-circuitpython-neopixel, adafruit-blinka 등의 라이브러리를 사용하였다. 결과적으로, 기기가 켜지면 빨간색 불이 켜지며, 칵테일 제조를 시작할 때는 초록색, 끝났을 때에는 파란색 불이 켜지도록 하였다.

구동부

아두이노 우노는 시스템에서 Step Motor, Linear Motor, DC Motor 제어를 수행한다.


구동부 회로

Step Motor

1.step 계산
스테핑 모터는 스마트 바텐더의 디스크를 회전시키기 위해 사용된다. 디스크에는 총 8개의 재료가 위치하며, 디스크를 360도 회전하면 모든 재료를 한 바퀴 돌게 된다. 따라서 각 재료 사이의 간격은 360°를 8로 나눈 45°가 된다. 디스크의 기어와 스테핑 모터 기어의 기어비는 8:1이다. 즉, 디스크를 45° 움직이기 위해서는 스테핑 모터가 45°* 8 = 360°, 즉 한 바퀴를 회전해야 한다. 현재 구입한 스테핑 모터는 한 상이 1.8°이다. 스테핑 모터의 특성상 1010, 1001, 0101, 0110 총 4상을 계속 반복해서 모터를 제어하기 때문에 한 스텝은 1.8°* 4 = 7.2°로 계산된다. 따라서 디스크를 45°돌리기 위해서는 스테핑 모터를 360°회전시켜야 하며, 필요한 스텝의 개수는 360/7.2=50스텝이 된다. 즉, 디스크의 8칸 중 한 칸을 이동하는데 스테핑 모터에서는 50스텝이 필요하다.
2. soft start/soft stop
디스크 위에 배치된 컵에는 액체류가 채워져 있으며, 뚜껑이 없는 상태에서 이동하므로 디스크의 회전 도중 발생하는 원심력과 관성에 의해 컵의 위치가 변동하거나 내용물의 이탈 혹은 튐 현상이 발생할 수 있다. 이를 방지하기 위하여 하드웨어적인 안정화 조치(컵 가이드 제작)와 모터 제어 기술을 활용하여 동적 안정성을 향상시킨다.
스테핑 모터는 상이 변화하는 딜레이를 조절하여 속도를 제어할 수 있다. 각 상에서의 딜레이 설정은 모터의 회전 속도를 조절하며, 긴 딜레이는 회전 속도를 늦추고, 짧은 딜레이는 회전 속도를 높인다. 실험을 통해, 딜레이 1ms에서는 모터가 가장 빠르게, 딜레이 50ms에서는 모터에 무리 없이 느리게 동작함을 확인하였다. 스테핑 모터는 DC 모터와 달리 속도가 증가해도 토크가 일정하게 유지되므로, 고속 운전의 필요성이 낮다. 따라서, 최대 속도를 낼 때의 딜레이를 4ms로 설정하고, 최소 속도를 낼 때의 딜레이를 50ms로 설정하였다. 이 값들은 모터의 데이터 시트가 없어 실험을 통해 얻은 결과다.
디스크의 회전을 위한 스테핑 모터의 전체 스텝 수는 50이다. 여기서, soft start와 soft stop에 각각 최대 25 스텝을 할당하여, 회전 시작 및 정지 시 내용물의 동적 변화를 최소화한다. 만약 디스크가 4바퀴 회전한다면, 총 200 스텝이 필요하며, 이 중 25 스텝은 soft start, 150 스텝은 고속 회전(additional step), 나머지 25 스텝은 soft stop으로 구성된다.
각가속도는 각 속도에 대한 시간(ω/t )으로 정의된다. 따라서, 스테핑 모터의 스텝 당 변위각이 7.2°일 때, 딜레이를 조절함으로써 원심력을 최소화한다. 원운동에서는 선형 가속도 대신 각가속도가 작용하며, 각가속도를 최소화하기 위해 모터의 시간에 따른 각속도(ω) 변화량을 최소화하는 제어가 필요하다.
스텝과 딜레이의 관계를 나타내는 그래프에서, 딜레이의 최대치는 50ms, 최소치는 4ms이며, 스텝은 총 25 스텝으로 설정된다. 이는 기본적으로 선형적 감소를 나타내는 그래프 y = -46/25x + 50을 통해 모델링 할 수 있다. 각 스텝의 증가에 따라 딜레이를 점차 줄여 속도를 증가시키며, 이는 각속도를 시간에 따라 지수함수적으로 감소시키는 방식으로 구현된다. 따라서, 이상적인 soft start 및 soft stop을 위해 y = 49 * e^(-0.19 * x) + 4 공식을 적용하여 각 스텝에서의 딜레이를 결정한다.


y = -46/25x + 50 그래프
y = 49 * e^(-0.19 * x) + 4 그래프


한 스텝을 이동할 때 각 스텝에 대한 딜레이를 적용시켜 모터를 제어하면 soft start를 구현할 수 있다. Soft stop은 딜레이 값들을 역순으로 정렬하여 구현한다. 따라서 모터가 멈출 때에도 원심력이 최대한 작용하지 않게 제어한다.


소프트 스타트/스탑


Linear Motor

리니어 모터는 디스펜서로부터 음료를 추출하기 위해 사용한다. 모터드라이버의 제어를 통해 수직 상승과 하강 운동이 가능하다. 또한, DC 모터를 이용하기 때문에 속도와 출력이 비례한다. 리니어 모터는 최소 60의 PWM 신호에서 작동하기 시작하며 255에서 최고속도로 동작한다.

DC Motor

DC 모터는 컵을 회전시켜 음료와 얼음의 열교환을 돕기 위해 사용한다. DC 모터는 컵을 시계 방향과 반시계 방향으로 번갈아가며 회전하도록 하여 컵 내의 음료와 얼음이 충분히 마찰하도록 돕는다. 컵을 번갈아가며 회전하도록 하기 위해 양방향 모터드라이버를 사용하며 PWM 신호를 통해 회전 방향과 속도를 조절한다.

통신

통신 방식

통신은 Wi-Fi 통신과 Bluetooth 통신 방식을 비교하여 블루투스 통신 방식을 적절하다고 판단하였고, 이를 채택하여 데이터 송수신을 하였다. Wi-Fi 통신과 비교한 블루투스 통신의 이점은 다음과 같다.

  1. 더욱 간결한 구조 :
    와이파이와 블루투스 통신 개략도
    그림과 같이 Wi-Fi 통신을 이용하려면 공유기를 통해 포트포워딩을 설정해야 하며, 외부로부터의 데이터 수신을 차단한 시설에서는 이용에 제약이 있다. 또한, 공인 IP를 할당받아야 하는 등 엔지니어링적인 요소가 필요해 실용적인 용도에 제약이 발생한다. 반면에 블루투스 통신의 경우, 라즈베리파이 내부에 이미 블루투스 칩이 내장되어 있어 공유기와 같은 별도의 모듈이 필요하지 않다. 또한, 더욱 간결해진 통신 구조로 인해 디버깅 및 문제점 파악이 용이할 뿐만 아니라 엔지니어링 요소와 비용이 확연하게 줄어들었다.
  2. 적절한 통신 거리 : Bluetooth 통신은 근거리 통신에 적합하여, 사용자가 바텐더 근처에 있을 때만 명령을 내릴 수 있다. 이는 스마트 바텐더의 사용 환경과 맞아떨어진다.
  3. 보안 강화 : Bluetooth 통신은 물리적인 근거리에서만 작동하기 때문에 원거리에서 악의적인 데이터 전송을 방지할 수 있다.

프로토콜

스마트 바텐더가 안드로이드로부터 받는 데이터는 사용자의 레시피 제작을 위한 레시피 데이터와 잔량 확인을 위해 요청하는 특정 데이터 신호이며, 이에 스마트 바텐더는 안드로이드로 적절한 응답을 전송해야 한다.

이전에는 스마트 바텐더와 안드로이드 간의 데이터 교환에는 일관된 규칙이 부재했다. 통일된 데이터 규칙을 도입함으로써 두 기기 간의 통신 신뢰성을 강화할 수 있으며, 명확한 데이터 구조는 스마트 바텐더와 안드로이드 간의 통신을 더욱 효율적으로 만든다. 따라서, 효율적인 송수신을 위해 자체적인 데이터 규칙(프로토콜)을 정의하여 사용한다.

프로토콜에서 각 데이터 영역은 '\n\n'(개행문자 두 개)로 구분되며, 데이터 영역 내에서 인덱스는 '\n'(개행문자 한 개)로 구분된다.
상세 내용 및 규칙은 아래와 같다:
프로토콜 상세 규칙
  1. Head: 안드로이드가 스마트 바텐더에게 데이터를 요청할 때, 어떤 요청을 했는지 식별하는 번호로 기능한다. 스마트 바텐더가 안드로이드에게 응답을 보낼 때, 바텐더가 요청한 명령어의 실행 성공 여부 또는 잔량 요청에 따른 현재 잔량 데이터를 전달한다. 자세한 Head 규칙은 다음과 같다.
    데이터 송신자와 상세 설명
    데이터 송신자 Head 상세 설명
    안드로이드 1 스마트 바텐더의 베이스 칵테일 잔량을 요청
    2 순서가 없는(스터링) 칵테일 레시피 제조를 요청
    3 순서가 있는(빌드) 칵테일 레시피 조주를 요청
    4 스마트 바텐더에 베이스 칵테일 용액 추가(관리자 모드)
    스마트 바텐더 0 안드로이드가 요청한 명령어 실행에 실패(불가능)
    1 안드로이드가 요청한 명령어 실행에 성공(가능)
  2. Data_1: 베이스 칵테일의 잔량과 칵테일 레시피, 관리자 모드에서 갱신된 칵테일의 양 등의 데이터를 포함하는 영역이다.
  3. Data_2: 순서가 있는 레시피(빌드) 제조를 요청할 때 사용하는 데이터 영역이며, Data_1에서 기재된 칵테일 순서를 명시한다.
프로토콜 전송 예시
프로토콜 규칙을 적용한 데이터 전송 예시는 위와 같다.
안드로이드에서 아두이노까지의 데이터 전송 과정
최종적으로, 블루투스 통신을 통해 받은 데이터를 프로토콜 규칙을 이용해 디코딩하여 명령을 해석하는 기능을 구현하였고, 해석된 명령은 스마트 바텐더의 다양한 모터(리니어, 스텝, DC)를 제어하는 명령으로 변환된다. 이를 통해 사용자의 요구에 따른 음료를 제조하기 위해 필요한 모터 제어를 가능하게 하였다.

멀티스레드 블루투스 서버 구현

단일서버와 멀티스레드 서버의 통신 차이
  • 멀티 스레드 서버
스마트 바텐더는 사용자의 개인 안드로이드 폰으로 제어되며, 동시접속을 통해 음료 제조 가능 여부, 음료의 잔량 확인, 칵테일 음료 추천 등을 처리해야 한다. 일반적인 서버는 다수의 클라이언트 요청을 처리하지만, 단일 스레드 서버는 하나의 요청을 처리할 때 다른 요청을 처리할 수 없다. 이를 해결하기 위해, listen을 위한 메인 스레드를 생성하고, 클라이언트 요청 시 새로운 스레드를 생성하여 요청을 처리한다. 이렇게 하면 각 클라이언트의 요청이 독립적으로 처리되어 서버의 효율성이 향상된다.
  • 공유 데이터 접근 보호 및 세마포어 활용
Mutex를 활용한 공유데이터 및 자원 처리
스레드를 이용해 공유변수인 칵테일 잔량 데이터를 읽거나 쓰는 작업 시, 세마포어를 사용하여 크리티컬 섹션에서 의도치 않은 데이터 변경 및 잘못 읽히는 경우를 방지하는 코드를 작성하였다. 세마포어는 동시 접근을 제어하며, 특정 자원의 가용 개수를 지정할 수 있다. 이를 통해 칵테일을 제조하는 동안에는 다른 프로세스가 해당 데이터에 접근하지 못하게 한다.
데이터 접근 보호 및 세마포어 활용의 주요 이점은 다음과 같다.
  1. 세마포어를 통해 데이터의 동시 접근을 제한하여 데이터 일관성을 유지한다.
  2. 칵테일 제조 기간 동안 세마포어를 사용하여 데이터 접근을 제한함으로써 시스템 오류를 방지한다.
이렇게 세마포어를 사용하면 시스템의 안정성과 데이터의 무결성을 보장할 수 있다.
칵테일 동시 접속 예시
위 사진은 스마트 바텐더와 여러 사용자가 동시 접속하여 데이터를 송수신하는 상황을 보여주고 있다. A 사용자는 먼저 칵테일 제조 요청을 보낸 후 제조 완료까지 대기 중이고, B 사용자는 칵테일을 제조 중인 상황에서 추가 제조 요청을 보내면 'Wait'라는 토스트 메시지를 통해 대기 중임을 알린다. 이를 통해 멀티스레드 서버를 통해 다중 클라이언트의 요청을 처리하고, 세마포어를 활용하여 예외 상황을 처리할 수 있게 된다.

안드로이드 앱

시작화면

앱 시작 화면
안드로이드 앱의 시작 화면이다.

먼저, 사용자는 Pairing 버튼을 눌러 주변 블루투스 장치를 스캔해야 한다. 페어링 결과 ‘SmartBartender’라는 이름을 가진 블루투스 장치를 발견하면, 해당 항목을 클릭하여 스마트 바텐더와 연결된 후, 사용자는 다음 기능들을 사용할 수 있다: 칵테일 고르기, 칵테일 커스텀하기, 칵테일 추천받기, 개발자 모드

칵테일 고르기

사용자가 칵테일 고르기를 선택하면 스마트 바텐더는 현재 보유한 재료를 기반으로 사전에 정의된 칵테일 목록을 화면에 표시한다. 이러한 칵테일 목록은 스마트 바텐더가 사용하는 재료를 기준으로 사전에 구성된 것이다. 각 칵테일은 이름과 함께 이미지로 제시되어, 사용자가 칵테일을 쉽게 구분할 수 있다.

칵테일 목록(왼쪽), 칵테일 상세정보 예시(오른쪽)

사용자가 원하는 칵테일을 선택하면, 해당 칵테일에 대한 상세 정보가 표시된다.

사용자는 칵테일 정보를 확인한 후 해당 칵테일을 만들기 원한다면 선택 버튼을 누른다. 선택 버튼을 누르면 앱은 블루투스 통신을 이용해 해당 칵테일의 레시피를 라즈베리파이로 전송한다. 선택 버튼을 누르지 않는 경우, 사용자는 뒤로가기 버튼을 눌러 다른 칵테일 목록을 확인할 수 있다.

칵테일 추천받기

칵테일 추천받기 기능은 OpenAI API를 이용해 사용자의 선호나 현재 기분을 바탕으로 적절한 칵테일을 챗봇 형식으로 구현한다. 해당 기능을 구현하는 데에는 두 가지 고려사항이 있다. 첫째는 사용자가 챗봇과 대화하며 자신의 요구사항을 말할 수 있어야 한다는 것이다. 이는 OpenAI API중 Assistants 기능을 활용하여 진행 중이다. 둘째는 시스템 내부에서 칵테일 레시피를 사용할 수 있도록 정의한 형식에 따라 레시피를 반환해야 한다는 것이다. 이는 Few-shot Learning 방법을 사용해 원하는 형식의 응답을 받을 수 있도록 했다. 두 가지 고려사항에 대한 자세한 설명은 아래로 이어진다.

  • OpenAI API
본격적인 설명에 앞서, 안드로이드 앱에서 OpenAI API를 어떻게 사용했는지 설명한다. 공식 OpenAI API는 파이썬에서만 지원되므로, API를 사용하기 위해서는 파이썬 파일을 실행시켜 ChatGPT와 대화를 주고받아야 한다. 그러나, 안드로이드 앱 개발 언어인 코틀린은 해당 API를 사용할 수 없고, 안드로이드 스튜디오는 파이썬 컴파일러를 지원하지 않는다. 따라서 OpenAI API 공식 문서에 기재된 커뮤니티 라이브러리 중 코틀린에 해당하는 라이브러리([1])를 찾아 사용하기로 했다.
  • Assistants 기능
OpenAI API는 Assistants 기능을 제공한다. Assistants는 제공된 Instructions에 따라 작동하며 새로운 응답을 생성할 때 이전 대화의 토큰들을 참조하여 응답할 수 있게 한다.
Assistant 모델은 ‘gpt-4-turbo’를 사용했다. 가장 최근에 공개된 ‘gpt-4o’는 최근에 공개된 만큼 파이썬에서는 사용 가능하나 코틀린 라이브러리에서는 아직 지원되지 않기 때문이다.
  • Few-shot Learning
사용자의 입력에 대해 원하는 형식의 응답을 받도록 하기 위해 Few-shot learning을 사용한다. 이는 단순히 프롬프트를 생성함에 있어 몇 가지의 예시를 줌으로써 간단하게 구현할 수 있다.


결론적으로, 칵테일 추천 기능은 사용자의 기분, 선호도, 원하는 칵테일의 맛이나 분위기 등을 자유롭게 입력받아, 적합한 칵테일을 추천하고 해당 칵테일의 레시피를 제공할 수 있게 한다. 아래는 예시 동작 사진이다.

칵테일 추천 예시(왼쪽) 추천받은 칵테일을 선택한 경우(오른쪽)

칵테일 커스텀하기

사용자가 칵테일 커스텀하기를 선택하면, 현재 보유한 재료 목록을 보여준다. 각 재료는 “+”버튼과 "-” 버튼을 통해 사용자가 원하는 양을 조절할 수 있다. 이때 버튼을 눌러 조절하는 재료 양의 단위는 펌프(한 펌프당 30mL)이다. 즉, 사용자가 “+”버튼을 한 번 누르면 해당하는 재료가 한 펌프 추가되었음을 뜻한다. 우측 상단에 있는 ! 버튼을 클릭하면 이에 대한 설명을 나타내는 다이얼로그가 표시된다.

커스텀하기 기본 화면(왼쪽), 커스텀 단위 표시 다이얼로그(오른쪽)

재료의 총합이 7펌프(210mL)을 넘는 경우에는 음료가 넘칠 수 있으므로 이에 대한 주의를 알리는 다이얼로그를 표시한다.

커스텀하기 기본 화면(왼쪽), 커스텀 단위 표시 다이얼로그(오른쪽)

사용자가 적절한 양으로 원하는 재료를 조합한 경우, 앱은 해당 칵테일의 제조 방법을 묻는다. 제조 방법은 ‘Stir’과 ‘Build’로 나뉜다. Stir은 모든 재료를 넣고 섞는 방법이고, build는 각 재료를 정해진 순서대로 넣는 방법이다. 이때, build는 stir와 달리 재료들을 섞으면 안 된다. 사용자가 stir을 선택했을 경우 선택한 재료와 그 양의 데이터가 라즈베리파이로 전송된다. 라즈베리파이는 받은 레시피 데이터를 기반으로 스마트 바텐더를 조작해 칵테일을 제조한다. 반면 사용자가 build를 선택한 경우, 사용자는 재료가 컵에 들어갈 순서를 지정해야 한다. Build를 선택한 후 표시되는 화면은 사용자가 선택한 재료의 이름 기준으로 오름차순 목록을 보여준다. 사용자는 해당 목록을 드래그 앤 드롭 방식으로 옮겨 직관적으로 순서를 바꿀 수 있다.

커스텀시 선택한 재료 목록 예시. 기본적으로 재료의 이름 기준으로 오름차순으로 표시된다(왼쪽). 드래그 앤 드롭으로 순서를 변경한 화면 예시(오른쪽).

개발자 모드

기존에는 칵테일의 잔량을 수동으로 수정하기 위해 개발자가 직접 코드 수준에서 데이터를 변경해야 했다. 그러나 개발자 모드의 도입을 통해 다음과 같은 개선이 이루어졌다.

1. 실시간 소스 코드 확인 기능: 개발자 모드를 통해 현재 칵테일의 레시피 데이터를 실시간으로 확인할 수 있게 되었다. 이를 통해 관리자는 즉각적으로 잔량을 검토하고 필요한 변경 사항을 파악할 수 있다.

2. 직관적인 양 조절 기능: -, + 버튼을 통해 원하는 인덱스에 해당하는 칵테일의 잔량을 쉽게 변경할 수 있다. 이 기능은 관리자가 직관적으로 필요한 양을 조절할 수 있게 하여 작업의 효율성을 높인다.

3. 블루투스 통신을 통한 소스 코드 갱신: 버튼을 통해 블루투스 통신으로 소스 코드의 내용을 갱신할 수 있다. 이를 통해 소스 코드 변경 사항이 실시간으로 반영되며, 관리자는 빠르고 효율적으로 작업을 진행할 수 있다.

개발자 모드 화면 예시. 각 재료의 잔량을 한눈에 파악할 수 있다.

챗봇: OpenAI API

챗봇의 주요 기능 및 고려사항

  • 주요 기능
1. 사용자와 대화하듯 해당 칵테일을 추천하는 이유를 설명한다.
2. 대화를 통해 최종적으로 사용자가 선택한 칵테일의 레시피를 제공한다.
  • 모듈 개발 시 고려사항
1. 챗봇과 사용자가 대화하는 느낌을 주기 위해 이전 대화를 참조할 수 있어야 한다.
→ OpenAI API의 Assistants 기능 사용
2. 시스템 내부에서 칵테일 레시피를 사용할 수 있도록 미리 정의한 형식에 따라 레시피를 반환해야 한다.
Few-shot Learning 방법을 사용해 원하는 형식의 응답을 생성

Assistants

  • Assistants?
OpenAI의 API는 Assistants 기능을 제공하며, 이는 제공된 Instructions에 따라 작동하며 한 Assistants는 Thread 개념으로 새로운 응답을 생성할 때 이전 대화의 토큰들을 참조하여 응답할 수 있다.
Assistant 동작 원리
  • 프로젝트에서 사용한 Assistants instructions
You are an AI bartender. First, receive the inventory as a dictionary named 'example_dict', then consider the user's mood and preferences to recommend a cocktail. Ensure that the total volume of ingredients does not exceed 250ml. Use a specific delimiter (@) to separate the cocktail recommendation from the recipe, which should be provided in a structured list format, Integer number of 30ml pumps required for each ingredient. Ingredients in order: [Vodka, Rum, Gin, Triple Sec, Diluted Lemon Juice, Orange Juice, Grapefruit Juice, Cranberry Juice]
위와 같은 Instruction을 사용함으로써 Assistant의 역할을 설명해주고, 레시피로 작성해야 할 순서를 알려줄 수 있다.


Few-shot Learning

  • Few-shot Learning?
현재 많은 모델은 Pretraining – Fine-tuning 과정을 거친다. Pretraining 단계에서는 방대한 데이터를 사용해 언어 모델을 학습시키고, 이로써 생성된 Pretrained 모델을 기반으로 각 용도에 맞게 Fine-tuning을 진행한다. 그러나 Fine-tuning 과정에서도 많은 양의 데이터와 GPU 리소스가 필요하며, 해당 프로젝트에서 그러한 데이터셋을 생성하기에는 제한적일 수 있다. 이때 매우 적은 양의 데이터로 원하는 응답을 얻을 수 있는 방법이 Few-shot Learning이다.
본 프로젝트에서는 사용자의 입력에 대해 원하는 형식의 응답을 받도록 하기 위해 Few-shot learning을 사용한다. 이는 단순히 Prompt를 생성함에 있어 몇 가지의 예시를 줌으로써 간단하게 구현할 수 있다.
  • Few-shot Learning 적용
example_user_mood1 = "오늘은 상쾌하고 열대의 풍미가 가득한 칵테일을 마시고 싶어요."
example_gpt_response1 = "그런 날에는 '허리케인'을 추천드릴게요. 럼과 오렌지 주스가 어우러져 상쾌하고 달콤한 맛이 특징입니다. 열대 지방의 느낌을 가득 느끼실 수 있을 거예요.@[0,2,0,0,0,2,0,1]"

example_user_mood2 = "오늘 뭔가 상큼하고 쌉쌀한 맛이 나는 칵테일이 생각나네요."
example_gpt_response2 = "그렇다면 '그레이 하운드'를 추천드려요. 보드카와 자몽 주스가 어우러져 상큼하면서도 쌉쌀한 맛이 매력적인 칵테일입니다.@[2,0,0,0,0,0,0,3]"

example_user_mood3 = "오늘은 좀 세련된 분위기의 칵테일을 마시고 싶어요."
example_gpt_response3 = "세련된 분위기를 원하신다면 '코스모폴리탄'이 제격입니다. 보드카와 크랜베리 주스, 그리고 트리플 섹이 어우러져 우아한 맛을 느끼실 수 있습니다.@[2,0,0,1,0,0,0,1]"

example_user_mood4 = "오늘은 강렬한 맛이 나는 칵테일이 마시고 싶어요."
example_gpt_response4 = "그렇다면 '레드 데빌'을 추천드립니다. 보드카와 크랜베리 주스, 그리고 레몬 주스가 어우러져 강렬하고 상큼한 맛을 느끼실 수 있습니다.@[2,0,0,0,1,0,0,4]"

example_user_mood5 = "오늘은 깔끔하면서도 우아한 칵테일을 마시고 싶어요."
example_gpt_response5 = "그렇다면 '화이트 레이디'를 추천드려요. 진과 트리플 섹, 그리고 레몬 주스가 어우러져 깔끔하고 우아한 맛을 자랑하는 칵테일입니다.@[0,0,2,1,1,0,0,0]"

example_user_mood6 = "친구들과 함께 즐길 수 있는 재미있는 칵테일이 필요해요."
example_gpt_response6 = "그렇다면 '롱 비치 아이스티'를 추천드릴게요. 보드카, 럼, 진, 그리고 크랜베리 주스가 어우러져 강렬하면서도 상쾌한 맛을 느끼실 수 있습니다.@[1,1,1,1,1,0,0,0]"

example_user_mood7 = "오늘은 상큼하고 달콤한 칵테일이 생각나요."
example_gpt_response7 = "상큼하고 달콤한 맛을 원하신다면 '레몬 드롭 마티니'를 추천드립니다. 보드카와 레몬 주스, 그리고 트리플 섹이 어우러져 상큼하면서도 달콤한 맛이 일품인 칵테일입니다.@[2,0,0,1,1,0,0,0]"
Few-shot prompt
위의 예시와 같이 미리 정의해둔 사용자 입력 - GPT 응답 시퀀스를 예시로 주어 Few-shot learning을 구현할 수 있다. 기존에는 5개의 예시만을 사용했지만, 응답의 강건성을 향상하기 위해 예시를 7개로 늘리고, 너무 많은 제약사항으로 인해 응답에서 사소한 제약이 어긋나는 경우를 보완하기 위해 후처리 모듈 또한 개발하였다.
  • 사용 예시
응답 예시
Prompt에 예시를 추가했다면, 위와 같이 새로운 응답 또한 예시와 비슷한 형태로 나올 것이라고 기대할 수 있다. 아래와 같은 응답을 받음으로써 우리 시스템은 정해진 구분자('@')를 기준으로 데이터를 파싱하고, 대화 부분만 출력하여 챗봇을 구현할 수 있다. 또한, 사용자가 해당 칵테일을 선택하면 리스트 형태의 데이터를 라즈베리파이로 보내 칵테일 조주를 시작한다.

후처리 모듈

  • 후처리 모듈?
현재의 요청에는 많은 제약 사항이 존재한다. 한글로 대답해야 하며, 특정 구분자(‘@’)를 기준으로 앞부분에는 해당 칵테일을 추천하는 이유, 뒷 부분에는 정형화된 형식으로 레시피를 작성해야 한다. 이러한 엄격한 조건으로 인해 API의 응답에는 종종 사소한 제약들이 무시된다. 예시로 ‘최대 용량’이 있다. 따라서 이러한 사소한 제약을 벗어나는 응답을 후처리 하기 위한 모듈을 개발했다.
  • 정량 유지
해당 모듈에서는 사용자가 해당 칵테일로 선택했을 때(예시에서는 ‘q’를 입력했을 때) 레시피를 저장하는데, 해당 레시피의 총량이 7을 초과하는지(210ml) 확인한 후 칵테일의 비율을 최대한 유지하며 절대적인 양을 조건 이내로 들어오도록 처리한다. 아래의 사진을 예시로 들 수 있다.
정량 초과로 인한 조정 예시
  • 챗봇 고도화
또한, 위와 같은 모듈과 마찬가지로 응답의 강건성을 보완하기 위해 정형화된 형식으로 응답이 생성되지 않았을 때를 처리하는 기능을 개발했다. 해당 기능에서는 사용자와 간단한 의사소통을 할 수 있도록 하기 위해 사용자가 ‘안녕? 좋은 아침!’이라는 요청을 보냈을 때, ‘안녕하세요! 좋은 아침입니다! 오늘 아침은 어떤 종류의 칵테일을 선호하시나요? 상큼하고 발랄한 아침에 어울리는 칵테일을 추천해 드릴게요!’와 같은 응답을 생성하더라도 화면에 표시할 수 있도록 처리하는 부분이다.
간단한 의사소통 예시


이렇게 다양한 방법을 적용하여 프로젝트 목적에 맞는 칵테일 추천 모듈의 완성도를 높일 수 있었다.

역할 분담

역할 담당자 세부 내용
하드웨어 설계 정* 모든 하드웨어 구성요소 선정, 시스템 모델링 및 테스트
유*원 하드웨어 구성 및 시스템 통합
Low-level 제어 로직 개발 유*원 제어 시스템 알고리즘 개발, 모터 등의 하드웨어 제어 구현
정* 모터 제어 로직의 안전성 검증
통신 조*빈 안드로이드 애플리케이션과 하드웨어 간의 통신 프로토콜 구현
김*랑 프로토콜 구현 및 테스트
안드로이드 애플리케이션 개발 김*랑 사용자 인터페이스(UI)의 디자인 및 기능 구현, 애플리케이션 주요 기능 개발
조*빈 애플리케이션의 추가적인 기능 구현 및 테스트
High-level 로직 개발 이*헌 프로젝트 관리, OpenAI API 관리 및 Chat GPT 기능 구현

평가 항목

최종 평가

필요 요구사항 및 평가 기준
필요 요구사항 평가 기준 달성도
오류 없이 칵테일을 정확하게 제조할 수 있는가 각 재료 결과물 오차 5ml 이하 100%
사용자가 원하는 칵테일을 추천할 수 있는가 설문 - 추천 기능 만족도 8점 이상 100%
챗봇 응답시간은 불편을 느끼지 않을 만큼 신속한가 2초 이내[2] 94%
컵이 정확한 위치로 이동하는가 20회 반복 측정 오차각 5도 이하 100%
실시간 재고가 정확하게 갱신되는가 재고 관리 이상으로 인한 오류 발생 X 100%
안드로이드 애플리케이션의 인터페이스가 직관적이며 사용자 친화적인가 설문 - 앱 만족도 8점 이상 100%

설문조사

  • 설문조사 QR 코드
설문QR.png
  • 설문조사 항목
설문 조사 항목
번호 질문
Q1 기기의 외관이 분위기를 해치지 않게 제작되었다.
Q2 칵테일 제조 과정 중 기기가 안정적으로 동작했다.
Q3 칵테일의 애플리케이션에서 제공받은 설명대로 제조되었다.
Q4 챗봇의 응답 속도가 만족스러웠다.
Q5 챗봇의 칵테일 추천이 적절하고 취향에 맞았다.
Q6 애플리케이션의 인터페이스가 직관적이었다.
Q7 애플리케이션의 디자인이 주제에 잘 어울린다.
Q8 칵테일 제조 기기와 애플리케이션 간의 연결이 안정적이었다.
Q9 전반적인 사용자 경험에 만족하였다.
Q10 [관리자] 재고 관리 기능이 편리하고 불편함 없이 진행되었다.
Q11 [관리자] 기기와 애플리케이션의 연동이 원활하게 이루어졌다.


  • 설문조사 결과
설문조사 결과
총 22명의 사용자를 대상으로 설문조사를 진행한 결과, 위와 같이 높은 점수의 결과를 얻었다.
평균 점수는 5점 만점에 4.92이며 '전반적인 사용자 경험에 만족하였다.' 항목에서는 22명 설문 응답자 모두에게 5점의 평가를 받았다.

결과

  • Github
BartendAiRtist
  • 발표 포스터
발표 포스터

개발 과정 영상

블루투스 시연(실패)

하드웨어 뼈대 구축

최종 시연 영상

현장 시연(설문조사)

최종 발표

최종 시연

프로젝트를 진행하며 느낀 점

이*헌

한 학기 동안 자신의 부족함을 많이 알 수 있는 과목이었다. 주제 선정부터 제품이 기여할 수 있는 부분을 생각하고 실제로 사용하게 되었을 때 고객의 니즈를 고려하여 사양을 설계하고 실제로 구현하는 과정 등에서 많은 어려움이 있었지만, 재미있게 진행할 수 있는 주제를 선택한 점과 최선을 다해 임해준 팀원들 덕분에 프로젝트를 완수할 수 있었다. 또한 부족한 우리 팀을 위해 많은 조언을 해주신 조교님들과 교수님께 감사드리고, 칵테일 관련 자문에 흔쾌히 협조해주신 'MNED' 사장님께도 감사 인사를 드리고 싶다. 막상 되돌아보면 팀장으로써의 기여는 크지 않았던 것 같지만 해당 프로젝트의 경험을 가지고 실제로 사회에 나가서 어떤 조직의 일원으로 활동하며 마주치게 될 여러 문제들을 유연하게 대응할 수 있을 것이라고 생각한다.

김*랑

지금까지 내가 학부 수업에서 진행한 프로젝트 중 가장 큰 규모라고 생각한다. 나는 안드로이드 앱 개발을 주로 맡았는데, 충분한 지식을 갖추지 못한 상태에서 시작했기 때문에 초반에는 두려움과 막막함이 가득했다. 하지만 꾸준히 강의나 자료를 찾아보며 앱을 점차 완성해가는 과정에서 자신감이 생겼고, 나중에는 두려움보다 즐거움을 더 많이 느꼈던 것 같다. 하드웨어와 통합 후, 우리가 만든 앱으로 스마트 바텐더를 조작해 칵테일을 만드는 것은 정말 신기한 경험이었다. 또한, 이번 프로젝트를 통해 문서 작업의 중요성과 팀원 간 소통이 얼마나 중요한지 알게 되었다. 프로젝트를 진행하면서 체계적인 문서 작업이 결과물의 완성도에 큰 영향을 미친다는 것을 배웠다. 또한, 팀원 간의 원활한 의사소통이 프로젝트의 성공에 필수적이라는 것을 실감했다. 이러한 경험은 앞으로의 프로젝트에서도 큰 도움이 될 것이다.

설계에 어려움을 겪을 때 많은 도움을 준 교수님과 조교님들, 칵테일 관련 자문에 큰 도움을 준 'MNED' 사장님께 감사 인사를 전한다. 또한, 각자의 자리에서 최선을 다하고 어려움을 느낄 때 적극적으로 도움을 준 팀원들에게도 진심으로 감사한다.

유*원

임베디드 시스템은 지금까지 들었던 그 어떤 학부 수업보다도 난이도와 시간 소요가 컸지만, 동시에 가장 큰 성취감을 얻을 수 있었던 과목이었다. 난생 처음 회로 구현을 시도하면서 막연함과 걱정이 많았지만, 교수님의 조언과 조교님들의 피드백, 그리고 학부 동기들의 도움 덕분에 회로를 잘 구성할 수 있었다. 특히, 회로 구현뿐만 아니라 처음 접해본 아두이노 코딩과 모터 제어에서도 많은 성장을 할 수 있었다. 개발 과정과 통합 과정에서 생긴 어려움을 함께 고민하고 해결해준 팀원들에게 깊이 감사드린다. 초기 개발과정에서 칵테일 제조 방법과 재료들을 상세히 알려주신 MNED 매니저님의 도움 덕분에 프로젝트를 순조롭게 진행할 수 있었다. 또한, 시연회 때 찾아와 주신 모든 분들께도 감사의 인사를 전한다. 이번 프로젝트를 통해 얻은 지식과 경험은 단순한 학습을 넘어 문제 해결능력을 키우는 데 큰 도움이 되었고, 협업의 중요성도 깊이 깨달을 수 있었다. 이 모든 경험은 앞으로의 학업과 직장생활에서 큰 자산이 될 것이라 확신한다.

정*

최적설계와 구조해석을 공부하고 있는 나에게 임베디드시스템이라는 과목은 다소 낮선 과목이었다. 그럼에도 이 과목을 수강한 이유는 임베디드시스템이 전국에 있는 기계과 중 서울시립대 기계정보공학과에서만 들을 수 있는 과목이라고 생각했던 것이 가장 컸다. 또한, 직접 제작한 기구를 제어하는 프로젝트를 수행한다는 것에 마음이 끌린 것 같다. 낯선 과목이었던 만큼 수업을 따라가는 것이 벅찼다. “컴퓨터구조론”과 “시스템소프트웨어”가 선수과목은 아니나 여기서 연장되는 개념이 많았던 만큼 이론을 이해하는 것이 어려웠다. 실습 역시도 가장 늦게 따라가서 수업조교님들이 소위 "금쪽이"를 보살피듯 딱 붙어서 관리해주셨던 것 같다. 이렇게 여러 분들의 도움을 받으며 포기하지 않고 따라간 결과, 후반부에 가서는 실습도 혼자 하고 과제도 잘 해낼 수 있었다. 옆에서 쳐다보며 답답했겠지만 그럼에도 열심히 알려준 교수님, 조교님 그리고 동기들에게 감사한 마음을 전하고 싶다.

프로젝트에서는 기구부 설계와 제작을 메인으로 맡고, 회로 구성과 Low-Level 로직 개발을 서브로 맡았다. 이런저런 기구설계를 자작자동차 소모임과 최적설계연구실에서 해본 적이 있으나 기계시스템을 처음부터 끝까지 다 설계하고 제작해야 하는 것은 임베디드시스템 프로젝트가 처음이라 꽤 걱정이 되었다. 회로 구성과 Low-Level 로직 개발은 나와 유*원 학생 둘 다 처음 해 보는 것이라 더더욱 걱정이었다. 이렇게 걱정이 많았던 만큼 평소에도 이것들에 대해 계속 고민하고 찾아보며 불완전성을 줄이기 위해 노력했다. 한 학기 동안의 이런 노력을 바탕으로 좋은 결과를 낳게 되어 기쁘게 생각하며, 도움을 주신 많은 분들(사촌형, MNED 바텐더님 등)께 감사함을 전하고 싶다. 해당 프로젝트를 발전시켜 "임베디드시스템 소프트웨어 경진대회"에 나가기로 결정한 만큼 현재 결과물에 남아있는 문제점을 해결하고 좋은 성적을 얻기 위해 최선을 다할 것이다.

조*빈

결코 혼자서는 여기까지 올 수 없었을 것이라고 생각한다. 결과적으로 최종 시연까지 교수님의 조언과 조교님들의 관심 덕분에 좋은 결과를 얻을 수 있었고, 칵테일에 대해 잘 모르고 무작정 달려들었던 우리들에게 재료부터 레시피까지 모두 도와준 ‘MNED’ 바텐더님께도 깊이 감사드린다. 또한, 프로젝트를 함께 진행하며 기술 공유와 우리가 놓쳤던 피드백을 제안해준 ‘베리해적단 팀’, 마지막으로, 한 명도 빠짐없이 프로젝트에 진심을 다해 자기 역할에 충실했던 팀원들까지, 좋은 사람들 덕분에 좋은 결과를 얻을 수 있었고 마무리까지 훌륭하게 끝낼 수 있었다. 이 모든 경험을 통해 많은 것을 배웠으며, 모든 분들께 진심으로 감사드린다.

  1. 팀장
  2. 챗봇 응답시간 기준의 자료가 없어, 일반적인 시스템에서 사용자가 몇 초 이내의 응답 시간을 선호하는지에 대한 조사 결과로 1~2초 이내의 응답이면 사용자는 불편을 느끼지 않는다는 근거 하에 책정