RTコンポーネントの作成

RTコンポーネント作成の基本

データポートのあるコンポーネント

ここでは、データポートのあるコンポーネントを二つ作成し、二つのコンポーネント間でデータの送受信を行ってみます。 作成するコンポーネントの仕様は以下の通りです。

  • コンポーネント1
    • OutPort を一つもつ
    • OutPort のデータ型は TimedLong
    • コンソールから入力した値を OutPort から出力
  • コンポーネント2
    • InPort を一つ持つ
    • InPort のデータ型は TimeLong
    • コンフィギュレーションパラメーターを一つ持つ
    • コンフィギュレーションパラーメーターは int型
    • コンフィギュレーションパラーメーターのデフォルト値は1
    • InPort 変数から読み出すときはパラメーターを掛けた値を読み出す
    • InPort から読み出した値をコンソールへ出力

rtc-template によるソース生成

上記の仕様を持つコンポーネントを作成する為に以下のようなシェルスクリプトを gen.sh という名前で用意します。

 #!/bin/sh
 
 rtc-template -bcxx      --module-name=ConsoleIn --module-type='DataFlowComponent'      --module-desc='Console input component'      --module-version=1.0 --module-vendor='MyName'      --module-category=example      --module-comp-type=DataFlowComponent --module-act-type=SPORADIC      --module-max-inst=10 --outport=out:TimedLong
 
 rtc-template -bcxx      --module-name=ConsoleOut --module-type='DataFlowComponent'      --module-desc='Console output component'      --module-version=1.0 --module-vendor='MyName'      --module-category=example      --module-comp-type=DataFlowComponent --module-act-type=SPORADIC      --module-max-inst=10 --inport=in:TimedLong      --config="multiply:int:1"

最初の rtc-template の実行でコンポーネント1:ConsoleInComp、次の rtc-template の実行でコンポーネント2:ConsoleOutComp が作成されます。

 > sh gen.sh 
   File "ConsoleIn.h" was generated.
   File "ConsoleIn.cpp" was generated.
   File "ConsoleInComp.cpp" was generated.
   File "Makefile.ConsoleIn" was generated.
   File "ConsoleInComp_vc8.vcproj" was generated.
   File "ConsoleIn_vc8.vcproj" was generated.
   File "ConsoleInComp_vc9.vcproj" was generated.
   File "ConsoleIn_vc9.vcproj" was generated.
   File "ConsoleIn_vc8.sln" was generated.
   File "ConsoleIn_vc9.sln" was generated.
   File "copyprops.bat" was generated.
   File "user_config.vsprops" was generated.
   File "README.ConsoleIn" was generated.
   File "ConsoleIn.yaml" was generated.
   File "ConsoleOut.h" was generated.
   File "ConsoleOut.cpp" was generated.
   File "ConsoleOutComp.cpp" was generated.
   File "Makefile.ConsoleOut" was generated.
   File "ConsoleOutComp_vc8.vcproj" was generated.
   File "ConsoleOut_vc8.vcproj" was generated.
   File "ConsoleOutComp_vc9.vcproj" was generated.
   File "ConsoleOut_vc9.vcproj" was generated.
   File "ConsoleOut_vc8.sln" was generated.
   File "ConsoleOut_vc9.sln" was generated.
 "copyprops.bat" already exists. Overwrite? (y/n)y
   File "copyprops.bat" was generated.
 "user_config.vsprops" already exists. Overwrite? (y/n)y
   File "user_config.vsprops" was generated.
   File "README.ConsoleOut" was generated.
   File "ConsoleOut.yaml" was generated.

ConsoleIn の実装

生成されたソースを編集して、ConsoleIn コンポーネントを実装していきます。

ConsoleIn.h

このコンポーネントはアクティブ化されたときに、入力待ちを行い入力された値を OutPort から出力するコンポーネントです。 従って、アクティブ状態の時にループ実行される onExecute メンバ関数のみ実装すればよいので、生成された ConsoleIn.h を以下のようにコメントアウトされている onExecute 関数のコメントをはずします。

     :略
   // The execution action that is invoked periodically
   // former rtc_active_do()
   virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);
     :略

また、ConsoleIn.h の下の方に、rtc-template で指定した OutPort の変数宣言があります。

     :略
   // DataOutPort declaration
   // <rtc-template block="outport_declare">
   TimedLong m_out;
   OutPort<TimedLong> m_outOut;
   // </rtc-template>

TimedLong m_out と宣言されているのが、OutPort にバインドされる変数。

OutPort<TimedLong> m_outOut と宣言されているのが、OutPort のインスタンスです。

ConsoleIn.cpp

ConsoleInの 実装は簡単です。 コメントアウトされている onExecute のコメントをはずし、以下のように実装します。

 RTC::ReturnCode_t ConsoleIn::onExecute(RTC::UniqueId ec_id)
 {
   std::cout << "Please input number: ";
   std::cin >> m_out.data;
   std::cout << "Sending to subscriber: " << m_out.data << std::endl;
   m_outOut.write();
 
   return RTC::RTC_OK;
 }

ここで行われていることは、
  1. cin >> m_out.data でユーザーからの入力待ちをする。
  2. 入力された値を、m_out.data(long型) へ格納
  3. 入力された値を確認の為表示
  4. m_outOut.write() で OutPort からデータを出力。

ConsoleOut の実装

ConsoleOut コンポーネントは少し複雑です。 InPort に入ってきたデータにコンフィギュレーションパラメーター multiply を掛けた値を格納しなければなりません。 これは、InPort にコールバックオブジェクトをセットするという方法で実現できます。

コールバックオブジェクト

コールバックオブジェクトとは、InPort や OutPort のバッファにあるイベントが発生したときに呼ばれるoperator()が定義されたオブジェクトです。 今回は、InPort のバッファに書き込まれるときに値を変換する為のコールバック OnWriteConvert を使用します。

RTC::OnWriteConvert を継承して以下のようなクラスを定義します。

 class Multiply
   : public RTC::OnWriteConvert<RTC::TimedLong>
 {
   int& m_mul;
 public:
   Multiply(int& multiply) : m_mul(multiply) {};
   RTC::TimedLong operator()(const RTC::TimedLong& value)
   {
     RTC::TimedLong ret(value);
     ret.data = value.data * m_mul;
     return ret;
   };
 };

ConsoleOut.h

上記のコールバッククラスを ConsoleOut.h の include の行の直後に挿入します。 さらに、このコールバッククラスのインスタンスを ConsoleOut クラスのメンバ変数として宣言します。 場所は、private のすぐ下辺りでよいでしょう。

 private:
   Multiply m_owc;
   int dummy;

このコンポーネントはアクティブ化されたときに、InPort からデータを読み込み標準出力にデータを表示するコンポーネントです。 従って、アクティブ状態の時にループ実行される onExecute メンバ関数のみ実装すればよいので、生成された ConsoleOut.h を以下のようにコメントアウトされている onExecute 関数のコメントをはずします。

     :略
   // The execution action that is invoked periodically
   // former rtc_active_do()
   virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);
     :略

また、ConsoleOut.h の下の方に、rtc-template で指定したコンフィギュレーション変数の宣言と InPort の変数宣言があります。

ConsoleOut では InPort のバッファとして RingBuffer を使用するので、RingBuffer.h をインクルードする必要があります。 ConsoleOut.h の先頭部分をで以下のように RingBuffer.h をインクルードしてください。

 #include <rtm/idl/BasicDataTypeSkel.h>
 #include <rtm/Manager.h>
 #include <rtm/DataFlowComponentBase.h>
 #include <rtm/CorbaPort.h>
 #include <rtm/DataInPort.h>
 #include <rtm/DataOutPort.h>
 #include <rtm/RingBuffer.h> //これを追加する

また、InPort の宣言部分でデフォルトでは InPort<TimedLong> m_inIn となっているところを InPort<TimedLong, RTC::RingBuffer> m_inIn のように書き換えて、InPort が RingBuffer を使用するように変更してください。

     :略
   // Configuration variable declaration
   // <rtc-template block="config_declare">
   int m_multiply;
     
   // </rtc-template>
   
   // DataInPort declaration
   // <rtc-template block="inport_declare">
   TimedLong m_in;
   InPort<TimedLong, RTC::RingBuffer> m_inIn;

int m_multiplyと宣言されているのが、コンフィギュレーション「multiply」にバインドされる変数。

TimedLong m_in と宣言されているのが、InPort にバインドされる変数。

InPort<TimedLong> m_inIn と宣言されているのが、InPort のインスタンスです。

ConsoleOut.cpp

ConsoleOutクラスのコンストラクタで、先ほど定義した Multiply のインスタンスの初期化を追加します。

 ConsoleOut::ConsoleOut(RTC::Manager* manager)
   : RTC::DataFlowComponentBase(manager),
     // <rtc-template block="initializer">
     m_inIn("in", m_in),
     
     // </rtc-template>
     m_owc(m_multiply), 
     dummy(0)
※ Remark :

「m_owc(m_multiply),dummy(0)」 の部分を追加するだけでなく、「m_inIn("in", m_in)」を「m_inIn("in", m_in),」と変更することも忘れずに(カンマ「,」の追加もれに注意)。


 さらに、コールバックオブジェクトを InPort に追加する為、コンストラクタ内で以下のように記述します。

   m_inIn.setOnWriteConvert(&m_owc); //これを追加
   // Registration: InPort/OutPort/Service
   // <rtc-template block="registration">
   // Set InPort buffers
   registerInPort("in", m_inIn);

コメントアウトされている onExecute のコメントをはずし、以下のように実装します。

 RTC::ReturnCode_t ConsoleOut::onExecute(RTC::UniqueId ec_id)
 {
   if (m_inIn.isNew())
     {
       m_inIn.read();
       std::cout << "Received: " << m_in.data << std::endl;
       std::cout << "TimeStamp: " << m_in.tm.sec << "[s] ";
       std::cout << m_in.tm.nsec << "[ns]" << std::endl;
     }
   usleep(1000);
 
   return RTC::RTC_OK;
 }

ここで行われていることは、
  1. m_inIn.isNew() で InPort にデータが入ってきているかチェックする。
    1. isNew() というメンバ関数は RingBuffer に定義されている関数です。
  2. 新しいデータが入っていたら、m_inIn.read() で変数にデータを読み込む。
  3. そのデータ (m_in.data) を表示する。

    コンパイル

実装が終わったら以下のようにソースをコンパイルします。

 > make -f Makefile.ConsoleIn
 > make -f Makefile.ConsoleOut

コンパイルエラーが出た場合はスペルミスなどがないかどうかチェックして再度コンパイルを行ってください。

実行

  • ネームサーバ起動していることを確認します。
  • 適切な rtc.conf * を作成しておきます。
    * 適切な rtc.conf とは… 次に一例をしめします。
     corba.nameservers: localhost
     naming.formats: %h.host_cxt/%n.rtc
  • 二つのターミナルから、ConsoleInComp および ConsoleOutComp を実行します。
  • RtcLink を起動して、二つのコンポーネントを接続し、アクティブ化します。


ConsoleInConsoleOut2.png


 ConsoleIn を実行したターミナルで、入力を促す Please input number: という表示が出るので、適当な数字を入力します。

 Please input number: 1
 Sending to subscriber: 1
 Please input number: 2
 Sending to subscriber: 2
 Please input number: 3
 Sending to subscriber: 3

 ConsoleOut を実行したターミナルでは、以下のように表示されるはずです。

 Received: 1
 TimeStamp: 0[s] 0[ns]
 Received: 2
 TimeStamp: 0[s] 0[ns]
 Received: 3
 TimeStamp: 0[s] 0[ns]

 次に RtcLink のコンフィギュレーションビューで multiply の値を10に変更してみましょう。すると、ConsoleIn から上記のように入力すると、以下のようにそれぞれ10倍された値が出力されるはずです。

 Received: 10
 TimeStamp: 0[s] 0[ns]
 Received: 20
 TimeStamp: 0[s] 0[ns]
 Received: 30
 TimeStamp: 0[s] 0[ns]


RTコンポーネント作成(OpenCV編 CameraImage型の使用)

はじめに

ここでは、OpenCV ライブラリを VC9 にて RTコンポーネント化する手順を紹介します。

OpenCV とは

OpenCV とはインテルが開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。

Wikipedia より抜粋。

作成する RTコンポーネント

  • Flip コンポーネント: OpenCV ライブラリのうち、cvFlip() 関数を用いて画像の反転を行う RTコンポーネント。

OpenCV ライブリの RTコンポーネント化 (Flipコンポーネント)

ここでは、OpenCV ライブラリのうち、画像の反転を行う cvFlip() を VC9 にて RTコンポーネント化します。

以下は、作業の流れです。

  • cvFlip 関数について
  • コンポーネントの概要
  • 動作環境・開発環境
  • Flip コンポーネントの雛型の生成
  • アクティビティ処理の実装
  • コンポーネントの動作確認

cvFlip 関数について

cvFlip 関数は、2次元配列を垂直、水平、または両軸で反転します。

 void cvFlip(IplImage* src, IplImage* dst=NULL, int flip_mode=0);
 #define cvMirror cvFlip
  
 src       入力配列
 dst       出力配列。もし dst = NULL であれば、src が上書きされます。
 flip_mode 配列の反転方法の指定内容:
  flip_mode = 0: X軸周りでの反転(上下反転)
  flip_mode > 0: Y軸周りでの反転(左右反転)
  flip_mode < 0: 両軸周りでの反転(上下左右反転)

コンポーネントの概要

InPort からの入力画像を反転し OutPort から出力するコンポーネント。


反転の対象軸は、RTC のコンフィギュレーション機能を使用して flipMode という名前のパラメーターで指定します。

flipMode は、反転したい方向に応じて下記のように指定してください。

  • 上下反転したい場合、0
  • 左右反転したい場合、1
  • 上下左右反転したい場合、-1


作成する RTC の仕様は以下のとおりです。

  • InPort
    • キャプチャーされた画像データ (CameraImage)
  • OutPort
    • 反転した画像データ (CameraImage)
  • Configuration
    • 反転方法の指定 (int)

※ CameraImage型は、OpenRTM-aist の InterfaceDataTypes.idl にて下記のように定義されているデータ型です。

   struct CameraImage
     {
         /// Time stamp.
         Time tm;
         /// Image pixel width.
         unsigned short width;
         /// Image pixel height.
         unsigned short height;
         /// Bits per pixel.
         unsigned short bpp;
         /// Image format (e.g. bitmap, jpeg, etc.).
         string format;
         /// Scale factor for images, such as disparity maps,
         /// where the integer pixel value should be divided
         /// by this factor to get the real pixel value.
         double fDiv;
         /// Raw pixel data.
         sequence<octet> pixels;
     };


図1は、それぞれの flipMode での画像処理のイメージ図です。


cvFlip_and_FlipRTC.png
図1. Flip コンポーネントの flipMode の指定パターン


動作環境・開発環境

Flip コンポーネントの雛型の生成

Flip コンポーネントの雛型の生成は、RTCBuilder を用いて行います。

RTCBuilder の起動

新規ワークスペースを指定して Eclipse を起動すると、以下のような「ようこそ」画面が表示されます。

fig1-1EclipseInit.png
図2. Eclipseの初期起動時の画面


この「ようこそ」画面左上の「X」をクリックして画面を閉じると、以下の画面が表示されます。 右上の [Open Perspective] ボタンをクリックし、プルダウンの「Other…」を選択します。

fig2-2PerspectiveSwitch.png
図3. パースペクティブの切り替え


「RTC Builder」を選択し、[OK] ボタンをクリックします。

fig2-3PerspectiveSelection.png
図4. パースペクティブの選択


RTCBuilder が起動します。

NewRTCBEditor.png
図5. RTC Builderの初期起動時画面


RTCBuilder用 プロジェクトの作成

まず最初に,RT コンポーネントを作成するための Eclipse プロジェクトを作成します。 画面上部のメニューから [ファイル] > [新規] > [プロジェクト] を選択します。

fig2-5CreateProject.png
図6. RTC Builder 用プロジェクトの作成 1


表示された「新規プロジェクト」画面において、[その他] > [RTC ビルダ] を選択し、[次へ] をクリックします。

fig2-6CreateProject2.png
図7. RTC Builder 用プロジェクトの作成 2


「プロジェクト名」欄に作成するプロジェクト名を入力して [完了] をクリックします。

RT-Component-BuilderProject.png
図8. RTC Builder 用プロジェクトの作成 3


指定した名称のプロジェクトが生成され、パッケージエクスプローラー内に追加されます。

PackageExplolrer.png
図9. RTC Builder 用プロジェクトの作成 4


生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。

RTC プロファイルエディタの起動

RTC プロファイルエディタを開くには、ツールバーの「 [Open New RtcBuilder Editor] ボタンをクリックするか、メニューから [ファイル] > [Open New Builder Editor] を選択します。

Open_RTCBuilder.png
図10. ツールバーから Open New RtcBuilder Editor



fig2-10FileMenuOpenNewBuilder.png
図11. File メニューから Open New Builder Editor


コンポーネントのプロファイル情報入力とコードの生成

1. 「基本」タブを選択し、基本情報を入力します。


-モジュール名: Flip
  • モジュール概要: Flip image component
  • バージョン: 1.0.0
  • ベンダ名: AIST
  • モジュールカテゴリ: Category
  • コンポーネント型: STATIC
  • アクティビティ型: PERIODIC
  • コンポーネント種類: DataFlowComponent
  • 最大インスタンス数: 1
  • 実行型: PeriodicExecutionContext
  • 実行周期: 0.0 (図13では1.0となってますが,0.0として下さい.)
    -Output Project: Flip


Basic.png
図13. 基本情報の入力


2. 「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。

Flip コンポーネントでは、onActivated()、onDeactivated()、onExecute()コ ールバックを使用します。 図14のように①の onAtivated をクリック後に ②のラジオボタンの [ON] にチェックを入れます。onDeactivated、onExecute についても同様の操作を行います。


Activity.png
図14. アクティビティコールバックの選択


3. 「データポート」タブを選択し、データポートの情報を入力します。


-InPort Profile:
    • ポート名: originalImage
    • データ型: CameraImage
    • 変数名: originalImage
    • 表示位置: left
      -OutPort Profile:
    • ポート名: flippedImage
    • データ型: CameraImage
    • 変数名: flippedImage
    • 表示位置: right


DataPort.png
図15. データポート情報の入力


4. 「コンフィギュレーション」タブを選択し、Configuration の情報を入力します。

ラジオボタンでコンフィギュレーションの変更を行います。


-flipMode
    • 名称: flipMode
    • データ型: int
    • デフォルト値: 1
    • 変数名: flipMode
    • 制約条件: (-1, 0, 1) ※ (-1: 上下左右反転, 0: 上下反転, 1: 左右反転)
    • Widget: radio


Configuration.png
図16. コンフィグレーション情報の入力


5. 「言語・環境」タブを選択し、プログラミング言語を選択します。

今回は、C++(言語)を選択します。


Language.png
図17. プログラミング言語の選択


6. 「基本」タブにある [コード生成] ボタンをクリックし、コンポーネントの雛型を生成します。


Generate.png
図18. 雛型の生成(Generate)


※ 生成されるコード群は、Eclipse 起動時に指定したワークスペースフォルダーの中に生成されます。現在のワークスペースは、[ファイル] > [ワークスペースの切り替え] で確認することができます。

アクティビティ処理の実装

Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCV の cvFlip() 関数にて変換します。その後、変換された画像を OutPort から送信します。

onActivated()、onExecute()、onDeactivated() での処理内容を図19に示します。

FlipRTC_State.png
図19. アクティビティ処理の概要


onExecute() での処理を図20に示します。


FlipRTC.png
図20. onExucete()での処理内容


OpenCV用 ユーザープロパティシートのコピー

プロパティシートとは、コンパイルに必要な種々のオプション(インクルードパス、ライブラリロードバス、ライブラリ)やマクロを記述した VC の設定ファイルの一種です。 RTCBuilder や rtc-template で生成した VC用のプロジェクトでは、VC のプロパティシートを使用して各種オプションを与えています。また、ユーザーが追加でオプションを追加できるように、ユーザー定義のプロパティシートもインクルードするようになっています。
  • rtm_config.vsprop:OpenRTM に関する情報を含むプロパティシート。インストールされている OpenRTM-aist に依存するファイルであるため、copyprops.bat を使用して OpenRTM のシステムディレクトリーからカレントのプロジェクトへコピーします。
  • user_config.vsprops:ユーザー定義のプロパティシート。デフォルトは空です。使い方は、ソースコード:OpenRTM-aist/win32/OpenRTM-aist/example/USBCamera の中に入っているuser_config.vsprops (OpenCV用の設定)を参考にしてください。

以下の内容を user_config.vsprops というファイル名で保存し、Flip フォルダーにコピーしてください。

もしくは、下記より vsprops ファイルをダウンロードし、Flip フォルダーに保存してください。


user_config.vsprops

※ 既に Flip フォルダーには user_config.vsprops ファイルが存在していますが、上書きして構いません。


 <?xml version="1.0" encoding="shift_jis"?>
 <VisualStudioPropertySheet
     ProjectType="Visual C++"
     Version="8.00"
     Name="OpenCV21"
     >
     <Tool
         Name="VCCLCompilerTool"
         AdditionalIncludeDirectories="$(cv_includes)"
     />
     <Tool
         Name="VCLinkerTool"
         AdditionalLibraryDirectories="$(cv_libdir)"
     />
     <UserMacro
         Name="user_lib"
         Value="$(cv_lib)"
     />
     <UserMacro
         Name="user_libd"
         Value="$(cv_libd)"
     />
     <UserMacro
         Name="cv_root"
         Value="C:\OpenCV2.1"
     />
     <UserMacro
         Name="cv_includes"
         Value="&quot;$(cv_root)\include\opencv&quot;"
     />
     <UserMacro
         Name="cv_libdir"
         Value="&quot;$(cv_root)\lib&quot;"
     />
     <UserMacro
         Name="cv_bin"
         Value="$(cv_root)\bin"
     />
     <UserMacro
         Name="cv_lib"
         Value="cv210.lib cvaux210.lib highgui210.lib cxcore210.lib"
     />
     <UserMacro
         Name="cv_libd"
         Value="cv210d.lib cvaux210d.lib highgui210d.lib cxcore210d.lib"
     />
 </VisualStudioPropertySheet>

copyprops.bat の実行

copyprops.batというファイルを実行することで、rtm_config.vspropsというファイルがコピーされます。

rtm_config.vspropsファイルは、RTコンポーネントをVC++でビルドするために必要なインクルードパスやリンクするライブラリ等が記述されたファイルです。

ヘッダファイルの編集

  • OpenCV のライブラリを使用するため、OpenCV のインクルードファイルをインクルードします。

 //OpenCV 用インクルードファイルのインクルード
 #include<cv.h>
 #include<cxcore.h>
 #include<highgui.h>

  • この cvFlip コンポーネントでは、画像領域の確保、Flip 処理、確保した画像領域の解放のそれぞれの処理を行います。これらの処理は、それぞれ onActivated()、onDeactivated()、onExecute() のコールバック関数にて行います。

   /***
    *
    * The activated action (Active state entry action)
    * former rtc_active_entry()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The deactivated action (Active state exit action)
    * former rtc_active_exit()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The execution action that is invoked periodically
    * former rtc_active_do()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);

  • 反転した画像の保存用にメンバー変数を追加します。

   IplImage* m_imageBuff;
   IplImage* m_flipImageBuff;

ソースファイルの編集

下記のように、onActivated()、onDeactivated()、onExecute() を実装します。

 RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id)
 {
   // イメージ用メモリーの初期化
   m_imageBuff = NULL;
   m_flipImageBuff = NULL;
 
   // OutPort の画面サイズの初期化
   m_flippedImage.width = 0;
   m_flippedImage.height = 0;
  
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id)
 {
   if(m_imageBuff != NULL)
   {
     // イメージ用メモリーの解放
     cvReleaseImage(&m_imageBuff);
     cvReleaseImage(&m_flipImageBuff);
   }
 
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id)
 {
   // 新しいデータのチェック
   if (m_originalImageIn.isNew()) {
     // InPort データの読み込み
     m_originalImageIn.read();
 
     // InPort と OutPort の画面サイズ処理およびイメージ用メモリーの確保
     if( m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height)
       {
     m_flippedImage.width = m_originalImage.width;
     m_flippedImage.height = m_originalImage.height;
 
     // InPort のイメージサイズが変更された場合
     if(m_imageBuff != NULL)
       {
         cvReleaseImage(&m_imageBuff);
         cvReleaseImage(&m_flipImageBuff);
       }
 
     // イメージ用メモリーの確保
     m_imageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3);
     m_flipImageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3);
       }
 
     // InPort の画像データを IplImage の imageData にコピー
     memcpy(m_imageBuff->imageData,(void *)&(m_originalImage.pixels[0]),m_originalImage.pixels.length());
 
     // InPort からの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り
     cvFlip(m_imageBuff, m_flipImageBuff, m_flipMode);
 
     // 画像データのサイズ取得
     int len = m_flipImageBuff->nChannels * m_flipImageBuff->width * m_flipImageBuff->height;
     m_flippedImage.pixels.length(len);
 
     // 反転した画像データを OutPort にコピー
     memcpy((void *)&(m_flippedImage.pixels[0]),m_flipImageBuff->imageData,len);
 
     // 反転した画像データを OutPort から出力する。
     m_flippedImageOut.write();
   }
 
   return RTC::RTC_OK;
 }

ビルドの実行

Flip_vc9.sln ファイルをダブルクリックし、Visual C++ 2008 を起動します。

Visual C++ 2008 の起動後、図21のようにし、コンポーネントのビルドを行います。


VC++_build.png
図21. ビルドの実行


Flip コンポーネントの動作確認

ここでは、「OpenCV用RTC群 (Win32用インストーラ)」にてインストールした USBCameraAqcuireComp コンポーネントと、USBCameraMonitorCom コンポーネント、それと、今回作成した Flip コンポーネントを接続し動作確認を行います。

NameService の起動

omniORB のネームサービスを起動します。


[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [C++] > [tools] の順に辿り、「Start Naming Service」をクリックしてください。

&color(RED){※ 「Star Naming Service」をクリックしても omniNames が起動されない場合は、フルコンピューター名が14文字以内に設定されているかを確認してください。

rtc.confの作成

RTコンポーネントでは、ネームサーバーのアドレスやネームサーバーへの登録フォーマットなどの情報を rtc.conf というファイルで指定する必要があります。

下記の内容を rtc.conf というファイル名で保存し、Flip\FlipComp\Debug(もしくは、Release)フォルダーに置いて下さい。

※ Eclipse 起動時に workspace をデフォルトのままにしていた場合、Flip フォルダーのパスは、

C:\Documents and Settings\<ログインユーザー名>\workspace となります。

 corba.nameservers: localhost
 naming.formats: %n.rtc

Flip コンポーネントの起動

Flip コンポーネントを起動します。

先程 rtc.conf ファイルを置いたフォルダーにある、FlipComp.exe ファイルを実行して下さい。

USBCameraAqcuire、USBCameraMonitor コンポーネントの起動

USB カメラのキャプチャ画像を OutPort から出力する USBCameraAqcuireComp コンポーネントと、InPort で受け取った画像を画面に表示する USBCameraMonitorComp コンポーネントを起動します。

これら2つのコンポーネントは、下記の手順にて起動できます。

[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [components] > [C++] > [OpenCV] を選択し、「USBCameraAqcuireComp」と「USBCameraMonitorComp」をそれぞれクリックして実行します。

コンポーネントの接続

図22のように、RTSystemEditor にて USBCameraAqcuireComp、Flip、USBCameraMonitorComp コンポーネントを接続します。


RTSE_Connect.png
図22. コンポーネントの接続


コンポーネントの Activate

RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティベートします。

正常にアクティベートされた場合、図23のように黄緑色でコンポーネントが表示されます。


RTSE_Activate.png
図23. コンポーネントのアクティベート


動作確認

図24のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。

Flip コンポーネントのコンフィギュレーションパラメーター「flipMode」を「0」や「-1」などに変更し、画像の反転が行われるかを確認してください。


RTSE_Configuration.png
図24. コンフィギュレーションパラメーターの変更


Flip コンポーネントのソースファイル

 // -*- C++ -*-
 /*!
  * @file  Flip.cpp
  * @brief Flip image component
  * @date $Date$
  *
  * $Id$
  */
 
 #include "Flip.h"
 
 // Module specification
 static const char* flip_spec[] =
   {
     "implementation_id", "Flip",
     "type_name",         "Flip",
     "description",       "Flip image component",
     "version",           "1.0.0",
     "vendor",            "AIST",
     "category",          "Category",
     "activity_type",     "PERIODIC",
     "kind",              "DataFlowComponent",
     "max_instance",      "1",
     "language",          "C++",
     "lang_type",         "compile",
     // Configuration variables
     "conf.default.flipMode", "1",
     // Widget
     "conf.__widget__.flipMode", "radio",
     // Constraints
     "conf.__constraints__.flip_mode", "(-1,0,1)",
     ""
   };
 
 /*!
  * @brief constructor
  * @param manager Maneger Object
  */
 Flip::Flip(RTC::Manager* manager)
   : RTC::DataFlowComponentBase(manager),
     m_originalImageIn("originalImage", m_originalImage),
     m_flippedImageOut("flippedImage", m_flippedImage)
 {
 }
 
 /*!
  * @brief destructor
  */
 Flip::~Flip()
 {
 }
 
 
 RTC::ReturnCode_t Flip::onInitialize()
 {
   // Registration: InPort/OutPort/Service
   // Set InPort buffers
   addInPort("originalImage", m_originalImageIn);
   
   // Set OutPort buffer
   addOutPort("flippedImage", m_flippedImageOut);
   
   // Bind variables and configuration variable
   bindParameter("flipMode", m_flipMode, "1");
 
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id)
 {
   // イメージ用メモリーの初期化
   m_imageBuff = NULL;
   m_flipImageBuff = NULL;
 
   // OutPort の画面サイズの初期化
   m_flippedImage.width = 0;
   m_flippedImage.height = 0;
  
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id)
 {
   if(m_imageBuff != NULL)
   {
     // イメージ用メモリーの解放
     cvReleaseImage(&m_imageBuff);
     cvReleaseImage(&m_flipImageBuff);
   }
 
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id)
 {
   // 新しいデータのチェック
   if (m_originalImageIn.isNew()) {
     // InPort データの読み込み
     m_originalImageIn.read();
 
     // InPort と OutPort の画面サイズ処理およびイメージ用メモリーの確保
     if( m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height)
       {
     m_flippedImage.width = m_originalImage.width;
     m_flippedImage.height = m_originalImage.height;
 
     // InPort のイメージサイズが変更された場合
     if(m_imageBuff != NULL)
       {
         cvReleaseImage(&m_imageBuff);
         cvReleaseImage(&m_flipImageBuff);
       }
 
     // イメージ用メモリーの確保
     m_imageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3);
     m_flipImageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3);
       }
 
     // InPort の画像データを IplImage の imageData にコピー
     memcpy(m_imageBuff->imageData,(void *)&(m_originalImage.pixels[0]),m_originalImage.pixels.length());
 
     // InPort からの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り
     cvFlip(m_imageBuff, m_flipImageBuff, m_flipMode);
 
     // 画像データのサイズ取得
     int len = m_flipImageBuff->nChannels * m_flipImageBuff->width * m_flipImageBuff->height;
     m_flippedImage.pixels.length(len);
 
     // 反転した画像データを OutPortにコピー
     memcpy((void *)&(m_flippedImage.pixels[0]),m_flipImageBuff->imageData,len);
 
     // 反転した画像データを OutPortから出力する。
     m_flippedImageOut.write();
   }
 
   return RTC::RTC_OK;
 }
 
 
 extern "C"
 {
  
   void FlipInit(RTC::Manager* manager)
   {
     coil::Properties profile(flip_spec);
     manager->registerFactory(profile,
                              RTC::Create<Flip>,
                              RTC::Delete<Flip>);
   }
   
 };

Flip コンポーネントのヘッダファイル

 // -*- C++ -*-
 /*!
  * @file  Flip.h
  * @brief Flip image component
  * @date  $Date$
  *
  * $Id$
  */
 
 #ifndef FLIP_H
 #define FLIP_H
 
 #include <rtm/Manager.h>
 #include <rtm/DataFlowComponentBase.h>
 #include <rtm/CorbaPort.h>
 #include <rtm/DataInPort.h>
 #include <rtm/DataOutPort.h>
 #include <rtm/idl/BasicDataTypeSkel.h>
 #include <rtm/idl/ExtendedDataTypesSkel.h>
 #include <rtm/idl/InterfaceDataTypesSkel.h>
 
 //OpenCV 用インクルードファイルのインクルード
 #include<cv.h>
 #include<cxcore.h>
 #include<highgui.h>
 
 using namespace RTC;
 
 /*!
  * @class Flip
  * @brief Flip image component
  *
  */
 class Flip
   : public RTC::DataFlowComponentBase
 {
  public:
   /*!
    * @brief constructor
    * @param manager Maneger Object
    */
   Flip(RTC::Manager* manager);
 
   /*!
    * @brief destructor
    */
   ~Flip();
 
   /***
    *
    * The initialize action (on CREATED->ALIVE transition)
    * formaer rtc_init_entry() 
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
    virtual RTC::ReturnCode_t onInitialize();
 
   /***
    *
    * The activated action (Active state entry action)
    * former rtc_active_entry()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
    virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The deactivated action (Active state exit action)
    * former rtc_active_exit()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
    virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The execution action that is invoked periodically
    * former rtc_active_do()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
    virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);
 
  protected:
   // Configuration variable declaration
   /*!
    * 
    * - Name:  flipMode
    * - DefaultValue: 1
    */
   int m_flipMode;
 
   // DataInPort declaration
   CameraImage m_originalImage;
 
   /*!
    */
   InPort<CameraImage> m_originalImageIn;
   
   // DataOutPort declaration
   CameraImage m_flippedImage;
 
   /*!
    */
   OutPort<CameraImage> m_flippedImageOut;
 
  private:
   // 処理画像用バッファ
   IplImage* m_imageBuff;
   IplImage* m_flipImageBuff;
 };
 
 
 extern "C"
 {
   DLL_EXPORT void FlipInit(RTC::Manager* manager);
 };
 
 #endif // FLIP_H

Flip コンポーネントのビルド済みパッケージ

ビルド済みパッケージを下記からダウンロードできます。

拡張子を"zip_"としてますので、"zip"にリネームしてから解凍してください。

RTコンポーネント作成(VC++編)

データポートのあるコンポーネント

ここでは、データポートを2つ持つコンポーネント (MRCConvertor) を VC8 にて作成します。

コンポーネントの概要

入力デバイスからのデータを車輪2つの速度に変換し、OutPort から出力するコンポーネント。

Joystick等の入力デバイスにて移動ロボット(前輪2、後輪1、前輪だけが駆動)を操作する際に、使用することができる。

作成する RTC の仕様は以下のとおりです。

  • InPort
    • 移動ロボットに対する速度 (TimedFloatSeq)
  • OutPort
    • 移動ロボットの車輪の速度 (TimedFloatSeq)

MRCConvertor の雛型を生成

RtcTemplate にて雛型を生成します。

作業用フォルダーの作成

適当な場所に作業用フォルダーを作成します。

今回は、フォルダー名をコンポーネント名 (MRCConvertor) と同じにします。

  1. "マイコンピューター"をダブルクリック
  2. 作業用フォルダーを作成したいフォルダーへ移動
  3. 右クリックして [新規作成] > [フォルダー] でフォルダーを作成

CUI版 rtc-template 編 (gen.bat)

rtc-template の実行を簡単にするために、下記のようなバッチファイルを先ほど作成した作業用フォルダーに作成します。

 rtc-template.py -bcxx^
  --module-name=MRCConvertor --module-desc="Convertor component for MobileRobot component"^
  --module-version=1.0.0 --module-vendor=AIST --module-category=Category^
  --module-comp-type=DataFlowComponent --module-act-type=PERIODIC^
  --module-max-inst=1^
  --inport=velFromInput:TimedFloatSeq^
  --outport=velToWheel:TimedFloatSeq

rtc-template (gen.bat) の実行

下記のように gen.bat ファイルを実行します。

 >gen.bat
  rtc-template.py -bcxx
  --module-name=MRCConvertor
  --module-desc="Convertor component for MobileRobot component"
  --module-version=1.0.0 --module-vendor=AIST
  --module-category=Category
  --module-comp-type=DataFlowComponent --module-act-type=PERIODIC
  --module-max-inst=1
  --inport=velFromInput:TimedFloatSeq --outport=velToWheel:TimedFloatSeq
   File "MRCConvertor.h" was generated.
   File "MRCConvertor.cpp" was generated.
   File "MRCConvertorComp.cpp" was generated.
   File "Makefile.MRCConvertor" was generated.
   File "MRCConvertorComp_vc8.vcproj" was generated.
   File "MRCConvertor_vc8.vcproj" was generated.
   File "MRCConvertorComp_vc9.vcproj" was generated.
   File "MRCConvertor_vc9.vcproj" was generated.
   File "MRCConvertor_vc8.sln" was generated.
   File "MRCConvertor_vc9.sln" was generated.
   File "copyprops.bat" was generated.
   File "user_config.vsprops" was generated.
   File "README.MRCConvertor" was generated.
   File "MRCConvertor.yaml" was generated.

Eclipse版 RtcTemplate 編

Eclipse版 RtcTemplate での対応は下記のようになります。

  • Programing language selection: C++
  • Module definition
    • Module name: MRCConvertor
    • Module decription: Convertor component for MobileRobot component
    • Module version: 1.0.0
    • Module vender: AIST
    • Module category: Category
    • Component type: DataFlowComponent
    • Component's activity type: PERIODIC
    • Number of maximum instance: 1
  • InPort definition
    • Ports: Name:velFromInput Type:TimedFloatSeq
  • OutPort definition
    • Ports: Name:velToWheel, Type:TimedFloatSeq

copyprops.bat の実行

RtcTemplate 実行により、copyprops.bat ファイルが作業用フォルダーに生成されます。

この copyprops.bat ファイルを用い、コンポーネントのビルドに必要な rtm_config.vsprops を作業フォルダーにコピーします。

copyprops.bat ファイルをダブルクリックしてください。

MRCConvertor の実装

Visual Studio の起動

MRCConvertorComp_vc8.vcproj をダブルクリックし、Visual Studio を起動します。

ヘッダファイルの編集

Visual Studio のソリューションエクスプローラーにて、MRCConvertorComp > Header Files の順にクリックし、 MRCConvertor.h をダブルクリックします。

  • ヘッダファイルのインクルード
    今回は、std::vector と math ライブラリを使用しますので、2つのヘッダファイルをインクルードします。

 #include <vector>
 // VC8 にて Math::M_PI を使用するため
 #define _USE_MATH_DEFINES
 #include <math.h>

  • "virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);"関数のコメントをはずします。
  • メンバー変数とメソッドの宣言
    今回は、MobileRobot (前輪2、後輪1、前輪だけが駆動) の移動ベクトルを車輪速度に変換するためのメソッド convert()、変換に必要な係数 m_k を宣言します。

 private:  float m_k;
 
  /*!
   * @brief InPortからのデータ(X、Y) をMobileRobot用の車輪速度データに変換する。
   *        m_velFromInput.data[0]とm_velFromInput.data[1]だけを使用する。
   */
  std::vector<float> convert(float x, float y) {
    float th = atan2(y,x);
    float v  = m_k * hypot(x,y);
    std::vector<float> ret_val;
    ret_val.push_back(v * cos(th - (M_PI/4.0))); // left vel
    ret_val.push_back(v * sin(th - (M_PI/4.0))); // right vel
    return ret_val;
  }

ソースファイルの編集

Visual Studio のソリューションエクスプローラーにて、MRCConvertorComp > Source Files の順にクリックし、 MRCConvertor.cpp をダブルクリックします。

  • onExecute() の実装
    onExecute() のコメントをはずし、以下のように実装します。

 /*!
  * @brief InPort からのデータ (X、Y) を MobileRobot用の車輪速度データに変換し、
  *        OutPort から出力する。
  */
 RTC::ReturnCode_t MRCConvertor::onExecute(RTC::UniqueId ec_id)
 {
   if (m_velFromInputIn.isNew()) {
     m_velFromInputIn.read();
     if (m_velFromInput.data.length() > 2) {
       std::vector<float>  con_val = this->convert(m_velFromInput.data[0],m_velFromInput.data[1]);
       for (int i = 0; i < 2; i++)
         m_velToWheel.data[i] = con_val[i];
       m_velToWheelOut.write();
     }
   }
   return RTC::RTC_OK;
 }

ここで行われていることは、

  1. m_velFromInputIn.isNew() にて InPort にデータが届いているかをチェックする。
  2. 新しいデータが届いていたら、m_velFromInputIn.read() で変数にデータを読み込む。
  3. 読み込んだデータを convert() にて車輪速度に変換する。
  4. 変換したデータを OutPort の変数にセットし、OutPort のバッファに書き込む。

ビルド

Visual Studio のメニューから [ビルド] > [ソリューションのビルド] をクリックしコンポーネントのビルドを行います。

rtc.conf の作成

エディタにて以下の内容を記述し、rtc.conf というファイル名で Debug あるいは Release フォルダーに保存します。

 corba.nameservers: localhost
 naming.formats: %n.rtc

実行

ビルドにてエラーが無かった場合、Debug あるいは Release フォルダーに rtc.conf を作成し、MRCConvertorComp.exe を実行します。

MRCConvertorComp.exe を実行する前に NamingService を起動してください。

ネームサーバーの起動

<OpenRTM-aist インストールフォルダー>\bin\rtm-naming.batをダブルクリックしCORBAネームサーバーを起動します。

MRCConvertorComp.exe の実行

Debug あるいは Release フォルダーに移動し、MRCConvertorComp.exe を実行します。

RTコンポーネント作成(Java版)

概要

OpenRTM-aist-Java 概要

 OpenRTM-aist とは、独立行政法人産業技術総合研究所・知能システム研究部門・タスクインテリジェンス研究グループが実装・配布・メンテナンスを行っている RTミドルウェアの実装例です。RTミドルウェアおよび OpenRTM-aist は、ロボットの様々な機能要素を RTコンポーネントと呼ばれる部品単位に分割し、これらを自由に組み合わせることで多様なロボットシステムの構築を可能にするためのソフトウェアプラットフォームです。現在 OMG (Object Management Group) にて仕様策定中のロボット用ミドルウェア仕様(The Robotic Technology Component Specification)にも準拠しています。
OpenRTM-aist-Java は、これまで C++言語向けに提供されていた OpenRTM-aist を Java言語に移植したものです。C++版 OpenRTM-aist互換のインターフェースを有し、Java言語を使用して開発した RTコンポーネントと C++言語を利用して開発した RTコンポーネントの相互運用が可能としています。

対象

 本ドキュメントは、OpenRTM-aist-Java を利用して、Java言語版 RTコンポーネントを開発する方法について説明しています。なお、本ドキュメントでは Java言語については基本知識を既に持っている方を対象としています。

動作環境

 OpenRTM-aist-Java の動作に必要な環境は以下のとおりです。


表 1-1 動作環境

環境 備考
Java Development Kit 5.0 (JDK 5)
(http://java.sun.com/products/archive/j2se/5.0_14/index.html )
注意:Java1.4 では動作しません.


 OpenRTM-aist-Java のインストール方法の詳細につきましては、「OpenRTM-aist-Java インストールマニュアル(UNIX)」あるいは「OpenRTM-aist-Java インストールマニュアル(Windows)」を参照してください。特に、次のことを確認してから以下の作業に移ってください。
  • 「java -version」を実行したときのバージョンが上記 JDKと一致すること
  • 環境変数 RTM_JAVA_ROOT に OpenRTM-aist-Java のライブラリへのパス(ベースパス)が設定されていること
  • <RTM_JAVA_ROOT>で設定されるパスの直下のディレクトリー「jar」内にライブラリファイル OpenRTM-aist-0.4.x.jar と commons-cli-1.1.jar が存在すること(クラスパスとして設定するため)
      こちら を参考に独自のクラスパスの設定も可能です。

◆参考:

 ここでは、Java版 RTコンポーネントの開発を行う場合の手順をご紹介します。サンプルとして以下のような仕様のコンポーネントを取り上げます。

表 2-1 サンプルコンポーネントの仕様

基本プロファイル
コンポーネント名 sample
説明 SampleComponent
バージョン 1.0
ベンダ AIST
カテゴリ example
コンポーネントの型 DataFlowComponent
アクティビティタイプ SPORADIC
最大インスタンス数 5
データInPort
名称 in
TimedLong
サービスProvider
IDLパス IDLs/MyService.idl
ポート名 MySvcPort
サービス名 myservice0
サービス型 MyService
コンフィギュレーションパラメーター
名称 multiply
int
デフォルト値 1


MyService.idl は適当なエディタを用いて以下のような IDL ファイルを作成します。また、上の表中の「IDLパス」はMyService.idl のパスを記述します。Windowsの場合は、この「IDLパス」が MyService.idl へのフルパスでなければなりません。

 typedef sequence<string> EchoList;
 typedef sequence<float> ValueList;
 interface MyService
 {
   string echo(in string msg);
   EchoList get_echo_history();
   void set_value(in float value);
   float get_value();
   ValueList get_value_history();
 };
MyService.idl


なお、上記 MyService.idl は OpenRTM-aist-0.4-Java に付属するサンプルの「examples/Java/RTMExamples/SimpleService」ディレクトリー内にあるものと同一です。

GUI を用いた場合の RTコンポーネントの開発手順

 GUI ツールである RtcTemlate を用いて、RTコンポーネントを開発手順を説明します。RtcTemplate の詳細についてはRtcLink・RtcTemplateのインストールおよびRtcTemplateを参照してください。

RtcTemplate と JDT の連携

  • Eclipse を「新規」に開く  RTコンポーネントの開発は、統合開発環境 Eclipse のプロジェクトから行うことも可能です。新しいワークスペースを指定して [OK] ボタンをクリックします。Eclipse が起動します。(このとき「ようこそ」という画面が表示される場合がありますが、これは閉じます。)
    CheckedWorkSpace2.png
    図 2-2 新規の Workspace を指定

  • Java プロジェクトファイルの作成
     上部メニューから [ファイル] > [新規] > [プロジェクト] を選択します。
    MakeProjectForBulid1.png
    図 2-3 ビルド用プロジェクトの作成 1

 「新規プロジェクト」ウィザード内で「Javaプロジェクト」を選択し、[次へ] ボタンをクリックします。

MakeProjectForBulid2.png
図 2-4 ビルド用プロジェクトの作成 2

 「新規プロジェクト」ウィザードの次のステップで、作成する「プロジェクト名」を入力します。「JDK準拠」グループ内の設定が「5.0」以上(あるいは1.5以上)となっていることを確認した上で、[次へ] ボタンをクリックします(図 2-5)。一方、環境によっては「JDK準拠」フレームであるところが「JRE」フレームとなっており、プルダウンメニューから JDK5(あるいはJDK1.5)が選択できない場合があります(図 2-5')。その場合は、こちら を参照してJDKを選択できるようにします。

MakeProjectForBulid3.png
図 2-5 ビルド用プロジェクトの作成 3


PullDownMenu_Default.png
図 2-5' 環境によっては JDK を選択できない


参考:
 → ''FAQ'': Q. 新規 Java プロジェクトが JDK5 (1.5) 準拠として作成できない



 表示された「新規 Java プロジェクト」画面において、生成するプロジェクトの各種設定を行い [終了] ボタンをクリックします。

ProjectSettings.png
図 2-6 プロジェクトの各種設定をして終了

 指定したプロジェクトが生成され、「パッケージ・エクスプローラービュー」内に表示されます。

PackageExplorerView.png
図 2-7 パッケージエクスプローラビューの表示例



-自動ビルドを無効に
 Eclipse のメニューバーの [プロジェクト] > [自動的にビルド] がオンになっている場合は、チェックをはずし無効にしておいた方がいいでしょう。

プロジェクトの作成時の各種オプション、設定など Eclipse に関する詳細は、Eclipse のサイト(http://www.eclipse.org/ )などを参照してください。



RtcTemplate を利用したスケルトン・コードの生成

  • GUI版RtcTemplate エディタの起動
     RtcTemplate のエディタ画面を起動します。

参考:
 → RtcTemplate を直接起動する
RtcTemplate の使用方法等につきましては、RtcTemplateを参照してください。


  • RtcTemplate エディタで設定項目を編集
     GUI版の RtcTemplate を使用して''表2-1'' の仕様を持つ RTコンポーネントのスケルトン・コードを生成させる場合の設定を以下に示します。
    GUIrtc-templateSetting1.png
    GUIrtc-templateSetting2.png
    図 2-8 GUI RtcTemplateの設定


:注:Windowsの場合、上記 「Output directory」 や「IDL path:」などはフルパスで記述すること。|

  • [Generate] ボタンでコードを生成
     [Generate] ボタンをクリックし、コード生成を実行する。このとき必ず、RtcTemplate エディタ最下段の 「Output directory」 欄に、先に生成したプロジェクトのディレクトリーを指定すること( 図 2-8では「temp」となっている)。


GenCode.png
図 2-9 コードを生成


  • 生成された各種ファイルがプロジェクトに追加
     「Output directory」として指定したディレクトリーに以下のファイルが生成されます。
    • Sample.java ・・・・・・・・・・・・・・・・・・・ Component Profile、初期化処理などを定義したクラス
    • Sampleimpl.java ・・・・・・・・・・・・・・・ RTコンポーネント本体クラス
    • SampleComp.java ・・・・・・・・・・・・・ RTコンポーネント起動用クラス
    • MyServiceSVC_impl.java ・・・・・・・ サービス実装クラス
    • build_Sample.xml ・・・・・・・・・・・・・・ RTコンポーネント ビルドファイル
    • README.Sample ・・・・・・・・・・・・・・  Read Meファイル


: 「Output directory」欄にプロジェクトのディレクトリーを指定することにより、生成された各種ファイルがプロジェクトへ(自動的に)追加されることになります。|

Done.png
図 2-10 各種ファイルの追加


 
コード生成が完了しても生成されたファイルの内容がプロジェクトの「パッケージ・エクスプローラ」に反映されていない場合があります。対象プロジェクトを右クリックして表示されるコンテキストメニュー内から [更新] を選択して、情報の更新を行うことができます。
UpdateProject.png
図 2-11 プロジェクト表示の更新


サービスポートを定義している RTコンポーネントの場合、Rtctemplate が生成したファイルだけではエラーが表示されます。これは該当するファイルが、IDL ファイルから自動生成されるクラスを利用しているためです。これらのクラスはビルド実行時に自動生成されます。


クラスパスが有効となる場所に、OpenRTM-aist-Java がインストールされていない場合、エラーが表示されます。この場合、プロジェクトのプロパティから OpenRTM-aist-Java のインストールフォルダー(ディレクトリー)を指定してください。

ProjectContextMenu.png
図 2-12 プロジェクトを右クリック


ProjectProperty.png
図 2-13 OpenRTM-aist-Javaへのパスを追加



  • IDLファイルを「Output directory」に追加
     RtcTemplate エディタ上の「IDL path:」で指定したIDLファイルを、生成されたJavaソースコードが配置される「Output directory」に手動でコピーします。

    Eclipseを用いたビルド

  • Antビルド
     パッケージ・エクスプローラー内の build_Sample.xml を右クリックし、表示されるコンテキストメニュー中から [実行] > [Antビルド] を選択することで、対象 RTコンポーネントのビルドを行うことが可能です。自作の jar ライブラリ等を使う必要があったり、OpenRTM-aist を違う場所にインストールしたなどの事情でクラスパスを任意の場所に設定してビルドするにはこちら を参照してください。
    BuildProject.png
    図 2-14 プロジェクトのビルド


BuildProject-consoleview.png
図 2-15 ビルド実行時のコンソール画面


ビルドが成功すると、プロジェクト内の「classes」ディレクトリーに classファイルが生成されます。




:参考:|
 → ''FAQ'': Q. 任意のフォルダーにクラスパスを設定して Ant ビルドを行う方法は?



作成した RTコンポーネントの実行

  • rtc.confの作成
     プロジェクト内の「classes」に以下のような内容のファイル rtc.conf を作成しておきます。


 corba.nameservers: localhost
 naming.formats: %n.rtc
rtc.conf



 なお、上記 rtc.conf は OpenRTM-aist-0.4-Java に付属しているサンプルの「examples/Java/RTMExamples/SimpleService」ディレクトリー内にあるものと同一です。


-ネームサーバーと RtcLink の起動
 OpenRTM-aist-0.4-Java に付属のツールで「bin」ディレクトリー内にある start-orbd.vbs をダブルクリック(Windows)し、あるいは、start-orbd.sh を実行(UNIX)し、ネームサーバーを起動しておきます。また、RtcLinkを起動しておきます。
    • 参考:
      • ネームサーバの起動(UNIX) ネームサーバの起動(Windows)
      • RtcLink の起動

        -RTコンポーネントの実行
         コマンドプロンプトあるいはターミナルを起動して、前述「classes」ディレクトリーをカレントとします。
        --UNIX系システムの場合
         $ java -classpath .:$RTM_JAVA_ROOT/jar/OpenRTM-aist-0.4.1.jar:$RTM_JAVA_ROOT/jar/commons-cli-1.1.jar SampleComp
         
         あるいは、たとえば bash の場合などは、上記コマンドを分割して、
         
         $ export CLASSPATH=.:$RTM_JAVA_ROOT/jar/OpenRTM-aist-0.4.1.jar:$RTM_JAVA_ROOT/jar/commons-cli-1.1.jar
         $ java SampleComp
    • Windows系システムの場合
       > java -classpath ".;%RTM_JAVA_ROOT%\jar\OpenRTM-aist-0.4.1.jar;%RTM_JAVA_ROOT%\jar\commons-cli-1.1.jar" SampleComp
       
       あるいは、上記コマンドを分割して、
       
       > set CLASSPATH=.;%RTM_JAVA_ROOT%\jar\OpenRTM-aist-0.4.1.jar;%RTM_JAVA_ROOT%\jar\commons-cli-1.1.jar
       > java SampleComp
というコマンドを実行すれば、RtcLink上に RTコンポーネントがあらわれます。



JavaSampleOnRtcLink.png
図 2-16 Sample コンポーネントを実行に成功したときの RtcLink の状態





Java版 RTコンポーネントの詳細

Java版 RTコンポーネントの構成

Java版 RTコンポーネントのソースファイルと、各ファイル内で実行している概略機能との関係を図3-1に示します。また、比較のために C++版 RTコンポーネントおよび OpenRTM-aist-0.3用 Java版 RTコンポーネントの構成も併せて示します。

InnerRTcomponents.png
図3-1 RTコンポーネントの構成


既存の C++版 RTコンポーネントと Java版 RTコンポーネントを比較した場合、以下の点が異なります。
  • RTコンポーネント機能の実体の分離 Java版 RTコンポーネントでは、起動処理の関係などから、RTコンポーネント機能の実体をXXXImplクラス (図3-1中では<Sample>Impl.java) 側へ移動しております。これに伴い、元々の RTコンポーネントクラス(図3-1中では<Sample>.java)側では Component Profile 定義と、各種コンポーネント生成用処理のみが残った形となっております。
  • コールバック関数のインターフェース化 C++版 RTコンポーネントにてコールバック関数として定義されていた部分が、Java版 RTコンポーネントではインターフェース化されています。
    • ModuleInitProc:コンポーネント起動クラス起動用インターフェース
    • RtcNewFunc:RTコンポーネント生成用インターフェース
    • RtcDeleteFunc:RTコンポーネント破棄用インターフェース
この修正に伴い、コンポーネント起動用クラスは上記インターフェースの実装を行う必要がございます。

Java版 RTコンポーネントと C++版コンポーネントの相違点

データポート

OpenRTM-aist-Java では、データの受け渡しを行うためにホルダクラス(DataRefクラス)を追加しています。そのため、データポートの定義および利用方法が以下のように変更されております。

Java版RTコンポーネント C++版RT コンポーネント
//InPort の定義
protected TimedShort m_in_val;
protected DataRef<TimedLong> m_in;
protected InPort<TimedLong> m_inIn;

//InPort の登録
m_in_val = new TimedLong();
m_in = new DataRef<TimedLong>(m_in_val);
m_inIn = new InPort<TimedLong>(“in”,m_in);
registerInPort(TimedLong.class,“in”,m_inIn);

//InPort からのデータ読み込み
m_inIn.read();
入力データ = m_in.v.data;
//InPort の定義
TimedShort m_in;
InPort<TimedShort> m_inIn;

//InPort の登録
m_inIn = new InPort<TimedLong>(“in”,m_in);
registerInPort(“in”,m_inIn);

//InPort からのデータ読み込み
m_inIn.read();
入力データ = m_in.data;
//OutPort の定義
protected TimedFloat m_out_val;
protected DataRef<TimedFloat> m_out;
protected OutPort<TimedFloat> m_outOut;

//OutPort の登録
m_out_val = new TimedFloat();
m_out = new DataRef<TimedFloat>(m_out_val);
m_outOut = new OutPort<TimedLong>(“out”,m_out);
registerOutPort(TimedLong.class,“out”,m_outOut);

//OutPort へのデータの書き出し
m_out_val.data = 出力データ;
m_outOut.write();
//OutPort の定義
TimedFloat m_out;
OutPort<TimedFLoat> m_outOut;

//OutPort の登録
m_outOut = new OutPort<TimedFloat>(“out”,m_out);
registerOutPort(“out”,m_outOut);

//OutPort へのデータの書き出し
m_out.data = 出力データ;
m_outOut.write();

 データポート使用方法につきましては、「SeqIO」「SimpleIO」のサンプルをご参照ください。

 

サービスポート

 OpenRTM-aist-Java では、サービスポートを使用するための補助変数(<サービス名>Base)を追加しております。このため、サービスポートの定義、利用方法が以下のように変更されております。詳細につきましては、「SimpleService」のサンプルをご覧ください。

Java版RTコンポーネント C++版RTコンポーネント
//Consumer の定義
protected CorbaPort m_MyServicePort;
protected CorbaConsumer<MyService> m_myservice0Base
= new CorbaConsumer<MyService>(MyService.class);
protected MyService m_myservice0;

//Consumer の登録
m_MyServiceRef = new CorbaPort(“MyService”);
m_MyServicePort.registerConsumer
(“myservice0”,”MyService”,m_myservice0Base);
registerPort(m_MyServicePort);

//Consumer の利用
m_myservice0 = m_myservice0Base._ptr();
m_myservice0.echo(argv[1]);
//Consumer の定義
RTC::CorbaPort m_MyServicePort;
RTC::CorbaConsumer<MyService> m_myservice0;

//Consumer の登録
m_MyServicePort = new RTC::CorbaPort(“MyService”);
m_MyServicePort.registerConsumer
(“myservice0”,“MyService”, m_myservice0);
registerPort(m_MyServicePort);

//Consumer の利用
m_myservice0->echo(argv[1].c_str());
//Provider の定義
protected CorbaPort m_MyServicePort;
protected MyServiceSVC_impl m_myservice0
= new MyServiceSVC_impl();

//Provider の登録
m_MyServiceRef = new CorbaPort(“MyService”);
m_MyServicePort.registerProvider
(“myservice0”,”MyService”,m_myservice0);
registerPort(m_MyServicePort);
//Provider の定義
RTC::CorbaPort m_MyServicePort;
MyServiceSVC_impl m_myservice0;

//Provider の登録
m_MyServicePort = new RTC::CorbaPort(“MyService”);
m_MyServicePort.registerProvider
(“myservice0”,“MyService”,m_myservice0);
registerPort(m_MyServicePort);



コンフィギュレーション

データポートの場合と同様、コンフィギュレーションを利用する場合もデータの受け渡しのためにホルダクラスを利用しています。このため、コンフィギュレーション・データの定義、利用方法が以下のように変更されております。

Java版RTコンポーネント C++版RTコンポーネント
//Configuration Variable の定義
protected IntegerHolder m_int_param0 = new IntegerHolder();
protected StringHolder m_str_param0 = new StringHolder();
protected VectorHolder m_vector_param0= new VectorHolder();

//パラメーターのバインド
bindParameter(“int_param0”,m_int_param_0,“0”);
bindParameter(“str_param0”,m_str_param_0,“sample”);
bindParameter(“vector_param0”,m_vector_param_0,“0.0,1.0,2.0,3.0,4.0”);
//Configuration Variable の定義
int m_int_param0;
str::string m_str_param0;
str::vector<double> m_vector_param0;

//パラメーターのバインド
bindParameter(“int_param0”,m_int_param_0,“0”);
bindParameter(“str_param0”,m_str_param_0,“sample”);
bindParameter(“vector_param0”,m_vector_param_0,“0.0,1.0,2.0,3.0,4.0”);

コンフィギュレーション・データの使用方法につきましては、「ConfigSample」のサンプルをご参照ください。
OpenRTM-aist-Javaで、コンフィギュレーション・データ用ホルダとして用意しているホルダクラスの種類とデータ型の関係を表3-1に示します。

表 3-1 コンフィギュレーション・データ用ホルダ型対応関係

データ型 ホルダ・クラス
short jp.go.aist.rtm.RTC.util.ShortHolder
int jp.go.aist.rtm.RTC.util.IntegerHolder
long jp.go.aist.rtm.RTC.util.LongHolder
float jp.go.aist.rtm.RTC.util.FloatHolder
double jp.go.aist.rtm.RTC.util.DoubleHolder
byte jp.go.aist.rtm.RTC.util.ByteHolder
String jp.go.aist.rtm.RTC.util.StringHolder

C++版 OpenRTM-aist と同様に、OpenRTM-aist-Java におきましてもユーザーが定義した任意の型についてコンフィグレーション・データ用ホルダを作成することができます。
コンフィギュレーション・データ用のホルダを実装する場合は、jp.go.aist.rtm.RTC.util.ValueHolder の stringFrom メソッドを実装し、implements節に Serializable インターフェースを追加する必要があります。
jp.go.aist.rtm.RTC.util.ValueHolder の stringFrom メソッドは、引数で渡された文字列から対象データ型に変換するためのメソッドです。
コンフィグレーション・データ用ホルダにつきましては「ConfigSample」サンプル内の VectorHolder クラスをご参照ください。

Java版 RTコンポーネントの起動時の振る舞い

C++版 RTコンポーネントの起動時の振る舞いを以下に示します。コンポーネント起動時の振る舞いについては、Java版 RTコンポーネントも基本的には同様な振る舞いとなっておりますが、RTコンポーネント機能の実体が XXXImpl クラスに分離された関係から、コンポーネント生成時、コンポーネント初期化時のメッセージの送信先はXXXクラスへと変更となっております。
JavaRTcomponent1.png

JavaRTcomponent2.png

IDL用データ型と Java言語データ型の対応関係

CORBA IDL用データ型と Java言語データ型の対応関係を表3-2に示します。

表 3-2 データ型対応関係

CORBA IDL Java言語
short short
long int
float float
double double
long long long
long double double
char char
wchar char
octet byte
unsigned short short
unsigned long int
unsigned long long long
boolean boolean
string String
wstring String
any org.omg.CORBA.Any
void void

その他

Tips

Eclipse の自動起動の設定方法について

    • Windows の場合
      • 1.Eclipse プロジェクトのディレクトリーの中から「.project」ファイルを選択して右クリックします。表示されたコンテキストメニュー内から [プロパティ] を選択します。
        ※選択する Eclipse プロジェクトは任意のプロジェクトで構いません。


SelectProject.png
「.project」ファイルを選択

      • 2.表示された「プロパティ」画面の「全般」タブ内、中段にある「プログラム」行右側の [変更] ボタンをクリックします。
        Property.png
        アプリケーションの関連付けの変更
      • 3.表示された「ファイルを開くプログラムの選択」画面下側にある [参照] ボタンをクリックします。
        SelectApplication.png
        Eclipseを選択
      • 4.ファイル選択画面が表示されるので、自動起動を行う Eclipse がインストールされているディレクトリー内から「eclipse.exe」を選択します。
      • 5.「ファイルを開くプログラムの選択」画面、「プロパティ」 を [OK] ボタンをクリックして閉じます。
        ::以上の設定を行うことで、「.project」ファイルをダブルクリックすることで指定した Eclipse を自動起動することが可能となります。
        |
        :: 複数の Eclipse をインストールしている場合、ダブルクリック動作では上記操作で指定したバージョンの Eclipse が常に起動されますのでご注意ください。
        |
        ダブルクリックを行ったプロジェクトが、起動対象 Eclipse に設定された workspace 内に含まれない場合、起動された Eclipse のパッケージ・エクスプローラー内に表示されません。この場合は、対象プロジェクトを workspace 内にインポートするか、workspace の設定を変更してください。


        --Linuxの場合
         data オプションにて workspace を指定して Eclipse を起動することが可能です。
         > eclipse -data /home/devel/OpenRTM/workspace
        対象プロジェクトが含まれている workspace を指定してください。


        謝辞

 OpenRTM-aist-Java は、以下のライブラリを使用して開発されました。これらのプロジェクトの開発と設計にかかわった方々に感謝します。
  • Apache Commons CLI 1.1

This product includes software developed by The Apache Software Foundation (http://www.apache.org/ ).

RTコンポーネント作成(OpenCV編 for RTCB-RC1)

はじめに

ここでは、OpenCV ライブラリを VC9 にて RTコンポーネント化する手順を紹介します。

OpenCVとは

OpenCVとはインテルが開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。

Wikipediaより抜粋。

作成する RTコンポーネント

  • Flip コンポーネント: OpenCV ライブラリのうち、cvFlip() 関数を用いて画像の反転を行う RTコンポーネント。
  • ObjectTracking コンポーネント: OpenCV ライブラリを使用し、マウスで選択した対象物を追跡するコンポーネント。 追跡中の画像表示、追跡画像を OutPort から出力、追跡対象の移動量を OutPort から出力などの機能を持つ。

OpenCV ライブリの RTコンポーネント化 (Flipコンポーネント)

ここでは、OpenCV ライブラリのうち、画像の反転を行う cvFlip() を VC9 にて RTコンポーネント化します。

以下は、作業の流れです。

  • cvFlip 関数について
  • コンポーネントの概要
  • 動作環境・開発環境
  • Flip コンポーネントの雛型の生成
  • アクティビティ処理の実装
  • コンポーネントの動作確認

cvFlip 関数について

cvFlip 関数は、2次元配列を垂直、水平、または両軸で反転します。

 void cvFlip(IplImage* src, IplImage* dst=NULL, int flip_mode=0);
 #define cvMirror cvFlip
  
 src       入力配列
 dst       出力配列。もし dst=NULL であれば、src が上書きされます。
 flip_mode 配列の反転方法の指定内容:
  flip_mode = 0: X軸周りでの反転(上下反転)
  flip_mode > 0: Y軸周りでの反転(左右反転)
  flip_mode < 0: 両軸周りでの反転(上下左右反転)

コンポーネントの概要

InPort からの入力画像を反転し OutPort から出力するコンポーネント。
反転の対象軸は、RTCのコンフィギュレーション機能を使用して flip_mode という名前のパラメーターで指定します。

flip_mode は、反転したい方向に応じて下記のように指定してください。

  • 上下反転したい場合、0
  • 左右反転したい場合、1
  • 上下左右反転したい場合、-1


作成する RTC の仕様は以下のとおりです。

  • InPort
    • キャプチャされた画像データ (TimedOctetSeq)
  • OutPort
    • 反転した画像データ (TimedOctetSeq)
  • Configuration
    • 反転方法の指定 (int)

※ TimedOctetSeq型は、OpenRTM-aist の BasicDataType.idl にて下記のように定義されているデータ型です。

※ octet型とは、CORBA IDL の基本型で、転送時にいかなるデータ変換も施されないことが保証されている8ビット値です。

   struct Time
  {
        unsigned long sec;    // sec
        unsigned long nsec;   // nano sec
  };
 
   struct TimedOctetSeq
  {
        Time tm;
        sequence<octet> data;
  };


図1は、それぞれのflip_mode での画像処理のイメージ図です。


cvFlip_and_FlipRTC.png
図1. Flip コンポーネントのflip_mode の指定パターン


動作環境・開発環境

Flip コンポーネントの雛型の生成

Flip コンポーネントの雛型の生成は、RTCBuilder を用いて行います。

RTCBuilder の起動

新規ワークスペースを指定して Eclipse を起動すると、以下の「ようこそ」画面が表示されます。 この 「ようこそ」画面の左上の「X」をクリックすると以下の画面が表示されます。

fig1-1EclipseInit.png
図2. Eclipse の初期起動時の画面


右上の [Open Perspective] ボタンをクリックし、プルダウンの「Other…」を選択します。

fig2-2PerspectiveSwitch.png
図3. パースペクティブの切り替え


「RTC Builder」を選択し、[OK] ボタンをクリックします。

fig2-3PerspectiveSelection.png
図4. パースペクティブの選択


RTCBuilder が起動します。

fig2-4RTCBuilderInit.png
図5. RTC Builder の初期起動時画面


RTCBuilder 用プロジェクトの作成

まず最初に、RT コンポーネントを作成するための Eclipse プロジェクトを作成します。 画面上部のメニューから[ファイル] > [新規] > [プロジェクト] を選択します。

fig2-5CreateProject.png
図6. RTC Builder 用プロジェクトの作成 1


表示された「新規プロジェクト」画面において、[その他] > [RTC ビルダ] を選択し、[次へ] をクリックします。

fig2-6CreateProject2.png
図7. RTC Builder 用プロジェクトの作成 2


「プロジェクト名」欄に作成するプロジェクト名を入力して [完了] をクリックします。

fig2-7CreteProject3.PNG
図8. RTC Builder 用プロジェクトの作成 3


指定した名称のプロジェクトが生成され、パッケージエクスプローラー内に追加されます。

fig2-8CreateProject4.png
図9. RTC Builder 用プロジェクトの作成 4


生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。

データポートで使用するデータタイプ定義ファイルの在り処の設定

データポートやサービスポートで使用するデータ型が定義された IDL ファイルが置いてある場所を予め設定しておく必要があります。
※ ここでの設定内容は、ワークスペースを変更しない限り有効ですので、プロジェクト毎に設定する必要はありません。
下記の手順にて、データ型が定義されている IDL ファイルの在り処を設定して下さい。

 1. メニューバーの [ウィンドウ] > [設定] をクリックし、設定ダイアログを表示させます。
 2. [RtcBuilder] > [Data Type] をクリックし、図12の Data Type 入力画面を出します。
 3. Data Type入力画面の [Add] ボタンをクリックし、"IDL File Directories"を入力します。
     OpenRTM-aist で定義されているデータ型の IDL ファイルはデフォルトでは下記にインストールされます。
     
      C:\Program Files\OpenRTM-aist\1.0\rtm\idl
 
 4. [OK] ボタンをクリックし、設定を終了します。


RTCBuilder_datatype_setup.png
図12. データ型定義ファイルの在り処設定


RTC プロファイルエディタの起動

RTC プロファイルエディタを開くには、ツールバーの [Open New RtcBuilder Editor] ボタンをクリックするか、メニューの [ファイル] > [Open New Builder Editor] を選択します。

fig2-9ToolsBarOpenNewRtcBuilder.png
図10. ツールバーから Open New RtcBuilder Editor



fig2-10FileMenuOpenNewBuilder.png
図11. File メニューから Open New Builder Editor


コンポーネントのプロファイル情報入力とコードの生成

1. 「基本」タブを選択し、基本情報を入力します。


-Module name: Flip
  • Module description: Flip image component
  • Module version: 1.0.0
  • Module vender: AIST
  • Module category: Category
  • Component type: STATIC
  • Component's activity type: PERIODIC
  • Component kind: DataFlowComponent
  • Number of maximum instance: 1
  • Execution type: PeriodicExecutionContext
  • Execution Rate: 1.0
    -Output Project: Flip


RTCBuilder_base.png
図13. 基本情報の入力


2. 「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。

Flipコンポーネントでは、onActivated()、onDeactivated()、onExecute() コールバックを使用しますので、 図14のようにon_activated、on_deactivated、on_executeの3つにチェックを入れます。


RTCBuilder_activity.png
図14. アクティビティコールバックの選択


3. 「データポート」タブを選択し、データポートの情報を入力します。


  • InPort Profile:
    • Port Name: original_image
    • Data Type: TimedOctetSeq
    • Var Name: image_orig
    • Disp. Position: left

  • OutPort Profile:
    • Port Name: fliped_image
    • Data Type: TimedOctetSeq
    • Var Name: image_flip
    • Disp. Position: right


RTCBuilder_dataport.png
図15. データポート情報の入力


4. 「コンフィギュレーション」タブを選択し、Configuration の変数を入力します。


  • flip_mode
    • Name: flip_mode
    • TYpe: int
    • Default Value: 1
    • 変数名: flip_mode

  • image_height
    • Name: image_height
    • TYpe: int
    • Default Value: 240
    • 変数名: img_height

  • image_width
    • Name: image_width
    • TYpe: int
    • Default Value: 320
    • 変数名: img_width


RTCBuilder_config.png
図16. コンフィグレーション情報の入力


5. 「言語・環境」タブを選択し、プログラミング言語を選択します。

今回は、C++(言語) を選択します。


RTCBuilder_lang.png
図17. プログラミング言語の選択


6. 「基本」タブにある [コード生成] ボタンをクリックし、コンポーネントの雛型を生成します。


RTCBuilder_base.png
図18. 雛型の生成(Generate)


※ 生成されるコード群は、Eclipse 起動時に指定したワークスペースフォルダーの中に生成されます。現在のワークスペースは、[ファイル] > [ワークスペースの切り替え] で確認することができます。

アクティビティ処理の実装

Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCV の cvFlip() 関数にて変換します。その後、変換された画像を OutPort から送信します。

onActivated()、onExecute()、onDeactivated()での処理内容を図19に示します。

FlipRTC_State.png
図19. アクティビティ処理の概要


onExecute() での処理を図20に示します。


FlipRTC.png
図20. onExucete()での処理内容


OpenCV 用ユーザープロパティシートのコピー

プロパティシートとは、コンパイルに必要な種々のオプション(インクルードパス、ライブラリロードバス、ライブラリ)やマクロを記述した VC の設定ファイルの一種です。 RTCBuilder や rtc-template で生成した VC 用のプロジェクトでは、VC のプロパティシートを使用して各種オプションを与えています。また、ユーザーがオプションを追加できるように、ユーザー定義のプロパティシートもインクルードするようになっています。
  • rtm_config.vsprop:OpenRTM に関する情報を含むプロパティシート。インストールされている OpenRTM-aist に依存するファイルであるため、copyprops.bat を使用して OpenRTM のシステムディレクトリーからカレントのプロジェクトへコピーします。
  • user_config.vsprops:ユーザー定義のプロパティシート。デフォルトでは空っぽです。使い方は、ソースコード:OpenRTM-aist/win32/OpenRTM-aist/example/USBCamera の中に入っている user_config.vsprops (OpenCV 用の設定)を参考にしてください。

以下の内容を user_config.vsprops というファイル名で保存し、Flip フォルダーにコピーしてください。

もしくは、下記より vsprops ファイルをダウンロードし、Flip フォルダーに保存してください。


user_config.vsprops

※ 既にFlipフォルダーには user_config.vsprops ファイルが存在しておりますが、上書きして構いません。


 <?xml version="1.0" encoding="shift_jis"?>
 <VisualStudioPropertySheet
     ProjectType="Visual C++"
     Version="8.00"
     Name="OpenCV"
     >
     <Tool
         Name="VCCLCompilerTool"
         AdditionalIncludeDirectories="$(cv_includes)"
     />
     <Tool
         Name="VCLinkerTool"
         AdditionalLibraryDirectories="$(cv_libdir)"
     />
     <UserMacro
         Name="user_lib"
         Value="$(cv_lib)"
     />
     <UserMacro
         Name="user_libd"
         Value="$(cv_libd)"
     />
     <UserMacro
         Name="cv_root"
         Value="C:\Program Files\OpenCV"
     />
     <UserMacro
         Name="cv_includes"
         Value="&quot;$(cv_root)\cv\include&quot;;&quot;$(cv_root)\cvaux\include&quot;;&quot;$(cv_root)\cxcore\include&quot;;&quot;$(cv_root)\otherlibs\highgui&quot;;&quot;$(cv_root)\otherlibs\cvcam\include&quot;"
     />
     <UserMacro
         Name="cv_libdir"
         Value="&quot;$(cv_root)\lib&quot;"
     />
     <UserMacro
         Name="cv_bin"
         Value="$(cv_root)\bin"
     />
     <UserMacro
         Name="cv_lib"
         Value="cv.lib cvcam.lib highgui.lib cxcore.lib"
     />
     <UserMacro
         Name="cv_libd"
         Value="cv.lib cvcam.lib highgui.lib cxcore.lib"
     />
 </VisualStudioPropertySheet>

copyprops.bat の実行

copyprops.bat というファイルを実行することで、rtm_config.vsprops というファイルがコピーされます。

rtm_config.vsprops ファイルは、RTコンポーネントを VC++ でビルドするために必要なインクルードパスやリンクするライブラリ等が記述されたファイルです。

ヘッダファイルの編集

  • OpenCV のライブラリを使用するため、OpenCV のインクルードファイルをインクルードします。

 //OpenCV 用インクルードファイルのインクルード
 #include<cv.h>
 #include<cxcore.h>
 #include<highgui.h>

  • この cvFlip コンポーネントでは、画像領域の確保、Flip 処理、確保した画像領域の解放のそれぞれの処理を行います。これらの処理は、それぞれ onActivated()、onDeactivated()、onExecute() のコールバック関数で呼ぶことにしますので、これら3つの関数のコメントアウトを解除します。

   /***
    *
    * The activated action (Active state entry action)
    * former rtc_active_entry()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The deactivated action (Active state exit action)
    * former rtc_active_exit()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The execution action that is invoked periodically
    * former rtc_active_do()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);

  • 反転した画像の保存用にメンバー変数を追加します。

   IplImage* m_image_buff;
   IplImage* m_flip_image_buff;

ソースファイルの編集

下記のように、onActivated()、onDeactivated()、onExecute()を実装します。

 RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id)
 {
   // イメージ用メモリーの確保
   m_image_buff = cvCreateImage(cvSize(m_img_width, m_img_height), IPL_DEPTH_8U, 3);
   m_flip_image_buff = cvCreateImage(cvSize(m_img_width, m_img_height), IPL_DEPTH_8U, 3);
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id)
 {
   // イメージ用メモリーの解放
   cvReleaseImage(&m_image_buff);
   cvReleaseImage(&m_flip_image_buff);
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id)
 {
   // 新しいデータのチェック
   if (m_image_origIn.isNew()) {
     // InPortデータの読み込み
     m_image_origIn.read();
 
     // InPortの画像データをIplImageのimageDataにコピー
     memcpy(m_image_buff->imageData,(void *)&(m_image_orig.data[0]),m_image_orig.data.length());
 
     // InPortからの画像データを反転する。 m_flip_mode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り
     cvFlip(m_image_buff, m_flip_image_buff, m_flip_mode);
 
     // 画像データのサイズ取得
     int len = m_flip_image_buff->nChannels * m_flip_image_buff->width * m_flip_image_buff->height;
     m_image_flip.data.length(len);
 
     // 反転した画像データを OutPort にコピー
     memcpy((void *)&(m_image_flip.data[0]),m_flip_image_buff->imageData,len);
 
     // 反転した画像データを OutPort から出力する。
     m_image_flipOut.write();
   }
   return RTC::RTC_OK;
 }

ビルドの実行

図21のようにし、コンポーネントのビルドを行います。


VC++_build.png
図21. ビルドの実行


Flip コンポーネントの動作確認

ここでは、OpenRTM-aist のサンプルコンポーネントに含まれている USBCameraAqcuireComp コンポーネントと、USBCameraMonitorCom コンポーネント、それと、Flip コンポーネントを接続し動作確認を行います。

NameService の起動

omniORB のネームサービスを起動します。


[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [C++] > [examples] をクリックし、[Start Naming Service] をクリックしてください。

rtc.conf の作成

RTコンポーネントでは、ネームサーバーのアドレスやネームサーバーへの登録フォーマットなどの情報を rtc.conf というファイルで指定する必要があります。

下記の内容を rtc.conf というファイル名で保存し、Flip\FlipComp\Debug (もしくは、Release) フォルダーに置いてください。

 corba.nameservers: localhost
 naming.formats: %n.rtc

Flip コンポーネントの起動

Flip コンポーネントを起動します。

先程 rtc.conf ファイルを置いたフォルダーにある、FlipComp.exe ファイルを実行してください。

USBCameraAqcuire、USBCameraMonitor コンポーネントの起動

USB カメラのキャプチャ画像を OutPort から出力する USBCameraAqcuireComp コンポーネントと、InPort で受け取った画像を画面に表示する USBCameraMonitorCOmp コンポーネントを起動します。

これら2つのコンポーネントは、下記の手順にて起動できます。

[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [C++] > [examples] をクリックし、「USBCameraAqcuireComp」と「USBCameraMonitorCOmp」をそれぞれクリックして実行します。

コンポーネントの接続

図22のように、RTSystemEditor にて USBCameraAqcuireComp,Flip、USBCameraMonitorComp コンポーネントを接続します。


RTSystemEditor_connection_flip.png
図22. コンポーネントの接続


Flip コンポーネントのコンフィギュレーションの変更

図23のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。

ELECOM製 の UCAM-DLM 130HWH (白いUSBカメラ) を使用の場合は、image_height と image_width パラメーターを下記のように変更してください。

 image_height : 480
 image_width  : 640

また、ELECOM製 の UCAM-DLM 130HWH (白いUSBカメラ) を使用の場合は、USBCameraMonitor の image_height,image_width パラメーターも 上記のように変更する必要があります。


RTSystemEditor_config_edit.png
図23. コンフィギュレーションパラメータの変更


コンポーネントの Activate

RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティベートします。

正常にアクティベートされた場合、図24のように黄緑色でコンポーネントが表示されます。


RTSystemEditor_activate.png
図24. コンポーネントのアクティベート


動作確認

Flip コンポーネントのコンフィギュレーションパラメーター「flip_mode」を「0」や「-1」などに変更し、画像の反転が行われるかを確認してください。

Flip コンポーネントのソースファイル

 // -*- C++ -*-
 /*!
  * @file  Flip.cpp
  * @brief Flip image component
  * @date $Date$
  *
  * $Id$
  */
 
 #include "Flip.h"
 
 // Module specification
 static const char* flip_spec[] =
   {
     "implementation_id", "Flip",
     "type_name",         "Flip",
     "description",       "Flip image component",
     "version",           "1.0.0",
     "vendor",            "AIST",
     "category",          "Category",
     "activity_type",     "PERIODIC",
     "kind",              "DataFlowComponent",
     "max_instance",      "1",
     "language",          "C++",
     "lang_type",         "compile",
     "exec_cxt.periodic.rate", "1.0",
     // Configuration variables
     "conf.default.flip_mode", "1",
     "conf.default.image_height", "240",
     "conf.default.image_width", "320",
     ""
   };
 
 /*!
  * @brief constructor
  * @param manager Maneger Object
  */
 Flip::Flip(RTC::Manager* manager)
   : RTC::DataFlowComponentBase(manager),
     m_image_origIn("original_image", m_image_orig),
     m_image_flipOut("fliped_image", m_image_flip),
     dummy(0),
     m_image_buff(0),
     m_flip_image_buff(0)
 {
   // Registration: InPort/OutPort/Service
 
   // Set InPort buffers
   registerInPort("original_image", m_image_origIn);
   
   // Set OutPort buffer
   registerOutPort("fliped_image", m_image_flipOut);
 }
 
 /*!
  * @brief destructor
  */
 Flip::~Flip()
 {
 }
 
 
 
 RTC::ReturnCode_t Flip::onInitialize()
 {
   // Bind variables and configuration variable
   bindParameter("flip_mode", m_flip_mode, "1");
   bindParameter("image_height", m_img_height, "240");
   bindParameter("image_width", m_img_width, "320");
   return RTC::RTC_OK;
 }
 
 RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id)
 {
   // イメージ用メモリーの確保
   m_image_buff = cvCreateImage(cvSize(m_img_width, m_img_height), IPL_DEPTH_8U, 3);
   m_flip_image_buff = cvCreateImage(cvSize(m_img_width, m_img_height), IPL_DEPTH_8U, 3);
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id)
 {
   // イメージ用メモリーの解放
   cvReleaseImage(&m_image_buff);
   cvReleaseImage(&m_flip_image_buff);
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id)
 {
   // 新しいデータのチェック
   if (m_image_origIn.isNew()) {
     // InPort データの読み込み
     m_image_origIn.read();
 
     // InPortの 画像データを IplImage の imageData にコピー
     memcpy(m_image_buff->imageData,(void *)&(m_image_orig.data[0]),m_image_orig.data.length());
 
     // InPortからの画像データを反転する。 m_flip_mode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り
     cvFlip(m_image_buff, m_flip_image_buff, m_flip_mode);
 
     // 画像データのサイズ取得
     int len = m_flip_image_buff->nChannels * m_flip_image_buff->width * m_flip_image_buff->height;
     m_image_flip.data.length(len);
 
     // 反転した画像データを OutPort にコピー
     memcpy((void *)&(m_image_flip.data[0]),m_flip_image_buff->imageData,len);
 
     // 反転した画像データを OutPort から出力する。
     m_image_flipOut.write();
   }
   return RTC::RTC_OK;
 }
 
 extern "C"
 {
  
   void FlipInit(RTC::Manager* manager)
   {
     RTC::Properties profile(flip_spec);
     manager->registerFactory(profile,
                              RTC::Create<Flip>,
                              RTC::Delete<Flip>);
   }
   
 };

Flip コンポーネントのヘッダファイル

 // -*- C++ -*-
 /*!
  * @file  Flip.h
  * @brief Flip image component
  * @date  $Date$
  *
  * $Id$
  */
 
 #ifndef FLIP_H
 #define FLIP_H
 
 #include <rtm/Manager.h>
 #include <rtm/DataFlowComponentBase.h>
 #include <rtm/CorbaPort.h>
 #include <rtm/DataInPort.h>
 #include <rtm/DataOutPort.h>
 #include <rtm/idl/BasicDataTypeSkel.h>
 
 // (1)  OpenCV 用インクルードファイルのインクルード
 #include<cv.h>
 #include<cxcore.h>
 #include<highgui.h>
 
 
 using namespace RTC;
 
 /*!
  * @class Flip
  * @brief Flip image component
  *
  */
 class Flip
   : public RTC::DataFlowComponentBase
 {
  public:
   /*!
    * @brief constructor
    * @param manager Maneger Object
    */
   Flip(RTC::Manager* manager);
 
   /*!
    * @brief destructor
    */
   ~Flip();
 
 
   /*!
    *
    * The initialize action (on CREATED->ALIVE transition)
    * formaer rtc_init_entry() 
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
    virtual RTC::ReturnCode_t onInitialize();
 
   /***
    *
    * The activated action (Active state entry action)
    * former rtc_active_entry()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The deactivated action (Active state exit action)
    * former rtc_active_exit()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The execution action that is invoked periodically
    * former rtc_active_do()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);
 
 
  protected:
   // Configuration variable declaration
   /*!
    * flip_mode = 0: flipping around x-axis 
    * flip_mode > 1: flipping around y-axis 
    * flip_mode < 0: flipping around both axises
    * - Name: flip_mode flip_mode
    * - DefaultValue: 1
    */
   int m_flip_mode;
   /*!
    * 
    * - Name:  m_img_height
    * - DefaultValue: 240
    * - 画像の高さ
    */
   int m_img_height;
   /*!
    * 
    * - Name:  m_img_width
    * - DefaultValue: 320
    * - 画像の幅
    */
   int m_img_width;
 
 
   // DataInPort declaration
   TimedOctetSeq m_image_orig;
   InPort<TimedOctetSeq> m_image_origIn;
   
 
   // DataOutPort declaration
   TimedOctetSeq m_image_flip;
   OutPort<TimedOctetSeq> m_image_flipOut;
   
  private:
   int dummy;
   IplImage* m_image_buff;
   IplImage* m_flip_image_buff;
 };
 
 
 extern "C"
 {
   void FlipInit(RTC::Manager* manager);
 };
 
 #endif // FLIP_H

Flip コンポーネントのビルド済みパッケージ

ビルド済みパッケージを下記からダウンロードできます。

拡張子を"zip_"としてますので、"zip"にリネームしてから解凍して下さい。

おまけ(物体追跡コンポーネント)

OpenCVのライブラリを用いて、物体追跡を行うコンポーネントです。

コンポーネントの概要

InPort からの画像データを表示し、マウスで選択されたオブジェクトを追跡するコンポーネント。

OutPort からは、物体追跡画像と、マウスで選択した位置からの移動量を出力する。

画像のサイズと、明度、彩度はコンフィグレーションにて変更可能。

作成する RTC の仕様は以下のとおりです。

  • InPort
    • キャプチャされた画像データ (TimedOctetSeq)
  • OutPort
    • 物体追跡画像データ (TimedOctetSeq)
  • OutPort
    • マウスで選択したオブジェクトの中心位置の移動量 (TimedFloatSeq)
  • Configuration
    • 明度の最大値(int) default: 256
    • 明度の最小値(int) default: 10
    • 彩度の最小値(int) default: 30
    • 画像の高さ(int) default: 240
    • 画像の幅(int) default: 320

RTCBuilder でのプロファイル情報入力内容

基本情報

  • Module name: ObjectTracking
  • Module description: Object tracking component
  • OutputProject: ObjectTracking

データポート


  • InPort Profile:
    • Port Name: in_image
    • Data Type: TimedOctetSeq
    • Var Name: in_image
    • Disp. Position: left
  • OutPort Profile:
    • Port Name: tracing_image
    • Data Type: TimedOctetSeq
    • Var Name: tracking_image
    • Disp. Position: right
  • OutPort Profile:
    • Port Name: displacement
    • Data Type: TimedFloatSeq
    • Var Name: displacement
    • Disp. Position: right

コンフィギュレーションパラメーター


  • brightness_max
    • Name: brightness_max
    • TYpe: int
    • Default Value: 256
    • 変数名: b_max

  • brightness_min
    • Name: brightness_min
    • TYpe: int
    • Default Value: 10
    • 変数名: b_min

  • saturation_min
    • Name: saturation_min
    • TYpe: int
    • Default Value: 30
    • 変数名: s_min

  • image_height
    • Name: image_height
    • TYpe: int
    • Default Value: 240
    • 変数名: img_height

  • image_width
    • Name: image_width
    • TYpe: int
    • Default Value: 320
    • 変数名: img_width

ObjectTracking コンポーネントのソースファイル

 // -*- C++ -*-
 /*!
  * @file  ObjectTracking.cpp
  * @brief Object tracking component
  * @date $Date$
  *
  * $Id$
  */
 
 #include "ObjectTracking.h"
 
 #define HIST_RANGE_MAX 180.0
 #define HIST_RANGE_MIN 0.0
 
 // 入力画像用 IplImage
 IplImage* g_image;
 
 // CamShift トラッキング用変数
 CvPoint g_origin;
 CvRect g_selection;  // 
 
 // 初期追跡領域の設定判別フラグ値 (0: 設定なし, 1: 設定あり)
 int g_select_object;
 
 // トラッキングの開始/停止用フラグ値 (0: 停止, -1: 開始, 1: トラッキング中)
 int g_track_object;
 
 // オブジェクト選択判定用フラグ(0: 1以外 1: 選択された直後)
 int g_selected_flag;
 
 // 選択された範囲の中心座標
 float g_selected_x;
 float g_selected_y;
 
 
 // Module specification
 static const char* objecttracking_spec[] =
   {
     "implementation_id", "ObjectTracking",
     "type_name",         "ObjectTracking",
     "description",       "Object tracking component",
     "version",           "1.0.0",
     "vendor",            "AIST",
     "category",          "Category",
     "activity_type",     "PERIODIC",
     "kind",              "DataFlowComponent",
     "max_instance",      "1",
     "language",          "C++",
     "lang_type",         "compile",
     "exec_cxt.periodic.rate", "1.0",
     // Configuration variables
     "conf.default.brightness_max", "256",
     "conf.default.brightness_min", "10",
     "conf.default.saturation_min", "30",
     "conf.default.image_height", "240",
     "conf.default.image_width", "320",
     ""
   };
 
 /*!
  * @brief constructor
  * @param manager Maneger Object
  */
 ObjectTracking::ObjectTracking(RTC::Manager* manager)
   : RTC::DataFlowComponentBase(manager),
     m_in_imageIn("in_image", m_in_image),
     m_tracking_imageOut("tracing_image", m_tracking_image),
     m_displacementOut("displacement", m_displacement),
     dummy(0),
     m_hsv(0), m_hue(0), m_mask(0), m_backproject(0),
     m_hist(0), m_backproject_mode(0),m_hdims(16),
     m_init_flag(0)
 {
   // Registration: InPort/OutPort/Service
   // Set InPort buffers
   registerInPort("in_image", m_in_imageIn);
   
   // Set OutPort buffer
   registerOutPort("tracing_image", m_tracking_imageOut);
   registerOutPort("displacement", m_displacementOut);
 
   m_hranges_arr[0] = HIST_RANGE_MIN;
   m_hranges_arr[1] = HIST_RANGE_MAX;
   m_hranges = m_hranges_arr;
 }
 
 /*!
  * @brief destructor
  */
 ObjectTracking::~ObjectTracking()
 {
 }
 
 
 
 RTC::ReturnCode_t ObjectTracking::onInitialize()
 {
   // Bind variables and configuration variable
   bindParameter("brightness_max", m_b_max, "256");
   bindParameter("brightness_min", m_b_min, "10");
   bindParameter("saturation_min", m_s_min, "30");
   bindParameter("image_height", m_img_height, "240");
   bindParameter("image_width", m_img_width, "320");
   m_displacement.data.length(2);
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t ObjectTracking::onActivated(RTC::UniqueId ec_id)
 {
   m_init_flag = 0;
   // ウインドウを生成する
   cvNamedWindow( "ObjectTracking", 1 );
   // マウス操作時のコールバック処理の登録
   cvSetMouseCallback( "ObjectTracking", on_mouse, 0 );
   g_selected_flag = 0;
   g_selected_x = 0.0;;
   g_selected_y = 0.0;;
 
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t ObjectTracking::onDeactivated(RTC::UniqueId ec_id)
 {
   if (m_init_flag) {
     // メモリーを解放する
     cvReleaseImage(&g_image);
     cvReleaseImage(&m_hsv);
     cvReleaseImage(&m_hue);
     cvReleaseImage(&m_mask);
     cvReleaseImage(&m_backproject);
   }
   // ウインドウを破棄する
   cvDestroyWindow("ObjectTracking");
   m_init_flag = 0;
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t ObjectTracking::onExecute(RTC::UniqueId ec_id)
 {
   if (m_in_imageIn.isNew()) {
     m_in_imageIn.read();
 
     if(!m_init_flag) allocateBuffers();
 
     memcpy(g_image->imageData,(void *)&(m_in_image.data[0]),m_in_image.data.length());
     cvShowImage("ObjectTracking", g_image);
 
     // キャプチャされた画像を HSV 表色系に変換して hsv に格納
     cvCvtColor( g_image, m_hsv, CV_BGR2HSV );
 
     // g_track_objectフラグが0以下なら、以下の処理を行う
     if( g_track_object )
       {
     cvInRangeS( m_hsv, cvScalar(HIST_RANGE_MIN,m_s_min,MIN(m_b_min,m_b_max),0),
             cvScalar(HIST_RANGE_MAX,256,MAX(m_b_min,m_b_max),0), m_mask );
     cvSplit( m_hsv, m_hue, 0, 0, 0 );
 
     if( g_track_object < 0 ) calcHistogram();
 
     // バックプロジェクションを計算する
     cvCalcBackProject( &m_hue, m_backproject, m_hist );
     // backProjectionのうち、マスクが1であるとされた部分のみ残す
     cvAnd( m_backproject, m_mask, m_backproject, 0 );
     // CamShift法による領域追跡を実行する
     cvCamShift( m_backproject, m_track_window,
             cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),
             &m_track_comp, &m_track_box );
     m_track_window = m_track_comp.rect;
     
     if( m_backproject_mode )
       cvCvtColor( m_backproject, g_image, CV_GRAY2BGR );
     if( g_image->origin )
       m_track_box.angle = -m_track_box.angle;
     cvEllipseBox( g_image, m_track_box, CV_RGB(255,0,0), 3, CV_AA, 0 );
 
     // マウスで選択された領域の中心座標を保存
     if (g_selected_flag) {
       g_selected_x = m_track_box.center.x;
       g_selected_y = m_track_box.center.y;
       g_selected_flag = 0;
     }
 
     // マウスで選択された位置からの移動量をOutPortから出力
     m_displacement.data[0] = m_track_box.center.x - g_selected_x;
     m_displacement.data[1] = -(m_track_box.center.y - g_selected_y);
     m_displacementOut.write();
       }
         
     // マウスで選択中の初期追跡領域の色を反転させる
     if( g_select_object && g_selection.width > 0 && g_selection.height > 0 )
       {
     cvSetImageROI( g_image, g_selection );
     cvXorS( g_image, cvScalarAll(255), g_image, 0 );
     cvResetImageROI( g_image );
       }
 
     // 画像を表示する
     cvShowImage( "ObjectTracking", g_image );
 
     // 画像をOutPortから出力する
     int len = g_image->nChannels * g_image->width * g_image->height;
     m_tracking_image.data.length(len);
     memcpy((void *)&(m_tracking_image.data[0]),g_image->imageData,len);
     m_tracking_imageOut.write();
 
     // キー入力を待ち、押されたキーによって処理を分岐させる
     int c = cvWaitKey(10);
     // while無限ループから脱出(プログラムを終了)
     if( (char) c == 27 ) {
       this->exit();
     }
   }
   return RTC::RTC_OK;
 }
 
 /*!
  * 全てのイメージ用メモリーの確保
  */
 void ObjectTracking::allocateBuffers() {
   g_image = cvCreateImage( cvSize(m_img_width,m_img_height),8, 3 );
   m_hsv = cvCreateImage( cvSize(m_img_width,m_img_height),8, 3 );
   m_hue = cvCreateImage( cvSize(m_img_width,m_img_height),8, 1 );
   m_mask = cvCreateImage( cvSize(m_img_width,m_img_height),8, 1 );
   m_backproject = cvCreateImage( cvSize(m_img_width,m_img_height),8, 1 );
   m_hist = cvCreateHist( 1, &m_hdims, CV_HIST_ARRAY, &m_hranges, 1 );
   m_init_flag = 1;
 }
 
 /*!
  * ヒストグラムの計算
  */
 void ObjectTracking::calcHistogram() {
   float max_val = 0.f;
   cvSetImageROI( m_hue, g_selection );
   cvSetImageROI( m_mask, g_selection );
   // ヒストグラムを計算し、最大値を求める
   cvCalcHist( &m_hue, m_hist, 0, m_mask );
   cvGetMinMaxHistValue( m_hist, 0, &max_val, 0, 0 );
   // ヒストグラムの縦軸(頻度)を0-255のダイナミックレンジに正規化
   cvConvertScale( m_hist->bins, m_hist->bins, max_val ? 255. / max_val : 0., 0 );
   // hue,mask画像に設定された ROI をリセット
   cvResetImageROI( m_hue );
   cvResetImageROI( m_mask );
   m_track_window = g_selection;
   // track_object をトラッキング中にする
   g_track_object = 1;
 }
 
 
 extern "C"
 {
  
   void ObjectTrackingInit(RTC::Manager* manager)
   {
     RTC::Properties profile(objecttracking_spec);
     manager->registerFactory(profile,
                              RTC::Create<ObjectTracking>,
                              RTC::Delete<ObjectTracking>);
   }
   
 
   //
   //    マウスドラッグによって初期追跡領域を指定する
   //
   //    引数:
   //        event    : マウス左ボタンの状態
   //        x        : マウスが現在ポイントしているx座標
   //        y        : マウスが現在ポイントしているy座標
   //        flags    : 本プログラムでは未使用
   //        param    : 本プログラムでは未使用
   //
   void on_mouse( int event, int x, int y, int flags, void* param )
   {
     // 画像が取得されていなければ、処理を行わない
     if( !g_image )
       return;
 
     // 原点の位置に応じてyの値を反転(画像の反転ではない)
     if( g_image->origin )
       y = g_image->height - y;
 
     // マウスの左ボタンが押されていれば以下の処理を行う
     if( g_select_object )
       {
     g_selection.x = MIN(x,g_origin.x);
     g_selection.y = MIN(y,g_origin.y);
     g_selection.width = g_selection.x + CV_IABS(x - g_origin.x);
     g_selection.height = g_selection.y + CV_IABS(y - g_origin.y);
         
     g_selection.x = MAX( g_selection.x, 0 );
     g_selection.y = MAX( g_selection.y, 0 );
     g_selection.width = MIN( g_selection.width, g_image->width );
     g_selection.height = MIN( g_selection.height, g_image->height );
     g_selection.width -= g_selection.x;
     g_selection.height -= g_selection.y;
       }
 
     // マウスの左ボタンの状態によって処理を分岐
     switch( event )
       {
       case CV_EVENT_LBUTTONDOWN:
     // マウスの左ボタンが押されたのであれば、
     // 原点および選択された領域を設定
     g_origin = cvPoint(x,y);
     g_selection = cvRect(x,y,0,0);
     g_select_object = 1;
     break;
       case CV_EVENT_LBUTTONUP:
     // マウスの左ボタンが離されたとき、width と height がどちらも正であれば、
     // g_track_objectフラグを開始フラグにする
     g_select_object = 0;
     if( g_selection.width > 0 && g_selection.height > 0 ) {
       g_track_object = -1;
       g_selected_flag = 1;
     }
     break;
       }
   }
 
 };

ObjectTracking コンポーネントのヘッダファイル

 // -*- C++ -*-
 /*!
  * @file  ObjectTracking.h
  * @brief Object tracking component
  * @date  $Date$
  *
  * $Id$
  */
 
 #ifndef OBJECTTRACKING_H
 #define OBJECTTRACKING_H
 
 #include <rtm/Manager.h>
 #include <rtm/DataFlowComponentBase.h>
 #include <rtm/CorbaPort.h>
 #include <rtm/DataInPort.h>
 #include <rtm/DataOutPort.h>
 #include <rtm/idl/BasicDataTypeSkel.h>
 
 #include <cv.h>
 #include <highgui.h>
 #include <stdio.h>
 #include <ctype.h>
 
 
 using namespace RTC;
 
 /*!
  * @class ObjectTracking
  * @brief Object tracking component
  *
  */
 class ObjectTracking
   : public RTC::DataFlowComponentBase
 {
  public:
   /*!
    * @brief constructor
    * @param manager Maneger Object
    */
   ObjectTracking(RTC::Manager* manager);
 
   /*!
    * @brief destructor
    */
   ~ObjectTracking();
 
   /*!
    *
    * The initialize action (on CREATED->ALIVE transition)
    * formaer rtc_init_entry() 
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
    virtual RTC::ReturnCode_t onInitialize();
 
 
   /***
    *
    * The activated action (Active state entry action)
    * former rtc_active_entry()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The deactivated action (Active state exit action)
    * former rtc_active_exit()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id);
 
   /***
    *
    * The execution action that is invoked periodically
    * former rtc_active_do()
    *
    * @param ec_id target ExecutionContext Id
    *
    * @return RTC::ReturnCode_t
    * 
    * 
    */
   virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);
 
 
   /*!
    *    入力された1つの色相値を RGB に変換する
    *
    *    引数:
    *        hue        : HSV表色系における色相値H
    *    戻り値:
    *        CvScalar: RGB の色情報が BGR の順で格納されたコンテナ
    */
   CvScalar hsv2rgb( float hue ) {
     int rgb[3], p, sector;
     static const int sector_data[][3]=
       {{0,2,1}, {1,2,0}, {1,0,2}, {2,0,1}, {2,1,0}, {0,1,2}};
     hue *= 0.033333333333333333333333333333333f;
     sector = cvFloor(hue);
     p = cvRound(255*(hue - sector));
     p ^= sector & 1 ? 255 : 0;
 
     rgb[sector_data[sector][0]] = 255;
     rgb[sector_data[sector][1]] = 0;
     rgb[sector_data[sector][2]] = p;
     
     return cvScalar(rgb[2], rgb[1], rgb[0],0);
   }
   
 
   /*!
    * 全てのイメージ用バッファの確保
    */
   void allocateBuffers();
 
   /*!
    * ヒストグラムの計算
    */
   void calcHistogram();
 
  protected:
   // Configuration variable declaration
   /*!
    * 
    * - Name:  b_max
    * - DefaultValue: 256
    * - 明度の最大値
    */
   int m_b_max;
   /*!
    * 
    * - Name:  b_min
    * - DefaultValue: 10
    * - 明度の最小値 
    */
   int m_b_min;
   /*!
    * 
    * - Name:  s_min
    * - DefaultValue: 30
    * - 彩度の最小値
    */
   int m_s_min;
   /*!
    * 
    * - Name:  m_img_height
    * - DefaultValue: 240
    * - 画像の高さ
    */
   int m_img_height;
   /*!
    * 
    * - Name:  m_img_width
    * - DefaultValue: 320
    * - 画像の幅
    */
   int m_img_width;
 
   // DataInPort declaration
   TimedOctetSeq m_in_image;
   InPort<TimedOctetSeq> m_in_imageIn;
   
   // DataOutPort declaration
   TimedOctetSeq m_tracking_image;
   OutPort<TimedOctetSeq> m_tracking_imageOut;
 
   TimedFloatSeq m_displacement;
   OutPort<TimedFloatSeq> m_displacementOut;
 
  private:
   int dummy;
   IplImage* m_hsv;         // HSV 表色系用 IplImage
   IplImage* m_hue;         // HSV 表色系のHチャンネル用 IplImage
   IplImage* m_mask;        // マスク画像用 IplImage
   IplImage* m_backproject; // バックプロジェクション画像用 IplImage
 
   CvHistogram * m_hist; // ヒストグラム処理用構造体
 
   // 処理モード選択用フラグ
   int m_backproject_mode; // バックプロジェクション画像の表示/非表示用フラグ値 (0: 非表示, 1: 表示)
 
   // CamShiftトラッキング用変数
   CvRect m_track_window;
   CvBox2D m_track_box;
   CvConnectedComp m_track_comp;
 
   // ヒストグラム用変数
   int m_hdims;                // ヒストグラムの次元数
   float m_hranges_arr[2]; // ヒストグラムのレンジ
   float* m_hranges;
 
   // 初期化判定フラグ
   int m_init_flag;
 };
 
 
 
 extern "C"
 {
   void ObjectTrackingInit(RTC::Manager* manager);
   void on_mouse( int event, int x, int y, int flags, void* param );
 };
 
 #endif // OBJECTTRACKING_H

コンポーネントの接続

図25は、USBCameraAcquire,Flip,ObjectTracking,SeqIn コンポーネントの接続例です。

まず、USBCameraAcquire コンポーネントにて USBカメラの画像を取得します。

次に、Flip コンポーネントにて左右を反転させます。

反転させている理由は、物体追跡コンポーネントの出力をジョイスティックとして使用する場合に、画像が鏡のように表示されていた方が操作しやすいためです。

次に、ObjectTracking コンポーネントで、あらかじめ選択された追跡対象物の移動量を OutPort (displacement) から出力し、SeqIn コンポーネントで移動量を表示します。


RTSystemEditor_connection.png
図25. コンポーネントの接続例


ObjectTrackingコンポーネントのビルド済みパッケージ

ビルド済みパッケージを下記からダウンロードできます。

拡張子を"zip_"としてますので、"zip"にリネームしてから解凍して下さい。

RTコンポーネント作成(NXTway編)


はじめに

  • NXT MINDSTORM と OpenRTM の Bluetooth コンポーネントと接続し、NXT MINDSTORM をコントロールする方法を説明する。
  • NXT MINDSTORM に対するソフトウェアのインストール方法および基本的な操作、RTコンポーネントとの接続する方法を説明する。
    gaiyou.png

環境構築

LEGO の組み立て

nxtOSEK 環境設定(Windows XP/Vista)

kannkyousetumei.png

  • Cygwin のインストール
    • Cygwin は Windows 環境で各種の Linux ソフトウェアを実行できることである
    • Download : http://www.cygwin.com/
    • 設定オプション
      • Root Directory : 「c:\cygwin」
      • Select Packages : Devel カテゴリにある「make 3.81-1」と Libs カテゴリにある「libintl3」を選択してインストールする
  • GNU ARM のインストール(必ず4.0.2)
    • GNU ARM は NXT の ARM7コアプロセッサ(AT91SAM7S256)に対応した GCC コンパイラパッケージである
    • Download : http://www.gnuarm.com/bu-2.16.1_gcc-4.0.2-c-c++_nl-1.14.0_gi-6.4.exe
    • 設定オプション
      • Diriectory : 「c:\cygwin\GNUARM」
      • Select Components : 「Floating Point Unit」選択しない
      • Select Additional Tasks : 「Install Cygwin DLLs...」選択しない
  • NeXTTool のダウンロード
    • NeXTTool は NXTと通信用 PC コンソールで、*.rxe(アプリ) と *.rfw(ファームウェア)を NXT にアップできる
    • Download : http://bricxcc.sourceforge.net/nexttool.zip
    • 設定オプション
      • 解凍ディレクトリ : 「c:\cygwin\nexttool」
  • 拡張 NXT ファームウェアのダウンロード
    • 拡張 NXT ファームウェアは標準 NXT ファームウェアをベースに機能拡張したものである
    • NXT の ARM7 コアCPUのネイティブコードも実行できる
    • Download : http://bricxcc.sourceforge.net/lms_arm_jch.zip
    • 設定オプション
      • 「lms_arm_nbcnxc_107.rfw」だけをコピーする
      • コピーディレクトリー : 「c:\cygwin\nexttool」
  • 「sg.exe」ファイル追加
    • Download : http://www.toppers.jp/download.cgi/osek_os-1.1.lzh
    • 解凍して「toppers_osek」フォルダーの下に「sg」フォルダーの下にある「sg.exe」ファイルを「c:\cygwin\nxtOSEK\toppers_osek\sg」フォルダーにコピーする

拡張 NXT ファームウェアの NXT へのアップロード

  • NXT ファームウェアアップデートモード
    • 電流が ON の状態でリセットボタンを安全ピンの先で5秒ぐらい押し続ける
      resetButton.jpg
    • NXT スピーカーに小さなクリック音が聞こえるようになる
    • NXT と PC の USB 接続
  • Cygwin 起動及び拡張ファームウェアアップロード cygwin-icon.gifcygwinを起動する
    • 下記のコマンドを入力
       $cd c:\cygwin\nexttool
       $./NeXTTool.exe /COM=usb -firmware=lms_arm_nbcnxc_107.rfw
    • アップロードが完了すると、NXT の液晶画面が砂嵐画面のようになる
    • NXT のボタン操作できない場合は NXT のバッテリを外し再び装着することで拡張 NXT ファームウェアが起動

NXTway ソース修正及びコンパイル

NXT 側のソース修正及びコンパイル、アップロード

  • ソース修正
    • 「c:\cygwin\nxtOSEK\samples_c++\cpp\NXTway_GS++」フォルダーにある「sample.cpp」の中で
      samplecpp.png
    • 「btConnection.connect」で検索したら、下記の「Main Task」の中にある関数が検索できる
       //=============================================================================
       // Main Task
       TASK(TaskMain)
       {
           // establish blutooth connection with a PC to use a PC HID GamePad controller
           BTConnection btConnection(bt, lcd, nxt);
           (void)btConnection.connect(BT_PASS_KEY);
       
           for (U32 i = 5; i <= Lcd::MAX_CURSOR_Y; i++) lcd.clearRow(i);
           lcd.cursor(0,5);
           lcd.putf("snsns", "TOUCH:START/STOP", "STAND IT UP AND", "WAIT FOR A BEEP.");
           lcd.disp();
           SetRelAlarm(Alarm4msec, 1, 4); // Set 4msec periodical Alarm for the drive event
       
           while(1)
           {
               sonarDriver.checkObstacles(sonar);
               clock.wait(40); // 40msec wait
           }
       }
    • 「(void)btConnection.connect(BT_PASS_KEY);」を下記のように修正する
       (void)btConnection.connect(BT_PASS_KEY, "alias")
    • "alias"の部分に自分が確認できる名前を入力する
  • コンパイル cygwin-icon.gifcygwinを起動する
     $cd c:\cygwin\nxtOSEK\samples_c++\cpp\NXTway_GS++
    • cygwinの上で上のフォルダ「C:\cygwin\nxtOSEK\samples_c++\cpp\NXTway_GS++」で
       $make all
       ...
       Generating binary image file: nxtway_gs++_rom.bin
       Generating binary image file: nxtway_gs++_ram.bin
       Generating binary image file: nxtway_gs++_.rxe
       $
    • 上のようなメッセージを確認すればコンパイル完了
  • NXT にアップロード
    • 拡張 NXT ファームウェアが入っている NXT を USB で接続する
    • コンパイルした cygwin の状態で
       $sh ./rxeflash.sh
       Executing NeXTTool to upload nxtway_gs++.rxe...
       nxtway_gs++.rxe=34240
       NeXTTool is terminated.
       $
    • 上のようなメッセージを確認したらアップロード完了

動作確認

NXT側の起動

  • 下の図で⑦番の状態で待機する
    NXTStart.jpg
    ;

PC 側 RTC の起動

  • OpenRTM-aist の Naming Service をスタートする
    NamingServiceStart.png
  • TkJoyStickComp コンポーネントを起動する
    TkJoyStickCompStart.png
  • Bluetooth 接続
    • Bluetooth を PC に接続する
      BluetoothPlus.png
    • "alias"の部分で入れたネームの Bluetooth ディバイスを接続する
      BluetoothPlus2.png
    • Password は基本的に「1234」に設定されている
      BluetoothPass.png
    • 接続した Bluetooth の Comport を確認する
      BluetoothComport.png
  • Bluetooth コンポーネントを起動
    • BluetoothComp.zipをダウンロード
    • BluetoothComp.zipを解凍した後、「components」フォルダーにある「NXTBlueToothComp.exe」ファイルを実行する
      NXTBluetoothCompStart.png
  • RT System Editor を起動
    • 下の図のようにRT System Editor を起動する
      RTSystemEditorStart.png
  • Naming Service 追加
    • 下の図のように追加ボタンをクリックし、「127.0.0.1」を追加する
      NamingServicetuika.png
  • コンポーネント配置
    • 下の図のようにコンポーネントを配置する
      Componenthiichi.png
  • TkJoyStickComp と Bluetooth コンポーネントを接続する
    • 下の図のように TkJoyStickComp と BluetoothComp を接続する
      Componentsetuzoku.png
    • Bluetooth コンポーネントの Configuration View で「m_COM」変数に確認した Comport の番号を入れる
      Setupm_COM.jpg
  • Activate する
    Activate.png
    • 接続が完了したら NXT 側が下の図のようになる
      8.png

LEGO Mindstorm NXT RTコンポーネント

LEGO Mindstorm NXT (以下NXT) は3つのモーターと4種類のセンサー、これらを制御するためのインテリジェントブロックNXTから構成される、ロボットを作ることができる LEGOブロックです。 NXT は USB または Bluetooth で PC と接続して、インテリジェントブロックをプログラミングしたり、直接制御したりすることができます。 また、自作のプログラムをインテリジェントブロックにロードしてスタンドアロンで動作させることもできます。

ここでは、インテリジェントブロックを PC上で RTコンポーネント化し、他のコンポーネントからモーターを動かしたり、センサーデータを読み出したりできるようにします。 NXT を RTコンポーネント化することで、既存の RTコンポーネント化された入力デバイスで NXT を制御したり、センサーデータを読み出したりすることが簡単にできるようになります。

Mindstorm NXT 設定

NXT を RTC化するにあたり、まずは NXT と PC の設定を行います。 PC と NXT は USB または Bluetooth で接続することができるますが、せっかく電池で動く NXT を紐付きでは使いたくないのでBluetoothで接続します。

ブロックの組み立て

ここでは、写真に示すような構成を例にとり、NXT を RTC化します。

TribotBase.png

これは、Tribot と呼ばれる構成の移動ベースの部分に、目玉のような超音波センサーを前面に取り付けただけの簡単な構成です。 移動ベースの組み立て方法は、Mindstorm NXT の箱に入っている「Start Here」と書かれた小箱の中の小冊子に詳しく記載されているので参照してください。 この小冊子で解説されている移動ベースに、いくつかの LEGOブロックを追加して、超音波センサーを取り付ければ完成です。

Bluetoothデバイスのインストール

NXT のインテリジェントブロックには最初から Bluetooth が内蔵されています。 一方、PC に Bluetoothデバイスが内臓されていない場合は、写真のような Bluetoothデバイスを PC に取り付けることで、NXT と通信することができるようになります。

まずは、これらのデバイスを取り付けて、必要ならデバイスドライバをインストールするなどして使えるようにします。 Windows-XP などではたいていの市販の Bluetooth デバイスなら、ドライバを改めてインストールすることなくデフォルトのドライバで動作するようです。

BluetoothDevices.png

Bluetooth が適切にインストールされていれば、コントロールパネルに Bluetooth のアイコンが現れます。 これをクリックすると図のような Bluetooth設定ダイアログが現れます。

BthDialogOption.png

「オプション」を開き、
  • 「発見機能を有効にする」
  • 「Bluetooth アイコンを通知領域に表示する」 をチェックします。

次は、NXT と PC を接続するので、このダイアログはとりあえずそのままにしておきます。

PC と NXT の接続

PC と NXT を Bluetoothで接続する手順はおおよそ以下のとおりです。

  1. NXT の電源を入れる
  2. NXT を Bluetooth 検索モードにし検索する
  3. 自分の PC を選択
  4. チャネル選択
  5. PC の Bluetooth 設定ダイアログから接続ウィザードを起動
  6. パスキーを設定する
  7. PC と NXT 両者で接続ボタンを押す
  8. 接続後 NXT を再起動する

NXT の起動

NXT のインテリジェントブロックの中央のオレンジ色のボタンを押して電源を入れます。 電源が入ると「ピロリロリ♪」と音が鳴ってブロックが起動します。(音量の設定を0にしている場合には音は出ない。) 電源を入れると、図のような画面「My files」モードになります。

NXTBoot.png

このような画面にならない場合は、オレンジ色のボタンのしたの四角いボタンを何度か押すことで、「My files」モードにすることができます。

Bluetooth デバイスの検索

「My files」モードの状態で、オレンジ色ボタンの左右にある、灰色の三角ボタンを押し「Bluetooth」モードにカーソルを合わせ、オレンジボタンを押します。

NXTBluetooth.png

さらに、左右の三角ボタンを押し、「Search」モードにカーソルを合わせます。

NXTBthSearch.png

この状態で、オレンジ色のボタンを押し、実際に接続先を検索します。 検索状態の画面を下に示します。

NXTBthSearching.png

デバイスの接続

PC の Bluetooth が有効で NXT から PC が見えれば、図のように PC の名前が表れるはずです。 ここで PC の名前とは Windows における「コンピューター名」です。

NXTBthPCfound.png

近くに Bluetooth搭載 PC があれば、何台かの PC が見えるかもしれません。 三角ボタンを押して自分の PC にカーソルを合わせ、オレンジ色の確認ボタンを押します。

次に Bluetooth のチャネル選択画面になるので、そのままオレンジ色の確認ボタンを押します。 Connecting と表示された後、パスキー入力画面になるので、そのままオレンジボタンを押します。

NXTBthPasskey.png

PC側で図のようなバルーンが表示されるのでこのバルーンをクリックします。

PCballoon.png

パスキーの入力を求められるので、先ほど NXT に表示されていたパスキーを入力します。 接続が完了すると、図のようなダイアログが現れます。 他のデバイスを認識しないように「発見機能を無効にする」をチェックし「完了」を押して終了します。

PCConnectComp.png

接続の確認

Bluetooth設定ダイアログの「デバイス」タブで見ると、図のように NXT が接続されていることが確認できます。

PCDevlistNXT.png

NXT Python のインストール

NXT Python のインストール

NXT RTC を作成する前に、Python から Bluetooth を使用するためのモジュール PyBlues と、NXT を制御するためのモジュール NXT Python をインストールします。

PyBlues のインストール

上記リンクからWindows用 のインストーラをダウンロードします。 ダウンロードした実行ファイルを実行すればインストールは完了です。

NXT Python のインストール

上記リンクから zip ファイルをダウンロードします。 NXT Python は setup.py スクリプトを使用してインストールします。
  • .pyに関連付けがされている場合
     setup.py install
  • 関連づけされていない場合:
     c:\Python24\python setup.py install

 Microsoft Windows XP [Version 5.1.2600]
 (C) Copyright 1985-2001 Microsoft Corp.
 
 C:\tmp\nxt_python-0.7>setup.py install
 running install
 running build
 running build_py
 ...
 copying build\scripts-2.4\nxt_filer -> c:\python24\Scripts
 copying build\scripts-2.4\nxt_push -> c:\python24\Scripts
 copying build\scripts-2.4\nxt_test -> c:\python24\Scripts
 
 C:\tmp\nxt_python-0.7>

NXT Python のテスト

NXT を ON にしてPCに接続されている状態で、example 以下のサンプルを実行し、NXT Python から NXT が制御できるかどうか確認します。

example ディレクトリーには以下のサンプルがあります。

  • latency.py: センサー読出しのレイテンシを計測する。
  • mary.py: NXT のサウンド機能で「メリーさんの羊」を演奏する
  • message_test.py: インテリジェントブロックの液晶画面にメッセージを表示する
  • spin.py: PortB, PortC のモーターを動かしスピンさせる
  • test_sensors.py: 全センサーの値を表示する

これらのテストを実行する際には、それぞれのサンプルが使用するモーター、センサーなどが接続された状態で実行しなければなりません。

NXT Python RTC 化

以上で、PC から Python を使用して NXT を制御する準備が整いました。

RtcTemplate などを使用して、NXT のコンポーネントの雛形を生成してコーディングに取り掛かりたいところです。 NXT Python はきれいにまとまっている Python モジュールですが、NXT Python のコードを直接 RTC の雛形に書き込むのはちょっと待ってください。

サンプルを見てもわかるように、NXT Python はロケータ、モーター、センサー等の複数のモジュールから構成されていて、モーターやセンサーへの細かな機能も制御できるため、アクセス方法も若干煩雑です。

ここは、NXT Python の複数のモジュールをまとめてひとつのインターフェースからアクセスするためのクラスを作ることにします。 これはソフトウエアパターンで言うところの Façade パターンになります。

Facade パターンあるいは Facade パターン(ファサード・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義された、コンピュータソフトウェアのデザインパターンの1つです。 Facade とは「窓口」を意味します。関連するクラス群を使用するための手続きを、窓口となる一つのクラスに集約することにより、プログラムの冗長性を無くすことを目的とします。(出典: フリー百科事典『ウィキペディア(Wikipedia)』Facade パターン)

こうした便利クラスを作成すると以下の利点があります。

  • 作成したクラスを RTC 以外にも利用できる
  • デバッグが楽になる
    • Façade クラス単体でデバッグした方がデバッグしやすい
    • 直接 RTC 化すると、この例で言えば、NXT Python の使い方の問題なのか、RTC の使い方の問題なのか切り分けが難しい
  • 変更がしやすくなる
    • Façade クラスを変更しても、インターフェースを変更しなければ RTC側のコードは変える必要がない
    • たとえば、get_xxx()、set_xxx()といった関数の入出力にリミッタをつけたい場合、Façade クラス側のみの変更で済む
  • NXT 以外のデバイスへの変更が容易になる
    • たとえば、NXT の次のバージョンのブロックが出た場合、インターフェースさえ同じであれば、対応が容易になる。

これは、RTC を作成する際全般に言えることです。 対象とするロボットやデバイスなどを、うまくクラス化することで、デバイスの変更や、RTC 自体の変更・バージョンアップにも柔軟に対応でき、変更に強いソフトウエアを作ることができます。

決して、onExecute() などで

ioctl(xxxx, xxxx); // UNIX デバイスへの直アクセス inb(xxx); // I/O への直アクセス outb(xxx); // I/O への直アクセス

のようなプリミティブな関数呼び出しをしてはいけません。 理想的には、

 onExecute(ec_id)
 { // 擬似コード
      if (m_inport.isNew())
     {
        retval = m_myrobot.set_actuator(m_inport.read());
        if (retval == fatal_error) return RTC::RTC_ERROR;
     }
     if (myrobot.get_sensor(sensor_data) == true)
     {
         m_outport.wrtie(sensor_data);
     } 
     else
     {// リードエラーの場合の処理
         if (fatal_error) return RTC::RTC_ERROR;
     }
     return RTC::RTC_OK;
 }

のように、「InPortから値を読んで処理」する関数の呼び出しと、「何らかの処理により値を取得し OutPort から出力」する関数の呼び出しのみからなる、「抽象レベルの高い」コードにするべきです。

NXT Python Facade クラス

NXT Python Facade クラス

NXT Python 自体は NXTインテリジェントブロックのほとんどすべての機能を使用することができますが、それらの機能すべてを直接使用するのは煩雑になるため、利用したい機能のみをインターフェースする Facade クラスを作ります。

NXT の主な機能を列挙します。

  • 入力
    • モーター速度値を与える
    • サウンドの音程値を与える
    • 液晶表示に出力するメッセージを与える
  • 出力
    • モーターエンコーダ値を読む
    • センサー値(サウンド、超音波、タッチ、光)の値を読む
    • システム情報を読む
  • その他
    • NXT の検索
    • NXT への接続
    • NXT のファイルの読み書き
    • ファームウエアの読み書き

これらすべてを一度にサポートするクラスを作るのでは Façade クラスを作る意味がなくなってしまいます。 必要な機能が出てきたらそのつど Façade クラスを更新すればよいのです。 したがって、ここで作成する Façade クラスでは、今回作成するロボットに合わせて、以下の機能のみをサポートすることにします。

  • 入力
    • モーター速度値を与える: setMotors()
  • 出力
    • モーターエンコーダ値を読む: getMotors()
    • センサー値 (サウンド、超音波、タッチ、光) の値を読む: getSensors()

関数名はおおよそのイメージです。 この考え方で作成したクラス (NXTBrick.py) を以下に示します。

 #!/usr/bin/env python
 # @file NXTBrick.py
 # -*- coding:shift_jis -*-
 import nxt.locator
 from nxt.sensor import *
 from nxt.motor import *
 class NXTBrick:
     def __init__(self, bsock=None):
         コンストラクタ
         NXT ブロックへのコネクションを行い、モーターや、センサーオブジェクトを
         作成する。モーターのエンコーダのリセットを行う。
         """
         if bsock:
             self.sock = bsock
         else:
             self.sock = nxt.locator.find_one_brick().connect()
         self.motors = [Motor(self.sock, PORT_A),
                        Motor(self.sock, PORT_B),
                        Motor(self.sock, PORT_C)]
             
         self.sensors = [TouchSensor(self.sock, PORT_1),
                         SoundSensor(self.sock, PORT_2),
                         LightSensor(self.sock, PORT_3),
                         UltrasonicSensor(self.sock, PORT_4)]
         self.resetPosition()
 
     def close(self):
         """
         NXT ブロックとの接続を終了する
         """
         self.sock.close()
 
     def resetPosition(self, relative = 0):
         """
         NXT のモーターのエンコーダをリセットする
         """
         for m in self.motors:
             m.reset_position(relative)
 
     def setMotors(self, vels):
         """
         配列を受け取り、モーターのパワーとしてセットする。
         vels の数とモーターの数が一致しない場合、両者の要素数のうち小さい方でループを回す。
         """
         for i, v in enumerate(vels[:min(len(vels),len(self.motors))]):
             self.motors[i].power = max(min(v,127),-127)
             self.motors[i].mode = MODE_MOTOR_ON | MODE_REGULATED
             self.motors[i].regulation_mode = REGULATION_MOTOR_SYNC
             self.motors[i].run_state = RUN_STATE_RUNNING
             self.motors[i].tacho_limit = 0
             self.motors[i].set_output_state()
 
     def getMotors(self):
         """
         モーターの位置(角度)を取得する
         
         """
         state = []
         for m in self.motors:
             state.append(m.get_output_state())
         return state
 
     def getSensors(self):
         """
         センサの値を取得する。得られたデータは配列で返される。
         """
         state = []
         for s in self.sensors:
             state.append(s.get_sample())
         return state
 
 
 """
 テストプログラム
 モーターに適当な出力を与え、角度を読み表示する。
 センサから値を読み込み表示する。
 """
 if __name__ == "__main__":
     import time
     nxt = NXTBrick()
     print "connected"
     
     # モーターのテスト
     for i in range(100):
         nxt.setMotors([80,-80,80])
         print "Motor: "
         mstat = nxt.getMotors()
         for i, m in enumerate(mstat):
             print "(" , i, "): ", m
         time.sleep(0.1)
     nxt.setMotors([0,0,0])
 
     # センサーのテスト
     for i in range(100):
         sensors = ["Touch", "Sound", "Light", "USonic"]
         sval = nxt.getSensors()
         for s in sensors:
             print s + ": " + sval
             print ""
             time.speel(0.1)

最後の if name == "main":から始まる部分はテストプログラムです。このモジュールを単体で動かしたときに実行されます。 まずは、このモジュールが完全に動くまでテストをすることが重要です。

以上で、NXT の Façade クラスができました。 非常に簡単なクラスですが、モーターに速度を与え、ポジションを読み、センサーの値を読むことのできるクラスができました。 はじめから何でもできるようにしようとすると、結局何をするためのクラスなのかよくわからないクラスができてしまいます。 バージョンアップはいつでもできるので、まずは最低限の機能でもちゃんと動くクラスを作ることが重要です。

NXT RTC の実装

NXT RTC の実装

では、上で実装した NXTBrick クラスを組み込むための RTC を作成します。 作成する RTC の仕様は以下のとおりです。

  • InPort
    • モーター速度 (TimedFloatSeq)
  • Outport
    • モーター位置 (TimedFloatSeq)
    • センサーデータ (TimedFloatSeq)

NXTRTC の雛形を生成

NXTRTC の雛形を生成

RtcTemplateで雛形を作成します。 雛型を作成する方法には、コマンドライン版の rtc-template を使う方法と、Eclipse版の RtcTemplate を使う方法があります。

rtc-template を実行するために、以下のようなバッチファイルを作成します。

 python "C:\Program Files\OpenRTM-aist\0.4\utils\rtc-template\rtc-template.py" -bpython^
  --module-name=NXTRTC --module-desc="NXT sample component"^
  --module-version=0.1 --module-vendor=AIST --module-category=example^
  --module-comp-type=DataFlowComponent --module-act-type=SPORADIC^
  --module-max-inst=10^
  --inport=vel:TimedFloatSeq^
  --outport=pos:TimedFloatSeq --outport=sens:TimedFloatSeq^
  --config="map:string:A,B"

Eclipse版 RtcTemplate では下記のようになります。
  • Programing language selection: Python
  • Module definition
    • Module name: NXTRTC
    • Module decription: NXT sample component
    • Module version: 0.1
    • Module vender: AIST
    • Module category: example
    • Component type: DataFlowComponent
    • Component's activity type: SPORADIC
    • Number of maximum instance: 10
  • InPort definition
    • Ports: Name:vel Type:TimedFloatSeq
  • OutPort definition
    • Ports: Name:pos, Type:TimedFloatSeq
    • Ports: Name:sens, Type:TimedFloatSeq
  • ConfigurationSet definition
    • Cfg Sets: Name:map, Type:string, Default Value: A,B

rtc-template(gen.bat)の実行

 > gen.bat
  python "C:\Program Files\OpenRTM-aist\0.4\utils\rtc-template\rtc-template.py"
  -bpython --module-name=NXTRTC --module-desc="NXT sample component" 
  --module-version=0.1 --module-vendor=AIST --module-category=example 
  --module-comp-type=DataFlowComponent --module-act-type=SPORADIC 
  --module-max-inst=10 --inport=vel:TimedFloatSeq 
  --outport=pos:TimedFloatSeq --outport=sens:TimedFloatSeq
  --config="map:string:A,B"
 
   File "NXTRTC.py" was generated.
   File "README.NXTRTC" was generated.
   File "NXTRTC.yaml" was generated.

以上のように、NXTRTC.py などのひな型ファイルができました。

サンプルコードの説明

サンプルコードの説明

ここから、NXTRTC.py に先ほど作成した NXTBrick.py の機能を組み込んでいきます。

  • NXTBrick.py のインポート NXTBrick クラスを使用するために NXBrick.py をインポートします。 import の記述方法は、下記のように拡張子(.py)を除いたファイル名を指定します。
     import NXTBrick
  • onInitialize(self) の実装 onInitialize()で、NXTBrick クラスのインスタンス化を行います。 NXTBrick クラスのインスタンス化過程にてエラーが発生した場合、RTC_ERROR が戻り 終了状態へ遷移します。
            # create NXTBrick object
            try:
                self._nxtbrick = NXTBrick.NXTBrick()
            except:
                print "NXTBrick create failed."""
                return RTC.RTC_ERROR
  • onActivated(self, ec_id),onDeactivated(self, ec_id) の実装 onActivated(),onDeactivated()では、NXTBrickクラスのresetPosition() メソッドをコールします。
            self._nxtbrick.resetPosition() 
  • onExecute(self, ec_id) の実装 onExecute() では、下記の処理を行ってます。
    • データ InPort からの速度の読み込み。
    • Configuration 機能を用い NXT のどのポートへ速度出力を行うかを決定。
    • NXTBrick の setMotors() メソッドにてモーターへ速度を出力。
    • NXTBrick の getSensors() にて NTX の超音波センサー値を取得し、データがあれば OutPort から出力。
    • NXTBrick の getMotors() にて NTX の回転角度[deg]を取得し、データがあれば OutPort から出力。

上記をまとめたサンプルコードを以下に示す。

 #!/usr/bin/env python
 # -*- coding:shift_jis -*-
 # -*- Python -*-
 
 import sys
 import time
 sys.path.append(".")
 
 # Import RTM module
 import OpenRTM
 import RTC
  
 # import NXTBrick class
 import NXTBrick
  
 # This module's spesification
 # <rtc-template block="module_spec">
 nxtrtc_spec = ["implementation_id", "NXTRTC", 
          "type_name",         "NXTRTC", 
          "description",       "NXT sample component", 
          "version",           "0.1", 
          "vendor",            "AIST", 
          "category",          "example", 
          "activity_type",     "DataFlowComponent", 
          "max_instance",      "10", 
          "language",          "Python", 
          "lang_type",         "SCRIPT",
          "conf.default.map", "A,B",
          ""]
 
 # </rtc-template>
 
 class NXTRTC(OpenRTM.DataFlowComponentBase):
     def __init__(self, manager):
         OpenRTM.DataFlowComponentBase.__init__(self, manager)
 
         # DataPorts initialization
         # <rtc-template block="data_ports">
         self._d_vel = RTC.TimedFloatSeq(RTC.Time(0,0),[])
         self._velIn = OpenRTM.InPort("vel", self._d_vel, OpenRTM.RingBuffer(8))
         self.registerInPort("vel",self._velIn)
         self._d_pos = RTC.TimedFloatSeq(RTC.Time(0,0),[])
         self._posOut = OpenRTM.OutPort("pos", self._d_pos, OpenRTM.RingBuffer(8))
         self.registerOutPort("pos",self._posOut)
         self._d_sens = RTC.TimedFloatSeq(RTC.Time(0,0),[])
         self._sensOut = OpenRTM.OutPort("sens", self._d_sens, OpenRTM.RingBuffer(8))
         self.registerOutPort("sens",self._sensOut)
 
         # initialize of configuration-data.
         # <rtc-template block="configurations">
         self._map = [['A', 'B']]
         self._nxtbrick = None
         self._mapping = {'A':0,'B':1,'C':2}
          
     def onInitialize(self):
         # Bind variables and configuration variable
         # <rtc-template block="bind_config">
         self.bindParameter("map", self._map, "A,B")
 
         # create NXTBrick object
         try:
             print "Connecting to NXT brick ...."
             self._nxtbrick = NXTBrick.NXTBrick()
             print "Connection established."
         except:
             print "NXTBrick connection failed."
             return RTC.RTC_ERROR
 
         return RTC.RTC_OK
 
     def onFinalize(self):
         self._nxtbrick.close()
 
     def onActivated(self, ec_id):
         # reset NXTBrick's position.
         self._nxtbrick.resetPosition()
 
         return RTC.RTC_OK
 
     def onDeactivated(self, ec_id):
         # reset NXTBrick's position.
         self._nxtbrick.resetPosition()
 
         return RTC.RTC_OK
 
     def onExecute(self, ec_id):
         cnt = 0
         # check new data.
         if self._velIn.isNew():
             # read velocity data from inport.
             self._d_vel = self._velIn.read()
             vel_ = [0,0,0]
             vel_[self._mapping[self._map[0][0]]] = self._d_vel.data[0]
             vel_[self._mapping[self._map[0][1]]] = self._d_vel.data[1]
             # set velocity
             self._nxtbrick.setMotors(vel_)
 
         # get sensor data.
         sensor_   = self._nxtbrick.getSensors()
         if sensor_:
             self._d_sens.data = sensor_
             self._sensOut.write()
 
         # get position data.
         position_ = self._nxtbrick.getMotors()
         if position_:
             self._d_pos.data =                  [position_[self._mapping[self._map[0][0]]][9],                       position_[self._mapping[self._map[0][1]]][9]]
         # write position data to outport.
         self._posOut.write()
 
         return RTC.RTC_OK
 
   def MyModuleInit(manager):
     profile = OpenRTM.Properties(defaults_str=nxtrtc_spec)
     manager.registerFactory(profile,
                             NXTRTC,
                             OpenRTM.Delete)
 
     # Create a component
     comp = manager.createComponent("NXTRTC")
 
  def main():
     mgr = OpenRTM.Manager.init(len(sys.argv), sys.argv)
     #mgr = OpenRTM.Manager.init(sys.argv)
     mgr.setModuleInitProc(MyModuleInit)
     mgr.activateManager()
     mgr.runManager()
 
 if __name__ == "__main__":
     main()

サンプルコードの説明(コールバックオブジェクトの使用例)

このサンプルでは上記のサンプルにコールバック OnWrite を追加し、InPort のバッファへデータが書き込まれた際にモーターへの速度出力を行うように拡張します。

  • コールバッククラス
    下記のようにコールバッククラスを記述します。

 # @class CallBackClass
 # @brief callback class
 #
 # when data is written in the buffer of InPort,
 # it is called.
 class CallBackClass:
     def __init__(self, nxtbrick_, map_):
         self._nxtbrick = nxtbrick_
         self._map = map_
         self._mapping = {'A':0,'B':1,'C':2}
 
     def __call__(self, pData):
         vel_ = [0,0,0]
         vel_[self._mapping[self._map[0][0]]] = pData.data[0]
         vel_[self._mapping[self._map[0][1]]] = pData.data[1]
         # set velocity
         self._nxtbrick.setMotors(vel_)

このクラスでは、NTXBrick クラスのオブジェクトとコンフィギュレーション変数 map を引数にとります。

  • コールバッククラスの登録
    下記のように、setOnWrite() メソッドにてコールバックオブジェクトを登録します。

   # set callback class
   self._velIn.setOnWrite(CallBackClass(self._ntxbrick,self._map))

setOnWrite() により、InPort へデータが書き込まれた際に、CallBackClassのcall() メソッドが呼ばれるようになります。

上記をまとめたサンプルコードを以下に示す。

 #!/usr/bin/env python
 # -*- coding:shift_jis -*-
 # -*- Python -*-
 
 import sys
 import time
 sys.path.append(".")
 
 # Import RTM module
 import OpenRTM
 import RTC
  
 # import NXTBrick class
 import NXTBrick
 
  # This module's spesification
 # <rtc-template block="module_spec">
 nxtrtc_spec = ["implementation_id", "NXTRTC", 
          "type_name",         "NXTRTC", 
          "description",       "NXT sample component", 
          "version",           "0.1", 
          "vendor",            "AIST", 
          "category",          "example", 
          "activity_type",     "DataFlowComponent", 
          "max_instance",      "10", 
          "language",          "Python", 
          "lang_type",         "SCRIPT",
          "conf.default.map", "A,B",
          ""]
 
 # </rtc-template>
 
 # @class CallBackClass
 # @brief callback class
 #
 # when data is written in the buffer of InPort,
 # it is called.
 class CallBackClass:
     def __init__(self, nxtbrick_, map_):
         self._nxtbrick = nxtbrick_
         self._map = map_
         self._mapping = {'A':0,'B':1,'C':2}
 
     def __call__(self, pData):
         vel_ = [0,0,0]
         vel_[self._mapping[self._map[0][0]]] = pData.data[0]
         vel_[self._mapping[self._map[0][1]]] = pData.data[1]
         # set velocity
         self._nxtbrick.setMotors(vel_)
 
  class NXTRTC(OpenRTM.DataFlowComponentBase):
     def __init__(self, manager):
         OpenRTM.DataFlowComponentBase.__init__(self, manager)
 
         # DataPorts initialization
         # <rtc-template block="data_ports">
         self._d_vel = RTC.TimedFloatSeq(RTC.Time(0,0),[])
         self._velIn = OpenRTM.InPort("vel", self._d_vel, OpenRTM.RingBuffer(8))
         self.registerInPort("vel",self._velIn)
         self._d_pos = RTC.TimedFloatSeq(RTC.Time(0,0),[])
         self._posOut = OpenRTM.OutPort("pos", self._d_pos, OpenRTM.RingBuffer(8))
         self.registerOutPort("pos",self._posOut)
         self._d_sens = RTC.TimedFloatSeq(RTC.Time(0,0),[])
         self._sensOut = OpenRTM.OutPort("sens", self._d_sens, OpenRTM.RingBuffer(8))
         self.registerOutPort("sens",self._sensOut)
 
         # initialize of configuration-data.
         # <rtc-template block="configurations">
         self._map = [['A', 'B']]
         self._nxtbrick = None
         self._mapping = {'A':0,'B':1,'C':2}
          
     def onInitialize(self):
         # Bind variables and configuration variable
         # <rtc-template block="bind_config">
         self.bindParameter("map", self._map, "A,B")
 
         # create NXTBrick object
         try:
             print "Connecting to NXT brick ...."
             self._nxtbrick = NXTBrick.NXTBrick()
             print "Connection established."
         except:
             print "NXTBrick connection failed."
             return RTC.RTC_ERROR
 
         # set callback class
         self._velIn.setOnWrite(CallBackClass(self._ntxbrick,self._map))
 
         return RTC.RTC_OK
 
     def onFinalize(self):
         self._nxtbrick.close()
 
     def onActivated(self, ec_id):
         # reset NXTBrick's position.
         self._nxtbrick.resetPosition()
 
         return RTC.RTC_OK
  
     def onDeactivated(self, ec_id):
         # reset NXTBrick's position.
         self._nxtbrick.resetPosition()
 
         return RTC.RTC_OK
  
     def onExecute(self, ec_id):
         # get sensor data.
         sensor_   = self._nxtbrick.getSensors()
         if sensor_:
             self._d_sens.data = [sensor_[3]]
             # write sensor data to outport.
             self._sensOut.write()
 
         # get position data.
         position_ = self._nxtbrick.getMotors()
         if position_:
             self._d_pos.data = [position_[self._mapping[self._map[0][0]]][9],position_[self._mapping[self._map[0][1]]][9]]
             # write position data to outport.
             self._posOut.write()
 
         return RTC.RTC_OK
  
 def MyModuleInit(manager):
     profile = OpenRTM.Properties(defaults_str=nxtrtc_spec)
     manager.registerFactory(profile,
                             NXTRTC,
                             OpenRTM.Delete)
 
     # Create a component
     comp = manager.createComponent("NXTRTC")
  
 def main():
     mgr = OpenRTM.Manager.init(len(sys.argv), sys.argv)
     #mgr = OpenRTM.Manager.init(sys.argv)
     mgr.setModuleInitProc(MyModuleInit)
     mgr.activateManager()
     mgr.runManager()
 
 if __name__ == "__main__":
     main()

はじめの例では、モーターへの出力、センサー値の読み込み、モーターエンコーダの読み込みをすべて同一ループで同期的に行っていましたが、コールバックを使用することで、データが来たらすぐにモーターへ値を出力できるようになります。

NXT RTC 動作確認

NXT RTC 動作確認

では、作成した NXTRTC を実際に動作させてみます。

ネームサーバーの起動

ネームサーバーを起動します。 Windows用の OpenRTM-aist(C++版) をインストールしている場合は、スタートメニューの「OpenRTM-aist」>「C++」>「tools」>「Start Naming Service」から起動できます。

rtc.conf の作成

rtc.conf を作成します。

 corba.nameservers: localhost
 naming.formats: %n.rtc
 manager.shutdown_auto: NO

corna.nameservers の項目は使用したいネームサーバーのアドレスに合わせて設定します。 ここでは、ローカルのネームサーバーを使用しますので、localhostとします。

起動したいコンポーネントが入っているそれぞれのディレクトリーにコピーしておきます。 今回作成した NXTRTC のほかに
  • TkJoystickComp
  • TkMotorPosComp
  • TkSliderMonitorComp を起動することにします。

RTSystemEditor の起動

RTSystemEditor を起動します。 RTSystemEditor を起動後、ネームサーバー(ここでは localhost)に接続します。 また、SystemDiagram エディタを開くために、ツールバーの SystemDiagram アイコンをクリックします。

コンポーネントの起動

以下のコンポーネントを起動します。
  • NXTRTC
  • TkJoystickComp
  • TkMotorPosComp
  • TkSliderMonitorComp 起動すると、RTSystemEditorのNameServcie ビュー上にコンポーネントが表示されます。

TkJoystickComp

TkJoyStickComp は GUI上でジョイスティックを模擬するためのコンポーネントです。 図に示すような GUI があらわれ、中央の丸をドラッグすることでジョイスティックのように、X-Yの値を OutPort(上) から出力するコンポーネントです。 TkJoyStickComp にはもうひとつ OutPort(下) が付いていて、こちらは作動駆動型移動ロボットを操作するのに便利な左右の車輪の速度を出力するためのデータポートです。 NXTRTC の入力ポートに直接接続すれば、Tribot を制御することができます。

TkJoystick.png

TkMotorComp

TkMotorComp は GUI上で車輪に見立てた丸いアイコンが、InPort へ入力されたデータ(角度)に従って回転する、車輪の回転の様子をモニタリングするためのコンポーネントです。 NXTRTC の車輪の角度を出力する OutPort に接続することで、NXT の車輪の回転の様子を見ることができます。 NXT の車輪を手で回しても、その様子をモニタすることができます。

TkMotor.png

TkSliderMonitorComp

TkSliderMonitorComp は、InPort へ入力されたデータの値をGUIのスライダで表示するためのコンポーネントです。 NXTRTC のセンサー出力ポートに接続することで、NXT のセンサーデータのモニタリングをすることができます。

コンポーネントの接続

すべてのコンポーネントが起動したら、コンポーネントを接続します。 NameService ビューから SystemDiagram エディタへ各コンポーネントをドラッグアンドドロップします。 接続したいポートからポートへドラッグアンドドロップすることでポートを接続することができます。 下に、幾つかのコンポーネントを接続した例を示します。

NXTに超音波センサーが取り付けられていない場合、NXTRTC の Activate 後にエラー状態(赤色)になります。

RtcLink.png

これらのコンポーネントを使って、NXTRTC が正しく動いているかどうかを確認しましょう。