안녕하세요? 이번 포스팅에서는 TJBot을 Bluemix의 IoT 서비스인 IoT Foundation과 연동하는 내용을 다루도록 하겠습니다.

본 글은 순서대로 이어지는 내용이라 앞서 글의 내용이 진행된 상태를 가정합니다. 처음 이글을 보시는 분들은 앞의 글을 먼저 확인 부탁드립니다.

앞서 포스팅에서는 Watson Conversation을 구성하고 사용자의 의도를 파악하고 명령으로 인식하도록 TJBot에게 메시지를 전달하는 방식이었습니다. 비록 Dialog를 이렇게 할 수도 있다는 예시로서 포스팅을 하긴 했지만, Watson Conversation 메시지 분석을 위한 서비스인데 이를 TJBot의 명령 처리를 위해 로직을 추가하는 것이 적합한 접근 방법인지에 대한 의문은 남아 있는 상태였습니다.

그래서, 이번에는 TJBot의 제공 기능을 단위 기능으로 정의하고 서비스를 구성 할 때 이를 조합하는 형태를 구상하게 되었습니다. 우선은 서비스를 위한 서버가 필요하고 TJBot과 통신하기 위해 API를 정의해야 하는 형태가 되어야 하며 또한 Conversation 서비스까지 연동이 필요하게 되는 end-to-end 구조가 되어야 하는데 이를 아무런 기반 없이 처음부터 설계하고 개발하기에는 무리가 있는 상황이었습니다. 그래서 생각해 본 것이 Bluemix에서 제공하는 IoT Foundation Platform이었습니다.

IBM IoT Platform은 IoT 단말과 및 서비스앱에 대한 아키텍쳐를 제공하므로 TJBot을 IoT 단말이라고 가정하고 서비스를 구성하는 것이 TJBot에 대한 제어를 원격으로 할 수 있는 빠른 방법이라고 생각해 볼 수 있었습니다. 게다가 Bluemix에서 제공하는 IoT Starter Kit은 WebUI 도구로서 서비스 로직을 손쉽게 구성 할 수 있는 Node-RED를 제공하므로 TJBot이 가진 기능과 로직 기능 구현에 좀 더 집중 할 수 있을 것이란 생각으로 시작하게 되었습니다.

IoT 단말로서 TJBot 구성

IoT 단말로서 TJBot을 구성한다면 다음과 같은 서비스 플로우에 사용하는 것으로 구성해 보았습니다.

tjbot_iotf

TJBot의 단위 기능 정의

TJBot을 IoT 단말로 본다면, TJBot이 할 수 있는 것을 정리하고 이를 IoT Command와 IoT Event로 매핑해야 합니다.

다음과 같이 TJBot에서 내릴 수 있는 명령(Input)의 종류는 다음과 같이 정리 볼 수 있습니다.

  • LED 제어 : LED On/Off 및 색상 변경
  • 서보 모터 제어 : TJBot의 팔을 올리거나, 내리거나 특정 위치로 움직입니다.
  • 음성 출력 : Text를 음성으로 변환하여 스피커로 출력

그리고, TJBot이 생성(Output) 할 수 있는 기능은 다음과 같습니다.

  • 음성 인식 : 마이크로 받은 음성을 Watson STT 서비스를 이용하여 Text로 변환합니다.

이렇게 정의한 TJBot의 단위 기능을 IoT Command와 Event로 매핑해 보도록 하겠습니다.

기능 명령 데이터 데이터 타입
LED제어 lighting color code string
서보제어 servo precent number
음성 출력 speak text string

기능 이벤트 데이터 데이터 타입
음성 인식 listen text string

이를 바탕으로 본격적으로 IoT 환경을 구성해 봅니다.

IoT Starter App 생성 및 Conversation 연결

IoT Foundation을 이용하려면 Bluemix의 IoT Foundation 서비스를 생성해야 합니다. TJBot을 제어하려면 IoT Foundation 서비스만을 사용하는 것이 아니라 Node-RED 애플리케이션을 생성해야 하므로 Bluemix Catalog의 Internet of Things Starter 를 선택합니다. IoT Starter는 Node.js 런타임 기반의 Node-RED, Cloudant 및 IoT Foundation 서비스가 연결된 Application을 자동으로 생성해 줍니다. Bluemix에서 IoT Starter App을 생성은 Watson IoT 플랫폼을 이용한 IoT 스타터 앱 따라하기를 참고하여 생성합니다.

IoT Starter App이 생성되었다면 이 App에 연결된 서비스 정보를 볼 수 있습니다.

tjbot-bluemix-00

이제 이 서비스에 Watson Conversation을 사용 할 수 있도록 서비스를 추가합니다. Application 대시보드의 왼쪽 메뉴를 보면 Connections 메뉴가 있습니다. 이를 클릭하면 현재 이 앱에 연결되어 있는 서비스들에 대한 정보를 볼 수 있습니다. 기존에 생성되어 있는 Watson Conversation 서비스를 연결하려면 Connecting Exists 버튼을 클릭합니다. 만약 신규로 생성하여 연결하는 경우라면 Connect new를 선택하면 됩니다.

tjbot-bluemix-01

버튼을 눌러 화면에 나타난 리스트에는 기존에 생성되어 있는 서비스들이 있으며Conversation 서비스를 선택후 Connect 버튼을 클릭하여 완료합니다.

tjbot-bluemix-02

서비스가 애플리케이션에 연결되는 과장에서 기존 앱을 중지 후 다시 배포하는 절차가 진행됩니다. 이에 대한 메시지를 확인 할 수 있습니다.

tjbot-bluemix-03

재배포와 별개로 서비스 연결이 정상적으로 진행되면 아래와 같이 대시보드에 선택한 Conversation 서비스가 연결되어 있는 것을 볼 수 있습니다.

tjbot-bluemix-04

재배포하는 과정에서 시간이 걸릴 수 있으니 참고하기 바라며, 재시작이 완료되었다면, IoT Starter Kit으로 생성한 앱URL을 웹브라우저로 접속하면 다음과 같은 초기 화면으로 진입합니다.

tjbot-nodered-01

화면에 보이는 Go to your Node-RED flow editor 버튼을 클릭하면 Node-RED 플로우 편집화면으로 진입하며 이곳에서 노드와 플로우를 조합하여 IoT 단말의 서비스를 구성 할 수 있습니다.

image41

화면의 왼편에는 플로우 처리에 필요한 노드에 대한 팔레트가 있고, 오른쪽에는 debug 노드에서 발생한 메시지를 볼 수 있는 debug 패널 그리고 선택된 노드에 대한 설명이 탭 UI로 구성되어 있습니다. 처음 IoT Starter App을 생성하면 Flow1 탭에 기본 예제가 보이는데 마우스로 영역을 선택하여 삭제합니다.

이제 새로운 Flow를 생성할 준비가 되었습니다. Flow를 만들기 전에 IoT Foundation 디바이스 정보를 등록해야 합니다. Bluemix의 애플리케이션 대시보드로 다시 되돌아갑니다.

TJBot 디바이스 정보 등록

앱에 연결된 서비스 중 Internet of Things Platform 서비스를 클릭하면 다음과 같은 화면을 볼 수 있습니다.

image13

image14

그리고, 여기 대시보드 실행 버튼을 눌러 Internet of Things 대시보드를 실행합니다.

여러개의 탭 메뉴 중 Device Type 메뉴를 선택합니다.

tjbot-iotf-01

+ Create Type 버튼을 눌러 디바이스 종류 추가를 시작합니다. IoT Foundation에 디바이스를 등록하려면 디바이스의 종류를 먼저 등록해야 하므로 반드시 먼저 진행 되어야 합니다. 아래와 같이 두 가지를 선택할 수 있는데 우리는 gateway가 아닌 일반 단말이므로 create device type을 선택합니다.
tjbot-iotf-02

디바이스 종류에 대한 이름을 TJBot으로 입력 후 다음으로 진행합니다. 이후 추가 항목이나 Metadata 정보 입력에 대한 화면이 나오지만 다른 변경 사항 없이 기본 상태로 생성합니다.
tjbot-iotf-03

TJBot 디바이스 타입이 생성된 것을 볼 수 있습니다.
tjbot-iotf-04

디바이스 타입을 생성 했다면 이번에는 디바이스 자체에 대한 정보를 추가할 차례입니다. Browse 탭을 눌러 현재 등록된 디바이스의 정보 및 상태를 확인 합니다. 그리고 화면 오른쪽의 + Add Device 버튼을 눌러 단말 정보를 등록합니다.
tjbot-iotf-05

디바이스 타입은 TJBot이 됩니다.
tjbot-iotf-06

디바이스 ID는 보통 단말의 고유한 정보를 기반으로 합니다. 이더넷카드의 맥주소를 이용하거나 Manufacturer serial 번호를 사용하기도 합니다만, 임의로 사용한다면 아래와 같이 tjbot-red 와 같은 형태도 가능합니다.
tjbot-iotf-07

다음은 인증에 대한 부분인데 IoT Foundation에 임의의 단말이 붙어서 서비스하지 안도록 인증 키를 설정합니다. 만약 별도로 보관된 인증키가 있다면 그 정보를 사용해도 되며 만약 없다면 자동 생성하는 두 가지 방법이 있습니다. 본 글에서는 후자, 자동 생성 방식을 이용합니다.
tjbot-iotf-08

최종 생성 정보에 대한 요약 정보입니다.
tjbot-iotf-09

디바이스가 정상적으로 등록되면 다음과 같이 내용이 화면에 표시되게 됩니다. 자동 인증 키 생성인 경우 이 화면에 나타나는 키 정보를 잘 보관해야 합니다. 등록 완료 이후 시점에서는 해당 정보를 다시 확인 할 수 있는 방법이 없으니 주의해야 합니다.
tjbot-iotf-10

디바이스 등록을 마쳤으니 이제 TJBot을 IoT Foundation에 연동하도록 합니다.

TJBot 설정

라즈베리파이에 접속하여 다음과 같은 명령으로 소스 코드를 다운로드 받습니다.

mkdir ~/iot
cd ~/iot
git clone http://github.com/mc500/tjbot.git

그리고, 다음 명령으로 모듈을 설치합니다.

cd ~/iot/tjbot/recipes/tjbot-iot
npm install

설치가 완료 되었다면 다음 명령으로 config.js 파일을 수정합니다.

nano config.js

config.js에는 STT, TTS를 위한 Watson Service 접속 정보와 IoT Foundation 접속에 필요한 IoT 서버 및 단말 정보가 입력되어야 합니다.

모든 정보가 정확하게 입력되었다면 다음 명령으로 tjbot-iot를 시작해 볼 수 있습니다.

sudo node tjbot-iot

이제 TJ hello 라고 이야기하면 음성을 인식하고 인식된 text인 hellolisten 이벤트로 IoT Foundation으로 전송합니다. listen 이벤트가 잘 전송되었는지는 Node-RED 화면에서 확인해 보도록 하겠습니다.

Node-RED 이벤트 플로우

Node-RED에서는 IoT관련 이벤트를 받을 수 있는 노드를 제공합니다. 오른쪽 input 팔레트에서 ibmiot 노드를 선택하여 화면에 내려 놓습니다. 그리고 output 팔레트에서 debug 노드를 선택하여 내려 놓습니다. 다음과 같이 input 노드와 output 노드를 마우스를 이용하여 연결하고 화면 오른쪽의 DEPLOY 버튼을 클릭하여 수정된 플로우를 배포합니다.

tjbot-nodered-03

ibmiot 노드를 선택하면 이 노드에 대한 설정 화면을 볼 수 있는데 다음과 같이 TJBot이 보내는 이벤트를 받는 것으로 설정합니다.
tjbot-nodered-02

내용을 수정했으니 다시 DEPLOY 버튼을 눌러 수정된 내용을 배포 반영 합니다. 그리고, tjbot에게 tj hello라고 다시 말을 해 봅니다. 디버그 창에 TJBot으로 부터 전달된 ‘hello’라는 메시지가 정상적으로 올라온 것을 확인 할 수 있습니다.

tjbot-nodered-04

TJBot이 이벤트를 잘 전송하는 것을 확인 했으니 이제 명령을 준비합니다.

Node-RED 명령 플로우

TJBot이 처리할 세 가지 명령 (speak, servo, lighting)에 대한 각각의 노드를 준비해야 합니다. output 팔레트에서 ibmiot 노드를 이용하여 다음과 같은 세가지 노드를 생성합니다.

tjbot-nodered-05

tjbot-nodered-06

tjbot-nodered-07

그리고, 이 노드가 정상 동작하는지 input 팔레트에서 inject 노드를 선택하여 연결합니다. inject 노드는 Node-RED화면에서 클릭하면 설정된 값이 입력으로 다음 노드로 전달하는 일종의 트리거 입력입니다.

다음과 같이 speak명령을 위한 inject 노드를 생성합니다.

tjbot-nodered-09

tjbot-nodered-12

Inject를 왼쪽 버튼을 클릭하면 Text 입력이 시작되는데 실행 시 다음과 같이 JSON 입력이 아니라는 오류를 발생하게 됩니다.

tjbot-nodered-08

그러므로 Text 정보를 변경하기 위해 function 팔레트의 function을 선택하여 다음과 같이 입력하여 Text를 JSON으로 변환합니다.

tjbot-nodered-10

그리고 다음과 같이 플로우 중간에 연결합니다.
tjbot-nodered-11

두 번째로 lighting 명령을 위한 inject 노드를 생성합니다.

tjbot-nodered-16

LED로 나타낼 색상 정보를 code 값으로 입력해 놓습니다.
tjbot-nodered-13

마지막으로 servo 명령에 대한 inject 노드도 생성합니다.

tjbot-nodered-15-fix

tjbot-nodered-14

모든 노드를 준비했다면 이제 DEPLOY 버튼을 눌러 배포를 한 후 TJBot에게 명령을 내려볼 수 있습니다.

TJBot이 명령을 잘 인식했다면 이제 Node-RED에서 Conversation을 연결해 볼 차례입니다.

Node-RED Conversation 플로우

Node-RED에는 Bluemix Watson 서비스 연동을 위한 노드를 제공하고 있습니다.

tjbot-nodered-19

watson 팔레트에서 conversation 노드를 선택하고 다음과 같이 설정합니다.
tjbot-nodered-17

이 때 입력하는 workspace-id는 앞서 IoT Starter App에 연결했던 Conversation 서비스에서 사용하는 Workspace ID입니다.

아래와 같이 debug 노드와 inject 노드 사이로 연결하고 변경된 플로우를 배포 합니다.
tjbot-nodered-18

inject 노드를 클릭하여 conversation에 메시지를 보내면 그 응답이 debug 패널로 출력되는 것을 확인 해 볼 수 있습니다.

tjbot-nodered-respose-nice-to-see-you

Watson Conversation 설정

Conversation Dialog에서 Hello라고 입력했을 때 응답 메시지를 보면 anything_else 노드가 실행되어 얻어진 응답인 것을 확인 할 수 있습니다. 그런데 동일한 Dialog를 Watson API로 호출하면 input/output 정보뿐만 아니라 intent, entity 그리고 context와 같은 다양한 정보들이 같이 전달됩니다. 특히 intent의 경우 하나 이상이 될 수 있으므로 배열 형식으로 전달이 되며, Watson이 판단한 confidence 정보가 함께 전달됩니다. 예를 들면 아래와 같은 형태로 전달 됩니다.

"intents" : [
  {
    "intent" : "lighting",
    "confidence" : 1
  }
]

여기서 좀 이상한 것을 발견할 수 있는데, Hello는 #lighting intent가 아니기 때문에 anything_else 노드의 응답을 얻었는데 왜 intent 정보는 #lighting 인가 하는 점입니다. 게다가 confidence 값도 1인데 이는 100%로 확신한다는 응답입니다. 뭔가 오류가 발생한 것이라고 생각할 수 있지만, 이는 오류라기보다는 현재 Watson이 판단 할 수 이는 Intent가 #lighting 하나만 있어 선택의 여지가 없기 때문에 이런 결과를 보여주는 것입니다. 그렇다면 새로운 intent를 추가한다면 명확하게 우리가 원하는 #lighting intent를 구분할 수 있는지 확인을 해 봐야겠죠?

Conversation Dash Board에 가서 아래와 같이 #greeting이라는 새로운 Intent를 추가해 보도록 합니다.

tjbot-conversation-add-greeting-intent

이제 다시 Hello를 입력해 볼까요?

"intents" : [
  {
    "intent" : "greeting",
    "confidence" : 1
  }
]

이번에는 Hello를 #greeting intent로 인식했습니다. 그럼 좀 생뚱 맞은 world라는 말은 어떨까요?

"intents" : [
  {
    "intent" : "greeting",
    "confidence" : 0.8542913515814917
  }
]

#greeting intent로 인식했지만 자신감이 85%로 떨어졌네요. 물론 world라는 값이 #greeting intent는 아니라고 볼 수 있지만 Watson이 판단하기에 #lighting과 #greeting 중에서는 #greeting에 더 가깝다고 판단한 것입니다. 더 많은 Intent를 추가하면 할 수록 더 잘 구분해 낼 수 있게 되니 되도록 많은 자료와 예시가 필요한 이유인 셈입니다.

이제 Node-RED 플로우를 이용하여 서비스를 확장해 보도록 하겠습니다.

Node-RED 플로우를 이용한 서비스 확장

다시 처음 구성한 그림으로 되돌아 봅시다.

tjbot_iotf

Node-RED의 영역은 논리 플로우로서 TJBot 기능과 Conversation을 이어주는 역할을 합니다. 즉, 앞서 테스트용으로 만들어 놓았던 Command 입력 노드와 Event 수신 노드 그리고 Conversation 노드를 조합하여 논리 서비스를 생성할 수 있습니다. Node-RED의 좋은 점 중 하나인데 미리 만들어 놓았던 노드들을 선택해서 복사후 붙여 넣기가 가능하므로 잘 복사해서 서비스 흐름만 이어주면 바로 서비스가 구성되는 것을 볼 수 있습니다.

다음과 같은 과정으로 서비스를 조합해 보도록 합니다.

  1. event를 입력받아 이를 conversation으로 전달해 준다
  2. conversation의 응답을 보고 그 값이 #lighting intent이면 tjbot에게 command로 전달한다.

간단합니다. 아래와 같이 flow만 이어놓으면 됩니다.

tjbot-nodered-flow1

그런데 약간 문제가 있습니다. 아래와 같이 몇 가지를 더 생각해야 합니다.

첫 번째로 event를 입력 받아 conversation으로 전달해 주는 부분에서 오류가 발생합니다.

conversation의 경우 payload에 문자열을 전달하는 방식인데 event는 전달되는 값이 다음과 같은 형식입니다.

{
  "payload":{
    "d":{
      "text":"Hello"
    }
  }
}

따라서 이를 변환해 줄 다음과 노드가 event와 conversation 노드 사이에 있어야 합니다.

tjbot-nodered-function-to-conversation

두 번째로는 conversation에서 전달되는 응답을 보고 그 값이 #lighting intent인지 판단하는 부분이 빠졌습니다.

이 부분 역시 function 노드를 추가해서 만들어 놓을 수 있지만 다음과 같이 Node-RED에서 제공하는 switch 노드를 이용하는 방법을 이용해 볼 수 있습니다.

tjbot-nodered-switch-lighting

그리고 마지막, #lighting intent 여부를 판단한 다음이라도 Event에서 처럼 Command도 Command에 맞는 형식으로 전달 할 수 있어야 합니다.
따라서, 아래와 같이 conversation 응답을 lighting 명령 형식으로 변환하는 function 노드를 생성합니다.



전체적으로 완성된 모습은 다음과 같습니다.

이제 편집한 플로우를 배포하고 TJBot에게 다음과 같이 이야기해 봅니다.

TJ, change the lamp blue

파란색으로 잘 동작하는 것을 확인했다면, 이번엔 갈색으로 잘 나오는지 다음과 같이 이야기 해 봅니다.

TJ, change the lamp brown

정상적으로 잘 동작한다면, 나머지 servo와 speak도 마찬가지로 적절하게 조합하여 원하는 서비스를 만들어 해 볼 수 있습니다.

참고자료