qimessaging.js 使い方の注意点

iBotは、ブラウザから、ロボットのモジュールにアクセスし対話や動作をコントロールする。その際最も重要な役割を果たすのが、javascriptで書かれたqimessaging.jsである。Aldebaranから提供されていて、ロボット本体が持っているウェッブサーバーのドキュメントフォルダに置かれている。
javascriptはクライアント側でブラウザを操作するものなので、このロボットにあるqimessaging.jsをユーザー側のブラウザに取込まなければならない。その際、通常のjavascriptと同様に、

<script src="htttp://192.168.11.3/libs/qimessaging/1.0/qimessaging.js"></script>

のように、ヘッダー部分に置くことになる。IPアドレスは、ロボットのものでなければならない。
第1の注意点は、このqimessaging.jsをjqueryの$.getScriptコマンドをつかって、実行途中にダイナミックに取込むと、機能しないということだ。理由はあるのだろうがわからなかった。IPアドレスを取得したあと、phpでヘッダー部分に書き込むようにして、ページをリロードするという作業が必要だった。
このjsを取込むと、
var session = new QiSession(robotip);
というかたちで、Qisessionのインスタンスが作成可能になる。robotipはロボットのIPアドレスである。
第2の注意点は、このQisessionは、接続中に2度作成してはならないということである。2度作成すると、qimessaging.jsがエラーを引き起こす。当然のようであり単純なことなのだが、javaスクリプトファイルをいくつも作っているうちに問題を引き起こしがちである。
一旦、上記のsessionを作成すると、serviceメソッドを利用して、ロボットのnaoqiのさまざまなモジュールを利用できるようになる。基本的に、javascript特有の非同期的組み立てが幾重も必要になる。

XercesからTinyxml2へ

iBotシステムの、個々のロボットの設定ファイルは、xmlフォーマットで書かれていて、ロボット内のiBotモジュールから読み込まれ処理される。モジュールはC++で書いているので、C++でxmlファイルをparseしなければならない。
parserをxercesというライブラリを使うと書いたが、これで作成すると、ローカルモジュールだけじゃなく、このライブラリ自身もロボットに送り込まなければならず、これが数十メガバイトもあり重たい。しかも、ロボットは32ビットcpuという制約もある。
そこで、xercesを改め、もっと軽いそれ自体が、簡単な一つのクラスと一つの実行ファイルでできているtinyxml2に変えた。実に軽く簡単にparseできるようになった。そかし、このために、1日使った。

ローカルモジュールの出力を呼び出し側にかえす

ローカルモジュールからNAOqiへのログ出力については既に書いた。コンソール出力は、ただ、stdoutにストリームを出せばよい。ここで問題にするのは(それほど大げさなことではないが)ローカルモジュールのあるメソッドを実行させた場合に、その戻り値を呼出側でとらえる方法だ、難しくはない。
たとえば、ローカルもジュールのバージョンを獲得する場合を考えよう。次のようなrRobotクラスメソッドをモジュールの中に書き込む。
std::string Robot::getVersion()
{
std::string version = "0.0.4";
return version;
}
これをPythonから呼び出す場合、
>>> ws = ALProxy("Robot","127.0.0.1",9559)
>>> rep = ws.getVersion()
>>> print rep
これで、versionが出力される。
ただし、APIマニュアルでは、クラスのコンストラクタで
functionName("getVersion", getName(), "ibotのバージョンを取得する");
setReturn("char*", "return version");
BIND_METHOD(Robot::getVersion);
とsetReturnを入れてバインドするようにかかれているが、必須ではない。
ただ、一つは困ったのは、文字列をchar*で出力すると、モジュールがコアダンプして止まってしまうことだ。理由はわからない。std::stringで出力するようにしたら正常に獲得できた。どこかに考え違いがあるようだ。

認識した顔情報をQiChatスクリプト変数に戻す

どうやるか、相当時間考えたが現状の結論はこうだ。
まず、顔認識が実行されたときに発生させるイベント関数(ALFaceDetectionのEvent APIを参照する)を設定しておいて(このやり方はspeechRecognitionのサンプルを使えばいい)、このCallBack関数に与えられるデータから、認識された顔情報をALMemoryのInsertメソッドを通じて、変数に値を組み込む。
ということなのだが。

QiChatスクリプトLoad時の例外処理

QiChatスクリプトをロボットに組み込むときに、スクリプトにバグがあると正常に組み込めない。C++でこのプロセスをALDialogのloadメソッドでやると、組み込み失敗でプログラムが止まってしまう。例外処理をやらないといけないのだ。
APIマニュアルには例外を取れとだけ書いてあってどのような例外がスローされるか書いてなかった。例外処理をしないと実用上とてもまずいので、また、Aldebaranのdeveloper forumに質問したところ早速解答があった。
catch(const std::exception& e){
std::cout << e.what() << std::endl;
}
と、
catch (const AL::ALError& e)
{
std::cout << e.what() << std::endl;
}
のどちらも、正常に機能した。基本的に同じものであるようだ。どちらかでよい。

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

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

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

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

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日追記)
単に保存するパスを間違えていたためであることが分かった。