まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:nodered

ビジュアルデータフローエディタの Node-RED は、IoT を始めとするデータの取り込みや加工、書き出しを視覚的に行う便利なツールです。更に IBM Bluemix 環境であれば、「ボイラープレート」と呼ばれるテンプレート機能を使うことで、サーバー管理とかミドルウェアインストールとかを意識することなく、簡単に Node-RED 環境を構築して、すぐに使い始めることができます。


が、簡単すぎるが故の課題もあります。典型的な例の1つが「バージョンアップ」です。まあクラウドの宿命といえなくもないのですが、ミドルウェアやアプリケーションのバージョン管理をどうするか、という課題です。クラウド環境の場合、バージョン管理含めてクラウド業者に手放したい人もいれば、バージョン管理は自分でやりたいという人もいるので、1つの正解というものが存在しない、難しい問題ではあります。Bluemix では新規にサーバーを作る際のミドルウェア/アプリケーションバージョンは原則最新のものが用意されますが、一度作ったサーバーのミドルウェア/アプリケーションバージョンが勝手に変更されることはありません。つまり使い続ける間は利用者が管理する必要があります。 

・・・と、ここまではいいのですが、問題は「最初の一歩が簡単すぎる&中で何がどう動いているか分からなくても使い始めることができる故、バージョンアップがやけに難しく感じる」ことです(苦笑)。

一応難しく(というか、ややこしく)なっている理由を解説すると、Bluemix 環境では Node.js サーバーがランタイムとして用意されます。そしてその上に Node-RED アプリケーションが導入されて動いているわけですが、このアプリケーション部分である Node-RED のバージョンアップをする必要があるわけです。この Node-RED のバージョンアップの際に、前提となる Node.js のバージョンも合わせて上げる必要が生じるケースもあります。また Node.js では npm というパッケージ管理の仕組みが使われていて、npm の作法でバージョンを管理する必要があります。 普通の Node.js 環境の場合、自分で npm を管理したり、npm に指示を出すような設定ファイルを用意したりするのですが、Bluemix はその辺りを全く知らなくても(事前に何も用意しなくても)インターネット上に Node-RED 環境が作れてしまうのです。で、バージョンアップの段階になってこれらの用意がないことが話をややこしくする要素になるのでした。


という背景の説明はここまでにして、以下は実際に(数ヶ月前のバージョンが古かった頃から動いているような)Bluemix 上の Node-RED をバージョンアップする方法を紹介します。

まずは Node-RED 環境にアクセスし、画面右上のハンバーガーメニューから Node-RED のバージョンを確認します。この図では "0.13.1" というバージョンになっていることが確認できます。2017/Feb/09 時点での Node-RED の最新バージョンは "0.16.2" なので、この環境はバージョンアップが可能な状態にある、ということになります:
2017020801


(Node.js や)Node-RED のバージョンアップのためには Node-RED のスターターコードと呼ばれるファイル一式か、IBM DevOps サービス等を使ったソースコード一式が必要になります。バージョンアップの対象となる Node-RED 環境を作った際にこれらの環境ごと用意されているのであれば、そのソースコードを用意してください。 以下はスターターコードも IBM DevOps サービスも使わず、ソースコードが手元にない状態からの入手方法になります。

改めて IBM Bluemix にログインし、対象の Node-RED ランタイムのプロジェクトを選択します。そして「開始」タブを開くと、Node-RED のカスタマイズの節内に "DOWNLOAD STARTER CODE" と書かれたボタンがあります(バージョンによって微妙に表現が異なるかもしれません)。ここをクリックしてスターターコードの zip ファイルをダウンロードします:
2017020802


ダウンロードした zip ファイルを展開します。この中に package.json というファイルがあるので、これをテキストファイルで開きます。もともとスターターコードをダウンロード済みであったり、IBM DevOps サービス等でソースコードを管理済みだった場合もお手元のコードの package.json を開いてください:
2017020803


package.json ファイルの中身を確認してみます:
2017020901


この中で利用する各コンポーネントモジュールとそのバージョンを管理しています。まず Node-RED 自体は
      :
  "node-red": "0.x"
      :

と指定されていました。これは「0.ナントカの中で最新のもの」を使うよう指定されていることになり、この指定であれば現時点での最新版 Node-RED である 0.16.2 が使われることになります。もしこのような指定になっていなかった場合はこのように変更してください。

次に Node.js のバージョンを確認します。Node-RED v0.16.0 からは Node.js のバージョンが 4.0 以上のみをサポートしており、Node.js のバージョンが古いと最新版の Node-RED は動きません。そこで Node.js のバージョンも合わせておく必要があります。こちらについては
      :
  "node": "4.x"
      :

と指定されている必要があります。もしこのような指定になっていなかった場合はこのように変更してください。


ここまでの変更・確認の上で(必要であれば他のモジュールや public フォルダ以下に静的ファイルを追加した上で)、cf コマンドでプッシュするか、IBM DevOps サービスを使って Deploy してください。

再デプロイ後に改めて Node-RED のバージョンを確認します:
2017020805


↑最新版になっていることが確認できれば成功です。

 

Node-REDnode-red-node-twitter ノードを使って、ツイッターのリアルタイム検索を行い、その結果を表示するウェブアプリケーションを作ってみました。Node-RED 環境に node-red-node-twitter ノードを追加することで以下の作業が可能になります。或いは IBM Bluemix から Node-RED スターターを使って作成した環境であれば、はじめから同ノードが組み込まれているため利用可能です。

まずは Node-RED のキャンバス内に以下のようにノードを配置します:
balloon_nodered


Twitter のノードにはリアルタイム検索を行うキーワードを指定しておきます。以下の例では "iPhone" というキーワードで Twitter 内をストリーム検索するように指定しています:
2017020501


そして WebSocket 出力ノードでは出力先を /ws/tweets に指定しています。ここは任意の文字列でもいいのですが、後述の HTMLテンプレートの内容が "/ws/tweets" の WebSocket を監視するような内容になっているので、これらの内容を一致させる必要があります:
2017020502


また HTTP 入力ノードでは GET の /tweets を指定しています。つまりウェブブラウザで /tweets というページを参照した時にここで定義するページ内容が表示されるようにしています:
2017020503


その際のページ内容をこちらのテンプレートノードで定義しています。以下のように HTML が指定されており、それがそのまま出力されます:
2017020504


このテンプレートノードの中身は、こちらの template ファイルの内容をそのままコピー&ペーストしてお使いください:
https://github.com/dotnsf/balloon_tweets


※なお、上記で紹介したのとまったく同じノード構成をこちらに用意しておきました。自分でノードを構成しなくても(単に動かしたいという目的だけであれば)この JSON ファイルの内容を Node-RED にインポートして使っていただいてもかまいません:
http://dotnsf.blog.jp/balloon_tweets.json


ノード構成の準備ができたら、Node-RED 画面右上のボタンでデプロイします:
2017020505


デプロイが成功するとここで定義したノードが動き出し、指定したキーワード(今回の場合は "iPhone")で Twitter のリアルタイムツイート検索が行われます。該当するツイートは画面右の debug タブ内に次々と表示されていきます:
2017020506


この様子をもう少し見やすくしたのが HTML テンプレートです。Node-RED と同じホストを指定して、http://(Node-RED のホスト)/tweets をウェブブラウザで開くと、検索されたツイートが画面内に次々と吹き出しを伴って表示されていく様子を確認できます:
2017020500


実際にツイートが次々と追加されていく様子はこちらの動画を御覧ください。"iPhone" くらいに頻度の高いワードで検索すると、こんな感じのスピードでツイートされている、というのが分かる動画になっています:




IBM Bluemix からも提供しているビジュアルフローエディタ Node-RED の最新バージョン v0.16 がリリースされました(2017/Jan/17 時点での最新版は v0.16.1):
https://libraries.io/npm/node-red/


新機能などの情報についてはこちらの公式ブログエントリや、
http://nodered.org/blog/2017/01/11/version-0-16-released

こちらのリリースノートを参照ください:
https://github.com/node-red/node-red/releases/tag/0.16.0


さて、上記ブログエントリにも書かれているのですが、この v0.16 より Node.js の v0.10 および v0.12 がサポート外になりました。Node.js も v4 以上のものを用意する必要があります。

これで問題になるのが CentOS/RHEL 6.x を使っている場合です。現在、CentOS/RHEL 6.x で(epel リポジトリなどを使って)普通に Node.js を導入すると v0.10.48 が導入されます:
# node -v
v0.10.48

IBM からも IBM SDK for Node.js として IBM 版の Node.js がリリースされていますが、こちらも CentOS/RHEL 6.x は v0.12 までしか用意されておらず、Node.js v4 以上を使おうとすると CentOS/RHEL であれば 7.x を使う必要があります:
https://developer.ibm.com/node/sdk/

2017011702


ちなみに Node.js v0.10.x が導入された状況下で深く考えずに npm を使って Node-RED を導入すると、最新バージョン(今であれば v0.16.1)が導入されてしまいます。その結果起動時にエラーが発生し、ユーザー画面にもエラーメッセージが表示されてしまいます:
# npm install node-red -g  (このコマンドは成功する)

# node-red
  :
  :
Welcome to Node-RED
===================

17 Jan 10:10:33 - [info] Node-RED version: v0.16.1
17 Jan 10:10:33 - [info] Node.js  version: v0.10.48
17 Jan 10:10:33 - [error] *****************************************************************
17 Jan 10:10:33 - [error] * Unsupported version of Node.js. Requires: >=4 Found: v0.10.48 *
17 Jan 10:10:33 - [error] *****************************************************************
17 Jan 10:10:33 - [info] Linux 2.6.32-642.11.1.el6.x86_64 x64 LE
17 Jan 10:10:33 - [info] Loading palette nodes

/usr/lib/node_modules/node-red/node_modules/mqtt/lib/connect/index.js:100
    const isSecure = ['mqtts', 'wss'].indexOf(opts.protocol) !== -1
    ^^^^^
17 Jan 10:10:35 - [warn] ------------------------------------------------------
17 Jan 10:10:35 - [warn] [rpi-gpio] Info : Ignoring Raspberry Pi specific node
17 Jan 10:10:35 - [warn] [mqtt] SyntaxError: Use of const in strict mode.
17 Jan 10:10:35 - [warn] ------------------------------------------------------
17 Jan 10:10:35 - [info] Settings file  : /root/.node-red/settings.js
17 Jan 10:10:35 - [info] User directory : /root/.node-red
17 Jan 10:10:35 - [info] Flows file     : /root/.node-red/flows_xxxxxxxx.xxxxx.xxx.json
17 Jan 10:10:35 - [info] Creating new flow file
17 Jan 10:10:35 - [info] Server now running at http://127.0.0.1:1880/
17 Jan 10:10:35 - [info] Starting flows
17 Jan 10:10:35 - [info] Started flows

↑起動が成功したように見えるが、バージョン非互換のエラーが発生している

2017011701


では改めて CentOS/RHEL 6.x のバージョンアップをせずにどうやって Node-RED を導入すればよいでしょうか? Node-RED の最新版を動かすために工夫する、というのも1つの方法ですが、Node-RED が最新版でなくてもよい場合であれば Node-RED v0.14.x を使う、という方法もあります。このバージョンであれば CentOS/RHEL 6.x でも動作します。

その場合の手順を紹介します。まず上記のコマンドを実行してしまって Node-RED v0.16.x を導入してしまった場合はアンインストールします:
# npm uninstall node-red -g

リリースサイトを見ると、v0.14.x の最新バージョンは v0.14.6 のようです(2017/Jan/17 時点)。というわけで、改めてこのバージョンを指定して npm でインストールします:
# npm install node-red@0.14.6 -g

こうしてインストールした Node-RED を起動すればエラーは発生しません:
# node-red
  :
  :
Welcome to Node-RED
===================

17 Jan 10:15:55 - [info] Node-RED version: v0.14.6
17 Jan 10:15:55 - [info] Node.js  version: v0.10.48
17 Jan 10:15:55 - [info] Linux 2.6.32-642.11.1.el6.x86_64 x64 LE
17 Jan 10:15:55 - [info] Loading palette nodes
17 Jan 10:15:58 - [warn] ------------------------------------------------------
17 Jan 10:15:58 - [warn] [rpi-gpio] Info : Ignoring Raspberry Pi specific node
17 Jan 10:15:58 - [warn] ------------------------------------------------------
17 Jan 10:15:58 - [warn] Missing node modules:
17 Jan 10:15:58 - [warn]  - node-red: yaml
17 Jan 10:15:58 - [info] Removing modules from config
17 Jan 10:15:58 - [info] Settings file  : /root/.node-red/settings.js
17 Jan 10:15:58 - [info] User directory : /root/.node-red
17 Jan 10:15:58 - [info] Flows file     : /root/.node-red/flows_xxxxxxxx.xxxxx.xxx.json
17 Jan 10:15:58 - [info] Creating new flow file
17 Jan 10:15:58 - [info] Starting flows
17 Jan 10:15:58 - [info] Started flows
17 Jan 10:15:58 - [info] Server now running at http://127.0.0.1:1880/

↑起動成功

というわけで、現時点で比較的簡単に CentOS/RHEL 6.x で Node-RED を動かす場合はこの方法になるのかな、と思ってます:
2017011703


なお、CentOS/RHEL 6.x に Node.js v4.x を導入してから Node-RED v0.16.x を入れる、という方法もあります。CentOS/RHEL 6.x に Node.js v4.x を入れる方法についてはこちらに詳しく書かれていました:
CentOS 6.xにLTS(4.3.0)のnode.jsをインストールする

以前に CentOS 環境に StrongLoop LoopBack を導入する手順を紹介しました:

上記の手順ではまず Node.js を導入し、その Node.js 上に StrongLoop LoopBack を導入して OpenAPI 環境を構築していました。リンク先でも紹介されているように、この方法では LoopBack(Node.js) の起動をコマンドラインで行う必要がありました。つまりシステムが起動した後に明示的に LoopBack(Node.js) サーバーをアプリケーションとして起動する、という方法でした:
# cd myapp
# node .

今回の目的はこれをデーモン化することです。

LoopBack に限った話ではないのですが、Node.js アプリケーションをデーモン化する場合には forever というツールを使うことが多いです。forever は Node.js スクリプトの死活監視ツールで、 npm を使ってインストールします:
# npm install forever -g

forever で Node.js アプリをデーモン化して起動する場合は、forever コマンドの start オプションとして実行する JavaScript ファイルを指定します。LoopBack の場合はアプリケーションディレクトリ(上記例の場合は myapp)に package.json というファイルがあり、その中に以下のような記述があります:
{
  "name": "myapp",
  "version": "1.0.0",
  "main": "server/server.js",
  "scripts": {
    "start": "node .",
      :
      :

つまり LoopBack の場合は server/server.js が起動時に実行されていることがわかります。したがって forever を使って LoopBack アプリケーションを起動する場合はアプリケーションディレクトリからこのように server/server.js ファイルを指定すればよい、ということになります:
# cd myapp
# forever start server/server.js

これで LoopBack が実行されます。ウェブブラウザで http://(LoopBack アプリサーバー):3000/explorer にアクセスすると、Open API の API Reference にアクセスできることが確認できます:
2016061601


なお forever は start 以外にも以下の様なコマンドが用意されています:
コマンド目的
forever start (Node.js JavaScript ファイル)指定したアプリケーションの起動
forever list実行中のスクリプトの表示
forever stop (Node.js JavaScript ファイル)指定したアプリケーションの停止
forever stopall起動中の全アプリケーションの停止
forever restart (Node.js JavaScript ファイル)指定したアプリケーションの再起動
forever restartall起動中の全アプリケーションの再起動







 

※(注)最初にお断りしておきます。マジメぶって書いてますが馬鹿エントリです。


全てのきっかけは最近たまにみかけるこういった記事でした:

人工知能やコグニティブエンジンと呼ばれる技術の発達により、これまで人間の脳でないと判断できなかったようなことをコンピュータができるようになり、人間の仕事がより低コストな人工知能やそれらを搭載したロボットによって奪われてしまう時代がくる、という啓発記事です。

個人的にはそこまでそんな時代が身近に迫ってきているとは思っていません。ただし、その一方で企業間の競争が働いたこともあり、ここ数年における人工知能分野の発展はすさまじいものがあります。静止画像や個人の機械認識率はぐんと上がり、各社が API を公開している背景も手伝って、人工知能に触れる機会がより身近な世界になりつつあるのも事実だと思っています。以下は個人的見解ですが、クリエイティブな仕事(例えば小説を書く、など)を学習させるのはまだ難しいにせよ、脳を使わない単純作業や、ルーチン化された業務などは意外と早い段階で人間の効率を追い抜く日がやってくるかもしれない(そうなると作業コストで勝てるわけがないので、本当に仕事を失う日がやってくるかもしれない)、と思っています。

例えばお客様とお話ししていて、ただ頷いて聞いているだけ。お客様が話し終わったらすかさず相槌を打つ、そんなフローチャートのような業務では近い将来に職を奪われてしまうかもしれないのです!
(注 実在する誰かのことではありません)
2015022305


さて話は変わって、先日秋葉原でこのような部品を買ってしまいました:

2015022300



この SEN02281P はいわゆる「音センサー」です。画像左上にある大きな丸い部分がマイクになっていて、ここで音を拾って、その情報を電気回路を通じて外部に知らせることができる、というものです。買った後で知ったのですが、Arduino に接続したり、Raspberry PiGrovePi という拡張モジュールを取り付けて簡単に使う方法がネットなどで紹介されていました。

・・・ん、もしかすると、この SEN02281P と(例えばラズベリーパイとかの)演算機能を使えば、上記のような簡単なデータフローが実現できてしまうんじゃないだろうか? つまり「誰かが喋っている時は頷き、喋り終わったら相槌を入れる、という仕組みは、このセンサーとアルゴリズムを実装するプログラムだけで実現できてしまうんじゃないだろうか?」ということに気付いてしまったのです! というわけで、よく調べずにとりあえず買ってしまいました。


自分は「ラズベリーパイならメジャーだからまあ繋がるだろう、その方法もネットで見つかるだろう」とタカを括っていたのですが、これが意外と苦戦しました。結論からいうと GrovePi を使わない方法(ラズベリーパイの GPIO に直接繋げる方法)を見つけることができませんでした。えーマジで!?自分で調べるしかないの??電子回路は苦手なんだよなあ。。まあ挑戦してみました。以下、やってみたことのおさらいの意味で書いてます。当方こっち方面はド素人なので、間違いを見つけたり、こうするともっといいよ、という方法があればウェルカム、というか教えてください。

・・・改めてパーツを眺めてみました。接続端子はこの↓画面上部の白い四角の中に生えた4つの突起部分です:
Loudness_101020063_01

これを裏返すとこんな感じ(上図とは左右が逆になった状態):
2015022301


拡大するとこんな感じ。ちょっと見難いのですが、この画面の左から順に(反対から見た場合は右から順に) SIG / NC / VCC / GND と書かれています:
2015022302


GND はアース(Ground)、VCC は電圧、これは(他の部品とほぼ共通なので)分かる。上記の商品ページを見ると、このパーツの動作電圧は 5V(3.5~10V) と書かれているので、とりあえず 5V の電圧ピンにジャンパケーブルを繋いであればいいかな。そして SIG はシグナル、つまりここを GPIO27 とかに繋げて音の信号を受け取るんだろうな・・・ で、NC ?なんだこれ、見たことないぞ・・・

で、ここだけ調べて分かったのは NC = Not Connected 、つまり「どことも繋がない」という端子らしい。そうなんだ。。じゃ、なんで存在してるんだろ?? うーん・・・まあ、いいやw

というわけで、ジャンパケーブルを使ってこんな感じで自分のラズベリーパイの GPIO に接続しました:
2015022303

これで 5V の電源を供給し、アースも備え、SEN02281P が感知した音を取り込む仕組みが動くはずです。実際の写真はこんな感じです(メス-メスのジャンパーケーブルがあればもっと綺麗に接続できたのに・・・):
2016022400


そして、ラズベリーパイ側にはこのような Python プログラムを導入しました:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# SEN02281P ----- RaspberryPi GPIO
# =SIG ---------- 13
# =NC
# =VCC ---------- 2
# =GND ---------- 20

import paho.mqtt.client as mqtt
import time
import RPi.GPIO as GPIO

def on_connect(client,userdata,flags,rc):
	print( "Connection with result code " + str(rc) )
	client.subscribe( "sen02281p" )

def on_message(client,userdata,msg):
	print( msg.topic + " " + str(msg.payload) )

def reading(sensor):
	sum = -1 
	if sensor == 0:
		sum = 0
		for i in range(0,20):
			time.sleep(0.1)
			a = GPIO.input(SIG)
			sum += a
	else:
		print "Incorrect function."

	return sum

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
SIG = 13
GPIO.setup(SIG,GPIO.IN)

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

client.connect( "iot.eclipse.org", 1883 )
while client.loop() == 0:
	msg = reading(0);
	client.publish( "sen02281p", msg, 0, True )
	pass

GPIO.cleanup()
※事前に pip install paho-mqtt をして、Paho の Python ライブラリを導入済みです。

ちなみに上記ソースファイル(sen02281p.py)はこちらからダウンロードできます:
https://raw.githubusercontent.com/dotnsf/sen02281p/master/sen02281p.py


GPIO の(SEN02281P の SIG 端子とつながっている)13番ピンからのインプット情報を 0.1 秒ごとに20回(つまり2秒間)取得し、その20回中音が確認できた回数を MQTT ブローカー(iot.eclipse.org)に投げる、というものです。つまり MQTT ブローカーに対しては2秒おきにどれくらい音が識別できたかを 0 から 20 の整数値でパブリッシュする、というものです。トピックは上記例では "sen02281p" と指定していますが、皆さんがもしこのソースを使う場合は少し変えていただけると嬉しいです。

次に Bluemix 上の NodeRED 環境を使って、このパブリッシュされたメッセージを取り出す仕組みを用意します。MQTT インプットノードを用意し、ホスト名に上記の MQTT ブローカーホスト(iot.eclipse.org:1883)、トピックに "sen02281p" を指定しています。このノードからは2秒に1回、ラズベリーパイの接続された SEN02281P のマイクから拾った音の頻度が渡されてくる、という仕組みとなります。また、その取り出した結果を /ws/unzk_sensor というパスの WebSocket に出力しています:
2015022304


後はこの /ws/unzk_sensor からリアルタイムにデータを取り出して動く WebSocket アプリケーションを用意してあげればマイクで拾った音の頻度をリアルタイムに可視化するようなアプリケーションを作ることができる、ということになります。そのサンプルアプリ(やその中で使う画像)も合わせてこちらで公開しておきます:
https://github.com/dotnsf/sen02281p


このアプリを上記の NodeRED 環境にデプロイしてアプリケーション(hoho.html)を開くと、このような画面になります:
2016022401


ひたすら「頷いている」画面になっています。何もデータが送られていないとただ頷いているだけですが、一応この画面が出ればデプロイには成功していることになります。

ではラズベリーパイ側のアプリも起動します。ネットに接続されたラズベリーパイ上で先程の MQTT パブリッシャーアプリを実行します:
# python sen02281p.py

そして先程の頷き画面をリロードすると・・・ ラズベリーパイに接続されたマイクが音を拾っている間は頷き、音が途切れたと判断した時に「ほほー」と相槌を売ってくれるようになります!
2016022402


この「音を拾っているか」「音が途切れたか」の判断がまだ少し甘いところがあるかもしれませんが、一応それっぽく動いていることが確認できました。これで忙しい営業さんに変わってお客様のお話しを上手に引き出してくれるロボットができました(笑)。


実際に動いている動画を Ustream に上げておきます:

Live streaming video by Ustream




このページのトップヘ