Download
latest Releases : 2.0.0-RELESE
2.0.0-RELESE | Download page |
Number of Projects
RT-Component | 153.5 |
RT-Middleware | 35 |
Tools | 22 |
Documentation | 2 |
Choreonoid
Motion editor/Dynamics simulator
OpenHRP3
Dynamics simulator
OpenRTP
Integrated Development Platform
AIST RTC collection
RT-Components collection by AIST
TORK
Tokyo Opensource Robotics Association
DAQ-Middleware
Middleware for DAQ (Data Aquisition) by KEK
コンフィギュレーションとは
ロボットシステムを構築するうえで、システムの外部環境、使用状況や、個別のデバイス、ロボットの特性に応じて作成するプログラム内のパラメーターを変更ことが度々あります。 単純な実験をするための簡単なプログラムでは、パラメーターをハードコード(埋め込んで)して、変更する度に直接書き換え、コンパイルすることで対処できるかもしれません。 もう少し進んで、パラメーターをファイル等から読み込んだり、コマンドライン引数で渡したり等の工夫をすることで再利用性はぐっと高くなります。 一つのプログラムを用途に応じて再利用するためには、こうしたパラメーターを埋め込まずに外部化することが非常に重要になってきます。
RTコンポーネントによって構築されるRTシステムでは、様々な人が作った多様なコンポーネントが協調して動作しますので、コアロジック内部で使用されるパラメーターをユーザーが自由に定義し、実行時に外部から変更するための機能が用意されています。 これをコンフィギュレーション(機能)と呼びます。コンフィギュレーションは複数のパラメーターセットを持つことができ、パラメーターセットを一斉に入れ替えることもできます。 パラメーターを予め変更可能にしておくことで、RTCを様々なシステムで簡単に再利用することができます。
このセクションでは、RTコンポーネントの重要な機能の一つであるコンフィギュレーションについて、仕組みと実際の使い方について説明していきます。
コンフィギュレーションの仕組み
下図はコンフィギュレーションの大まかな仕組みを表しています。
パラメーターの名前と値のペアをコンフィギュレーションパラメーターと呼びます。 一つのコンポーネントは複数のコンフィギュレーションパラメーターを定義することができ、その集合をコンフィギュレーションセットと呼びます。
さらに一つのコンポーネントは複数のコンフィギュレーションセットを持つことができ、そのうち一つのコンフィギュレーションのみが、実際のパラメーターの値となります。 このコンフィギュレーションセットを、アクティブコンフィギュレーションと呼びます。コンフィギュレーションセットは名前を付けることができ、その名前により区別されます。
外部のツール(RTSystemEditorやrtshell等)を利用して、個々のパラメーター、あるいはアクティブなコンフィギュレーションセットを変更することができます。 コンフィギュレーションの内容は、コンフィギュレーションに結び付けられた変数(パラメーター変数)に反映され、RTコンポーネント内のロジックで使用することができます。 こうして、ロジック内部で使用されるパラメーターを外部から容易に変更できるようにすることでコンポーネントの再利用性を高めることができます。
型のある言語では、コンフィギュレーションパラメーターはその言語で利用可能な型であればどのような型でもパラメーターとして利用することができます。 もちろん、型のない言語でも同様ですが、重要な点は、こうしたパラメーターを外部から設定する際には、その値は文字列によって与えられるということです。
コンフィギュレーションは、文字列をそれぞれのパラメーター型に変換して、実際の変数にセットします。 構造体や配列など、文字列からデータに簡単には変換できないようなデータ型でも、変換関数を定義することで、どのような型のデータでも同じように扱うことができます。 これは、あらかじめIDL定義が必要なデータポートやサービスポートとは大きく異なる点です。
パラメーターを定義する
RTコンポーネント内で利用するパラメーターを定義する方法にはいくつかあります。
以下では、それぞれの方法について説明します。
RTCBuilderによる定義
コンフィギュレーションパラメーターを定義するもっとも簡単な方法はRTCの設計ツールであるRTCBuilderで、RTC設計時にコンフィギュレーションパラメーターを定義することです。
下図はRTCBuilderのコンフィギュレーションの定義画面です。 この画面で必要なパラメーターを定義することで、コンフィギュレーションパラメーターを利用するために必要なコードが言語を問わず自動的に生成されます。
コンフィギュレーションパラメーターを利用するためには、RTCBuilderのコンフィギュレーションタブを押し、パラメーターリスト横の[Add]ボタンをクリックします。 すると、コンフィギュレーションパラメーターが一つ追加されますので、適切な
を入力します。
名称は(デフォルトではconf_name0等となっているので)、そのパラメーターの性質を端的に表す分かりやすい名前を付けてください。 ドロップダウンリストから選択できる型名は、各言語において適切に変換され定義されます。 Python等明示的に型宣言が必要ない言語では、ここで設定された型名はコード上には現れないかもしれません。
上でも述べたように、コンフィギュレーションパラメーターは値を文字列として与えて、文字列を特定の型に変換することで様々な型のパラメーターに対応可能です。 ただし、外部から文字列として値が入力されるため、変換不可能な文字列など不正なパラメーター入力があった場合、変換がエラーになる場合もあります。 ここで設定されたデフォルト値は、設定された値の変換が不正な場合に代わりに使用される値です。
このほか、必須でない項目として以下の項目があります。必要に応じて入力してください。
詳細については、画面右側のヒントや、RTCBuilderのマニュアルを参照してください。
rtc-templateによる定義
rtc-templateはコマンドラインから使用するコンポーネントテンプレートジェネレータです。 rtc-templateでコンフィギュレーションを使用するには以下のように指定します。
これは、サンプルに付属しているConfigSampleでの指定例です。
のように指定します。データ型については、その言語で使用するデータ型を指定しますが、プリミティブ型以外ではうまく動作しなかったり、手動で修正が必要な場合があります。
手動による定義
あまり推奨されませんが、手動でもコンフィギュレーションパラメーターを定義することができます。 新たにパラメーターを追加したくなった場合等に有効ですが、ドキュメントやRTC.xmlファイル等を更新しないと、第三者がこのRTCを使用した場合に仕様と実装の整合性が取れていないために、混乱を来たす可能性がありますので注意してください。
ただし、コンフィギュレーションがどのように宣言され使用されるのかを知ることは意味がありますのでここで説明します。
コンフィギュレーションを使用するには以下の手続きが必要です。
コンフィギュレーションパラメーター(以下パラメーター)の用途、名称、型を決める
上で述べたように、コンポーネントのどの部分でパラメーターを使用するのか、またそのパラメーターの特徴を表す名称と実装時の型名(型のある言語の場合)を決めます。
パラメーターの変数をコンポーネントのヘッダ(private/protected)に宣言する
RTCBuilderやrtc-templateで生成したファイルであれば、以下のようなタグに囲まれた部分がありますので、ここにコンフィギュレーションパラメーターのための変数を宣言します。
上のConfigSampleの例であれば以下のようになります。
コンポーネントの実装ファイルのstatic変数<コンポーネント名>_spec[]にパラメーターの宣言とデフォルト値を追加する
コンフィギュレーションパラメーターはコンポーネント内で、Propertiesというデータストアに入れられ管理されます。このProperties内では、
というキーでコンフィギュレーションパラメーターを保持しています。デフォルト値としてdefaultというコンフィギュレーションセット名が予約済みとなっており、デフォルト値はすべてこのdefaultコンフィギュレーションセットとして定義されます。
上のConfigSampleの場合、以下のように追加します。
Configuration variables以下の部分がデフォルトコンフィギュレーションセットの定義になります。
各変数を初期化子で初期化する
RTCBuilderやrtc-templateで生成された変数はコンストラクタの初期化子による初期化は行われませんが、可能であればすべての変数はコンストラクタの初期化子で初期化したほうがよいでしょう。 また、各変数にデフォルト値がセットされるのはonInitialize()関数の中のbindParameter()関数が呼ばれた後ですので、原則としてそれ以前には使用してはいけません。
bindParameter()関数でパラメーターと変数をバインドする
最後に変数とパラメーターの名称、デフォルト値、さらに変換関数をバインドすることで、単なる変数をコンフィギュレーションパラメーターにします。 RTObjectクラスのメンバ関数(メソッド)であるbindParameter()を使用します。
上のConfigSample(C++の例)では以下のようになります。
こうすることで、各変数とコンフィギュレーションパラメーターがバインドされ、RTSystemEditor等からこれらの変数を操作することができる、コンフィギュレーションパラメーターが利用可能になります。
なお、bindParameter()に与える変換関数は、組込み型については、上記の例のように不要で、特に明示的に与える必要はありません。 しかし、独自の構造体や複雑な型等をコンフィギュレーションパラメーターとして使用したい場合は、文字列からそれらの型への変換を定義しここに与える必要があります。 変換関数の詳細については後述します。
パラメーターを使う
パラメーターを使うのは非常に簡単です。これまで述べてきたように、コンフィギュレーションパラメーターとして宣言された変数を単に利用するだけです。 ただし、使用に当たってはいくつかの条件があり、これを守って利用する必要があります。
変数が使用できるコールバック関数
コンフィギュレーション変数は、特定のコールバック関数(onXXX())内でしか利用することはできません。 外部からのコンフィギュレーション変数の変更は非同期的に行われます。 通常このような場合には、ミューテックス等で変数への排他アクセス制御を行う必要がありますが、これを実現するにはコンポーネント開発者も各変数へのアクセス時にミューテックス保護を行う必要があります。 これを回避するために、OpenRTM-aistでは外部からのコンフィギュレーションの変更は、コールバック関数の外で行われるようになっています。
利用できるコールバック関数は、以下のものになります。
ほぼすべてのコールバック関数内でコンフィギュレーションパラメーターを利用することができます。 ただし、onInitialize()においては、bindParameter()を行う前には当然コンフィギュレーションパラメーターを利用できません。 また、onFinalize()内では、その呼び出しの直前にコンフィギュレーションパラメーターに対してなされた変更が反映されない可能性があります。
変数は読み出し専用
コンフィギュレーションパラメーターの変数は、コンポーネントの外部から変更されその値がパラメーター用変数に代入されます。しかし、パラメーター用変数に onExecute()等内部の関数内で書きこんでも、外から見えるパラメーターの値には反映されません。
このように、変数の値の変更は一方通行ですので、コンポーネント内部からの変数に対する書き込みは意味がありません。 コンフィギュレーション変数はread onlyで使いましょう。
値が正しいか常にチェックする
コンフィギュレーションパラメーターの値は、上述したように外部から文字列として与えられたものを変換関数で変換したものが実際使用される変数に代入されます。 文字列ですので、本来数値が代入されるべきところに文字列が代入されたり、short intで宣言された変数に、上限以上の大きさの数値が代入されることもあり得ます。 従って、受け取った側では変数が想定されている値の範囲内に入っているか、あり得ない値が代入されていないかについて、プログラム上で使用前には常にチェックすることが推奨されます。
パラメーターを設定する
コンフィギュレーションパラメーターは、いくつかのセットを持ち、実行時にそれらを同時に変更できることを上で述べました。 その一方で、RTCBuilderやrtc-templateでコンポーネントを設計する時点では、デフォルトコンフィギュレーションセットしか定義できませんでした。 ここでは、コンフィギュレーションセットの使い方について説明します。
コンポーネント設定ファイル
デフォルトコンフィギュレーションセットはソースコードに埋め込まれます。 同じ方法で、他のコンフィギュレーションセットも原理的にはソースコードに埋め込むことで増やすことができます。 しかし、RTCコンフィギュレーション機能の目的は、ソースコードを変更しないで、用途に応じてパラメーターを変更することで、一つのコンポーネントを様々な用途に使うことでしたので、ソースコードに他のコンフィギュレーションセットを埋め込むのは本末転倒です。
コンフィギュレーションセットはコンポーネントのコンフィギュレーションファイルで与えることができます。 コンポーネントの設定を行うファイルにはrtc.confがありますが、これは主にコンポーネントを管理するミドルウエアのための設定ファイルで、コンポーネントのための設定ファイルは、rtc.conf内で以下のように指定することができます。
example.ConfigSample.config_fileの部分がコンポーネントのコンフィギュレーションファイルの指定部分です。コンフィギュレーションファイルを指定する部分は以下のようになっています。
また、コンポーネントのモジュール名の代わりにインスタンス名を与えることもできます。
したがって、インスタンス毎に異なるコンフィギュレーションファイルを与えることもできます。
コンフィギュレーションセットの設定
コンフィギュレーションファイルの中には、使用したいコンフィギュレーションセットを記述します。
アクティブコンフィギュレーションセットの指定
最初の行のconfiguration.active_configで、アクティブなコンフィギュレーションセット名を指定しています。ここではmode1というセット名で、当然、存在するセット名を指定する必要があります。
コンフィギュレーションセットの設定
次に、conf.mode0で始まるパラメーターのリストがありますが、これがセット名mode0のコンフィギュレーションパラメーターのリストです。指定の仕方は、ソースコードとほぼ同じように
となっています。必ず、存在するすべてのコンフィギュレーションパラメーターについて指定してください。 指定がない場合はデフォルト値が使用されます。その次に、conf.mode1で始まるパラメーターのリストがありますが、これもmode0同様、mode1というセット名のパラメーターの設定です。
拡張機能
conf._ widget_ 設定
次に、conf._ widget_で始まる設定リストがあります。これは、RTSystemEditorで使用される特殊なパラメーターです。 RTCBuilderでコンフィギュレーションパラメーターを設定するときwidgetを指定できることを上で説明しましたが、ここで設定された内容が、conf.widget設定されます。 slider、radio、spin、textの4種類を設定することができ、それぞれRTSystemEditorでコンフィギュレーションパラメーター設定ダイアログを開いたときに、スライダー、ラジオボタン、スピンボタン、テキストボックスでパラメーターを操作することができます。
上記ののように設定することで、スライダーの刻み幅を5にすることができます。現在のところ、この刻み幅を小数にすることはできません。 ただし、今後のバージョンアップで改善される可能性があります。
スピンボタンのステップ幅は常に1刻みです。int等の整数値パラメーターにのみ使用することをお勧めします。
これらconf.widgetパラメーターを設定した場合、conf._ constraints_パラメーターも設定する必要があります。
conf.__onstraints_の設定
conf._ constraints_パラメーターは、値の範囲を設定するための特殊なパラメーターです。下記に設定例を示します。不正なパラメーターを設定すると、ウィジェットが正常に表示されないので注意が必要です。
下記に、上記設定によるRTSystemEditorでの表示例を示します。
変換関数について
C++等では、intやdoubleなどの組込み型については特に変換関数を指定する必要はありません。一方で、構造体やSTLコンテナなどユーザー独自の型を利用した い場合もあります。この場合、文字列からそれぞれの型への変換をどのようにするかをbindParameter()に関数として与えてあげる必要があります。
変換関数については以下のように、各言語ごとにルールがあります。以下、各言語ごとの方法を述べます。
C++の場合の変換関数
C++におおいては、bindParameterのプロトタイプ宣言は
のようになっており、第4引数の trans に適当な関数ポインタを与えることで、文字列から当該型への変換が行われます。デフォルトでは、coilライブラリ関数の stringTo() 関数が与えられています。 自分でこのstringTo() に相当する変換関数を書いて、関数ポインタを与えることもできますが、coil::stringTo() 関数自体も関数テンプレートとなっており、std::stream に対する operator >>()関数
が定義されていれば、自動的にこれを利用して文字列から特定の型への変換が行われます。
すなわち、std::cin >> <ある型の変数>のような書き方ができるのであれば、その型はoperator>>()が定義されており、特に変換関数を書かなくともコンフィギュレーションのパラメーターとして利用することができます。
もし、変換関数がない場合、例えば、以下のようにカンマ区切りの数値列
を std::vector<double>へ変換するための変換関数は、
このように実装することができます。なお、これはOpenRTM-aist C++版のサンプル、ConfigSampleコンポーネントのソースに含まれるVectorConvert.hです。
これを、bindParameter()が呼ばれるソース(例えば、ConfigSampleコンポーネントであればConfigSample.cpp)、通常はコンポーネントの実装ソースでincludeしてあげれば、コンパイル時にコンパイラが判断して適当な変換関数が利用されます。
Javaの場合の変換関数
Javaの場合は、変換関数というものを別途与えるのではなく、コンフィギュレーション変数のホルダクラスにおいて定義されるstringFrom()メソッドに文字列から実際の型への変換を記述します。
以下に、OpenRTM-aist Java版のConfigSampoleで定義されている、カンマ区切り数値列をVector型に変換するための変換関数を示します。
Pythonの場合の変換関数
Python版OpenRTM-aistでは、デフォルトでは基本型とそのリストに対応しており、それ以外の変換が必要なら、bool stringTo(type, string)であるような関数を定義して、bindParameter()の第4引数に関数オブジェクトを渡します。
何をパラメーターにするか?
RTコンポーネントを作成するうえで、何をコンフィギュレーションパラメーターにすればよいのか考えてみましょう。
あるパラメーターがあり、これを外部から変更するにはいくつかの方法が考えられます。 データポートを利用して変更する方法、サービスポートを利用して変更する方法、そしてコンフィギュレーションを利用して変更する方法です。
コンフィギュレーション機能はコンポーネント内部のパラメーターを変更するための機能です。 したがって、ロジック内のパラメーターはコンフィギュレーションパラメーターとして外部から変更できるようにするべきです。 しかし、ある変量をコンフィギュレーションパラメーターにすべきなのかそうでないのか迷うケースもあると思います。 ここではそういったケースについて少し考えてみます。
更新頻度
コンフィギュレーションパラメーターは、通常はシステムが動き出す前に1度だけ、あるいは設定変更が必要になった場合にだけ、外部からパラメーターを与えるために利用します。 更新頻度がシステムのライフサイクル上で1回ないしは数回程度であれば、コンフィギュレーションを使うのがよいでしょう。
また、上記でも述べましたが、コンフィギュレーションはツールやアプリケーションからは文字列として与えられ、コンポーネント内でそれぞれの型に変換します。 変換にはある程度(最近のPCでは数usから数百us程度ですが)時間がかかりますので、例えば1ms周期でデータを送る用途には向きません。 ではそのくらいの頻度でパラメーターを変更できるのでしょうか?実際に使用する際には、パラメーターの数やコンピューター、ネットワークの速度にも依存しますが、数百msまたはそれ以上の頻度では事実上問題なくパラメーターを変更できます。 ただし、そのように周期的に何度も値を変更する必要があるものはデータポートを使うべきでしょう。
更新のタイミング
コンフィギュレーションパラメーターはRTSystemEditorやrtshellなどのツールから、いつでも更新することができます。 しかし、実際に変更されたパラメーターはonExecuteやonActivatedなどの関数で使用する関数内で参照される前にあるタイミングで実際の変数に反映されます。 更新のタイミングは以下の通りです。
データかパラメーターか?
例えば、遠隔地のセンサーから定期的にデータを中央のサーバに送るシステムを考えます。 データは1時間に1回だけ送られ、サーバー側ではそれをログに記録するとします。 このとき、このデータはデータポートを使って送るべきでしょうか?それとも、サービスポートを使うべきか、あるいはコンフィギュレーションを使うべきなのでしょうか?
送られるものはセンサーのデータですので、データポートを利用して送るのが最も適しているといえます。 コンフィギュレーションは外部からパラメーターを設定するための仕組みですので、たとえ更新頻度が1時間に一回であっても、このデータをコンフィギュレーションでコンポーネントに伝達するのは不適切といえます。 ただし、データポートでは実現できなクライアントとサーバー側の複雑なやり取り(トランザクション等)を実現したい場合は、サービスポートが使われるかもしれません。
サービスかパラメーターか?
データポートにすべきか、コンフィギュレーションにすべきかは、実際にはあまり迷うことはないでしょう。 一方で、RTCロジック内のパラメーターをサービスポートから変更するべきか、コンフィギュレーションパラメーターにすべきか迷う場面は多いと思います。
コンポーネントがある種の典型的かつある程度まとまった機能を提供する場合、その機能はサービスポートのインターフェースによって外部に提供されます。 サービスインターフェースでは、対象の状態を取得したり、設定・モード・パラメーターを変更したりするためのオペレーションを提供します。 状態の取得は別として、設定を行ったり、モード・パラメーターを変更したりする機能はコンフィギュレーションと大変似ています。
結局のところはどちらで設定しても大差ないのですが、対象とするRTCの機能がすでにサービスインターフェースとして定義されていたり、状態の取得と設定が必要になるなど、ある程度複雑な機能を提供する場合、サービスインターフェースを介した操作が適していると言えるでしょう。 それ以外の簡単なパラメーター・モード等の設定にはコンフィギュレーションを利用するとよいでしょう。
まとめ
ここでは、コンフィギュレーション機能について定義の仕方や使い方について説明しました。 ロジック内のパラメーターはコンポーネントの再利用性を向上させるために、できるだけこの機能を利用して外部化するべきです。 何をコンフィギュレーションパラメーターにすべきか、すべきでないかといったことについても注意を払う必要があります。 コンフィギュレーション機能を有効に利用すれば、作成するコンポーネントも再利用性の高いものになるでしょう。