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

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

タグ:nodered

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


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

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

個人的にはそこまでそんな時代が身近に迫ってきているとは思っていません。ただし、その一方で企業間の競争が働いたこともあり、ここ数年における人工知能分野の発展はすさまじいものがあります。静止画像や個人の機械認識率はぐんと上がり、各社が 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




IBM Bluemix からも提供されている NodeRED データフローエディタの中にある TCP インプットノードを使ってみました:
2016021600


この TCP インプットノードはホスト名とポート番号を指定して、TCP サーバーソケットに直結してデータを受信する、というノードです。

実際に NodeRED で使ってみました。データが受信できることを確認したかったので、以下のような TCP インプットノード1つに、デバッグアウトプットノード1つをつなぐ、というシンプルなデータフローを作ります:
2016021601


TCP インプットノードをダブルクリックして属性を指定します。上から順にポート番号(下図では 12345)とホスト名を指定します。なおここで指定するホスト名のサーバーには、NodeRED サーバーから繋がる必要があります(つまりプライベートネットワーク上にある場合は正しくポートフォワードされている必要があり、加えてファイアウォールで指定ポートに穴を開けておく必要もあります)。また受け取るメッセージの形式を単体文字列に指定しています。この状態で NodeRED 上でデプロイします:
2016021602



一方の TCP サーバー側のアプリケーションを用意します。今回はこんなシンプルな Java アプリを、NodeRED のホストとして指定したサーバー上に作ってみました。(デフォルトで)12345 番ポートでクライアントを待ち受けて、接続があればメッセージを返す、というものです。待受ポート番号は起動時に指定することもできるようにしていますが、今回は使いません:
//. myTcpServer.java
import java.io.*;
import java.net.*;

public class myTcpServer{
  public static void main( String[] argv ) throws Exception{
    int port = 12345; //. デフォルトで待ち受けるポート番号
    try{
      //. 実行時のパラメータで待受ポート番号を変更できるようにする
      if( argv.length > 0 ){
        try{
          port = Integer.parseInt( argv[0] );
        }catch( Exception e ){
        }
      }

      //. サーバーソケット作成
      ServerSocket server = new ServerSocket( port );

      while( true ){
        Socket socket = server.accept(); //. 接続してくるクライアントを待つ

        //. 接続してきたら、メッセージを返す
        OutputStream output = socket.getOutputStream();
        String message = "Connected with port: " + port;
        byte[] out = message.getBytes( "UTF-8" );
        output.write( out );

        //. クライアントを破棄(ループで接続待ち状態へ戻る)
        socket.close();
      }
    }catch( Exception e ){
      e.printStackTrace();
    }
  }
}


これをコンパイルして、実行します:
# javac myTcpServer.java
# java myTcpServer

すると NodeRED 側のリクエストがこのサーバーアプリに繋がり、定期的にメッセージが返されることが分かります:
2016021603


デバッグメッセージをよく見ると、10秒おきにメッセージが送られてくることが分かります。これが TCP インプットノードの仕様なんですかね:
2016021604
ともあれ、TCP インプットノードを使って、TCP ポート直結でデータを受け取ることができる、ということが確認できました。

MQTT を使う必要はなく、ホスト名(IPアドレス)とTCP ポートから直結してデータを受け取ることもできる、というサンプルでした。ただ現実問題としてインターネットに公開されたホストからわざわざ TCP ポート直結のファイアウォールを空けてまでデータを受け取る必要があるか、と言われると、そこはやはり MQTT の方がいいのかなあ、とは思っています。まあどうしても MQTT が使えないようなケースに限った利用ケースかもしれません。


Node-RED だけで SQL Database(DB2) 内のデータを検索して結果を表示する、という仕組みを作ってみます。

IBM Bluemix などから提供されているオープンソース・データフローエディタである Node-RED は、ノードと呼ばれる機能単位のブロックを組み合わせて、ほぼコーディングをすることなくデータのワークフローを作れるウェブアプリケーションです。そのフローにデータベースを組み合わせて問い合わせしたり、結果を格納したり、といったことも少ない(或いはほとんどない)コーディングで実現できる、という便利なツールです。特に Bluemix と組わせることで IBM IoT Foundation と連携した MQTT システムを簡単に構築することが可能になります:
2016020101


データのフローを得意とする一方で、Node-RED 単体では UI はあまり得意ではありません。全くできないわけではないのですが、基本的には HTML などの知識を前提として、動的に HTML を作成するような形になります。むしろサーブレット的というか、UI を外出しした仕組みの1パーツとして利用するのが便利だと思っています。


例えば、現在 SQL DB 内に MYTABLE というテーブルが作られ、以下の様な状態になっているものとします。ここから ID 値が 'fff57f93.000a8' であるデータを検索する、ということを目的としてみます。この ID はパラメータとして与えるようにして、リクエストの度に変更できるようなものにします:
2016020101


普通に SQL を使うのであれば(言語によって JDBC などの設定をした上で)、SELECT * FROM MYTABLE WHERE ID = 'fff57f93.000a8' といった具合にクエリーを実行して結果を得ることになると思います。

この処理を NodeRED で行うにはどうすればよいでしょうか?そのためにはまずこの SQLDB に NodeRED からアクセスできるようになっている必要があります。Bluemix 環境であれば、目的の NodeRED プロジェクトに、この SQLDB を事前にバインドしておきます(下図ではサービス名が SQL Database-iv となっています。適宜みなさんの実際の環境と読み替えてください):
2016020101


バインドした上で NodeRED のパレット内の SQLDB を使います。なお、ここでの SQLDB ノードは(検索をしたいので)左右両方にノード接続点があるものを使います。
2016020102


そして以下の様な NodeRED データフローチャートを作ります。HTTP Request ノード、SQL DB ノード、JSON ノード、Template ノード、HTTP Response ノード、そして Debug ノードの6つを以下のようにつなぎます。以下、各ノードの属性を説明します:
2016020101


HTTP Request ノードでは Method として GET、そして URL として /query を指定します。この URL で指定した内容が実際に問い合わせをする際の URL になります:
2016020102


SQL DB ノードでは問い合わせ処理を記述します。Service では SQL DB のサービス名をプルダウンから選択し、Query には ID をキーに MYTABLE テーブルを検索したいので select * from MYTABLE where id = ? と入力します。そしてこの ? パラメータに代入される値を直前の HTTP Request ノードの URL パラメータから取得するようにします(Prepared Statement っぽい感じ)。HTTP Request ノードの URL パラメータの値は msg.req.query で取得できます。今回は id というパラメータにこの値が入っているよう想定するので、Parameter Markers の欄に msg.req.query.id と指定します:
2016020103


JSON ノードは JSON ブロックをそのまま配置するだけです(名前は適当に指定してください):
2016020104


そして Template ノードで検索結果を表示用に整形します。ここでは HTML 形式で、背景を薄い黄色にして表示してみましょう。以下の様な内容を入力します。HTML 内の {{payload}} 部分に検索結果が入り、その結果を msg.payload として返す、という指定をしていることになります:
2016020105


最後に HTTP Response ノードと debug ノードをそれぞれ配置します。この状態でフローを Deploy します:
2016020106


そしてウェブブラウザで、http://(Node-RED のあるサーバー)/query?id=(SQL DB から検索したいレコードのID値) を指定してアクセスします。成功すると指定した ID 値を持つレコードが JSON テキスト形式で、指定したフォーマット(背景が薄い黄色)で表示されるはずです:
2016020107


URL パラメータの id の値を別のレコードのものに変えると、指定したレコードの内容に変わるはずです:
2016020108


同時にこれらの検索結果は Node-RED 画面の debug タブ内にも表示されているはずです:
2016020109


上記例では結果を(わざわざ)HTML 化して出力しましたが、現実には JSON のまま返したり、XML 化して返す、といったサーブレット的な処理をする機会が多いと思います。要は見た目については Node-RED の外で管理すべきと考えています。


・・・と、そう思っていたのですが、最近になって Node-RED に UI パーツを追加できるようなノード部品が提供されたようです。ある程度用途は限られていますが、UI も Node-RED で作れるようになっていくのかもしれません:
https://github.com/andrei-tatar/node-red-contrib-ui


とりあえず、Node-RED だけでサーブレット的な検索機能を実装することもできそうだ、ということが分かりました。


Bluemix 上のデータストレージサービスである Cloudant と dashDB はデータを連携することができます。以前には Cloudant から dashDB へのレプリケーションによって実現できる、という記事を紹介しました:
Cloudant => dashDB の単方向レプリケーション

簡単に紹介すると dashDB 側から Cloudant のデータをプルする形で複製を作ることで実現する、という内容だったのですが、2016/Jan/28 時点ではこの方法は使えなくなっています。代わりに Cloudant 側から dashDB 上にデータウェアハウス用DBを作る、という(逆の)データエクスポート機能によって実現できるようになりました。 というわけで、その手順を紹介します。


まずは Cloudant 側にエクスポートするデータが格納されている必要があります。既に Cloudant でデータが溜まっているのであればそのデータを使ってください。これからデータを集める場合は Bluemix の Node-RED スターターで集めるのが簡単だと思います:
2016012801


"Node-RED Starter" ボイラープレートを使って、アプリケーション環境を作成します:
2016012802


Bluemix 上に Node.js アプリケーションサーバーと Cloudant データベースサーバーが作られ、Node-RED アプリケーションが稼働している状態が作れました。ここから Node-RED 画面に移動します:
2016012803


ここで Node-RED を使ってセンサーのデータを集めます。この辺りの詳しい手順はこちらを参照してください:
Bluemix の Node-RED サービスで IoT アプリを作る(1/2)
Bluemix の Node-RED サービスで IoT アプリを作る(2/2)
2016012805


この例では Cloudant 上の "iotdata" データベースにデータを集めました。このデータベースを dashDB にエクスポートしてみます:
2016012804


左メニューの "Warehousing" を選び、サブメニューの "Warehouses" を選択すると、現在作成されたウェアハウスの一覧が表示されます。初回は一覧に何もないので "Create a warehouse" ボタンがあるだけだと思います。今からウェアハウスを作るのでこのボタンをクリックします(またはメニューから "Warehousing" - "Create  a Warehouse" を選択します):
2016012807


「ウェアハウス」の実体が Bluemix 上の dashDB になります。というわけで、ここで Bluemix の ID とパスワードを入力してログインします:
2016012801


ログイン後、作成するウェアハウスの情報を入力します。ウェアハウスの名前を指定し、そして(今回は)ウェアハウスの dashDB を新規に作成するので "Create new dashDB instance" を選択します:
2016012802


そしてウェアハウス化する Cloudant のデータベースを指定します。今回は "iotdata" というデータベースを対象にします。途中まで入力すると候補が出てくるのでそこから選びます:
2016012803


対象データベースに "iotdata" が追加されました。必要に応じてここで複数のデータベースを追加指定することも可能です。最後に "Create Warehouse" をクリックします:
2016012804


しばらく待ちます・・・・
2016012805


Cloudant 上の "iotdata" データベースが指定した名前でウェアハウス化されました。"Open in dashDB" をクリックすると dashDB 上のデータとして開くことができます:
2016012806


dashDB で開いた時の画面です:
2016012807


メニューから Tables を選び、データベース名(IOTDATA)を選択すると、Cloudant から複製されたデータベースが確認できます。下図は Table Definition タブが選択された状態で、テーブル定義を確認できます:
2016012808


隣の Browse Data タブを選ぶと、実際のデータレコードが確認できます。Cloudant のデータが RDB である dashDB 内に格納されていることが確認できました:
2016012809


ちなみに、この時 Bluemix のダッシュボードを確認すると、未バインドの dashDB インスタンスが米国南部データセンター内に作られていました(元の Cloudant が米国南部以外で作られていても、dashDB は米国南部に作られるようです):
2016012808


また、ウェアハウスを作成した段階では Cloudant にも "_warehouser" というデータベースが作成されていました。2ドキュメントなので、おそらく管理用データベースかな:
2016012801


 

IBM Bluemix から使える Node-RED 環境内には AlchemyAPI の画像解析機能を持ったノードがあります。analysis カテゴリ内にある "Image Analysis" と書かれたノードがそれです:
2015120810


このノードを Node-RED 内で使うと比較的簡単に画像認識機能を実装することができます。実際に使ってみましょう。まずはこのノードをキャンバスにドラッグ&ドロップします:
2015120817


同様にして input カテゴリの inject ノードと、output カテゴリの debug ノードをドラッグ&ドロップでキャンバスに貼り付け、以下のように線で結びます:
2015120811


各ノードの属性を順に指定していきます。まずは inject ノードです。ここでは画像解析する画像の URL を文字列で指定します。というわけで種類には "string" を選択し、その下のフィールドには解析したい画像の URL を指定します。ここでは以下の画像(の URL)を指定することにしましょう:
http://news-de-smile.com/wp-content/uploads/2015/02/america-obama.jpg

↑これが何の画像なのか、は普通の人間であればわかるはず。これがコンピュータにわかるか??


"Payload" の種類に "string" を、その下に画像 URL を入力します:
2015120812


次に Image Analysis ノードの属性を指定します。ここでは AlchemyAPI を使うため、AlchemyAPI の API Key を指定します(未取得の方はここから登録して取得してください、無料です)。また解析内容は顔認識機能を使うことにしましょう。Detect の種類には "Faces" を選択します:
2015120813


最後に debug ノードの属性を指定します。この Image Analysis ノードの結果はメッセージ内に "result" という名前が付いた状態で返されます(一般的な "payload" ではありません)。というわけで、debug ノードでは debug タブに msg.result を表示するよう指定します:
2015120814


これで準備は完了です。右上の "Deploy" ボタンでデプロイします。また解析結果は debug タブに出力されるため、debug タブが表示されるよう選択しておきます:
2015120815


では解析を実行してみましょう。inject ノードの左にあるボタン部分をクリックすると、指定した画像 URL が AlchemyAPI の FaceDetection API によって解析され、その解析結果が debug タブに出力されるはずです:
2015120816


おそらくこんな感じの JSON テキストが実行結果として得られるはずです:
[
 {
  "age": {
   "ageRange": "55-64",
   "score": "0.447907"
  }, 
  "gender": {
   "gender": "MALE",
   "score": "0.993307"
  },
  "height": "389",
  "identity": {
   "disambiguated": {
    "dbpedia": "http://dbpedia.org/resource/Barack_Obama",
    "freebase": "http://rdf.freebase.com/ns/m.02mjmr",
    "name": "Barack Obama",
    "subType": [ "Person", "Politician", "President", "Appointer", "AwardWinner", "Celebrity", "PoliticalAppointer", "U.S.Congressperson", "USPresident", "TVActor" ],
    "website": "http://www.whitehouse.gov/",
    "yago": "http://yago-knowledge.org/resource/Barack_Obama"
   },
   "name": "Barack Obama",
   "score": "0.960834"
  },
  "positionX": "223",
  "positionY": "125",
  "width": "389"
 }
]

この中を見ると、画像に写っているのは1人の男性でその確率は 99.3307% 、年齢層は 55-64 歳でその確率は 44.7907%。そしてその人が "Barak Obama" である確率が 96.0834% である、という結果になっていることがわかります。それ以外の情報も含まれていますが、ともあれ Node-RED と Image Analysis ノードを使って簡単に画像認識 API を呼び出すアプリが作れてしまいました。

このページのトップヘ