スクリプトの暗号化

QiChatはスクリプトであり、ロボットは可読なテキストファイルの状態でそれを入力する。作成したユーザーがそれを一貫して読めることは大事だが、そのスクリプトを他者に使わせるときに読まれることが望ましくない場合がある。
iBotシステムでも、ロボットが使用する瞬間には可読なテキストファイルでなければならないが、ユーザーから他者にわたったときには暗号化されているようにするつもりだ。それができるのは、スクリプトをロボットに読み込ませるのはiBotであって、iBotが暗号化されたスクリプトを複合できれば良いわけだから。
ユーザーは、結局最終的なスクリプトの利用者に複合化キーを渡さなければならないので、完全ではない。
iBotが固有の複合キーを持たせてスクリプトを隠蔽すれば、利用者はほぼ完全にテキストで見れなくなるが、iBotは、C++ で書かれ、コンパイルされ、バイナリファイルになっているので、それをリバースエンジニアリングで解読すればそれも見られてしまう。しかし、それは仕方がないだろう。

QiChatにおける変数の扱い

iBotシステムは、最後の詰めのところで、複雑で手間のかかる作業になっている。
特に変数の取り扱いだ。iBotは、Choregrapheを全く使わなくても、対話、動作、認知の機能を利用できるようにするシステムだ。中心には人との対話があり、それに起動されてさまざまな動作、認識機能が発動される。コンセプトがそうなっている。
その全体を直接コンんとロールするのがQiChatスクリプトである。QiCHatスクリプトにはめ込まれる機能を全て解釈して実行するのは、直接にはロボットなのだが、人とそのロボットの機能を媒介するシステムがiBotである。ほとんどそれはiBotで実現できるようになるのだが、NAOQiには、多様な機能があるので、全ては組み込まない。
その際、変数の扱いが面倒である。というのもNaoqiというロボットOSは、変数のイベント処理が重要な役割を持っている。変数に値が入力されたり変更されたりすると、それだけでイベントが発生し、プログラムの方でそれを獲得できるようになっているのだ。
たとえば、人の顔を学ぶとき、それが誰の顔かを変数で入れ籠まないと行けない。具体的な名前にすると、全く融通が利かないからだ。それをQiChatから、iBotシステム経由で、naoqiに渡す作業が必要になる。あるいは、認識した顔が誰であるかをQiChatのスクリプトの中に戻せなければならない。これら全てが変数によって媒介される。非常によくねられたシステムであることは間違いない。ちゃんと使えるようになっている。
iBotがどのように拡張されても、この変数を合理的に扱えるようなシステムにしたい。

編集機能の充実

iBotシステムのQiChatスクリプト編集機能で、簡単に入力できるように、また、質疑応答の構造かも容易になるような機能を組み込んでいる。当初は、これは後まわしにしてシステムを公開することを考えたが、この編集機能があるかないかで、スクリプトの開発スピードが格段に違うようになりそうなので、その部分も完成した後に公開しようと思う。

ファイル転送ローカルモジュール

ファイル転送ローカルモジュールがほぼできた。全体のシステム概要を示すと以下のようになる。

iBotロボット管理システム
iBotロボット管理システム

ファイル転送ローカルモジュールは、ロボットの中に組み込まれるために、ユーザーの管理のもと、サーバーから対話用のトピックファイル(スクリプト)をダウンロードするものだ。殿ファイルをダウンロードするかは、サーバーにあるユーザー専用の設定ファイルに記載されているものに従う。
対話を管理するローカルモジュールも必要なのだが、これはリモートモジュールとしては出来上がっているので、ローカルモジュールとしてロボットに組み込めるものに変更する必要がある。簡単に終わりそうだ。だから、先に初期化インストーラーを作成しよう。

xercesによるC++コンパイル

ユーザー設定ファイルのXMLをDOMで解析するために、にxerces3.1.1をUbuntuにソースからデフォルトの設定でインストールしたが、プログラムをそのままg++でコンパイルしても動かない。次の操作が必要だ。
(1)リンカ段階で、xerces-cライブラリをくっつける。すなわち、こんな感じ。g++ -o xmldom xmldom.cpp -lxerces-c
そうしないときらめくようなエラーの流れが発生する。
(2)/etc/ld.so.confに次の1行を加える。(デフォルトではそこにライブラリがインストールされている)
/usr/local/lib
(3)/sbin/ldconfigを実行しておく。
ただ、XMLのparse exceptionが発生する。と、思ったら、サンプルに使っているxmlファイルがおかしかったようだ。

ファイル転送モジュールのプロトコル

ロボット管理のクラウドサイトの原型はできて、ロボットとクラウドとのファイル転送を仲介するモジュールを作成している。すでに作成していたのは、httpプロトコルで、あくまでもウェッブサーバー上のファイルをロボットに取込むものだった。これでは、クラウド上に作成したスクリプトの取り組みには使えないので、ftpプロトコルでロボットにファイルを取込むモジュールに変更しているところ。
ロボットとサイト間通信のC++プログラムの基本はできているので、あとはFTPのプロトコルをそこに組み込むだけである。基本的に、ロボットはローカルネットワークのファイヤーウォールの内側にあることがメインであると想像されるから、ftpプロトコルはpassiveモードにしなければならない。
telnetを使って、このpassveモードの手続きをシミュレートしたので、記録のためにここに残しておく。あとはプログラムに組み込むだけである。
********** 端末1を開く ************
sample.jp(仮想サイト)からrobo.xxxx.jp(仮想サイト)への接続。$から始まる行は、クライアントが打ち込んだコマンド。
$ telnet sample.jp  21
Trying 133.12.158.102...
Connected to robo.xxxx.jp.
Escape character is '^]'.
220 (vsFTPd ....)
$ USER xxxxx
331 Please specify the password.
$ PASS yyyyy
230 Login successful.
$ TYPE I  (I:がバイナリモード、A:がアスキーモード)
200 Switching to Binary mode.
$ PASV
227 Entering Passive Mode (zzz,zzz,zzz,zzz,39,6).
(いま端末を動かしているクライアントが、NATなどのファイヤーウォールの内側にいると想定。普通は、このあとPORTコマンドで指示されたクライアントのポートに、サーバー側からつなぐのであるが、ファイヤーウォールのもとでは、それが正常にできないので、クライアントからつなぐ、サーバーのIPアドレスが、zzz,zzz,zzz,zzzとして、また、ポート番号が39×256+6=9990として指定されてきている。そこで、もう一つ端末を開いて、すなわち独立のコネクションを実行する。これもまた、クライアント側の端末であることに注意。)
******* 端末2のコネクション ********
telnet zzz,zzz,zzz,zzz 9990
Trying 133.12.158.102...
Connected to robo.xxxx.jp.
Escape character is '^]'.
(これは、このままなにもしない。)
******* 端末の1コネクションにもどる ********
$ CWD /home/xxxxx
(たとえば、上記コマンドで、フォルダを変更する:これ自体はPASV指定前でもできる。)
$ LIST
(こうすると、先ほどの端末2にフォルダのリストが出力される。このあたりがPASVモードの面白さと言うか、おどろきというか。)
******* 端末2のコネクション ********
drwxr-xr-x    3 1000     1000         4096 Jul 15 12:30 Desktop
drwxr-xr-x    3 1000     1000         4096 Dec 26  2012 Documents
drwxr-xr-x    2 1000     1000         4096 Jul 15 10:06 Downloads
drwxr-xr-x    2 1000     1000         4096 Dec 24  2012 Music
drwxr-xr-x    4 1000     1000         4096 Mar 02 06:13 Pictures
drwxrwxr-x   13 1000     1000         4096 Jun 21 22:29 Project
drwxr-xr-x    2 1000     1000         4096 Dec 24  2012 Public
(以上のように、実際のデータのやり取りは、端末2上で行われ、コマンドは、端末1でやりとりされる。)
というわけである。
※ ただ、ロボットがグローバルIPを持っていて、20番ポートでデータのやり取りができる場合でも、PASSIVEモードにしても良いかどうかは若干気がかりである。多分問題ないと思うが。問題があれば、おそらく20番ポートを指定してPORTコマンドを打ったときに、
500 Illegal PORT command.
と返した場合にだけ、PASSIVEが必要になるとすれば良いと思う。

Javascriptによるnaoqiモジュールのコントロール

戦略的には、誰でもどこでも自分のロボットを必要なレベルでコントロールできるようにすることを狙っている。そのために、ロボット内に自作のモジュールを組み込み、外のサーバーとQiChatスクリプトを組み込むようにしてきた。
次の段階としては、ブラウザから、ロボットおよび、そのコンテンツを持っているサーバーを制御し、また、ブラウザ上でHtml5の手法でスクリプトの作成などもできるようにすることだ。
今日、特別に作成したサイトから、Javascriptでロボットとブラウザを接続し、モジュールAltextSpeechを起動させ、ブラウザから入力したテキストをしゃべらせてみた。簡単にできた。他のモジュールを動かすこともそう難しくはないだろう。

Postureモーションのぎこちなさ問題が解決か・・・

先に書いた、座る、たつ、動作をC++のAPIをとおしてやると動きがぎこちなくなってしまうという問題を、たぶん、解決できたと思うので、記録のために記載しておく。
Choreguraphでこのような動作をやってもきわめてスムーズに行く。そのログをみると、AutonomousLifeをInteractiveの状態で行なっているのでそれをまねしようとしたがどれも失敗した。プログラマティカルに、Interactiveには移行できないと指摘しながら、C++でそこにいこうとしても、Documentoをどう読んでも書いていない。C++上は、StateはSolitaryになっていて、それをいったんDisabledにしてみたりしたが、ロボットは破壊的な動きをすることがわかって断念。
結局、ALAutonomousMovesを座る前にfalseにして、再び立ち上がってから、trueにするという方法で解決できたような気がする。プログラムはこんな感じだ。
/*****************************
amove = new AL::ALProxy(broker, "ALAutonomousMoves");
if(poseType == "Sit"){
// If sitdown, ALAutonomousMoves makes disabled
amove->callVoid("setExpressiveListeningEnabled",false);
}else{
// if stand up, enabled
amove->callVoid("setExpressiveListeningEnabled",true);
}
**************************/
ALAutonomousMovesとPostureがバッティングして、動きのぎこちなさが発生したと考えることができるのだ。
しかし、この間いろいろなことを試して、ロボットの動きについての知識が増え、また、C++でコントロールする方法が深まったことはとても良かったと思う。

C++ローカルモジュールからのログ出力

ローカルもモジュール(ロボットから直接起動するモジュール)のデバグ用にログを出力したいのだが、ただコンソール出力にしているだけ(リモートモジュールの場合は、これだけで良かった)では、どこにも出力されなくなってしまう。したがって、ログ出力のライブラリを使わなければならない。以下のようにする。
(1)main.cppに次のようにインクルードファイルを設定する。
#include <cstdlib> //for atexit
#include <qi/log.hpp>
(2)同じく、main関数の中で、次のような初期化を行う。
qi::log::init();
atexit(qi::log::destroy);
(3)使用するcppファイルに、
#include <qi/log.hpp>
を入れて、情報の場合は、
qiLogInfo("wsftmod") << "******** トピックリストの取得 *********";
などとコマンドを入れる。警告の場合は、
qiLogWarning("wsftmod") << "トピックリストを取得できませんでした;
などと入れる。
ここで、"wsftmod"は、出力の冒頭に記載される識別子で、自分が区別できれば何でもよい。モジュール名をいれておけば最低間に合う。
以上の操作で、以前示したロボットのログの中に情報が現れデバグが簡単にできてしまう。(gdbを使うまでもないと思う。多分使えない。)

Localモジュールの問題

研究会のサーバーから、ロボット用のtopicファイルをダウンロードしてロボットに組み込むモジュールを作成した。クラウドの入り口に立とうという狙いだ。モジュールがリモートだと、ダウンロードしたファイルをロボットに保存できないので、ロボット本体に組み込むローカルモジュールに変更した。
組み込んでみると、ロボットの電源を入れたときに、そのモジュールを読み取るはずなのだが、失敗している。ログを調べると、原因は、モジュールをコンパイルしているのは64ビットのLinuxマシンなのだが、ロボットは、32ビットのLinuxで動いていて、モジュール実行時の整合性がとれていないためのようだ。Cコンパイラを調整すれば、64ビットのLinuxでも、32ビットの実行ファイルを作成できるようなのだが、いろいろやってもうまくいかない。
そこで、Linux64を入れているVirtualBoxにLinux32も追加した。そして、そこに、naoqiのビルド関係の一式を再度インストールして、コンパイルし直した。それでモジュールとしてロボットに組み込むと、めでたく起動時にそれを読み込んだ。
外見上は特に問題なく機能しているようなのだが、ファイルをロボットに保存している段階で失敗しているようだ。パーミッションの問題なのかチェックしたが、ログがどこにも見つからない。はて困った。
(2014年8月18日追記)
単に保存するパスを間違えていたためであることが分かった。