前回 の記事から大分時間が経過してしまいました… あれからPepperの一般モデルが発売され、Choregraphe は v2.3.1 が公開されました。 前回の予告通りに実際の開発で得たノウハウを語る前に、Pepperの一般モデルで何が変わったっかをまず説明しましょう。

新旧 Pepper の違い

旧型と新型の比較表です。
旧型 新型
対象モデル 先行モデル(2014年9月20日)
開発者向けモデル(2015年2月27日)
2015年以降、月ベースで台数限定で予約開始
(6月20日, 7月31日, 8月29日)
OS NAOqi 2.0.x NAOqi 2.3.x
開発環境 Choregraphe 2.0.5
(Choregraphe 2.1.2は非公式)
Choregraphe 2.3.1〜
開発マシン windows7
Mac LION / Mountain Lion
windows7、8
Mac LION / Mountain Lion / Yosemite
CPUスペック Intel Atom Z530 (1コア/1.6GHz) Intel AtomE3845 (4コア/1.91GHz)
無期限ライセンスキーの取得 Pepper を購入時に付属 DEVELOPPER PROGRAM に参加登録する

◯ Choregraphe 2.3.1

NAOqi のバージョンで対応する Choregraphe の縛りが厳しいことに気をつけましょう。
NAOqi 2.3.x は当然 Choregraphe 2.3.1 以降でないと開発できませんが、NAOqi 2.0.5 向けのアプリを Choregraphe 2.3.1 で開発することもできません。(今後そういうケースはないと思われますが)
旧型向けに製作したアプリを新型でも動作させる為には、Choregraphe 2.3.1 でビルドしてから動作を確認しましょう。

◯ ハードウェア面の強化

CPUの処理性能が2倍になりました。これにより音声認識による応答のレスポンスが体感できるくらい早くなりました。
ハードウェア面の強化は CPU 以外にもあります。

  • 脚部に赤外線センサーが追加 障害物の検知の精度が上がったように思えます。ちょっと敏感すぎるのが困りものですが…
  • 肩周りがすっきりした。 可動域の向上や、熱放射の効率アップに関係している?

旧型の CPU は無償の取り替えサービスが始まりました。

デベロッパー先行モデルのCPUアップグレード

どうやって取り替えるかというと、アンパンマンよろしく頭を挿げ替えるようです。
配送料込みで、ジャムおじさん工場に運ばれて、20日前後で戻ってくるとのこと。

◯ 無期限ライセンスキーの取得方法

DEVELOPPER PROGRAM

に参加することで誰でも入手が可能になりました。 開発者人口が増えることを期待したいです!

ALMemory

さて開発で得たノウハウですが、今回は ALMemory について説明します。

ALMemory には、Pepper の各種センサーから得た情報がリアルタイムで格納されています。
また、アプリ(プログラム)上で制御するパラメータもここに格納されます。
この ALMemory の仕組みを理解すると 開発者が Pepper で出来ることの幅は大きく広がると思います。
本記事では、後者(アプリ上でのパラメータ制御) に重点をおいて説明をしていきます。

※ 以降は Choregraphe v2.0.5 で開発した場合の内容になります。

◯ メモリウォッチャー

Choregraphe のメモリウォッチャーで、ALMemory が管理しているデータやイベントを監視することができます。
メモリイベント(後述)を使った処理を実装する場合に役立ちます。

データをインサートしたり、イベントを起したりすると、

self.memory.insertData("MyApplication/HogeSay_value", "共有メモリにデータ保存")
self.memory.raiseEvent("MyApplication/HogeSay", "共有メモリにデータ保存と、イベント拡散")

下のように値が入力されて返ってきます。

image

メモリウォッチャー以外でも確認できます。
旧ロボットウェブページを開いた後、Advanced > Memory と遷移します。
検索窓にデータやイベントのキー名を入力して、Search を押下すると下のように確認ができます。

image

◯ メモリイベント

各種センサーから得た情報が、同時多発で流し込まれる性質上、ALMemory はスレッドセーフな領域になっています。
ゆえに Choregraphe は処理を並列して書けますが、self.memory.raiseEvent() などでメモリイベントを同じ瞬間に複数実行しても問題は起きないわけです。

– メモリイベントを発生させる方法 –

(1) Raise Event Box を使う

image

入力の onStart の引数に保存させたい情報を渡します。
Box の変数 "key" にメモリのキー名を設定します。
慣例的に「アプリ名/キー名」で記述することで、アプリ間で名称が被らないようにしています。

"MyApplication/HogeSay"

出力の onStopped は決してこのイベントのコールバックではないので注意してください。
( Raise Event Box の手続きの終了を示しているだけです )

ちなみに、Raise Event Box と使わずに Insert Box を使った場合は当然ですがメモリイベントは発生しません。
また、「Raise Event Box と Insert Box で同じ KEY 名を使用した」場合、メモリウォッチャーの性質は EVENT が優先されます。

(2) Box 内に python コードで記述する

raiseEvent メソッドに、イベントのキー名 ("MyApplication/HogeSay") と、値(value) を設定します

def onLoad(self):
    self.memory = ALProxy("ALMemory")

def onUnload(self):
    self.memory = None

def onInput_onStart(self, p):
    self.memory.raiseEvent("MyApplication/HogeSay", value)
    self.onStopped(p)

イベントに渡す値にはリストも設定出来ます。

self.memory.raiseEvent("MyApplication/HogeSay", ["山田", "太郎"])

メモリウォッチャーをみると、ちゃんと配列で格納されているのがわかります。

image

ディクショナリを渡すことも可能ですが、

self.memory.raiseEvent("MyApplication/HogeSay", {"Surname": "山田","Realname": "太郎"})

リストに変換されてしまうので、

image

ディクショナリ型に戻す必要があります。

def onInput_onStart(self, p):
    dic = dict(p)
(3) QiChat から発生させる

Pepepr が人間が喋った名前を復唱する QiChat のスクリプトを書いたとします。
"_*" で人間が喋った言葉の任意の箇所だけを切り抜いて、 "$MyApplication/HogeSay" に保存しています。

u:(私は _* です。) こんにちわ! $MyApplication/HogeSay = $1

「私は鈴木です。」と喋ったとしましょう。
「MyApplication/HogeSay」に"鈴木"という文字列がメモリに格納されて、

image

イベントも発生します。

image

これを応用して、proposal のタグ名を変数化して、会話の開始位置を制御することを思いついた方がいました。
他にも応用できそうで、興味深いです。

[Qiita]QiChatでタグ名は、変数にできる

– メモリイベントの通知を受ける方法 –

(1) Subscribe to Event Box を使う

image

Box の変数 "key" にメモリのキー名を設定します。
出力の onEvent の戻り値 に イベントで受けた値が返ってきます。
出力の onStopped は 「イベントの通知の監視」の停止時に発生します。
決してイベントのコールバックではないので注意してください。

(2) 「ALMemoryからのイベントの追加」で受け取る

image

Behavior や Box の入力インターフェイスとして設定することができます。

(3) Box 内に python コードで記述する

self.memory.subscribeToEvent() で、受け取るメモリイベントのキー名とコールバック先を設定します。

def onLoad(self):
        self.BIND_PYTHON(self.getName(), self.onMyEventCallback.__name__)
        self.memory = ALProxy("ALMemory")

    def onInput_onStart(self):
        self.memory.subscribeToEvent("MyApplication/HogeSay", self.getName(), self.onMyEventCallback.__name__)
        self.memory.subscribeToEvent("MyApplication/HogeListSay", self.getName(), self.onMyEventCallback.__name__)

    def onMyEventCallback(self, key, value, message):
        self.onOutEvent([key, value])
(4) QiChatで受け取る

e:イベント名で関連付けることができます。
$:変数名で値を参照することもできます。

#u:(e:MyApplication/HogeSay) $MyApplication/HogeSay です。

– メモリイベントの種類 –

本記事はアプリ(プログラム)上で制御するための自作のイベントの使い方の説明に止まりましたが、冒頭で述べたように各種センサーから得た情報をはじめ、NAOqiが管理しているあらゆる情報を参照できます。

・センサーから得た情報をイベントで受け取る

[Qiita]PepperのAPIを使って年齢認識

この記事の場合、Pepper が画像認識した結果などを参照しています。
"PeoplePerception/" 配下に認識中の人間のあらゆる情報が格納されています。

・QiChat 上の振る舞いをイベントで受け取る

[Qiita]QiChatを使ってPepperを自由に動かす。【イベント】

この記事の場合、"Dialog/" 配下から会話上の振る舞いを取得しています。
また、"e:onStart" で 対象のダイアログの Box の onStart が呼ばれることを利用してQiChatの開始をしています。

まとめ

ALMemoryに蓄えられている情報は膨大です。
一度に全て覚えるのは大変ですので、必要に応じて少しずつ覚えていけばよいでしょう。
徐々に Pepper でその瞬間何が行われているのかが見えてくると思います。

次回は、Pepper 内部のディレクトリ構造について語りたいと思います。
(なるべく間を空けずに)