#Pepperからのネットワーク利用
これまでのチュートリアルでは、センサーからの情報によりPepperの挙動を変えたり、音声や動き、タブレットなどを通じてPepperから利用者に対して情報を提示したりと、同じ空間にいる利用者とのコミュニケーションについて主に見てきました。
一方で、Pepperの持つネットワーク機能を用いることで、Pepperが利用者に何らかの情報を送ったり、ネットワークから得た情報に基づいてPepperが動きを変えたりすることも可能です。
たとえば、ここまでチュートリアルとしてみてきた標準ボックスライブラリには、 CommunicationフォルダにあるE-mail, Networkフォルダに、メールの受信、送信、ネットワーク接続の有無のチェックをおこなうボックスがあります。
また、Pythonボックスの仕組みを活用することで、Pythonスクリプトを用いてよりさまざまな機能を実現することができます。
ここでは、入力されたURLからデータを取得し、そこで得られたテキストを出力するボックスの作成を例として、Pythonボックスの作成方法や、考え方について具体的に見ていきます。
#HTTP Getボックスの作成
今回は、Pythonの標準的なライブラリである urllib2とHTMLParser を用いて、以下のようなボックスの作成を考えていきます。
HTTP Getボックスの仕様としては、
- 入力onStartは文字列型で、読み込みたいURLを受け取る
- onStart内でHTTPのGET処理をおこなう
- 成功した場合は、得られた内容をともなってonStoppedを出力する
- 失敗した場合は、エラーメッセージをともなってonFailedを出力する
今回はサンプルとして、onStoppedの出力文字列はSay Textのようなボックスへの接続を想定します。特に、HTML <body>
タグ以下のみを抽出し、すべてのタグは除去し、テキスト中の改行箇所に対して200ミリ秒待機するような制御命令を埋め込むものとします。
また、これらの動作は、以下のパラメータにより挙動を調整できるものとします。
パラメータ名 | 機能 | タイプ | 値の範囲 | デフォルト値 |
---|---|---|---|---|
Timeout | urllib2.urlopenの際のタイムアウト値(秒) | 整数 | 1~3600 | 30 |
Start line | 得られたテキストのうち、改行文字で区切られた各行のうちどの行からを出力するか (開始行=0) | 整数 | 0~10000 | 0 |
Max lines | 出力する最大行数 | 整数 | 1~10000 | 1 |
なお、実際にボックスとして作成する際は、HTTP Getボックスと、得られたHTML文字列を適切に加工するボックスを分けたほうが、より汎用性が高いボックスになるかもしれません。また、パラメータの設定もサンプルとして決めたものですので、利用場面など考慮の上アレンジを加えていってみてください。
#### (注意)HTTP Getボックスで処理できるコンテンツについて
この例では、デモとしてのボリュームを考え、例としてQiitaのHTMLを前提とし、標準ライブラリを使った簡易的なHTMLの解析を実装しています。
実際に外部のサービスを利用するためには、このような簡易的なHTMLのスクレイピングでは難しい場合も多いため、サービスが提供するAPIの利用を考えるなどしていただく必要があります。この場合、これから説明するPythonスクリプト部分を独自に実装していただく必要があります。
Pythonを使ってAPIにアクセスする方法などはそれぞれのサービスの説明などを参考にしてください。
###Pythonボックスの作成
まず、Pythonスクリプトを記述するための空のボックスを作成します。新規プロジェクトを作成し、以下の手順で操作していってください。
-
onStart入力の型を文字列に変更します。入力 onStart の**設定ボタンをクリック[A]**し、**Typeドロップダウンリストをクリック[B]**します
-
デフォルトで用意されるonStop入力を削除します。入力の**[onStop]を選択[A]**し、削除[-]ボタンをクリックします
-
onStopped出力の型を文字列に変更します。出力 onStopped の**設定ボタンをクリック[A]**し、**Typeドロップダウンリストから[文字列] を選択[B]**し、[OK]ボタンをクリックします
-
次に、出力にonFailedを追加します。出力の 追加[+]ボタンをクリックします
入力onStart, 出力onStoppedの際と同様に設定ダイアログがあらわれますので、Nameに **onFailed と入力 [A]し、Typeを[文字列]に変更 [B]**します
-
パラメータを追加します。変数の **追加[+]ボタンをクリック [A]し、Nameに Timeout と入力 [B]、Typeを[整数]に変更 [C]**すると、デフォルト値、値の範囲を入力するボックスがあらわれるので **デフォルト値, 最小値, 最大値を入力 [D]**し、[OK]ボタンをクリックします
-
これでボックスの入出力、パラメータの準備は完了です。[OK]ボタンを押すと、フローダイアグラム中に HTTP Getボックス が作成されます
これで空のボックスができました。次に、このボックス内にスクリプトを記述します。
###Pythonスクリプトの記述
HTTP Getボックスをダブルクリックしてスクリプトエディタを開き、以下のスクリプトを貼りつけてください。
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self)
def onLoad(self):
#put initialization code here
pass
def onUnload(self):
#put clean-up code here
pass
def onInput_onStart(self, url):
from HTMLParser import HTMLParser
import urllib2, contextlib
class ContentParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.inscript = False
self.inbody = False
self.text = ""
def handle_starttag(self, tag, attrs):
if tag == "body":
self.inbody = True
if tag == "script":
self.inscript = True
def handle_endtag(self, tag):
if tag == "body":
self.inbody = False
if tag == "script":
self.inscript = False
def handle_data(self, data):
if self.inbody and not self.inscript:
self.text = self.text + data
try:
timeout = int(self.getParameter("Timeout"))
self.logger.info("Loading... %s" % url)
with contextlib.closing(urllib2.urlopen(url, None, timeout)) as response:
parser = ContentParser()
parser.feed(response.read())
parser.close()
lines = [line for line in parser.text.splitlines() if len(line.strip()) > 0]
startline = int(self.getParameter("Start line"))
maxlines = int(self.getParameter("Max lines"))
text = "\\pau=200\\".join(lines[startline:startline + maxlines])
self.logger.info("Loaded: %s" % text)
self.onStopped(text)
except urllib2.URLError, message:
self.logger.warn("Failed to open: %s, message=%s" % (url, message))
self.onFailed(message)
コードのポイントは以下の通りです。
-
onStart入力に対する処理を、
onInput_onStart(self, url)
メソッドによって定義しています (参考: Pythonボックスの考え方#入力)def onInput_onStart(self, url): ... self.logger.info("Loading... %s" % url) ... with contextlib.closing(urllib2.urlopen(url, None, timeout)) as response:
引数urlには、onStartに入力された値(今回は文字列)が設定されます。
-
Timeoutなどのパラメータは、
self.getParameter
メソッドを呼び出すことによって取得しています (参考: Pythonボックスの考え方#パラメータ)timeout = int(self.getParameter("Timeout"))
-
得られた文字列は、 <出力名> メソッドを呼び出すことで渡しています (参考: Pythonボックスの考え方#出力)
text = "\\pau=200\\".join(lines[startline:startline + maxlines]) self.logger.info("Loaded: %s" % text) self.onStopped(text)
#### (参考)say文字列中の制御文字列 \pau=200\ について
この例では、Say Textボックス(ALTextToSpeech APIのsay()を使ったボックス)に文字列を渡す前提で、話す際の一時停止指定を埋め込んでいます。
これが \pau=200\ という文字列で、200ミリ秒の一時停止(pause)を指示しています。
他にも、さまざまな制御をおこなうことができます。詳しくは、ドキュメント中の ALTextToSpeech を検索し、 Overview > Acapela Mobility Text TAGS Documentation を参照してください。
#動作確認
動作確認として、Pepperチュートリアル (1):SDKインストールとアプリケーションの作成/実行の出だしを、ジェスチャー付きで読み上げる ということをおこなってみます。
-
作成したHTTP Getボックスに加え、standardボックスライブラリの以下のボックスを配置します。
- Data Edit > Text Edit
- Audio > Voice > Animated Say
#### (参考)Animated Sayボックス について
Animated Sayボックスは、ここまで見てきたSayボックスと異なり、しゃべるのと同時に動きをつけることができます。
この身振り手振りは自動的に付加されますが、文字列中のアノテーションによって明示することも可能です。
詳しくは、ドキュメント中の ALAnimatedSpeech を検索し、Overview などを参照してください。 -
Animated SayボックスはSayボックス同様、Localized Textボックス+Say Textボックスといった形ですので、この中から文字列を受け取るAnimated Say Textボックスを取り出します
-
Text Editに、 http://qiita.com/Atelier-Akihabara/items/c5f57358a7b333eef397 を貼りつけます
バーチャルロボットで動作確認をおこなうと、ダイアログパネルにテキストが表示されます。
また、Pepper実機で再生をおこなうと、身振りを交えながらPepperがチュートリアルの内容をしゃべっていきます。(リンクをクリックして再生できます。)
今回は改行に対して機械的に待ち時間を入れたため、区切りなどが自然ではない部分がありますが、テキストタグを適切に設定していくことで、自由に制御することができます。
このようにして、ネットワークからデータを取得してPepperの挙動に反映させたりといったことが、Pythonボックスを利用することで実現できます。