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

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

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

関節のStiffnessの問題

C++のモジュールから動きをつけるときの、動作のぎこちなさが完全に解消できないので、そのことをAldebaran社のフォーラムに投稿して聞いてみたところ、いくつかのチェック項目を指揮された。大概チェック済みのものだが、一つ、関節のStiffnessが1.0になっていない状態で動こうとすると、おかしくなるよというのがあった。
そこで、動作の前にStiffnessがどうなっているかをALMotionのメソッドで調べてみた。結果的に、全ての関節が1.0になっていて問題なかった。が、motionのAPIは、あまり触れる予定がなかったことなので、視野が開けた気はした。
なお、stiffnessについて一言説明する。例えば人間で言えば、筋肉の力を全て緩めてしまえば、立っていられず、グニャグニャとなってしまうように、ロボットも、関節のモーターに力を入れておかないと、しゃきっとはしていられなくなる。Stiffnessとは、このようにロボットの筋肉に力を入れることを意味している。

brokerとmotionの問題

昨日うまく行かなかった二つの問題が一部解決した。
(1)外側で起動されたAPIからのイベントメッセージをDIALOGの中で受け取ることで、これは、
AL::ALProxy memory(broker,"ALMemory");
memory.callVoid("declareEvent","didSitdown");
memory.callVoid("declareEvent","didStandup");
if(poseType == "Sit"){
memory.callVoid("raiseEvent","didSitdown",1);
}else if(poseType == "Stand"){
memory.callVoid("raiseEvent","didStandup",1);
}
この様にイベントをコールすればよい。すなわち、declareEventしてから、raseEventをやるのである。
(2)二つの目問題は、「座れ」に対してすわるのだが、甚だしくぎこちない、座ろうとして止めたりまたやったり、ビクッとしたり、見ていられない。なかなか原因が分からなかったが、色々やった末に、brokerが原因かもしれないと思うようになった。動きの調整がとれていないのだ。そこで、あらたにポーズをとるための専用brokerを作成して、使ってみることにした。brokerの作成のためには、IPとport番号が必要なのだが、それは、親のbrokerからとってくることにした。brokerは次のように作る。
broker = AL::ALBroker::createBroker(
"poseBroker", "0.0.0.0", 54000,
pbroker->getIP(), // 親brokerからのIPをセットする
pbroker->getPort(), // 親brokerのPortをセットする
0 // デフォールト
);
という感じだ。
これで、改善した気もするが、確信はないがいつまでも関わっていられない。Aldebaranのフォーラムに、問題を投稿して、当面保留か。
どなたかわかる方がいたら教えていただきたい。

C++モジュールのロボット上での機能

昨日までに作った、C++からALDialogモジュールを使用する一連の作業をロボット上で確認した。いくつか問題が生じた。
(0)モジュールはリモートでも、トピックファイルはロボット上に置かなければならないことがわかった。
(1)今までの呼び出し手順に、ALDialogプロキシから、subscribe("モジュール名")の呼び出しを追加しなければならないことがわかった。いままでは、基本的に、
ALDialogのインスタンス作成
言語の設定
トピックのロード
トッピックの有効化
の手順だったが、言語設定とロードの間に、subscribe("モジュール名")をはさむこと。これによってモジュールの機能がスタートするらしい。
(2)もう一つは、いままでは、三つのトッピックファイルについて、一つずつ、ロードしたら有効化していたのだが、コンパイルから有効化の間に、ログに訳の分からないエラーが出てくる。そこで、まず、三つのトピックファイルを一挙にロードし、コンパイル用に1秒のsleep関数を挟み、次にすべてを有効化する手順にしたら、エラーが消えて、ロボットがトピックファイルに従って返事をするようになった。このあたりになるとほとんど裏技である。
以上は、解決した問題なのだが、未解決の問題。
(3)ロボットの座る立つの動きがぎこちなさすぎる。言葉に従って、動作はするのだが、動きの途中で、素早すぎる動きがわずかに挟まれ、おかしい。原因不明。動きを0.5など、遅くしているのが原因かもしれない。
(4)最後の、坐れなどの動作のあとに、トピックファイルに仕込んだ、変数変化のイベントをコールバックすることがうまくいっていない。