SIGVerse用Pythonライブラリ
SIGVerse用Pythonライブラリを作ってみることにしました。
ControllerとServiceをPythonで記述することができます。ソースコードは、GitHubで公開中です。
このライブラリについて
SIGVerseは、基本的にsimserverとSIGViewerの2つがペアで動いています。simserverでは、定義された世界ファイルを読み込んで、3Dの物理シミュレーション可能な仮想環境を作成しており、Linux上でのみ動作しています。一方、SIGViewerは、simserverと通信を行いながら、仮想環境の可視化とカメラのビューの生成や距離センサの情報の生成も行っているようです。ただし、SIGVewerはWindows上でのみ動作しますので、SIGVerseを使う場合には、LinuxとWindowsの実行環境が必須になります。
また、SIGVerseでは、仮想環境内のロボットなどにコントローラを付加し、外部プログラムやSIGViewerなどから操作する機能もあります。このコントローラの機能とは別に、SIGServiceという概念もあり、コントローラやSIGViewerや他のSIGServiceのプログラムと通信し、様々な動作を実行させることができるようになっています。
上記のような状況ですので、コントローラはLinux上のC++で、SIGServiseは、WindowsとLinuxのC++でプログラムを書くの正式なやり方のようです。
しかしながら、コントローラやSIGServiceのプログラムの実装で、試行錯誤しながらプログラム開発をしたり、現在、広く普及しているロボットミドルウェアとの連携を考えるとPythonにより、コントローラとSIGServiceを開発することができれば、色々な応用の道も広がると思いますので、コントローラとSIGServiceをC++のAPIとほぼ同じように開発できるようなライブラリの実装をしてみました。
なお、このライブラリは、SIGVerseのserverとTutorialにあるサンプルプログラムなどを参考に、ほぼソースコードをよみながら作成したものですので、動作の保証がありません。また、テストしていないコードももあり、実装された関数群がすべて正常に動作するとは限りませんので、ご容赦お願いいたします。
動作環境
このライブラリは、Python2.7での動作を確認しています。Python3.xの処理系に関しては動作未確認のため、Python2.7などの 2.xの処理系で利用してください。
ライブラリのダウンロード
前述していますが、このライブラリはGitHubで公開しています。ダウンロードするには、プロジェクトのトップページに移動し、〔Download ZIP〕をクリックするかgitコマンドで
#git clone https://github.com/haraisao/sigclient_py
とすればよいと思います。
ファイル構成
このライブラリの構成は下記のようになっています。
- docs : 簡単なクラス構成およびAPI等のドキュメント群のフォルダ
- sample: このライブラリで動作するエージェントコントローラのサンプル(オフィシャルWikiのTutorialの一部)
- LICENSE.md:このライブラリのライセンス定義。(MITライセンスにしています)
- README.md: GitHubのプロジェクトトップの表題
- Quat.py: Quaternionsの計算用クラス (http://3dengine.org/Quaternions)
- sigcomm.py: SIGverseの通信用クラス群
- simobj.py: SIGServer内のオブジェクト用クラス群
- sig.py: エージェントコントローラ用クラス群
- sigservice.py: SIGService用クラス群
- sigrun.py: エージェントコントローラ実行用メイン関数
コントローラの作成
このライブラリを用いてエージェントコントローラを作成してみます。基本的な構造は、sample/ControllerSample.py となります。このコントローラの詳細は、オフィシャルWikiのTutorial 「動力学シミュレーションのサンプル」を参照してください。
sample/ControllerSample.py
# # Sample Controlller for SIGVerse # import sys import os import time import sig import math # # Sample controller for SIGVerse # class MyController(sig.SigController): def onInit(self, evt): return def onAction(self, evt): obj = self.getObj() obj.addForce(0, 0, 500) return 1.0 def onRecvMsg(self, evt): return def onCollision(self, evt): return # # # def createController(name, host, port): return MyController(name, host, port)
これをみてもわかるように、PythonでもほぼC++版に準じた記述ができるようになっています。
では、このソースコードで解説していきます。
まず最初に必須モジュールをインポートしてください。最低限、
import sig
は必要ですが、各イベント処理中に必要なモジュールを追記してください。
次に、エージェントコントローラクラスを作成します。この例では、'MyController'クラスを定義しています。この例でもわかるように、エージェントコントローラは、sig.SigControllerクラスを継承するだけでOPです。
エージェントコントローラのイベントのイベントハンドラとして、onInit, onAction, onRecvMsg, onCollisionの4つが定義可能です。これらの4つのイベントハンドラは、C++版と同じですが、
- onInit : シミュレーション開始時に実行される処理
- onAction ; エージェントコントローラの周期実行される処理。Double型の返り値(周期時間 (秒))が必要。
- onRecvMsg : 他のサービスプロバイダからのメッセージを受信したときの処理
- onCollision : エージェントコントローラが接続さえたオブジェクトに衝突が発生した場合の処理
となっています。
最後に定義したエージェントコントローラの実体化のための関数定義を行います。
def createController(name, host, port): return MyController(name, host, port)
この定義は、'MyController'以外は変更しないでください。この引数で sigrun.py から呼び出されます。
エージェントコントローラの実行
オリジナルのエージェントコントローラを起動させる場合には、Worldファイルのオブジェクト定義と同時に、接続するエージェントコントローラを記述するだけで、SIGServerの起動時にダイナミックリンクライブラリが読み込まれ、自動実行していました。
しかしながら、Pythonでエージェントコントローラを作成した場合には、この機能が働きません。そのため、別の方法をとる必要があります。
オリジナルのSIGVerseでは、この機能は、SIGServerの内部では、forkシステムコールを用いて環境変数SIGVERSE_RUNACで指定されたコマンドを引数付で起動しています。すなわち、SIGServerを起動後、別のShellで環境変数SIGVERSE_RUNACで指定されたコマンドを引数付で起動させているのとほぼ同等の処理をおこなっています。
# $SIGVERSE_RUNAC -h localhost -p <ポート番号> -l <エージェントコントローラのDLL> -n <エージェント名>
厳密には、Worldファイルで定義されていると実行のプライオリティも同時に設定されていますので、単にShellのコマンドラインから起動させるのとは若干異なります
そこで、Pythonで記述されたエージェントコントローラの起動のために、sigrun.pyというプログラムを用意しています。このプログラムは、上記の $SIGVERSE_RUNACとほぼ同じですが、引数の指定が若干異なります。
# python sigrun.py --host=localhost -p <ポート番号> -f <エージェントコントローラ.py> -n <エージェント名>
となっています。引数を変更したのは特に意味がないのですが、 '-h’は通常ヘルプ表示用jに使われますので、 --helpと指定するようにし、Pythonの場合ファイル名を指定しますので '-l' を '-f'に変更しています。
SIGServiceの作成
次に、SIGServeiceの作成に関して説明します。このライブラリを使った簡単なサービスプロバイダは、 sample/MyService.py です。この例は、オフィシャルWikiのTutorial「メッセージ送受信」で紹介されているサービスサンプルを実装したものです。動作の詳細に関しては、そちらを参照してください。
sample/MyService.py
#! /usr/bin/python from sigservice import * class MyService(SigService): def onAction(self, evt=None): return 1.0 def onRecvMsg(self, evt): sender = evt.getSender() msg = evt.getMsg() if msg == "Hello" : self.sendMsgToCtrl(sender, "Hello! this is MyService") return if __name__ == "__main__" : srv = MyService("MyService") srv.connect("localhost", 9000) srv.startLoop()
では、この例でサービスプロバイダを実装を見ていきます。
サービスプロバイダをPythonで記述する場合には、必要なモジュールをインポートしなければいけません。
from sigservice import *
です。この行は
import sigservice
でも問題ありませんが、その場合ネームスペースを忘れないでください。
このモジュールをインポートすれば、あとはサービスプロバイダのクラス定義を行います。この例でもわかりますが、サービスプロバイダは、SigServiceクラスを継承すればOKです。
あとは、エージェントコントローラと同様にイベントハンドラの定義をしてください。
この例では、onAction(周期実行イベントハンドラ)とonRecvMsg(メッセージ受信イベントハンドラ)を定義しています。
必要なイベントハンドラの実装がおわれば、あとはメイン関数部分を記述します。
この部分は、Pythonの実行部分と記述方法は同じです。
if __name__ == "__main__" : srv = MyService("MyService") srv.connect("localhost", 9000) srv.startLoop()
上記のように、起動したらlocalhost:90000 のポートで実行中のSIGServerに接続し、startLoop関数で周期実行とイベントのハンドリングを開始させます。
この例からもわかると思いますが、ほぼC++版のサービスプロバイダと同じような記述をすればOKです。