Case study

Tutorial (EV3)

このページでは RTM講習会での EV3 操作手順を説明します。
実習では以下の Educator Vehicle 改を制御します。


/jp/node/6038

LEGO Mindstorms EV3 は LEGO の Mindstorms シリーズの新しいパッケージです。EV3 のメインのコントローラーは、Linux が標準搭載され、様々な言語でロボットの開発が可能になりました。

仕様

LEGO Mindstorms EV3 仕様
プロセッサ ARM9 300MHz
メモリ(ROM) 16MB Flash
メモリ(RAM) 64MB RAM
OS Linuxベース
ディスプレイ 178 x 128 pixels
出力ポート 4個
入力ポート 4個
アナログ
デジタル 460.8kbit/s
USB通信速度 High Speed (480Mbps)
USBインターフェース EV3同士の連結可能 (最大4台)
Wi-Fi通信ドングル利用可能
SDカードスロット Micro SDカード 32GBまでサポート
スマートデバイス接続 iOS、Android、Windows
ユーザーインターフェース 6ボタン, イルミネーション機能
プログラムサイズ (ライントレースの場合) 0.950KB
センサー通信性能 1000 回/秒、1ms
データロギング 最大 1,000サンプリング/秒
Bluetooth通信 最大7台のスレーブと接続可能
動力 リチャージブルバッテリー または、単3電池 6本

デバイス

EV3 には以下のデバイスが付属しています。

ジャイロセンサー
45505_GyroSensor.jpg
確度モード: 精度 +/- 3°
角速度モード: 最大 440 deg/sec
サンプリングレート 1,000 Hz
カラーセンサー
45506_color.jpg
計測: 赤色光の反射光、 周囲の明るさ、色
検出カラー数: 8色 (無色、黒、青、緑、黄、赤、白、茶)
サンプリングレート 1,000 Hz
距離 約1mm~18mm(アフレル調査値)
タッチセンサー
45507_TouchSensor.jpg
オン (1), オフ (0)
スイッチ可動域: 約4mm
超音波センサー
45504_UltrasonicSensor.jpg
距離計測可能範囲: 3cmから250cm
距離計測精度: +/- 1 cm
前面電飾: 点灯:超音波発信中、 点滅:超音波観測中
EV3 Lモーター
45502_LargeMotor.jpg
フィードバック: 1°単位
回転数: 160から170RPM
定格トルク: 0.21 N・m (30oz*in)
停動トルク: 0.42 N・m (60oz*in)
重さ: 76 g
EV3 Mモーター
45503_MediumMotor.jpg
フィードバック 1°単位
回転数: 240から250RPM
定格トルク: 0.08 N・m (11oz*in)
停動トルク: 0.12 N・m (17oz*in)
重さ: 36 g

ダウンロード

最初にPC側で使用する RTC 等をダウンロードしてください。

ZIPファイルを Lhaplus 等で展開してください。

EV3 の組み立て方

EV3 は分解した状態で参加者に配ります。 組み立て方は以下の通りです。

※超音波センサー、カラーセンサー、ジャイロセンサーについては、講習で使用しないため取り付ける必要はありません。 以下の※の作業については、時間が余った人が実施してください。

まずは土台部分を取り出してください。


s_DSC00463.JPG

最初にMモーターにケーブル(25cm)を接続します※。


s_DSC00446.JPG

次に EV3 本体を取り付けます。 Mモーターにケーブルを接続した場合は、ケーブルが左側の隙間から出るようにしてください。


s_DSC00448.JPG s_DSC00450.JPG



右側のタッチセンサーを取り付けてください。


s_DSC00451.JPG s_DSC00452.JPG



超音波センサーを取り付けてください※。


s_DSC00454.JPG

ケーブルを接続してください。 必須なのは車輪駆動用のLモーター右、Lモーター左、タッチセンサーだけです。

Lモーター右 ポート C 25cmケーブル
Lモーター左 ポート B 25cmケーブル
Mモーター※ ポートA 25cmケーブル
タッチセンサー右 ポート 3 35cmケーブル
タッチセンサー左 ポート 1 35cmケーブル
超音波センサー※ ポート 4 50cmケーブル
ジャイロセンサー※ ポート 2 25cmケーブル

ケーブルは EV3 の上下に A~D と 1~4 のポートがあるのでそこにケーブルを接続します。


s_DSC00.JPG s_DSC00471.JPG




s_DSC00455.JPG s_DSC00456.JPG



左右にパーツを取り付けます※。 Lモーター右、Lモーター左、Mモーター、タッチセンサー右、タッチセンサー左のケーブルを挟むようにして取り付けてください※。
Lモーター右、タッチセンサー右のケーブルは右側から、Lモーター左、Mモーター、タッチセンサー左は左側から通してください※。


s_DSC00457.JPG


s_DSC00459.JPG

これでとりあえず完成ですが、余裕のある人はジャイロセンサーを取り付けてみてください※。


s_DSC00460.JPG s_DSC00461.JPG



電源の入れ方/切り方

電源の入れ方

中央のボタンを押せば電源が投入されます。


ev3_on.jpg

電源の切り方

EV3 の電源を切る場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Power Off」を選択してください。


ev3_off.jpg


s_DSC01033.JPG

再起動

再起動する場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Reboot」を選択してください。

リセット

ev3dev の起動が途中で停止する場合には、中央ボタン、戻るボタン(左上)、左ボタンを同時押ししてください。画面が消えたら戻るボタンを離すと再起動します。


ev3_reset.jpg

EV3 への接続

EV3 へは原則として無線LANで接続するようにしてください

無線LANアクセスポイントへの接続

まずは EV3 の中央のスイッチを押して電源を投入してください。
ここで電源を投入する前に無線LANアダプタを取り付けておいてください。

以下の作業でスクリプトを実行するとアクセスポイントが起動します。

EV3 の操作画面から「File Browser」を上下ボタンで選択して中央のボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
 [File Browser               > ]
  Device Browser             >
  Wireless and Networks      > 
  Battery                    >
  Open Roberta Lab           >
  About                      >
 ------------------------------

次に scripts を選択して中央ボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot
 ------------------------------
 [scripts                     ]
 ・・
 ・・
 ------------------------------

次の画面から start_ap.sh を選択して中央ボタンを押すとスクリプトが起動します。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot/scripts
 ------------------------------
 ../
 Component/
 ・・
 [start_ap.sh                 ]
 ------------------------------

しばらくすると無線LANアクセスポイントが起動するので、指定の SSID のアクセスポイントに接続してください。 SSID、パスワードは EV3 に貼り付けたテープに記載してあります。


tutorial_ev3_irex26.png

アクセスポイントへの接続方法は以下のページを参考にしてください。

まず右下のネットワークアイコンをクリックしてください。


/ja/node/6042

次に一覧から ev3_***を選択してください。


tu_ev3_11.png

パスワードを入力してください。


/ja/node/6042

USBケーブルでの接続

以下の作業は有線で接続する場合の作業なので、無線で接続する場合は不要です。

付属の USBケーブルで EV3 と PC を接続してください。


s_DSC00467.JPG

中央のボタンを押して電源を投入してください。

ここで EV3 の電源を投入する前に無線LANアダプタは取り外しておいてください。
以下の画面が表示されていれば ev3dev の起動に成功していますが、起動途中で停止した場合は この手順 で再起動してください。


s_DSC00470.JPG

デバイスマネージャで「EV3+ev3dev」が「その他のデバイス」の下にある場合は正しく機能していないので、以下の手順でデバイスソフトウェアの更新を行ってください。

まずはコントロールパネルからデバイスマネージャを開いてください。


tu_ev3_17.png


tu_ev3_18.png

開いたら EV3+ev3dev を右クリックして「ドライバソフトウェアの更新」を選択してください。
ここで EV3+ev3dev がネットワークアダプターの下にある場合は正常に認識されているので以下の作業は不要です。


tu_ev3_1.png

「コンピューターを参照してドライバー ソフトウェアを検索します」を選択してください。


tu_ev3_3.png

「コンピューター上のデバイス ドライバーの一覧から選択します」を選択してください。


tu_ev3_4.png

ネットワーク アダプターを選択して次へをクリックしてください。


tu_ev3_5.png

製造元は「Microsoft Corporation」、ネットワークアダプタは「Remote RNDIS Compatible Device」を選択して次へをクリックしてください。


tu_ev3_9.png

ドライバー更新警告が出た場合は「はい」を選択してください。


tu_ev3_7.png

正常に更新されたら閉じるを選択してください。


tu_ev3_8.png

事前準備

このページ の手順に従ってネームサーバー、RTシステムエディタを起動してください。 予めネームサーバーを起動してある場合は再起動してください。

またネットワークインターフェースが2つ以上ある場合に通信に失敗する可能性があるため、有線で接続した場合は他のネットワークデバイスを無効にしてからネームサーバーを起動してください。


/ja/node/6042


/ja/node/6042

ネームサーバー追加

続いて RTシステムエディタのネームサーバー追加ボタンで192.168.0.1(無線LANで接続する場合は192.168.11.1)を追加してください。


/jp/node/6042 tu_ev3_25.png



するとEducatorVehicle0という RTC が見えるようになります。

EducatorVehicle は EV3 の走行速度の入力、センサーのデータの出力等を行うためのコンポーネントです。


 /ja/node/6030

EducatorVehicle
InPort
名前 データ型 説明
velocity2D RTC::TimedVelocity2D 目標速度
angle RTC::TimedDouble Mモーターの角度
lcd RTC::TimedString LCDに表示する画像ファイル名
sound RTC::TimedString 出力する音声
OutPort
名前 データ型 説明
odometry RTC::TimedPose2D 現在の位置・姿勢
ultrasonic RTC::RangeData 超音波センサーで計測した距離
gyro RTC::TimedDouble ジャイロセンサーで計測した角度
color RTC::TimedString カラーセンサーで計測した色
light_reflect RTC::TimedDouble カラーセンサーで計測した反射光の強さ
touch RTC::TimedBoolean タッチセンサーのオンオフ。右側が0番目の要素、左側が1番目の要素
コンフィギュレーションパラメーター
名前 デフォルト値 説明
wheelRadius 0.028 車輪の半径
wheelDistance 0.054 タイヤ間距離の1/2
medium_motor_speed 1.6 Mモーターの速度

TimedVelocity2D型について

TimedVelocity2D型は以下のように定義されています。

     struct Velocity2D
     {
         double vx;
         double vy;
         double va;
     };

     struct TimedVelocity2D
     {
         Time tm;
         Velocity2D data;
     };

vx、vy、va はロボット中心座標系での速度を表しています。


tu_ev3_19.png

vx は X方向の速度、vy は Y方向の速度、va は Z軸周りの角速度です。

Educator Vehicle のように2個の車輪が左右に取り付けられているロボットの場合、横滑りしないと仮定すると vy は0になります。

vx、va を指定することでロボットの操作を行います。

音声の出力

sound による音声の入力には以下のコマンドを利用できます。

  • beep
    beep と入力するとビープ音が鳴ります。
  • tone
    以下のように tone、周波数、時間と入力すると指定周波数の音を指定ミリ秒数だけ鳴らします。

 tone,100,1000

  • それ以外
    それ以外は指定文字列を発音します

LCD に表示する画像について

LCD で表示する画像は付属資料の software/saveBinaryImage/EXE/saveBinaryImage.exeで変換したものを利用してください。
画像ファイルを saveBinaryImage.exe にドラッグ・アンド・ドロップすれば変換できます。

サンプルコンポーネント起動

付属資料のstart_component_ev3.batを起動してください。 ※OpenRTM-aist Python版をインストールしていない場合、もしくはインストールに失敗している場合は個別に実行ファイル入りのUSBメモリーを配布いたしますので、その中のstart_component_ev3_exe.batを利用してください。

すると以下の2つの RTC が起動します。

tu_ev3_24.png

動作確認

まずはジョイスティックで EV3 を操作してみます。

RTシステムエディタで EducatorVehicle、FloatSeqToVelocity、TkJoyStick を以下のように接続します。


tutorial_ev3_16.png

そして RTC をアクティブ化するとジョイスティックで EV3 の操作ができるようになります。


tutorial_ev3_21.png

自作の RTC で制御

まずは FloatSeqToVelocity の out と EducatorVehicle の target_velocity_in のコネクタを切断してください。

tutorial_ev3_17.png

FloatSeqToVelocity と EducatorVehicle の間に自作の RTC を接続して、タッチセンサーがオンになった場合に停止して音を鳴らすようにします。

ひな形コードの作成

RTC ビルダを起動してください。


/jp/node/6038

起動したら新規にプロジェクトを作成します。


/jp/node/6038

プロジェクト名は TestEV3CPP(TestEV3Py)にします。

以下のように設定を行ってください。 C++、もしくは Python で作成します。

基本
モジュール名 TestEV3CPP、もしくはTestEV3Py
アクティビティ
有効アクション onInitialize、onExecute、onActivated、onDeactivated
データポート
InPort
名前 データ型 説明
velocity_in RTC::TimedVelocity2D 入力目標速度
touch RTC::TimedBooleanSeq タッチセンサーのオンオフ
OutPort
名前 データ型 説明
velocity_out RTC::TimedVelocity2D 出力目標速度
sound RTC::TimedString 音声
コンフィギュレーション
名前 説明
sound_output string タッチセンサーが ON の時に発する音声。デフォルト値は beep
言語・環境
言語 C++、もしくはPython

[コード生成] ボタンを押したらコードが生成されます。


tutorial_raspimouse10.png

プロジェクト生成

コードが生成できたら C++ の場合は CMake で Visual Studio のプロジェクト(Ubuntu の場合は Code::Blocks)を生成してください。

まず CMake (cmake-gui) を起動します。

  • Windows 7

/jp/node/6038

  • Windows 8.1


/jp/node/6038

起動したらソースコードのディレクトリー、ビルドを行うディレクトリーに以下を指定します。 括弧内は eclipse の作業ディレクトリーを C:\workspace にした場合の例です。

Where is the source code RTCBuilder で生成したコードのフォルダー(C:\workspace\TestEV3CPP)
Where to build the binaries RTCBuilder で生成したコードのフォルダーの下に作成したbuildフォルダー(C:\workspace\TestEV3CPP\build)


tutorial_ev3_24.png

[Configure] ボタン→ [Generate] ボタンをクリックするとVisual Studioのプロジェクトが生成されます。

ソースコードの編集

build ディレクトリーの TestEV3CPP.sln を開いてください。

次にコードの編集を行います。

Pythonの場合はまず変数の初期化部分を修正してください。

  • TestEV3Py.py

     def __init__(self, manager):
         #self._d_velocity_in = RTC.TimedVelocity2D(*velocity_in_arg)
         self._d_velocity_in = RTC.TimedVelocity2D(RTC.Time(0,0),RTC.Velocity2D(0,0,0))

         #self._d_distance_sensor = RTC.TimedShortSeq(*distance_sensor_arg)
         self._d_distance_sensor = RTC.TimedShortSeq(RTC.Time(0,0),[])

         #self._d_velocity_out = RTC.TimedVelocity2D(*velocity_out_arg)
         self._d_velocity_out = RTC.TimedVelocity2D(RTC.Time(0,0),RTC.Velocity2D(0,0,0))

         #self._d_buzzer = RTC.TimedShort(*buzzer_arg)
         self._d_buzzer = RTC.TimedShort(RTC.Time(0,0),0)

まずは onExecute で入力速度をそのまま出力するコードを書いてみます。
C++の場合は以下のようになります。
isNew関数で新規の入力データが存在するかを確認して、read関数で変数(m_velocity_in)に格納します。 そして m_velocity_out に出力データを格納して write関数を呼び出すとデータが送信されます。

  • src/TestEV3CPP.cpp

     if (m_velocity_inIn.isNew())
     {
         m_velocity_inIn.read();
         //入力速度をそのまま出力
         m_velocity_out.data.vx = m_velocity_in.data.vx;
         m_velocity_out.data.vy = m_velocity_in.data.vy;
         m_velocity_out.data.va = m_velocity_in.data.va;
         setTimestamp(m_velocity_out);
         m_velocity_outOut.write();
 
 
     }

Pythonの場合は以下のようになります。

  • TestEV3Py.py

         if self._velocity_inIn.isNew():
             data = self._velocity_inIn.read()
             #入力速度をそのまま出力する
             self._d_velocity_out.data.vx = data.data.vx
             self._d_velocity_out.data.vy = data.data.vy
             self._d_velocity_out.data.va = data.data.va
             OpenRTM_aist.setTimestamp(self._d_velocity_out)
             self._velocity_outOut.write()

次にタッチセンサーがオンの場合に停止する処理を記述します。
常にタッチセンサーのデータが入力されるとは限らないので、センサーのデータを格納する変数を宣言します。
C++の場合は TestEV3CPP.h に記述します。

  • include/TestEV3CPP/TestEV3CPP.h

  private:
      bool m_last_sensor_data[2];


Pythonの場合はコンストラクタに記述します。

  • TestEV3Py.py

     def __init__(self, manager):
         OpenRTM_aist.DataFlowComponentBase.__init__(self, manager)
 
         self._last_sensor_data = [False, False]

次に onExecute に停止する処理を記述します。
C++の場合は以下のようになっています。
まずインポート touch に isNew 関数で新規にデータが入力されたかを確認して、入力されている場合は read関数で読み込みます。そして変数 m_last_sensor_data に格納します。
そしてインポート velocity_in で受信したデータの vx が 0 以上の場合には前進しているため障害物に接触するかもしれないと判定して、タッチセンサーがオンの場合は停止してブザーを鳴らします。

  • src/TestEV3CPP.cpp

 RTC::ReturnCode_t TestEV3CPP::onExecute(RTC::UniqueId ec_id)
 {
     //データを新規に受信した場合に、データをm_last_sensor_dataを格納する
     if (m_touchIn.isNew())
     {
         m_touchIn.read();
         if (m_touch.data.length() == 2)
         {
             for (int i = 0; i < 2; i++)
             {
                 //タッチセンサがOFFからONになった時に音を鳴らす
                 if (!m_last_sensor_data[i] && m_touch.data[i])
                 {
                     m_sound.data = m_sound_output.c_str();
                     setTimestamp(m_sound);
                     m_soundOut.write();
 
 
                 }
                 m_last_sensor_data[i] = m_touch.data[i];
             }
         }
     }
     if (m_velocity_inIn.isNew())
     {
         m_velocity_inIn.read();
         //vxが0以上(前進)のときのみ停止するか判定する
         if (m_velocity_in.data.vx > 0)
         {
             for (int i = 0; i < 2; i++)
             {
                 //タッチセンサがONの時に停止する
                 if (m_last_sensor_data[i])
                 {
                     //停止する
                     m_velocity_out.data.vx = 0;
                     m_velocity_out.data.vy = 0;
                     m_velocity_out.data.va = 0;
                     setTimestamp(m_velocity_out);
                     m_velocity_outOut.write();
 
 
                     return RTC::RTC_OK;
                 }
             }
         }
         //入力速度をそのまま出力
         m_velocity_out.data.vx = m_velocity_in.data.vx;
         m_velocity_out.data.vy = m_velocity_in.data.vy;
         m_velocity_out.data.va = m_velocity_in.data.va;
         setTimestamp(m_velocity_out);
         m_velocity_outOut.write();

   return RTC::RTC_OK;
 }

Pythonの場合は以下のようになっています。

  • TestEV3Py.py

     def onExecute(self, ec_id):
         #データを新規に受信した場合に、データをm_last_sensor_dataを格納する
         if self._touchIn.isNew():
             data = self._touchIn.read()
             if len(data.data) == 2:
                 for i in range(2):
                     #タッチセンサがOFFからONになった時に音を鳴らす
                     if not self._last_sensor_data[i] and data.data[i]:
                         self._d_sound.data = self._sound_output[0]
                         OpenRTM_aist.setTimestamp(self._d_sound)
                         self._soundOut.write()
                 self._last_sensor_data = data.data[:]
 
         if self._velocity_inIn.isNew():
             data = self._velocity_inIn.read()
             #vxが0以上(前進)のときのみ停止するか判定する
             if data.data.vx > 0:
                 for d in self._last_sensor_data:
                     #タッチセンサーがONの時に停止する
                     if d:
                         #停止する
                         self._d_velocity_out.data.vx = 0
                         self._d_velocity_out.data.vy = 0
                         self._d_velocity_out.data.va = 0
                         OpenRTM_aist.setTimestamp(self._d_velocity_out)
                         self._velocity_outOut.write()
                         
                         return RTC.RTC_OK
 
             #入力速度をそのまま出力する
             self._d_velocity_out.data.vx = data.data.vx
             self._d_velocity_out.data.vy = data.data.vy
             self._d_velocity_out.data.va = data.data.va
             OpenRTM_aist.setTimestamp(self._d_velocity_out)
             self._velocity_outOut.write()
         return RTC.RTC_OK

コードの編集が終わったら C++ の場合はビルドしてください。
ビルドに成功すると build\src\Release(Debug) に TestEV3CPPComp.exe が生成されます。

動作確認

TestEV3CPPComp.exe (TestEV3CPPComp.py) をダブルクリックして起動してください。
TestEV3CPP(TestEV3Py)を以下のように接続してください。


tutorial_ev3_18.png

最後に RTC をアクティブ化して動作確認してください。

RTシステム保存

RTシステムを保存する場合は System Diagram 上で右クリックして Save As... を選択してください。


tutorial_ev3_20.png


/jp/node/6038


RTシステム復元

復元する場合は Open and Restore を選択して、先ほど保存したファイルを選択してください。


/ja/node/6042

RTC 終了

RTC を終了する場合は RTシステムエディタ上で RTC を exit してください。


tutorial_ev3_19.png

補足

スクリプトファイルについて

EV3 のボタン操作で File Brower を選択すると/home/robot以下のディレクトリーの操作ができます。
scripts フォルダー内のシェルスクリプトを実行することで以下の操作ができます。

スクリプトファイル名 内容
run_rtcs.sh RTC を起動する
stop_rtcs.sh RTC を終了する

Development of Image Processing Component

Development of Image Processing Component (Windows 8.1, OpenRTM-aist-1.1.2-RELEASE, OpenRTP-1.1.2, CMake-3.5.2, VS2015)

はじめに

このケーススタディでは、簡単な画像処理をコンポーネント化する方法を紹介します。既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像を左右(または上下)に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。

画像を反転する処理は簡単に実装することができますが、ここではより簡単に実装するために OpenCV ライブラリを利用し、汎用性の高い RTコンポーネントを作成します。

OpenCVとは

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

Wikipediaより抜粋。

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

  • Flip コンポーネント: OpenCV ライブラリが提供する様々な画像処理関数のうち、cv::flip() 関数を用いて画像の反転を行う RTコンポーネント。

cv::flip 関数の RTコンポーネント化

入力された画像を左右または上下に反転し出力する RTコンポーネントを、OpenCV ライブラリの cv::flip 関数を利用して作成します。作成および実行環境は Windows上 の Visual Studio を想定しています。対象 OpenRTM-aist のバージョンは 1.1.2 です。

作成手順はおおよそ以下のようになります。

  • 動作環境・開発環境についての確認
  • OpenCV と cv::flip 関数についての確認
  • コンポーネントの仕様を決める
  • RTCBuilder を用いたソースコードのひな形の作成
  • アクティビティ処理の実装
  • コンポーネントの動作確認

cv::flip関数について

cv::flip 関数は、OpenCV で標準的に用いられている cv::Mat 型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転)に対して反転させます。関数プロトタイプと入出力の引数の意味は以下の通りです。

 void flip(const Mat& src, Mat& dst, int flipMode)
 
  
 src       入力配列
 dst       出力配列。もしdst=NULLであれば、srcが上書きされます。
 flipMode 配列の反転方法の指定内容:
  flipMode = 0: X軸周りでの反転(上下反転)
  flipMode > 0: Y軸周りでの反転(左右反転)
  flipMode < 0: 両軸周りでの反転(上下左右反転)

コンポーネントの仕様 &aname(flip_info)

これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。

このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。それぞれのポートの名前を 入力ポート(InPort)名: originalImage、出力ポート(OutPort)名: flippedImage とします。

OpenRTM-aist には OpenCV を使用したビジョン関連のコンポーネントがサンプルとして付属しています。これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。

   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;
     };

このFlipコンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage型 を InPort と OutPort に使用することにします。

CameraImage型 は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。

また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。パラメーター名は flipMode という名前にします。

flipMode は cv::flip 関数の仕様に合わせて、型は int 型とし 上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。

flipMode の各値での画像処理のイメージを下図に示します。

cvFlip_and_FlipRTC.png
Flipコンポーネントの flipMode 指定時の画像反転パターン

以上から Flip コンポーネントの仕様をまとめます。

コンポーネント名称 Flip
InPort
ポート名 originalImage
CameraImage
説明 入力画像
OutPort
ポート名 flippedImage
CameraImage
説明 反転された画像
Configuration
パラメーター名 flipMode
int
デフォルト値 0
制約 (0,-1, 1)
Widget radio
説明 反転モード
上下反転: 0
左右反転: 1
上下左右反転: -1

動作環境・開発環境

ここで動作環境および開発環境を確認しておきます。

OpenRTM-aist-1.1 以降では、コンポーネントのビルドに CMake を使用します。 また、RTC のひな形生成ツール RTCBuilder では、ドキュメントを入力してこれを Doxygen に処理させることで、コンポーネントのマニュアルも自動で生成することができるようになっており、CMake で Configure を行うときに Doxygen が要求されるため予めインストールしておく必要があります。

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

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

RTCBuilderの起動

Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。

  • C:\workspace

まずは Eclipse を起動します。 Windows 8.1の場合は「スタート」→「アプリビュー(右下矢印)」→「OpenRTM-aist 1.1.2」→「OpenRTP」をクリックすると起動できます。

最初にワークスペースの場所を尋ねられますので、上記のワークスペースを指定して [OK] をクリックしてください。

/ja/node/6026/

すると、以下のような「ようこそ」画面が表示されます。
「ようこそ」画面は必要ないので左上の [×] ボタンをクリックして閉じてください。

/ja/node/6026/
Eclipseの初期起動時の画面

右上の [Open Perspective] ボタンをクリックしてください。

/ja/node/6026/
パースペクティブの切り替え

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

/ja/node/6026/
パースペクティブの選択

新規プロジェクトの作成

Flipコンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。

左上の [Open New RTCBuilder Editor] のアイコンをクリックしてください。

CreateProject_0.png
RTC Builder 用プロジェクトの作成

「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [終了] をクリックします。

RT-Component-BuilderProject_0.png

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

PackageExplolrer_0.png

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

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

RTC.xmlが生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし起動しない場合はパッケージエクスプローラーの RTC.xml をダブルクリックしてください。

Open_RTCBuilder_0.png

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

まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。

  • モジュール名: Flip
  • モジュール概要: 任意(Flip image component)
  • バージョン: 任意(1.0.0)
  • ベンダ名: 任意
  • モジュールカテゴリ: 任意(ImageProcessing)
  • コンポーネント型: STATIC
  • アクティビティ型: PERIODIC
  • コンポーネント種類: DataFlowComponent
  • 最大インスタンス数: 1
  • 実行型: PeriodicExecutionContext
  • 実行周期: 1000.0


Basic_0.png
基本情報の入力


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

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


Activity_0.png
アクティビティコールバックの選択


さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションなので、変更しなくて結構です。


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

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


DataPort_0.png
データポート情報の入力


次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUIで値の変更を行うためのものです。

ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。


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


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


次に、「言語・環境」タブを選択し、プログラミング言語を選択します。ここでは、C++(言語)を選択します。なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。

また、C++の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式のVCのプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は [Use old build environment] をチェックしてください。

Language_0.png
プログラミング言語の選択


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


Generate_0.png
雛型の生成(Generate)


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

CMakeによるビルドに必要なファイルの生成

RTC Builder で生成したコードの中には CMake でビルドに必要な各種ファイルを生成するための CMakeLists.txt が含まれています。 CMake を利用することにより CMakeLists.txt から Visual Studio のプロジェクトファイル、ソリューションファイル、もしくは Makefile 等を自動生成できます。

CMakeList.txt の編集

src/CMakeLists.txtをメモ帳などで開いて編集します。 もしくは Eclipse 画面左のパッケージエクスプローラで src/CMakeLists.txt をダブルクリックもしくはエディタへドラッグアンドドロップしても編集できます。

EditCmakeLists_0.png
CMakeLists.txtの編集

このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリやライブラリサーチパスを与えてやる必要があります。幸い OpenCV は CMake に対応しており、以下の2行を追加・変更するだけで OpenCV のライブラリがリンクされ使えるようになります。

  • src/CMakeLists.txt を修正する
    • Eclipseのパッケージエクスプローラで src/CMakeLists.txt をダブルクリック
  • find_package(OpenCV REQUIRED)を追加
  • 最初のtarget_link_libraries に ${OpenCV_LIBS} を追加
    • target_link_libraries は2ヶ所あり、上がDLL、下が実行ファイルのライブラリ指定です

 set(comp_srcs Flip.cpp )
 set(standalone_srcs FlipComp.cpp)
 
 find_package(OpenCV REQUIRED) ←この行を追加
   :中略
 add_dependencies(${PROJECT_NAME} ALL_IDL_TGT)
 target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加
   :中略
 add_executable(${PROJECT_NAME}Comp ${standalone_srcs}
   ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS})
 target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加

CMake(cmake-gui)の操作

CMakeを利用してビルド環境のConfigureを行います。 まずはCMake(cmake-gui)を起動してください。「スタート」→「アプリビュー(右下矢印)」→「CMake 3.2.1」→「CMake (cmake-gui)」をクリックすると起動できます。

CMakeGUI0_0.png
CMake GUIの起動とディレクトリーの指定

画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所( CMakeList.txt がある場所) と、ビルドディレクトリーを指定します。

  • Where is the soruce code
  • Where to build the binaries

ソースコードの場所は Flip コンポーネントのソースが生成された場所で CMakeList.txt が存在するディレクトリーです。デフォルトでは <ワークスペースディレクトリー>/Flip になります。

ビルドディレクトリーとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。場所は任意ですが、この場合 <ワークスペースディレクトリー>/Flip/build のように分かりやすい名前をつけたFlipのサブディレクトリーを指定することをお勧めします。

Where is the soruce code C:\workspace\Flip
Where to build the binaries C:\workspace\Flip\build

指定したら、下のConfigureボタンを押します。すると下図のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回は Visual Studio 12 2013 とします。VS10 や VS11 を利用している方はそれぞれ読み替えてください。また、プロジェクトのタイプには32bitと64bitも選択できる場合がありますので、自分がインストールしている OpenRTM-aist に合わせて選択してください。

CMakeGUI1_0.png
生成するプロジェクトの種類の指定

ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と表示されますので、続けて [Generate] ボタンをクリックします。「Generating done」と表示されればプロジェクトファイル・ソリューションファイル等の出力が完了します。

なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。

ヘッダ、ソースの編集

次に先ほど指定した build ディレクトリーの中の Flip.sln をダブルクリックして Visual Studio 2013 を起動します。

ヘッダ (include/Flip/Flip.h) およびソースコード (src/Flip.cpp) をそれぞれ編集します。 Visual Studio のソリューションエクスプローラから Flip.h、Flip.cpp をクリックすることで編集画面が開きます。

VisualStudio0_0.png

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

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


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

FlipRTC_State_0.png
アクティビティ処理の概要


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


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


ヘッダファイル (Flip.h) の編集

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

 //OpenCV用インクルードファイルのインクルード
 #include <opencv2/opencv.hpp>

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

 private:
     // <rtc-template block="private_attribute">
  
     // </rtc-template>
 
     // <rtc-template block="private_operation">
  
     // </rtc-template>
         cv::Mat m_imageBuff;
         cv::Mat m_flipImageBuff;

ソースファイル (Flip.cpp) の編集

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

 RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id)
 {
 
        // 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.empty())
        {
            // 画像用メモリの解放
            m_imageBuff.release();
            m_flipImageBuff.release();
        }
 
        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;
 
                m_imageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3);
                m_flipImageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3);
 
             
            }
 
            // InPortの画像データをm_imageBuffにコピー
            memcpy(m_imageBuff.data, (void *)&(m_originalImage.pixels[0]), m_originalImage.pixels.length());
 
            // InPortからの画像データを反転する。 m_flipMode 0: X軸周り、1: Y軸周り、-1: 両方の軸周り
            cv::flip(m_imageBuff, m_flipImageBuff, m_flipMode);
 
            // 画像データのサイズ取得
            int len = m_flipImageBuff.channels() * m_flipImageBuff.cols * m_flipImageBuff.rows;
            m_flippedImage.pixels.length(len);
 
            // 反転した画像データをOutPortにコピー
            memcpy((void *)&(m_flippedImage.pixels[0]), m_flipImageBuff.data, len);
 
            // 反転した画像データをOutPortから出力する。
            m_flippedImageOut.write();
        }
 
      return RTC::RTC_OK;
 }

Visual Studioによるビルド

ビルドの実行

Visual Studioの「ビルド」→「ソリューションのビルド」を選択してビルドを行います。


VC++_build_0.png
ビルドの実行


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

ここでは、OpenRTM-aist-1.1 以降で同梱されるようになったカメラコンポーネント (OpenCVCameraComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。

NameServiceの起動

コンポーネントの参照を登録するためのネームサービスを起動します。


「スタート」→「アプリビュー(右下矢印)」→「OpenRTM-aist 1.1.2」の順に辿り、「Start Naming Service」をクリックして下さい。

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

rtc.confの作成

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

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

※ Eclipse起動時にworkspaceをデフォルトのままにしていた場合、Flipフォルダのパスは、 C:\Documents and Settings\<ログインユーザー名>\workspace となります。

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

Flipコンポーネントの起動

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

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

カメラコンポーネントとビューアコンポーネントの起動

USBカメラのキャプチャ画像を OutPort から出力する OpenCVCameraComp と、InPort で受け取った画像を画面に表示する CameraViewerComp を起動します。

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

「スタート」→「アプリビュー(右下矢印)」→「OpenRTM-aist 1.1.2」の順に辿り、 「OpenCVCameraComp」と「CameraViewerComp」をそれぞれクリックして実行します。

コンポーネントの接続

下図のように、RTSystemEditorにて OpenCVCameraComp と Flip、CameraviewerComp コンポーネントを接続します。

RTSE_Connect_0.png
コンポーネントの接続

コンポーネントのActivate

RTSystemEditor の上部にあります「All Activate」というアイコンをクリックし、全てのコンポーネントをアクティブ化します。正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。


RTSE_Activate_0.png
コンポーネントのアクティブ化


動作確認

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

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


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


Development of Image Processing Component (Windows XP, OpenRTM-aist-1.1, rtmtools-1.1.0-RC3, CMake, VC2010)

はじめに

このケーススタディーでは、簡単な画像処理をコンポーネント化する方法を紹介します。 既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像をj左右(または上下)に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。

画像を反転する処理は簡単に実装することができますが、ここではさらに簡単のために OpenCV ライブラリを利用しより汎用性の高い RTコンポーネントを作成します。

OpenCVとは

OpenCV (オープンシーブイ) とはかつてインテルが、現在は Willow Garage が開発・公開しているオープンソースのコンピュータビジョン向けライブラリです。

Wikipediaより抜粋。

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

  • Flipコンポーネント: OpenCV ライブラリが提供する様々な画像処理関数のうち、cvFlip() 関数を用いて画像の反転を行う RTコンポーネント。

cvFlip 関数の RTコンポーネント化

入力された画像を左右または上下に反転し出力するRTコンポーネントを、OpenCV ライブラリの cvFlip 関数を利用して作成します。 作成および実行環境は Windows上 のVisual C++ を想定しています。対象 OpenRTM-aist のバージョンは 1.1.0 です。

作成手順はおおよそ以下のようになります。

  • 動作環境・開発環境についての確認
  • OpenCV と cvFlip 関数についての確認
  • コンポーネントの仕様を決める
  • RTCBuilder を用いたソースコードのひな形の作成
  • アクティビティ処理の実装
  • コンポーネントの動作確認

cvFlip 関数について

cvFlip 関数は、OpenCV で標準的に用いられている IplImage型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転)に対して反転させます。 関数プロトタイプと入出力の引数の意味は以下の通りです。

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

コンポーネントの仕様

これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。

このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。 それぞれのポートの名前を 入力ポート(InPort)名: originalImage、出力ポート(OutPort)名: flippedImage とします。

OpenRTM-aist には OpenCV を使用したビジョン関連のコンポーネントがサンプルとして付属しています。 これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。

   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;
     };

この Flip コンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage型を InPort と OutPort に使用することにします。 CameraImage型は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。

また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。 これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。 パラメーター名は flipMode という名前にします。

flipMode は cvFlip 関数の仕様に合わせて、型は int 型とし 上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。

flipMode の各値での画像処理のイメージを下図に示します。

cvFlip_and_FlipRTC.png
Flip コンポーネントの flipMode 指定時の画像反転パターン

以上からFlipコンポーネントの仕様をまとめます。

コンポーネント名称 Flip
InPort
ポート名 originalImage
CameraImage
意味 入力画像
OutPort
ポート名 flippedImage
CameraImage
意味 反転された画像
Configuration
パラメーター名 flipMode
int
意味 反転モード
上下反転: 0
左右反転: 1
上下左右反転: -1

動作環境・開発環境

ここで動作環境および開発環境を確認しておきます。

OpenRTM-aist-1.1以降では、コンポーネントのビルドにCMakeを使用します。 また、RTC のひな形生成ツール RTCBuilder では、ドキュメントを入力してこれを Doxygen に処理させることで、コンポーネントのマニュアルも自動で生成することができるようになっており、このため CMake で Configureを行うときに Doxygen が要求されるため、予めインストールしておく必要があります。

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

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

RTCBuilder の起動

Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダーの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。

  • C:\rtcws

eclipse.exe をダブルクリックするとまず、ワークスペースの場所を尋ねられますので、上記のワークスペースを指定してください。 すると、以下のような Welcome ページが表示されます。


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

Welcome ページはいまは必要ないので左上の「×」ボタンをクリックして閉じてください。

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

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

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

「RTC Builder」を選択することで、RTCBuilder が起動します。メニューバーに「カナヅチとRT」の RTCBuilder のアイコンが現れます。


新規プロジェクトの作成

Flip コンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。プロジェクトを作成する方法は2種類あります。

  1. 画面上部のメニューから [ファイル] > [新規] > [プロジェクト] を選択 (Eclipse 共通)
    • 「新規プロジェクト」 画面において、「その他」 > 「RTCビルダ」を選択し、[次へ] をクリック
  2. メニューバーの「RTCBuilder」のアイコンをクリック
    fig2-5CreateProject.png
    RTC Builder 用プロジェクトの作成 1 (「ファイル」メニューから)

fig2-6CreateProject2.png
RTC Builder 用プロジェクトの作成 2(「ファイル」メニューから)

どちらの方法でも、次ようなプロジェクト作成ウィザードが開始されます。 「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [終了] ボタンをクリックします。

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

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

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

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

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

RTC.xml が生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。もし開かない場合は、ツールバーの [Open New RtcBuilder Editor] ボタンをクリックするか、メニューバーの [File] > [Open New Builder Editor] を選択します。

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

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

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

まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。

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


Basic.png
基本情報の入力


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

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


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


さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションで、そのままで結構です。


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


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


次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。 制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUI で値の変更を行うためのものです。

ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。


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


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


次に、「言語・環境」タブを選択し、プログラミング言語を選択します。 ここでは、C++(言語)を選択します。なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。

また、C++の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式のVCのプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は [Use old build environment] を チェックしてください。

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


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


Generate.png
雛型の生成(Generate)


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

仮ビルド

さて、ここまでで Flip コンポーネントのソースコードが生成されました。 処理の中身は実装されていないので、InPort に画像を入力しても何も出力されませんが、生成直後のソースコードだけでもコンパイルおよび実行はできます。

※サービスポートとプロバイダを持つコンポーネントの場合、実装を行わないとビルドが通らないものもあります。

では、まず CMake を利用してビルド環境の Configure を行います。Linuxであれば、Flip コンポーネントのソースが生成されたディレクトリーで

 $ cmake .
 $ make

とすれば、Configure およびビルドが完了するはずです。

Windows の場合は GUI を利用してConfigure してみます。 スタートメニューなどから CMake (cmake-gui) を起動します。

CMakeGUI0.png
CMake GUI の起動とディレクトリーの指定

画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所(CMakeList.txtがある場所) と、ビルドディレクトリーを指定します。

  • Where is the soruce code
  • Where to build the binaries

ソースコードの場所は Flip コンポーネントのソースが生成された場所で CMakeList.txt が存在するディレクトリーです。デフォルトでは <ワークスペースディレクトリー>/Flip になります。

Eclipse の画面左側、パッケージエクスプローラの Flip 直下の CMakeLists.txt を CMake GUI の Where is the soruce code のテキストボックスにドラッグアンドドロップするのが一番手っ取り早いでしょう。

ProjectToCMake.png
CMakeLists.txtの指定

ビルドディレクトリーとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。 場所は任意ですが、この場合 <ワークスペースディレクトリー>/Flip/build のように分かりやすい名前をつけた Flip のサブディレクトリーを指定することをお勧めします。

Where is the soruce code C:\rtcws\Flip
Where to build the binaries C:\rtcws\Flip\build

指定したら、下の [Configure] ボタンをクリックします。すると下図のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回はVisual Studio 10 とします。VS8やVS9を利用している方はそれぞれ読み替えてください。 また、プロジェクトのタイプには 32bit と 64bit も選択できる場合がありますので、自分がインストールしている OpenRTM-aist に合わせて選択してください。

CMakeGUI1.png
生成するプロジェクトの種類の指定

ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と出力されますので、続けて「Generate」ボタンをクリックします。 「Generating done」と出ればプロジェクトファイル・ソリューションファイル等の出力が完了します。

なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。

次に先ほど指定した build ディレクトリーの中の Flip.sln をダブルクリックして Visual Studio 2010 を起動します。

起動後、ソリューションエクスプローラーの「ALL_BUILD」を右クリックし「ビルド」を選択してビルドします。 特に問題がなければ正常にビルドが終了します。

VCbuild0.png
ビルド画面

ヘッダ、ソースの編集

ヘッダ (include/Flip/Flip.h) およびソースコード (src/Flip.cpp) をそれぞれ編集します。 Eclipse 画面左のパッケージエクスプローラでそれぞれのファイルをダブルクリックすると、通常は Visual C++ の編集画面が開きますので、そこで編集します。 Eclipse の中央のエディタにドラッグアンドドロップしても編集できます。

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

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


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

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


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


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


ヘッダファイル (Flip.h) の編集

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;

ソースファイル (Flip.cpp) の編集

下記のように、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;
 }

CMake によるビルドに必要なファイルの生成

CMakeList.txt の編集

Eclipse 画面左のパッケージエクスプローラで src/CMakeLists.txt をダブルクリックもしくはエディタへドラッグアンドドロップして編集します。

EditCmakeLists.png
CMakeLists.txt の編集

このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリやライブラリサーチパスを与えてやる必要があります。 幸いOpenCV は CMake に対応しており、以下の2行を追加・変更するだけで OpenCV のライブラリがリンクされ使えるようになります。

  • src/CMakeLists.txt を修正する
    • Eclipse のパッケージエクスプローラで src/CMakeLists.txt をダブルクリック
  • find_package(OpenCV REQUIRED)を追加
  • 最初のtarget_link_libraries に ${OpenCV_LIBS} を追加
    • target_link_libraries は2ヶ所あり、上がDLL、下が実行ファイルのライブラリ指定です

 set(comp_srcs Flip.cpp )
 set(standalone_srcs FlipComp.cpp)
 
 find_package(OpenCV REQUIRED) ←この行を追加
   :中略
 add_dependencies(${PROJECT_NAME} ALL_IDL_TGT)
 target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加
   :中略
 add_executable(${PROJECT_NAME}Comp ${standalone_srcs}
   ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS})
 target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加

VC++ によるビルド

ビルドの実行

CMakeList.txt を編集したので、再度 CMake GUI で Configure および Generate を行います。 CMake の Generate が正常に終了した事を確認し、Flip.slnファイルをダブルクリックし、Visual C++ 2010 を起動します。

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


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


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

ここでは、OpenRTM-aist-1.1 以降で同梱されるようになったカメラコンポーネント (OpenCVCameraComp、または DirectShowCamComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。

NameService の起動

コンポーネントの参照を登録するためのネームサービスを起動します。


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

&color(RED){※ 「Start 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 ファイルを実行してください。

カメラコンポーネントとビューアコンポーネントの起動

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

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

[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [components] > [C++] > [examples] > [opencv-rtcs] の順に辿り、「OpenCVCameraComp」と「CameraViewerComp」をそれぞれクリックして実行します。

OpenCVCameraComp ではうまくカメラを認識しない場合があります。その場合は DirectShowCamComp を使用してみてください。

コンポーネントの接続

下図のように、RTSystemEditor にて OpenCVCameraComp (またはDirectShowcomp) と Flip、CameraviewerComp コンポーネントを接続します。

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

コンポーネントのActivate

RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティブ化します。 正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。


RTSE_Activate.png
コンポーネントのアクティブ化


動作確認

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

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


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


Flip コンポーネントの全ソース

Flip コンポーネントソースファイル (Flip.cpp)

 // -*- 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コンポーネントのヘッダファイル (Flip.h)

 // -*- 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 コンポーネントの全ソースコード

Flip コンポーネントの全ソースコードを以下に添付します。

Flip.zip

Development of Image Processing Component (OpenRTM-aist-1.1, CMake, VC2010)

はじめに

このケーススタディでは、簡単な画像処理をコンポーネント化する方法を紹介します。 既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像を左右(または上下)に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。

画像を反転する処理は簡単に実装することができますが、ここでは、より簡単に実装するのため OpenCV ライブラリを利用して汎用性の高い RTコンポーネントを作成します。

OpenCV とは

OpenCV (オープンシーブイ) とはかつてインテルが、現在は Willow Garage が開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。

Wikipediaより抜粋。

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

  • Flipコンポーネント: OpenCV ライブラリーが提供する様々な画像処理関数のうち、cvFlip() 関数を用いて画像の反転を行う RTコンポーネント。

cvFlip 関数の RTコンポーネント化

入力された画像を左右または上下に反転し出力する RTコンポーネントを、OpenCV ライブラリの cvFlip 関数を利用して作成します。 作成および実行環境は Windows 上の Visual C++ を想定しています。対象 OpenRTM-aist のバージョンは 1.1.0 です。

作成手順はおおよそ以下のようになります。

  • 動作環境・開発環境についての確認
  • OpenCV と cvFlip 関数についての確認
  • コンポーネントの仕様を決める
  • RTCBuilder を用いたソースコードのひな形の作成
  • アクティビティ処理の実装
  • コンポーネントの動作確認

cvFlip 関数について

cvFlip 関数は、OpenCV で標準的に用いられている IplImage 型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転)に対して反転させます。 関数プロトタイプと入出力の引数の意味は以下のとおりです。

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

コンポーネントの仕様

これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。

このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。 それぞれのポートの名前を 入力ポート(InPort)名: originalImage、出力ポート(OutPort)名: flippedImage とします。

OpenRTM-aist には OpenCVを使用したビジョン関連のコンポーネントがサンプルとして付属しています。 これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。

   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;
     };

この Flip コンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage 型を InPort と OutPort に使用することにします。 CameraImage 型は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。

また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。 パラメーター名は flipMode という名前にします。

flipMode は cvFlip 関数の仕様に合わせて、型は int 型とし、上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。

flipMode の各値での画像処理のイメージを図1に示します。

cvFlip_and_FlipRTC.png
図1. Flip コンポーネントの flipMode 指定時の画像反転パターン

以上から Flip コンポーネントの仕様をまとめます。

コンポーネント名称 Flip
InPort
ポート名 originalImage
CameraImage
意味 入力画像
OutPort
ポート名 flippedImage
CameraImage
意味 反転された画像
Configuration
パラメーター名 flipMode
int
意味 反転モード
上下反転: 0
左右反転: 1
上下左右反転: -1

動作環境・開発環境

ここで動作環境および開発環境を確認しておきます。

OpenRTM-aist-1.1 以降では、コンポーネントのビルドに CMake を使用します。 また、RTC のひな形生成ツール RTCBuilder では、ドキュメントを入力して Doxygen に処理させることで、コンポーネントのマニュアルも自動で生成することができるようになっています。 このため CMake で Configure を行うときに Doxygen が要求されるので予めインストールしておく必要があります。

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

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

RTCBuilder の起動

新規ワークスペースを指定して Eclipse を起動すると、以下のような「ようこそ」画面が表示されます。
「ようこそ」画面は必要ないので左上の [×] ボタンをクリックして閉じてください。

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

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

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

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

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

RTCBuilder が起動します。

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


新規プロジェクトの作成

Flip コンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。メニューから [ファイル] > [新規] > [プロジェクト] を選択します。

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

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

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

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

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

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

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

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

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

RTC.xml が生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし開かない場合は、ツールバーの [Open New RtcBuilder Editor] ボタンをクリックするか、メニューから [ファイル] > [Open New Builder Editor] を選択します。

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

fig2-10FileMenuOpenNewBuilder.png
図11. ファイルメニューから Open New Builder Editor

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

まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。

  • モジュール名: 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. 基本情報の入力


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

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


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


次に、「データポート」タブを選択し、データポートの情報を入力します。先ほど決めた仕様を元に以下のように入力します。 なお、変数名や表示位置はオプションのため、変更しないで結構です。


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


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


次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。 制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUIで値の変更を行うためのものです。

ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。


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


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


次に、「言語・環境」タブを選択し、プログラミング言語を選択します。ここでは、C++(言語)を選択します。 なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。

また、C++ の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式の VC のプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は Use old build environment をチェックしてください。

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


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


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


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

仮ビルド

さて、ここまでで Flip コンポーネントのソースコードが生成されました。 処理の中身は実装されていないので、InPort に画像を入力しても何も出力されませんが、生成直後のソースコードだけでもコンパイルおよび実行はできます。

※サービスポートとプロバイダーを持つコンポーネントの場合、実装を行わないとビルドが通らないものもあります。

では、まず CMake を利用してビルド環境の Configure を行います。Linux であれば、Flip コンポーネントのソースが生成されたディレクトリーで

 $ cmake .
 $ make

とすれば、Configure およびビルドが完了するはずです。

Windows の場合は GUI を利用して Configure してみます。 スタートメニューなどから CMake (cmake-gui) を起動します。

CMakeGUI0.png
図19. CMake GUI の起動とディレクトリーの指定

画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所(CMakeList.txtがある場所) と、ビルドディレクトリーを指定します。

  • Where is the soruce code ^ Where to build the binaries

ソースコードの場所は Flip コンポーネントのソースが生成された場所で、CMakeList.txt が存在するディレクトリーです。 デフォルトでは <ワークスペースディレクトリー>/Flip になります。

また、ビルドディレクトリーとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。 場所は任意ですが、この場合 <ワークスペースディレクトリー>/Flip/build のように分かりやすい名前をつけた Flip のサブディレクトリーを指定することをお勧めします。

指定したら、[Configure] ボタンをクリックします。すると図20のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回は Visual Studio 10 とします。VS8 や VS9 を利用している方はそれぞれ読み替えてください。

CMakeGUI1.png
図20. 生成するプロジェクトの種類の指定

ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と出力されますので、続けて [Generate] ボタンをクリックします。 「Generating done」と出ればプロジェクトファイル・ソリューションファイル等の出力が完了します。

なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。

次に先ほど指定したbuild ディレクトリーの中の Flip.sln をダブルクリックして Visual Studio 2010 を起動します。

起動後、ソリューションエクスプローラーの「ALL_BUILD」を右クリックし「ビルド」を選択します。特に問題がなければ、正常にビルドが終了します。

VCbuild0.png
図21. ビルド画面

ヘッダ、ソースの編集

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

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


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

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


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


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


ヘッダファイル (Flip.h) の編集

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;

ソースファイル (Flip.cpp) の編集

下記のように、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;
 }

CMake によるビルドに必要なファイルの生成

CMakeList.txt の編集

このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリーやライブラリサーチパスを与えてやる必要があります。幸い OpenCV は CMake に対応しており、以下の4行を追加するだけで OpenCV のライブラリーがリンクされ使えるようになります。

 # check doxygen installed
 find_package(Doxygen)
 if(DOXYGEN_FOUND STREQUAL "NO")
     message(FATAL_ERROR "Doxygen not found.")
 endif()

 # 以下の5行をCMakeList.txt に加える
 cmake_policy(SET CMP0015 NEW)
 find_package(OpenCV REQUIRED)
 list(APPEND INCLUDE_DIRS ${OpenCV_INCLUDE_DIRS})
 list(APPEND LIBRARY_DIRS ${OpenCV_LIB_DIR})
 list(APPEND LIBRARIES ${OpenCV_LIBS})

VC++ によるビルド

ビルドの実行

CMakeList.txt を編集したので、再度 CMake GUI で ConfigureおよびGenerate を行います。 CMakeのGenerate が正常に終了した事を確認し、Flip.sln ファイルをダブルクリックし、Visual C++ 2010 を起動します。

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


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


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

ここでは、OpenRTM-aist-1.1 以降で同梱されるようになったカメラコンポーネント (OpenCVCameraComp、または DirectShowCamComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。

NameServiceの起動

コンポーネントの参照を登録するためのネームサービスを起動します。


[スタート] > [すべてのプログラム] > [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 ファイルを実行してください。 FlipComp.exe 画面が表示されます。

カメラコンポーネントとビューアコンポーネントの起動

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

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

[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [components] > [C++] > [examples] > [opencv-rtcs] を選択し、「OpenCVCameraComp」と「CameraViewerComp」をそれぞれクリックして実行します。 「OpenCVCameraComp」と「CameraViewerComp」画面が表示されます。

OpenCVCameraComp ではうまくカメラを認識しない場合があります。その場合は DirectShowCamComp を使用してみてください。

コンポーネントの接続

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

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

コンポーネントの Activate

RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティブ化します。正常にアクティベートされた場合、図26のように黄緑色でコンポーネントが表示されます。


RTSE_Activate.png
図26. コンポーネントのアクティブ化


動作確認

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

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


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


Flip コンポーネントの全ソース

Flip コンポーネントソースファイル (Flip.cpp)

 // -*- 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 コンポーネントのヘッダファイル (Flip.h)

 // -*- 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 コンポーネントの全ソースコード

Flip コンポーネントの全ソースコードを以下に添付します。

Flip.zip

Development of Image Processing Component (Ubuntu 16.04, OpenRTM-aist-1.1.2-RELEASE, OpenRTP-1.1.2, CMake-3.5.1, Code::Blocks-16.01)

はじめに

このケーススタディでは、簡単な画像処理をコンポーネント化する方法を紹介します。 既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像を左右(または上下)に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。

画像を反転する処理は簡単に実装することができますが、ここではより簡単に実装するために OpenCV ライブラリを利用し、より汎用性の高い RTコンポーネントを作成します。

OpenCVとは

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

Wikipediaより抜粋。

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

  • Flip コンポーネント: OpenCV ライブラリが提供する様々な画像処理関数のうち、cv::flip() 関数を用いて画像の反転を行う RTコンポーネント。

cv::flip関数のRTコンポーネント化

入力された画像を左右または上下に反転し出力する RTコンポーネントを、OpenCV ライブラリの cv::flip 関数を利用して作成します。作成および実行環境は Ubuntu上 の Code::Blocks (gccコンパイラ) を想定しています。 対象 OpenRTM-aist のバージョンは 1.1.2 です。

作成手順はおおよそ以下のようになります。

  • 動作環境・開発環境についての確認
  • OpenCVとcv::flip関数についての確認
  • コンポーネントの仕様を決める
  • RTCBuilder を用いたソースコードのひな形の作成
  • アクティビティ処理の実装
  • コンポーネントの動作確認

cv::flip関数について

cv::flip 関数は、OpenCV で標準的に用いられている cv::Mat型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転)に対して反転させます。 関数プロトタイプと入出力の引数の意味は以下の通りです。

 void flip(const Mat& src, Mat& dst, int flipMode)
 
  
 src       入力配列
 dst       出力配列。もし dst=NULL であれば、src が上書きされます。
 flipMode 配列の反転方法の指定内容:
  flipMode = 0: X軸周りでの反転(上下反転)
  flipMode > 0: Y軸周りでの反転(左右反転)
  flipMode < 0: 両軸周りでの反転(上下左右反転)

コンポーネントの仕様

これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。

このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。 それぞれのポートの名前を 入力ポート(InPort)名: originalImage、出力ポート(OutPort)名: flippedImage とします。

OpenRTM-aist には、OpenCV を使用したビジョン関連のコンポーネントがサンプルとして付属しています。 これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。

   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;
     };

この Flip コンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage 型を InPort と OutPort に使用することにします。 CameraImage 型は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。

また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。 パラメーター名は flipMode という名前にします。

flipMode は cv::flip 関数の仕様に合わせて、型は int 型とし 上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。

flipMode の各値での画像処理のイメージを下図に示します。

cvFlip_and_FlipRTC.png
Flip コンポーネントの flipMode 指定時の画像反転パターン

以上から Flip コンポーネントの仕様をまとめます。

コンポーネント名称 Flip
InPort
ポート名 originalImage
CameraImage
説明 入力画像
OutPort
ポート名 flippedImage
CameraImage
説明 反転された画像
Configuration
パラメーター名 flipMode
int
デフォルト値 0
制約 (0,-1,1)
Widget radio
説明 反転モード
上下反転: 0
左右反転: 1
上下左右反転: -1

動作環境・開発環境

Linux (ここでは Ubuntu 16.04 を仮定) 上に開発環境を構築します。

インストールスクリプトでインストールします。

 $ wget http://svn.openrtm.org/OpenRTM-aist/tags/RELEASE_1_1_1/OpenRTM-aist/build/pkg_install_ubuntu.sh
 $ sudo sh pkg_install_ubuntu.sh

OpenRTP のインストール

こちらのURL からLinux版の OpenRTP(コンポーネント開発ツール、システム開発ツール統合環境) をダウンロード、インストールします。 OepnRTP の実行には Java も必要となりますので default-jre パッケージをインストールします。

 $ apt-get install default-jre
 $ wget http://openrtm.org/pub/openrtp/packages/1.1.2.v20160526/eclipse442-openrtp112v20160526-ja-linux-gtk-x86_64.tar.gz
 $ tar xvzf eclipse442-openrtp112v20160526-ja-linux-gtk-x86_64.tar.gz

Eclipse 起動後、RTSystemEditor でネームサーバーに接続できない場合があります。その場合、/etc/hosts の localhost の行に自ホスト名を追記してください。

 $ hostname
 ubuntu1404 ← ホスト名は ubuntu1404
 $ sudo vi /etc/hosts

 127.0.0.1       localhost
 を以下のように変更
 127.0.0.1       localhost ubuntu1404

CMake のインストール

 $ sudo apt-get install cmake cmake-gui

OpenCV および OpenCV コンポーネントのインストール

OpenCV および OpenCV のコンポーネントをインストールします。

まず、Ubuntu が提供している OpenCV のパッケージを下記のようにインストールします。

 $ sudo apt-get install libopencv-dev libcv2.4 libcvaux2.4 libhighgui2.4

リポジトリからソースコードをチェックアウトして、手動でコンパイルを行います。

 $ svn  co http://svn.openrtm.org/ImageProcessing/trunk/ImageProcessing/opencv/
 $ cd opencv
 $ mkdir work
 $ cd work
 $ cmake ..
 $ make
 $ sudo make install
 AffineComp                       FlipComp               RockPaperScissorsComp
 Affine.so                        Flip.so                RockPaperScissors.so
 BackGroundSubtractionSimpleComp  HistogramComp          RotateComp
 BackGroundSubtractionSimple.so   Histogram.so           Rotate.so
 BinarizationComp                 HoughComp              ScaleComp
 Binarization.so                  Hough.so               Scale.so
 CameraViewerComp                 ImageCalibrationComp   SepiaComp
 CameraViewer.so                  ImageCalibration.so    Sepia.so
 ChromakeyComp                    ImageSubstractionComp  SubStractCaptureImageComp
 Chromakey.so                     ImageSubstraction.so   SubStractCaptureImage.so
 DilationErosionComp              ObjectTrackingComp     TemplateComp
 DilationErosion.so               ObjectTracking.so      Template.so
 EdgeComp                         OpenCVCameraComp       TranslateComp
 Edge.so                          OpenCVCamera.so        Translate.so
 FindcontourComp                  PerspectiveComp
 Findcontour.so                   Perspective.so

Code::Blocksのインストール

Code::Blocks は C/C++ に対応した統合開発環境です。 以下のコマンドでインストールできます。

 $ sudo apt-get install codeblocks

最新版を入手したい場合は以下のコマンドを入力します。

 $ sudo add-apt-repository ppa:damien-moore/codeblocks-stable
 $ sudo apt-get update
 $ sudo apt-get install codeblocks

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

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

RTCBuilderの起動

Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダーの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。

  • /home/ユーザー名/workspace

まずは Eclipse を起動します。 OpenRTP を展開したディレクトリーに移動して以下のコマンドを入力します。

 $ ./openrtp

最初にワークスペースの場所を尋ねられますので、上記のワークスペースを指定してください。

workspace_ubuntu.png


すると、以下のような「ようこそ」画面が表示されます。
「ようこそ」画面は必要ないので左上の [×] ボタンをクリックして閉じてください。

/ja/node/6026/
Eclipse の初期起動時の画面

右上の [Open Perspective] ボタンをクリックしてください。

/ja/node/6026/
パースペクティブの切り替え

「RTC Builder」を選択し、[OK] ボタンをクリックすることで、RTCBuilder が起動します。

/ja/node/6026/
パースペクティブの選択

新規プロジェクトの作成

Flip コンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。

左上の [Open New RTCBuilder Editor] のアイコンをクリックしてください。

/ja/node/6057/
RTC Builder 用プロジェクトの作成

「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [終了] をクリックします。

/ja/node/6057/

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

/ja/node/6057/

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

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

RTC.xml が生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし起動しない場合はパッケージエクスプローラーの RTC.xml をダブルクリックしてください。

/ja/node/6057/

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

まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。

  • モジュール名: Flip
  • モジュール概要: 任意(Flip image component)
  • バージョン: 任意(1.0.0)
  • ベンダ名: 任意
  • モジュールカテゴリ: 任意(ImageProcessing)
  • コンポーネント型: STATIC
  • アクティビティ型: PERIODIC
  • コンポーネント種類: DataFlowComponent
  • 最大インスタンス数: 1
  • 実行型: PeriodicExecutionContext
  • 実行周期: 1000.0


/ja/node/6057/
基本情報の入力


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

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


/ja/node/6057/
アクティビティコールバックの選択


さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションなので変更しないで結構です。


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

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


/ja/node/6057/
データポート情報の入力


次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUIで値の変更を行うためのものです。

ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。


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


/ja/node/6057/
コンフィグレーション情報の入力


次に、「言語・環境」タブを選択し、プログラミング言語を選択します。 ここでは、C++(言語)を選択します。なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。

また、C++ の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式の VC のプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は [Use old build environment] をチェックしてください。

/ja/node/6057/
プログラミング言語の選択


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


/ja/node/6057/
雛型の生成(Generate)


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

CMake によるビルドに必要なファイルの生成

RTC Builder で生成したコードの中には CMake でビルドに必要な各種ファイルを生成するための CMakeLists.txt が含まれています。 CMake を利用することにより CMakeLists.txt から Visual Studio のプロジェクトファイル、ソリューションファイル、もしくは Makefile 等を自動生成できます。

CMakeList.txt の編集

src/CMakeLists.txt を gedit などで開いて編集します。 もしくは Eclipse 画面左のパッケージエクスプローラで src/CMakeLists.txt をダブルクリックもしくはエディタへドラッグアンドドロップしても編集できます。

/ja/node/6057/
CMakeLists.txtの編集

このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリやライブラリサーチパスを与えてやる必要があります。 幸い OpenCV は CMake に対応しており、以下の2行を追加・変更するだけで OpenCV のライブラリがリンクされ使えるようになります。

  • src/CMakeLists.txt を修正する
    • Eclipse のパッケージエクスプローラで src/CMakeLists.txt をダブルクリック
  • find_package(OpenCV REQUIRED)を追加
  • 最初のtarget_link_libraries に ${OpenCV_LIBS} を追加
    • target_link_libraries は2ヶ所あり、上が DLL、下が実行ファイルのライブラリ指定です

 set(comp_srcs Flip.cpp )
 set(standalone_srcs FlipComp.cpp)
 
 find_package(OpenCV REQUIRED) ←この行を追加
   :中略
 add_dependencies(${PROJECT_NAME} ALL_IDL_TGT)
 target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加
   :中略
 add_executable(${PROJECT_NAME}Comp ${standalone_srcs}
   ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS})
 target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加

CMake(cmake-gui)の操作

CMake を利用してビルド環境の Configure を行います。 まずは CMake (cmake-gui) を起動してください。

 $ cmake-gui
/ja/node/6057/
CMake GUIの起動とディレクトリーの指定

画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所 (CMakeList.txtがある場所) と、ビルドディレクトリーを指定します。

  • Where is the soruce code
  • Where to build the binaries

ソースコードの場所は Flip コンポーネントのソースが生成された場所で CMakeList.txt が存在するディレクトリーです。 デフォルトでは <ワークスペースディレクトリー>/Flip になります。

ビルドディレクトリとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。 場所は任意ですが、この場合 <ワークスペースディレクトリー>/Flip/build のように分かりやすい名前をつけた Flip のサブディレクトリーを指定することをお勧めします。

Where is the soruce code /home/ユーザー名/workspace/Flip
Where to build the binaries /home/ユーザー名/workspace/Flip/build

指定したら、下のConfigureボタンを押します。すると下図のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回は CodeBlocks - Unix Makefiles を指定します。 Code::Blocks を使わない場合は Unix Makefiles を使ってください。

/ja/node/6057/
生成するプロジェクトの種類の指定

また cmake-gui を使用しない場合は以下のコマンドでファイルを生成できます。

 $ mkdir build
 $ cd build
 $ cmake .. -G "CodeBlocks - Unix Makefiles"

ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と表示されますので、続けて [Generate] ボタンをクリックします。 「Generating done」と表示されればプロジェクトファイル(.cbp)・Makefile 等の出力が完了します。

なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。

ヘッダ、ソースの編集

次に先ほど指定した build ディレクトリーの中の Flip.cbp をダブルクリックして Code::Blocks を起動します。

ヘッダ (include/Flip/Flip.h) およびソースコード (src/Flip.cpp) をそれぞれ編集します。 Code::Blocks の Projects から Flip.h、Flip.cpp をダブルクリックすることで編集画面が開きます。

/ja/node/6057/

64bitの環境の場合に Code::Blocks の動作が不安定になることがあります。その場合は code completion というプラグインを無効化すると動作することがあります。

「Plugins」>「Manage plugins...」を選択します。

/ja/node/6057/

「code completion」を選択して [Disable] をクリックします。

/ja/node/6057/

動作しないときはこの手順を試してください。

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

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


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

/ja/node/6057/
アクティビティ処理の概要


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


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


ヘッダファイル (Flip.h) の編集

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

 //OpenCV用インクルードファイルのインクルード
 #include <opencv2/opencv.hpp>

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

 private:
     // <rtc-template block="private_attribute">
  
     // </rtc-template>
 
     // <rtc-template block="private_operation">
  
     // </rtc-template>
         cv::Mat m_imageBuff;
         cv::Mat m_flipImageBuff;

ソースファイル (Flip.cpp) の編集

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

 RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id)
 {
 
        // 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.empty())
        {
            // 画像用メモリの解放
            m_imageBuff.release();
            m_flipImageBuff.release();
        }
 
        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;
 
                m_imageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3);
                m_flipImageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3);
 
             
            }
 
            // InPortの画像データをm_imageBuffにコピー
            memcpy(m_imageBuff.data, (void *)&(m_originalImage.pixels[0]), m_originalImage.pixels.length());
 
            // InPortからの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り
            cv::flip(m_imageBuff, m_flipImageBuff, m_flipMode);
 
            // 画像データのサイズ取得
            int len = m_flipImageBuff.channels() * m_flipImageBuff.cols * m_flipImageBuff.rows;
            m_flippedImage.pixels.length(len);
 
            // 反転した画像データをOutPortにコピー
            memcpy((void *)&(m_flippedImage.pixels[0]), m_flipImageBuff.data, len);
 
            // 反転した画像データをOutPortから出力する。
            m_flippedImageOut.write();
        }
 
      return RTC::RTC_OK;
 }

Code::Blocks によるビルド

ビルドの実行

Code::Blocksの [ビルド] ボタンをクリックしてビルドを行います。


/ja/node/6057/
ビルドの実行


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

ここでは、カメラコンポーネント (OpenCVCameraComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。

NameService の起動

コンポーネントの参照を登録するためのネームサービスを起動します。


 $ rtm-naming

rtc.conf の作成

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

下記の内容を rtc.conf というファイル名で保存し、Flip\build\srcフォルダーに置いて下さい。

※ Eclipse 起動時に workspace をデフォルトのままにしていた場合、Flip フォルダーのパスは、 C:\Documents and Settings\<ログインユーザー名>\workspace となります。

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

Flip コンポーネントの起動

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

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

 $ FlipComp -f rtc.conf

カメラコンポーネントとビューアコンポーネントの起動

USBカメラのキャプチャー画像を OutPort から出力する OpenCVCameraComp と InPort で受け取った画像を画面に表示する CameraViewerComp を起動します。

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

 $ /usr/local/share/openrtm-1.1/components/c++/opencv-rtcs/CameraViewerComp
 $ /usr/local/share/openrtm-1.1/components/c++/opencv-rtcs/OpenCVCameraComp

コンポーネントの接続

下図のように、RTSystemEditor にて OpenCVCameraComp と Flip、CameraviewerComp コンポーネントを接続します。

/ja/node/6057/
コンポーネントの接続

コンポーネントの Activate

RTSystemEditor の上部にあります [All Activate] というアイコンをクリックし、全てのコンポーネントをアクティブ化します。 正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。


/ja/node/6057/
コンポーネントのアクティブ化


動作確認

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

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


/ja/node/6057/
コンフィギュレーションパラメーターの変更


Development of Image Processing Component(OpenRTM-aist-1.1, CMake, Linux Ubuntu 14.04)

はじめに

このケーススタディでは、簡単な画像処理をコンポーネント化する方法を紹介します。既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像を左右 (または上下) に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。

画像を反転する処理は簡単に実装することができますが、ここではさらに簡単に実装するため OpenCV ライブラリを利用しより汎用性の高い RTコンポーネントを作成します。

OpenCVとは

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

Wikipediaより抜粋。

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

  • Flip コンポーネント: OpenCV ライブラリが提供する様々な画像処理関数のうち、cvFlip() 関数を用いて画像の反転を行う RTコンポーネント。

cvFlip 関数の RTコンポーネント化

入力された画像を左右または上下に反転し出力する RTコンポーネントを、OpenCV ライブラリの cvFlip 関数を利用して作成します。 作成および実行環境は Windows上の Visual C++ を想定しています。対象 OpenRTM-aist のバージョンは 1.1 です。

作成手順はおおよそ以下のようになります。

  • 動作環境・開発環境についての確認
  • OpenCV と cvFlip 関数についての確認
  • コンポーネントの仕様を決める
  • RTCBuilder を用いたソースコードのひな形の作成
  • アクティビティ処理の実装
  • コンポーネントの動作確認

動作環境の確認

Linux (ここでは Ubuntu 14.04 を仮定) 上に開発環境を構築します。

OpenRTM-aist のインストール

Choreonoid を利用する場合、以下のように PPA から OpenRTM-aistを インストールしてください。 一括インストールスクリプトをすでに実行している場合、/etc/apt/sources.list の下の方に追記されている openrtm.org のリポジトリをコメントアウトした上で、 apt-get update してください。

 $ sudo add-apt-repository ppa:hrg/daily
 $ sudo apt-get update
 $ sudo apt-get install openrtm-aist openrtm-aist-dev doxygen

OpenRTP のインストール

こちらのURL から Linux版の OpenRTP (コンポーネント開発ツール、システム開発ツール統合環境) をダウンロード、インストールします。 OepnRTP の実行には Java も必要となりますので default-jre パッケージをインストールします。

 $ apt-get install default-jre
 $ wget http://openrtm.org/pub/openrtp/packages/1.1.0.rc5v20150317/eclipse381-openrtp110rc5v20150317-ja-linux-gtk-x86_64.tar.gz
 $ tar xvzf eclipse381-openrtp110rc5v20150317-ja-linux-gtk-x86_64.tar.gz
 $ cd eclipse
 $ ./openrtp

eclipse 起動後、RTSystemEditor でネームサーバーに接続できない場合があります。その場合、/etc/hosts の localhost の行に自ホスト名を追記してください。

 $ hostname
 ubuntu1404 ← ホスト名は ubuntu1404
 $ sudo vi /etc/hosts

 127.0.0.1       localhost
 を以下のように変更
 127.0.0.1       localhost ubuntu1404

OpenCV および OpenCV コンポーネントのインストール

OpenCV および OpenCV のコンポーネントをインストールします。

まず、Ubuntu が提供している OpenCV のパッケージを下記のようにインストールします。

 $ sudo apt-get install libopencv-dev libcv2.4 libcvaux2.4 libhighgui2.4

OpenCV RTC のパッケージは下記 URL にありますので、手動でダウンロードして、dpkg コマンドでインストールします。

 $ wget http://openrtm.org/pub/Linux/ubuntu/dists/trusty/main/binary-amd64/imageprocessing-1.1.0.deb
 $ sudo dpkg -i imageprocessing-1.1.0.deb
 $ ls /usr/share/openrtm-1.1/components/c++/opencv-rtcs/
 AffineComp                       FlipComp               RockPaperScissorsComp
 Affine.so                        Flip.so                RockPaperScissors.so
 BackGroundSubtractionSimpleComp  HistogramComp          RotateComp
 BackGroundSubtractionSimple.so   Histogram.so           Rotate.so
 BinarizationComp                 HoughComp              ScaleComp
 Binarization.so                  Hough.so               Scale.so
 CameraViewerComp                 ImageCalibrationComp   SepiaComp
 CameraViewer.so                  ImageCalibration.so    Sepia.so
 ChromakeyComp                    ImageSubstractionComp  SubStractCaptureImageComp
 Chromakey.so                     ImageSubstraction.so   SubStractCaptureImage.so
 DilationErosionComp              ObjectTrackingComp     TemplateComp
 DilationErosion.so               ObjectTracking.so      Template.so
 EdgeComp                         OpenCVCameraComp       TranslateComp
 Edge.so                          OpenCVCamera.so        Translate.so
 FindcontourComp                  PerspectiveComp
 Findcontour.so                   Perspective.so

cvFlip 関数について

cvFlip 関数は、OpenCV で標準的に用いられている IplImage型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転) に対して反転させます。関数プロトタイプと入出力の引数の意味は以下の通りです。

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

コンポーネントの仕様

これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。

このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。 それぞれのポートの名前を 入力ポート(InPort)名: originalImage, 出力ポート(OutPort)名: flippedImage とします。

OpenRTM-aist には OpenCV を使用したビジョン関連のコンポーネントがサンプルとして付属しています。 これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。

   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;
     };

このFlipコンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage型を InPort と OutPort に使用することにします。 CameraImage型は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。

また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。 パラメーター名は flipMode という名前にします。

flipMode は cvFlip 関数の仕様に合わせて、型は int 型とし 上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。

flipModeの各値での画像処理のイメージを下図に示します。

cvFlip_and_FlipRTC.png
Flip コンポーネントの flipMode 指定時の画像反転パターン

以上から Flip コンポーネントの仕様をまとめます。

コンポーネント名称 Flip
InPort
ポート名 originalImage
CameraImage
意味 入力画像
OutPort
ポート名 flippedImage
CameraImage
意味 反転された画像
Configuration
パラメーター名 flipMode
int
意味 反転モード
上下反転: 0
左右反転: 1
上下左右反転: -1

動作環境・開発環境

ここで動作環境および開発環境を確認しておきます。

OpenRTM-aist-1.1 以降では、コンポーネントのビルドに CMake を使用します。また、RTC のひな形生成ツール RTCBuilder では、ドキュメントを入力してこれを Doxygen に処理させることで、コンポーネントのマニュアルも自動で生成することができるようになっています。 このため CMakeで Configure を行うときに Doxygen が要求されるため、予めインストールしておく必要があります。

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

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

RTCBuilder の起動

Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダーの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。

  • ~/workspace (/home/<ユーザー名>/workspace を意味する。)

Eclipse を起動すると、ワークスペースの場所を尋ねられます。Eclipse を最初に起動したとき、または -clean オプション付きで起動した場合は、デフォルトで上記のディレクトリーが指定されますので、そのまま [OK] ボタンをクリックしてください。以下のような「ようこそ」画面が表示されます。 「ようこそ」画面はいまは必要ないので左上の「×」ボタンをクリックして閉じてください。

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

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

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

「RTC Builder」を選択することで、RTCBuilder が起動します。メニューバーに「カナヅチとRT」の RTCBuilder のアイコンが表示されます。

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

新規プロジェクトの作成

Flip コンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。プロジェクトを作成する方法は2種類あります。

  1. 画面上部のメニューから [ファイル] > [新規] > [プロジェクト] を選択 (Eclipse 共通)
    • 「新規プロジェクト」 画面において,[その他] > [RTCビルダ] を選択し、[次へ] をクリックします。
  2. メニューバーの [RTCBuilder] のアイコンをクリック
    fig2-5CreateProject.png
    RTC Builder 用プロジェクトの作成 1 (「ファイル」メニューから)

fig2-6CreateProject2.png
RTC Builder 用プロジェクトの作成 2(「ファイル」メニューから)

どちらの方法でも、次ようなプロジェクト作成ウィザードが開始されます。 「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [完了] ボタンをクリックします。

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

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

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

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

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

RTC.xml が生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし開かない場合は、ツールバーの「Open New RtcBuilder Editor」ボタンをクリックするか、メニューバーの [ファイル] > [Open New Builder Editor] を選択します。

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

fig2-10FileMenuOpenNewBuilder.png
ファイルメニューから Open New Builder Editor

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

まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。

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


Basic.png
基本情報の入力


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

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


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


さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションで、そのままで結構です。


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


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


次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。 制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUI で値の変更を行うためのものです。

ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。


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


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


次に、「言語・環境」タブを選択し、プログラミング言語を選択します。ここでは、C++(言語)を選択します。 なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。

また、C++の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式の VC のプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は [Use old build environment] をチェックしてください。

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


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


Generate.png
雛型の生成(Generate)


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

仮ビルド

さて、ここまでで Flip コンポーネントのソースコードが生成されました。処理の中身は実装されていないので、InPort に画像を入力しても何も出力されませんが、生成直後のソースコードだけでもコンパイルおよび実行はできます。

※サービスポートとプロバイダを持つコンポーネントの場合、実装を行わないとビルドが通らないものもあります。

では、まず CMake を利用してビルド環境の Configure を行います。Linux であれば、Flip コンポーネントのソースが生成されたディレクトリーで

 $ cd workspace/Flip
 $ mkdir build
 $ cd build
 $ cmake ..
 $ make

とすれば、Configure およびビルドが完了するはずです。ビルド終了後、からの FlipComp を起動してみましょう。

 $ cd src
 $ ./FlipComp

起動後、RTSystemEditor などでアクセスしてみてください。Flip というコンポーネントが表示されているはずです。 カメラコンポーネント (OpenCVCameraComp) や表示コンポーネント (CameraViewerComp) などとも接続できますが、中で何も処理を行っていないでの、表示コンポーネントには何も表示されません。

次からは、コンポーネントのソースコードを作成し、中身を実装していきます。

ヘッダ、ソースの編集

ヘッダ (include/Flip/Flip.h) およびソースコード (src/Flip.cpp) をそれぞれ編集します。 Eclipse 画面左のパッケージエクスプローラーでそれぞれのファイルをダブルクリックすると、通常は Visual C++ の編集画面が開きますので、そこで編集します。 Eclipse の中央のエディタにドラッグアンドドロップしても編集できます。

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

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


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

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


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


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


ヘッダファイル (Flip.h) の編集

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;

ソースファイル (Flip.cpp) の編集

下記のように、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;
 }

CMake によるビルドに必要なファイルの生成

CMakeList.txt の編集

Eclipse 画面左のパッケージエクスプローラーで src/CMakeLists.txt をダブルクリックもしくはエディタへドラッグアンドドロップして編集します。

EditCmakeLists.png
CMakeLists.txtの編集

このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリやライブラリサーチパスを与えてやる必要があります。 幸い OpenCV は CMake に対応しており、以下の2行を追加・変更するだけで OpenCV のライブラリがリンクされ使えるようになります。

  • src/CMakeLists.txt を修正する
    • Eclipse のパッケージエクスプローラーで src/CMakeLists.txt をダブルクリック
  • find_package (OpenCV REQUIRED) を追加
  • 最初の target_link_libraries に ${OpenCV_LIBS} を追加
    • target_link_libraries は2ヶ所あり、上が DLL、下が実行ファイルのライブラリ指定です

 set(comp_srcs Flip.cpp )
 set(standalone_srcs FlipComp.cpp)
 
 find_package(OpenCV REQUIRED) ← この行を追加
   :中略
 add_dependencies(${PROJECT_NAME} ALL_IDL_TGT)
 target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加
   :中略
 add_executable(${PROJECT_NAME}Comp ${standalone_srcs}
   ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS})
 target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加

ビルド

ビルドの実行

CMakeList.txt を編集したので、再度 CMake で Configure および Generate を行います。

 $ cd workspace/Flip (eclipse ワークスペース下の Flip ディレクトリーへ移動)
 $ rm -rf build (念のため仮ビルドで作成したディレクトリーは削除)
 $ mkdir build (再度 build ディレクトリーを作成)
 $ cd build
 $ cmake ..
 $ make
 
CMake の Generate が正常に終了した事を確認し、makeします。Configure で問題がある場合、先ほど編集した Flip/src/CMakeList.txt の編集にミスがある可能性があります。 make でエラーが出た場合は、ソースコードやヘッダの編集に間違いがある可能性があります。 エラーメッセージをよく見て、修正してください。

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

ここでは、OpenRTM-aist-1.1 以降で同梱されるようになったカメラコンポーネント (OpenCVCameraComp、または DirectShowCamComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。

NameService の起動

コンポーネントの参照を登録するためのネームサービスを起動します。


 $ rtm-naming

rtc.conf の作成

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

下記の内容を rtc.conf というファイル名で保存し、workspace/Flip/build/src/ ディレクトリーに置いてください。

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

Flip コンポーネントの起動

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

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

 $ cd workspace/Flip/build/src (もし現在 build/src 以外にいる場合)
 $ ./FlipComp
 

カメラコンポーネントとビューアコンポーネントの起動

USBカメラのキャプチャー画像を OutPort から出力する OpenCVCameraComp、InPort で受け取った画像を画面に表示する CameraViewerComp を起動します。

 $ /usr/share/openrtm-1.1/components/c++/opencv-rtcs/OpenCVCameraComp
 $ /usr/share/openrtm-1.1/components/c++/opencv-rtcs/CameraViewerComp
 

コンポーネントの接続

下図のように、RTSystemEditor にて OpenCVCameraComp (またはDirectShowcomp) と Flip、CameraviewerComp コンポーネントを接続します。

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

Eclipse起動後、RTSystemEditor でネームサーバーに接続できない場合があります。その場合、/etc/hosts の localhost の行に自ホスト名を追記してください。

 $ hostname
 ubuntu1404 ← ホスト名は ubuntu1404
 $ sudo vi /etc/hosts

 127.0.0.1       localhost
 を以下のように変更
 127.0.0.1       localhost ubuntu1404

コンポーネントの Activate

RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティブ化します。正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。


RTSE_Activate.png
コンポーネントのアクティブ化


動作確認

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

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


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


Flip コンポーネントの全ソース

Flip コンポーネントソースファイル (Flip.cpp)

 // -*- 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 コンポーネントのヘッダファイル (Flip.h)

 // -*- 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 コンポーネントの全ソースコード

Flip コンポーネントの全ソースコードを以下に添付します。

Flip.zip

Development of RT Component

Making an OpenCV RT-Component (Flip component)

Making an OpenCV RT-Component (Flip component)

This section describes creating an RT-Component to invert an image using the OpenCV library and VC9:
  • The cvFlip() function
  • Component outline
  • Operating and development environment
  • Generate the Flip component template
  • Implement the component activities
  • Test the component

The cvFlip() function

The cvFlip() function flips a 2D array (i.e. an image) on the vertical axis, horizontal axis, or both axes at the same time.

 void cvFlip(IplImage* src, IplImage* dst=NULL, int flip_mode=0);
 #define cvMirror cvFlip
  
 src       Input array
 dst       Output array. If dst is NULL, the result will be written into src.
 flip_mode Flag setting the axes to flip on.
  flip_mode = 0: Flip on the X axis (swap top and bottom)
  flip_mode > 0: Flip on the Y axis (swap left and right)
  flip_mode < 0: Flip on both axes

Component outline

The component will flip an image received by an InPort and output it on an OutPort.
The flip mode will be controlled by an RTC configuration parameter called "flip_mode." Set flip_mode as below:

  • Flip on the X axis: 0
  • Flip on the Y axis: 1
  • Flip on both axes: -1


The specification of the component we will create is as follows:

  • InPort
    • Captured image data (TimedOctetSeq)
  • OutPort
    • Flipped image data (TimedOctetSeq)
  • Configuration
    • Flip mode (int)

※ TimedOctetSeq is a data type specified in BasicDataType.idl from OpenRTM-aist.

※ octet is a CORBA IDL base type that can store and transmit any data in 8-bit format without transformation.

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


Figure 1 illustrates the effects of different flip_mode settings.


cvFlip_and_FlipRTC.png
Figure 1. The effect of setting flip_mode in the Flip component.


Operating environment and development environment

  • Decompression tool (e.g. Lhaplus)

Generate the Flip component template

We will generate the Flip component template using RTCBuilder.

Starting RTCBuilder

Upon starting Eclipse with a new workspace, the Welcome page will be displayed.

fig1-1EclipseInit.png
Figure 2. Eclipse first-time startup screen.


Close this Welcome screen using the close button to display the default perspective view.

fig2-2PerspectiveSwitch.png
Figure 3. Changing perspective.


Click the "Open Perspective" button in the top right and select "Other..." from the menu.

fig2-3PerspectiveSelection.png
Figure 4. Perspective selection.


Select "RTC Builder".

fig2-4RTCBuilderInit.png
Figure 5. The RTC Builder start screen.


Making an RTCBuilder project

First, we will make an Eclipse project for the RT-Component. From the File menu, select "New," then "Project."

fig2-5CreateProject.png
Figure 6. Making an RTCBuilder project, step 1.


In the displayed dialog, select "Other," then "RTCBuilder" (RTC ビルダ) and click "Next".

fig2-6CreateProject2.png
Figure 7. Making an RTCBuilder project, step 2.


Enter a project name and click "Finish."

fig2-7CreteProject3.PNG
Figure 8. Making an RTCBuilder project, step 3.


A project using the given name will be created and displayed in the package explorer.

fig2-8CreateProject4.png
Figure 9. Making an RTCBuilder project, step 4.


Within the generated project will be an RTC profile XML (RTC.xml) containing default values.

Starting the RTC Profile Editor

To open the RTC Profile Editor, click the "Open New RtcBuilder Editor" button on the toolbar, or select "Open New Builder Editor" from the File menu.

fig2-9ToolsBarOpenNewRtcBuilder.png
Figure 10. "Open New RtcBuilder Editor" on the toolbar.



fig2-10FileMenuOpenNewBuilder.png
Figure 11. "Open New Builder Editor" entry in the File menu.


Data type definition files location

It is necessary to set the location of the IDL files that define data types used by data ports and service ports.
※ This value is valid for the entire workspace, so it does not need to be set for each project created within the same workspace.

 1. Open the settings dialog by selecting "Settings" from the "Window" menu.
 2. Expand the "RtcBuilder" branch and select "Data Type."
 3. Click the "Add" button and enter values for "IDL File Directories." IDL files included with OpenRTM-aist are installed to this path by default:
     
      C:\Program Files\OpenRTM-aist\1.0\rtm\idl
 
 4. Click the "OK" button to finish.


RTCBuilder_datatype_setup.png
Figure 12. Data file location settings dialog.


Entering the component profile and generating code

1. Select the "Basic" (基本) tab, and enter the basic component information as below.


-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
Figure 13. Entering basic component information.


2. Select the "Activity" (アクティビティ) tab and select the action callbacks that will be used.

For the Flip component, the onActivated(), onDeactivated() and onExecute() callbacks will be used. Check them as in Figure 14.


RTCBuilder_activity.png
Figure 14. Selecting activity callbacks.


3. Select the ""Data port" (データポート) tab and enter the data port information.


-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
Figure 15. Entering the data port information.


4. Select the "Configuration" (コンフィギュレーション) tab and enter the configuration parameters.


-flip_mode
    • Name: flip_mode
    • TYpe: int
    • Default Value: 1
    • Variable name: flip_mode

-image_height
    • Name: image_height
    • TYpe: int
    • Default Value: 240
    • Variable name: img_height

-image_width
    • Name: image_width
    • TYpe: int
    • Default Value: 320
    • Variable name: img_width


RTCBuilder_config.png
Figure 16. Entering configuration parameters.


5. Select the "Language/Environment" (言語・環境) tab and choose a programming language. For the Flip component, choose C++.


RTCBuilder_lang.png
Figure 17. Programming language selection


6. Click the "Generate code" (コード生成) button in the "Basic" tab to generate the component template.


RTCBuilder_base.png
Figure 18. Template generation.


※ The component template code will be generated in the currently-active Eclipse workspace directory. You can check this directory by selecting "Change workspace" from the File menu.

Basic RT Component Development

Components with Data Ports

In this sample, we will create two components with data ports, and try to send and receive data between two components. The specifications of the components that we will create are as follows.

  • Component1
    • It has one OutPort.
    • Data type of OutPort is TimedLong.
    • It outputs the values input via the console to its OutPort.
  • Component2
    • It has one InPort.
    • Data type of InPort is TimedLong.
    • It has one configuration parameter.
    • The configuration parameter is int type.
    • The default value of configuration parameter is 1.
    • It reads the value calculated by multiplying the parameter in case of reading from the InPort variable.
    • It outputs the values read from InPort to the console.

Generating Source Codes Using rtc-template

To create the components with above specifications, you will prepare the following shell script named 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"

The component1 is created by the first execution of rtc-template, and then the component2 is created by the next.

 > sh gen.sh 
   File "ConsoleIn.h" was generated.
   File "ConsoleIn.cpp" was generated.
   File "ConsoleInComp.cpp" was generated.
   File "Makefile.ConsoleIn" was generated.
   File "README.ConsoleIn" was generated.
   File "ConsoleOut.h" was generated.
   File "ConsoleOut.cpp" was generated.
   File "ConsoleOutComp.cpp" was generated.
   File "Makefile.ConsoleOut" was generated.
   File "README.ConsoleOut" was generated.

Implementaion of ConsoleIn

The ConsoleIn component will be implemented by editing the generated source code.

ConsoleIn.h

This component waits for input and outputs the inputted value to its OutPort, when activated. Therefore, you need to implement only its onExecute member function, which is executed periodically during the active state, so uncomment the commented-out onExecute function in ConsoleIn.h:

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

There is a declaration of the OutPort variables which is specified in rtc-template at the lower of ConsoleIn.h.

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

TimedLong m_out of declaration is the variable which is bound to OutPort.

OutPort<TimedLong> m_outOut of declaration is the instance of OutPort.

ConsoleIn.cpp

It is easy to implement ConsoleIn. Uncomment the commented-out onExecute function, and implement this way:

 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;
 }

This code executes those steps:
  1. Wait for input from a user by cin >> m_out.data
  2. Store the inputted value to m_out.data(long type)
  3. Print the inputted value for confirmation
  4. Output the data to OutPort by m_outOut.write()

Implementation of ConsoleOut

It is a little complicated in ConsoleOut component. You must store the result value which the data from InPort is multiplied by the configuration parameter "multiply". It can be realized by the way you set a callback object to InPort.

Callback Object

Callback Object is an object in which operator() method is defined, which is invoked when an event is occurred at buffers of InPort or OutPort. Here, we use an OnWriteConvert as the callback object, which converts data when the data are written to a buffer of InPort.

Inherit RTC::OnWriteConvert and define the next class:

 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

Insert this callback class just after the lines of include in ConsoleOut.h. Also, declare the instance of it as a member variable of ConsoleOut class. You may insert the declaration just after private:

 private:
   Multiply m_owc;
   int dummy;

When activated, this component reads data from InPort and prints the data to the standard output. Therefore, you need to implement only its onExecute member function, which is executed periodically during the active state, so uncomment the commented-out onExecute function in the generated ConsoleOut.h:

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

There are declarations of configuration variables and the InPort variables which is specified in rtc-template at the lower of ConsoleOut.h.

Since RingBuffer is used a buffer of InPort in ConsoleOut, you have to include RingBuffer.h. Around top of ConsoleIn.h, please include RingBuffer.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/RingBuffer.h> //add this

And, modify the part of InPort<TimedLong> m_inIn, which is the default declaration of InPort, to InPort<TimedLong, RTC::RingBuffer> m_inIn for the InPort to use a RingBuffer.

     :snip
   // 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 of declaration is the variable which is bound to the configuration "multiply".

TimedLong m_in of declaration is the variable which is bound to InPort.

InPort<TimedLong, RTC::RingBuffer> m_inIn of declaration is the instance of OutPort

ConsoleOut.cpp

Add an initialization of the instance of Multiply which you have defined before in the constructor of ConsoleOut class.

 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)

Also, describe the code to add the callback object to InPort in the constructor.

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

Uncomment the onExecute function, and implement this way:

 RTC::ReturnCode_t ConsoleIn::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;
 }

This code execute those steps:
  1. Check whether InPort has data by m_inIn.isNew()
    1. isNew() is a member function defined in RingBuffer
  2. If it has new data, load the data in the variable by m_inIn.read()
  3. Print the data(m_in.data)

Compile

Once you finished implementing, compile your sources as below:

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

In case of compile errors, check whether there are misspellings or other mistakes, and compile again.

Execute

Prepare an appropriate rtc.conf and run ConsoleInComp and ConsoleOutComp, using two terminals.

Start up RtcLink, connect the two components, and activate them.

#imgr(./figs/Manual/ConsoleInConsoleOut2.png,center,nolink)

In the terminal running ConsoleIn, a prompt Please input number: is shown, so enter an appropriate 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

In the other terminal, which is running ConsoleOut, results should be printed like this:

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

Next, on the configuration view of RtcLink, change the multiply value to 10. Then, values inputted in ConsoleIn should be multiplied by 10 and printed like this:

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

Implement the component activities

Implement the component activities

The Flip component will take image data received at its InPort, copy it into an image buffer, and use the cvFlip() function from OpenCV to transform it. The transformed data will be transmitted over the component's OutPort.

The process flow of onActivated(), onExecute() and onDeactivated() is illustrated in Figure 19.

FlipRTC_State.png
Figure 19. Activity flow outline.


The processing performed by onExecute() is illustrated in Figure 20.


FlipRTC.png
Figure 20. The processing performed in onExucete().


Copy the user property sheet for OpenCV

A property sheet defines the various options (such as install path, library paths and extra link libraries) and macros necessary to compile using Visual Studio. Both RTCBuilder and rtc-template generate VC projects when they generate component templates. The generated VC project uses a property sheet to specify various options. It can also include a user-defined property sheet for any extra user-defined options.
  • rtm_config.vsprop: A property sheet that contains information relating to OpenRTM. Installed with OpenRTM-aist. Execute the "copyprops.bat" file in your generated project directory to copy it from the OpenRTM directory to the project directory.
  • user_config.vsprops: A user-defined property sheet. The default sheet is empty. An example of its usage is given in the user_config.vsprops included with the OpenRTM USBCamera example (OpenRTM-aist/win32/OpenRTM-aist/example/USBCamera). This example sheet contains settings for OpenCV.

Copy the user_config.vsprops file given below into the Flip component's folder, or save the downloadable version into the component's folder.


user_config.vsprops

※ The user_config.vsprops file that already exists in the Flip folder can be overwritten.


 <?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>

Execute copyprops.bat

Upon executing copyprops.bat, the rtm_config.vsprops file will be copied to the component folder. The rtm_config.vsprops file contains settings such as the include path and extra link libraries necessary to compile an RT-Component using Visual C++.

Edit the header file

  • In order to use the OpenCV library, the OpenCV include files must be added.

 #include<cv.h>
 #include<cxcore.h>
 #include<highgui.h>

  • The Flip component will allocate a region of image data, perform the flip processing, and release that data. This processing is carried out in in the onActivated(), onExecute() and onDeactivated callbacks, respectively. Ensure these three functions are not commented out.

   /***
    *
    * 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);

  • Add member variables for storing the image data.

   IplImage* m_image_buff;
   IplImage* m_flip_image_buff;

Edit the source file

Implement onActivated(), onDeactivated() and onExecute() as below.

 RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id)
 {
   // Image memory store allocation
   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)
 {
   // Image memory store deallocation
   cvReleaseImage(&m_image_buff);
   cvReleaseImage(&m_flip_image_buff);
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id)
 {
   // Check for new data
   if (m_image_origIn.isNew()) {
     // Read data from the InPort
     m_image_origIn.read();
 
     // Copy the image data from the InPort to IplImage.imageData
     memcpy(m_image_buff->imageData,(void *)&(m_image_orig.data[0]),m_image_orig.data.length());
 
     // Flip the image data. m_flip_mode 0: flip on X, 1: flip on Y, -1: flip on both
     cvFlip(m_image_buff, m_flip_image_buff, m_flip_mode);
 
     // Get the image data size
     int len = m_flip_image_buff->nChannels * m_flip_image_buff->width * m_flip_image_buff->height;
     m_image_flip.data.length(len);
 
     // Copy the flipped image data to the OutPort
     memcpy((void *)&(m_image_flip.data[0]),m_flip_image_buff->imageData,len);
 
     // Output the image data
     m_image_flipOut.write();
   }
   return RTC::RTC_OK;
 }

Build the component

Build the component as in Figure 21.


VC++_build.png
Figure 21. Building.


Test the component

For testing, the Flip component will be connected to the USBCameraAcquireComp and USBCameraMonitorComp sample components supplied with OpenRTM-aist.

Start the name service

The omniORB name service must be started. From the Start menu, go to All Programs > OpenRTM-aist > C++ > examples > Start Naming Service.

Write rtc.conf

The rtc.conf file is used to tell an RT-Component such parameters as the address of the name server, the format to use when registering with the name server, etc. Copy the following lines into a file called "rtc.conf" and place that file in Flip\FlipComp\Debug or Flip\FlipComp\Release.

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

Start the Flip component

In the same folder that the rtc.conf file was placed, execute FlipComp.exe.

Start the USBCameraAqcuire and USBCameraMonitor components

The USBCameraAcquire component provides image data captured from a USB camera over its OutPort. The USBCameraMonitor component displays image data received over its InPort. Start them by selecting the following options from the Start menu:

Start > All Programs > OpenRTM-aist > C++ > examples > USBCameraAcquireComp / USBCameraMonitorCpomp

Connect the components

Using RTSystemEditor, connect the three components as in Figure 22.


RTSystemEditor_connection_flip.png
Figure 22. Connecting the components.


Changing the Flip component configuration parameters

The Flip component's configuration parameters can be changed as in Figure 23. For example, when using the Elecom UCAM-DLM 130HWH USB camera, set image_height and image_width as below.

 image_height : 480
 image_width  : 640

Do the same for the USBCameraMonitor component.


RTSystemEditor_config_edit.png
Figure 23. Changing configuration parameters.


Activate the components

Click the "All" icon in the toolbar for RTSystemEditor. All the components will be activated. If all components started succesfully, RTSystemEditor should resemble Figure 24.


RTSystemEditor_activate.png
Figure 24. Activated components.


Test

Try changing the Flip component's "flip_mode" configuration parameter to different values of -1, 0 and 1. The displayed image should change accordingly.

Flip component source file

 // -*- 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)
 {
   // Image memory store allocation
   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)
 {
   // Image memory store deallocation
   cvReleaseImage(&m_image_buff);
   cvReleaseImage(&m_flip_image_buff);
   return RTC::RTC_OK;
 }
 
 
 RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id)
 {
   // Check for new data
   if (m_image_origIn.isNew()) {
     // Read data from the InPort
     m_image_origIn.read();
 
     // Copy the image data from the InPort to IplImage.imageData
     memcpy(m_image_buff->imageData,(void *)&(m_image_orig.data[0]),m_image_orig.data.length());
 
     // Flip the image data. m_flip_mode 0: flip on X, 1: flip on Y, -1: flip on both
     cvFlip(m_image_buff, m_flip_image_buff, m_flip_mode);
 
     // Get the image data size
     int len = m_flip_image_buff->nChannels * m_flip_image_buff->width * m_flip_image_buff->height;
     m_image_flip.data.length(len);
 
     // Copy the flipped image data to the OutPort
     memcpy((void *)&(m_image_flip.data[0]),m_flip_image_buff->imageData,len);
 
     // Output the image data
     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 component header file

 // -*- 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)  Include the OpenCV headers
 #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
    * - Image height
    */
   int m_img_height;
   /*!
    * 
    * - Name:  m_img_width
    * - DefaultValue: 320
    * - Image width
    */
   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 component prebuilt package

A prebuilt package of the component can be downloaded from here. Remove the underscore from the name and decompress the file.

Development of RTC (OpenCV: use of the type 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"にリネームしてから解凍してください。

Create RT Component (VC++ version)

RT-Component with DataPorts

Here, how to develop RT-Component(MRCConverter) with DataPort would be shown.

Overview of the RTC

This RTC converts x-y value from input device (ex. joystick) to the wheel velocity of differential drive mobile robot.

The specification of the RTC is the following.

  • InPort
    • Input values of x-y from joystick (TimedFloatSeq)
  • OutPort
    • Wheel velocity to the mobile robot (TimedFloatSeq)

Creating template codes of MRCConverter

Here, how to create template codes by using RtcTemplate would be shown.

Creating working folder

Create working directory in any place. In the following, the directory name is MRCConverter.

  1. Double-click "My Computer"
  2. Move to working folder
  3. Create new folder on the target folder

Using rtc-template (CUI version)

Create the following batch file (ex. gen.bat) on the working folder to run the rtc-template easily.

 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

Running rtc-template (with gen.bat)

Run the gen.bat, and the result will be the following.

 >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.

Some source codes and other files would be generated in the woking diretory.

RtcTemplate on Eclipse (GUI version)

Input the following profiles in the RtcTempalte's editor window.

  • 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

After input the component profile, push "Generate" button. Some source codes and other files would be generated in the working folder.

Running copyprops.bat

As the result of the above mentioned operation, copyprops.bat would be generated in the working folder. The copyprops.bat copies rtm_config.vsprops (Visual Studio Property Sheet) from installed OpenRTM-aist folder to current working folder. This file is necessary to build RTC on Visual C++.

Double-click te copyprops.bat.

Implementing MRCConvertor

Launching Visual Studio

Double-click MRCConvertorComp_vc?.sln and launch Visual Studio. XXX_vc8.sln is a solution file for VC2005, XXX_vc9.sln is a solution file for VC2008. Please select an appropriate solution file for your development environment.

Implementing Header File

Open the MRCConverter.h in "MRCConverterComp" -> "Header Files" from Solution Explorer.

  • Including other header files
    Since this RTC uses STL vector and math library, include these two header files.

 #include <vector>
 // To use Math::M_PI in VC8
 #define _USE_MATH_DEFINES
 #include <math.h>

  • Uncomment "virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);"
  • Declaration of member variable and member function
    Declare a "convert()" function and its coefficient value "m_k" converting from input value to output wheel velocity.

 private:  float m_k;
 
  /*!
   * @brief This function converts input data from joystick to velocity of
   *        differential drive mobile robot.
   *        Only m_velFromInput.data[0 and 1] would be used.
   */
  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;
  }

Implementing Source File

Open the MRCConverter.cpp in "MRCConverterComp" -> "Source Files" from Solution Explorer.

  • Implementing onExecute()
    Uncomment "onExecute()" function and implement it as follows.

 /*!
  * @brief This function converts input data from joystick to velocity of
  *        differential drive mobile robot.
  */
 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;
 }

This function does the following.

  1. Checking if a new data is coming by using m_velFromInputIn.isNew() function.
  2. Reading the data by m_velFromInputIn.read() function, if a new data is coming.
  3. Converting input data to a velocity of wheels by convert() function.
  4. Setting converted data to the OutPort variable, and write to OutPort buffer.

Running Build Proces

Click "Build" menu button and build the project.

Creating rtc.conf

Create new file and input the following lines in it. Copy this file to Debug and Release folders.

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

Running Component

Starting CORBA Name Server

Before running an RT-Component, launch CORBA name server. You can find the name server launching batch file from "Start"->"Program"->"OpenRTM-aist"->"C++"->"Example"->"NameService.bat"

Running MRCConvertorComp.exe

Go to "Debug" or "Release" folder and run MRCConverter.exe.

Development of RT Component (Java version)

Overview

OpenRTM-aist Java Overview

OpenRTM-aist is a reference implementation of RT-Middleware which The National Institute of Advanced Industrial Science and Technology - Intelligent Systems Research Institute - Task Intelligence Research Group, has been implementing, distributing, maintaining. The RT-Middleware as well as OpenRTM-aist are the software platforms which divide the various functional components of the robot into the part unit called RT-Component and support the construction of various robotic systems by the integration of various these elements. Currently, in OMG(Object Management Group), these are conformed to the Robotic Technology Component Specification which have been decided.
OpenRTM-aist Java is that OpenRTM-aist for C++ has been ported to Java. OpenRTM-aist Java which has OpenRTM-aist compatible interfaces for C++ enable to use both RT-Component developed using Java and RT-Component developed using C++.

Target

This manual provides procedures for development of RT-Components for Java using OpenRTM-aist Java. This manual targets those who have the basic knowledge of Java.

Prerequisites

Here is a list of environments that are required to run OpenRTM-aist Java.

Table 3-1 Prerequisites

Environment Notes
Java Development Kit 5.0 (JDK 5)
(http://java.sun.com/products/archive/j2se/5.0_12/index.html )
Java1.4 is not supported.


For more details on the installation procedures, refer to "OpenRTM-aist Java Installation manual. Please check the following before moving on to the development procedures
---The version when running "java -version" matches the version of JDK above (Table1-1).

Here we explain on procedures to develop RT-Components for Java. We will use a component with the following specifications as a sample.

Table 3-2 Specifications of the Sample Component

Basic profile
Component Name sample
Discription SampleComponent
Version 1.0
Vendor AIST
Category example
Component Type DataFlowComponent
Activity Type SPORADIC
Maximum number of instances 5
Data InPort
Name in
Type TimedLong
ServiceProvider
IDLPath IDLs/MyService.idl See Note
Port Name MySvcPort
Service Name myservice0
Service Type MyService
Configuration parameter
Name multiply
Parameter Type int
Default value 1


Note: MyService.idl creates the following IDL file using the appropriate editor. Also, the "IDL Path" in above table is specified to the MyService.idl Path. On Windows, this "IDL Path" must be the full path to 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


The above MyService.idl is the same as the sample one in the folder of Examples/RTMExamples/SimpleService included in OpenRTM-aist-0.4 Java.

Development Procedure of RT-Component with CUI

Generating Skeleton Code with rtc-template

First, you generate the skeleton of RT-Component which you would like to develop using rtc-template. The rtc-templates which have used on CUI as well as GUI are distinguished as rtc-template (for CUI) and RtcTmplate (for GUI). (Otherwise, when rtc-template is described simply, it means that it is used as general term of CUI edition and GUI edition.)


-rtc-template for CUI
When RT-Component which has the following specifications described in Table3-2 is generated using rtc-template for CUI, run this command.

 
 rtc-template -bjava     --module-name=Sample --desc= 'Sample Component'     --module-version=1.0 --module-vendor=AIST --module-category=example     --module-comp-type=DataFlowComponent --module-act-type=SPORADIC     --module-max-inst=5 --inport=in:TimedLong     --service=MySvcPort:myservice0:MyService --service-idl=IDLs/MyService.idl     --config="multiply:int:1"

Note:When rtc-template is executed in the windows environment, you must enclose in double quotation (") such as "Sample component". When enclose in single quotation ('), the options can't work. Also, the line continuous symbols are need to be modified from (\) to (^).


:By this operation, these files are generated in the current directory.|
    • Sample.java ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Classes to define Component Profile, Initialization etc
    • Sampleimpl.java ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Main class for a RT-Component
    • SampleComp.java ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Class for launching RT-Component
    • MyServiceSVC_impl.java ・・・・・・・・・・・・ Service implementation class
    • build_Sample.xml ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Build file for the RT-Component
    • README.Sample ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Read Me file

Building RT-Component which was created

We will imprement the target RT-Component for development based on the skeleton code which was generated with rtc-template. After implementing, buid RT-Component. In OpenRTM-aist Java, the component can be build on both CUI and GUI. In this section, we will explain how to build on CUI.
  • Build using Apache Ant
    RT-Component is built using Apache Ant of the build tool via the console. You can download Apache Ant from the following URL. (For more details on the installation procedures for Apache Ant, refer to it, too.)
    http://ant.apache.org/
    After installing Apache Ant, move the directory which the build target exists and build it with this command.

 > $ANT_HOME$bin\ant -f build_Sample.xml


:After the build has completed successfully, the file class will be generated in the directory "classes" under the directory which the build executed. This is the sample screen for the build below.|
RTcomponentsBuild.png
Fig.3-5 Build RT-Component using Apache Ant




Development Procedure of RT-Component with GUI

Collaboration between RtcTemplate and JDT

  • Open "New" on Eclipse
    It is also possible to develop RT-Component via Project on Eclipse which is Integrated Development Environment. Specify a new workspace and launch Eclipse. (Although you see a "Welcome" screen at that time, close this.)
    CheckedWorkSpace2.png
    Fig.3-6 Specify a New Workspace
  • Create the Java project file
    Select [File]-[New]-[Project] in the upper menu bar.
    MakeProjectForBulid1.png
    Fig.3-7 Create the project to build (1)

Select "Java Project" in "New Project" wizard and Press [Next] button.

MakeProjectForBulid2.png
Fig.3-8 Create the project to build (2)


:On the next step of "New Project" wizard, enter "project name" in the dialog box. Check setting of "Compiler compliance level" in "JDK Compliance" is over "5.0" (or over 1.5) and press [Next] button.|

MakeProjectForBulid3.png
Fig.3-9 Create the project to build (3)


Set each setting of the creating project in "New Java Project" screen and click on [Finish] button.

ProjectSettings.png
Fig.3-10 Finished by setting each configuration



 
The specified project is generated and displayed in the "Package Explorer" view.

PackageExplorerView.png
Fig.3-11 Display on Package Explorer view




Note: For more details on the options, setting on Eclipse when creating the project, refer to Eclipse website(http://www.eclipse.org/).


Generating Skeleton Code with RtcTemplate

  • Launch of RtcTemplate editor for GUI
    Open the editor window of RtcTemplate -> See Launching RtcTemplate directly
    Note: please refer RtcTemplate manuals about how to use RtcTemplate and other topics.

  • Editing configuration items using RtcTemplate editor
    Here is the configurations when you generate skeleton codes for RT-Component which have the specification of Table 3-2, using RtcTemplate for GUI.
    GUIrtc-templateSetting1.png

    GUIrtc-templateSetting2.png
    Fig.3-12 Settings of GUI RtcTemplate


:Note:On Windows you must specify a full path for "Output directory", "IDL path:", and so on.|

  • Generating codes by [Generate] button
    Press [Generate] button to perform code generations. When you generate, make sure to specify the project directory which has been generated before, as "Output directory" field existing bottom of RtcTemplate editor.(Note: "temp" is specified in the figure.)
    GenCode.png
    Fig.3-13 Generate Codes

  • Adding a variety of generated files to the project
    These files are generated in the directory you specified as "Output directory".
    • Sample.java ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Class to define Component Profile, initialization process, and so on
    • Sampleimpl.java ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Main class for a RT-Component
    • SampleComp.java ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Class to launch the RT-Component
    • MyServiceSVC_impl.java ・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Service implementation class
    • build_Sample.xml ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Build file for the RT-Component
    • README.Sample ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Read Me file


: By specifying the project directory as "Output directory" field, a variety of generated files are to be added to the project (automatically).|

Done.png
Fig.3-14 Adding a variety of files


 
Note Even after code generations are finished, sometimes generated files are not reflected on the project at "Package Explorer". To refresh the information, right-click the target project and select [Refresh] from the context menu which is shown.
UpdateProject.png
Fig.3-15 Refrech of the shown project


Note In the case of RT-Components which define service ports, errors occur when there are only files generated by Rtctemplate. It is because some of generated files use classes which are generated from IDL files. These required classes will be generated automatically when you build the component.


Note:If OpenRTM-aist Java is not installed on a location which the classpath of the project refers, errors occur, In this case, modify Java Build Path properly of the project, and include the install folder(directory) of OpenRTM-aist Java in the classpath.

ProjectContextMenu.png
Fig.3-16 Right-click the project


ProjectProperty.png
Fig.3-17 Adding OpenRTM-aist Java to the build path



Building RT-Component with Eclipse

  • Ant Build
    You can build a target RT-Component by right-click build_Sample.xml in Package Explorer, then select [Execute]-[Ant Build].
    BuildProject.png
    Fig.3-18 Build of the project


BuildProject-consoleview.png
Fig.3-19 Console pane when the build executes


If the build has completed successfully, the class file will be generated in the directory "classes" in project.




Running RT-Component which was created

  • Create rtc.conf
    Create the file rtc.conf included the following contents in "classes" within the project.
     corba.nameservers: localhost
     naming.formats: %n.rtc
    rtc.conf

The above rtc.conf is the same as the sample in the folder of "Examples/RTMExamples/SimpleService" included in OpenRTM-aist-0.4 Java.

-Start the Name Server and RtcLink
Double-click start-orbd.bat in the folder "Examples" of samples included in OpenRTM-aist-0.4 Java (Windows), or execute start-orbd.sh (UNIX) to start the Name Server. In addition, launch RtcLink .
    • References
      • Start Name Server
      • Starting RtcLink

        -Execution of RT-Component
        Open a command prompt or terminal, and set the "classes" directory as current directory. If type like
         > java SampleComp
        RT-Component will be shown on RtcLink.
        JavaSampleOnRtcLink.png
        Fig.3-20 State of RtcLink when Sample component succeeds to execute




Details on RT-Components for Java

Structure of RT-Components for Java

The relation between source files of RT-Components for Java, and general functions included in each file is shown in Fig.3-21. For comparison, RT-Components for C++ and RT-Components Java for OpenRTM-aist-0.3 are also shown.

InnerRTcomponents.png
Fig.3-21 Structure of RT-Components


There are these differences between existing RT-Components for C++ and RT-Components for Java.
  • Separation of main body of RT-Components functions
    In RT-Components for Java, issues about launching and others moved main body of RT-Components functions to XXXImpl classes (In Fig.3-21, <Sample>Impl.java). This makes original RT-Component classes (In Fig.3-21, <Sample>.java) consist of only Component Profile definitions and generating operations to generate a variety of components.
  • Changing callback functions to interfaces
    Parts defined as callback functions in RT-Components for C++ are interfaces in those for Java.
    • ModuleInitProc: Interface for classes to launch components
    • RtcNewFunc: Interface to create RT-Components
    • RtcDeleteFunc: Interface to destruct RT-Components

This change make it mandatory for component launch classes to implement those interfaces.

Point of Difference between RT-Components for Java and RT-Components for C++

Data Ports

In OpenRTM-aist Java, Holder class (DataRef Class) is added to pass data. This changes on how to define and use data ports as follows:

RT-Components for Java RT-Components for C++
//Definition of InPort
protected TimedShort m_in_val
protected DataRef<TimedLong> m_in;
protected InPort<TimedLong> m_inIn;

//Registration of 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);

//Reading data from InPort
m_inIn.read();
Input Data = m_in.v.data;
//Definition of InPort
TimedShort m_in;
InPort<TimedShort> m_inIn;

//Registration of InPort
m_inIn = new InPort<TimedLong>("in",m_in);
registerInPort("in",m_inIn);

//Reading data from InPort
m_inIn.read();
Input Data = m_in.data;
//Definition of OutPort
protected TimedFloat m_out_val;
protected DataRef<TimedFloat> m_out;
protected OutPort<TimedFloat> m_outOut;

//Registration of 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);

//Writing data to OutPort
m_out_val.data = Output Data;
m_outOut.write();
//Definition of OutPort
TimedFloat m_out;
OutPort<TimedFLoat> m_outOut;

//Registration of OutPort
m_outOut = new OutPort<TimedFloat>("out",m_out);
registerOutPort("out",m_outOut);

//Writing data to OutPort
m_out.data = Output Data;
m_outOut.write();

For more detail on how to use Data Ports, refer to the samples of "SeqIO" and "SimpleIO".

 

Service Ports

In OpenRTM-aist Java, the auxiliary variables (<Service Name>Base) for using service ports are added. This changes on how to define and use service ports as follows. For more detail, refer to the samples of "SimpleService".

RT-Components for Java RT-Components for C++
//Definition of Consumer
protected CorbaPort m_MyServicePort;
protected CorbaConsumer<MyService> m_myservice0Base
= new CorbaConsumer<MyService>(MyService.class);
protected MyService m_myservice0;

//Registration of Consumer
m_MyServiceRef = new CorbaPort("MyService");
m_MyServicePort.registerConsumer
("myservice0","MyService",m_myservice0Base);
registerPort(m_MyServicePort);

//Use of Consumer
m_myservice0 = m_myservice0Base._ptr();
m_myservice0.echo(argv[1]);
//Definition of Consumer
RTC::CorbaPort m_MyServicePort;
RTC::CorbaConsumer<MyService> m_myservice0;

//Registration of Consumer
m_MyServicePort = new RTC::CorbaPort("MyService");
m_MyServicePort.registerConsumer
("myservice0","MyService", m_myservice0);
registerPort(m_MyServicePort);

//Use of Consumer
m_myservice0->echo(argv[1].c_str());
//Definition of Provider
protected CorbaPort m_MyServicePort;
protected MyServiceSVC_impl m_myservice0
= new MyServiceSVC_impl();

//Registration of Provider
m_MyServiceRef = new CorbaPort("MyService");
m_MyServicePort.registerProvider
("myservice0","MyService",m_myservice0);
registerPort(m_MyServicePort);
//Definition of Provider
RTC::CorbaPort m_MyServicePort;
MyServiceSVC_impl m_myservice0;

//Registration of Provider
m_MyServicePort = new RTC::CorbaPort("MyService");
m_MyServicePort.registerProvider
("myservice0","MyService",m_myservice0);
registerPort(m_MyServicePort);



Configuration

Holder classes are used for configurations as well as data ports. This changes the way how to define and use configuration data as follows:

RT-Components for Java RT-Components for C++
//Definition of Configuration Variable
protected IntegerHolder m_int_param0 = new IntegerHolder();
protected StringHolder m_str_param0 = new StringHolder();
protected VectorHolder m_vector_param0= new VectorHolder();

//Bind parameters
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");
//Definition of Configuration Variable
int m_int_param0;
str::string m_str_param0;
str::vector<double> m_vector_param0;

//Bind parameters
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");

Please refer to samples of "ConfigSample", about how to use configuration data.
Table 3-3 shows kinds of holder classes provided in OpenRTM-aist Java to hold configuration data, and their releations with their data types.

Table 3-3 Relation in holders for configuration data

Data type Holder class
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

As well OpenRTM-aist for C++, users can create any holder for configuration data which corresponds with any type they define, in OpenRTM-aist Java.
To implement a holder for configuration data, implement stringFrom method of jp.go.aist.rtm.RTC.util.ValueHolder and declare the holder implements Serializable interface in its implements clause.
"stringFrom" method of jp.go.aist.rtm.RTC.util.ValueHolder is a method to convert a string passed by its argument to its targeted data type.
Please refer to VectorHolder class in "ConfigSample" sample, about holders for configuration data.

Behavior when RT-Components for Java start

Here is the behavior when RT-Components for C++ start. This is basically same as the behavior when RT-Components for Java start, however separation of main body of RT-Components for to XXXImpl classes change receivers of message which are sent when components are created and when components are initialized, to XXX classes.
JavaRTcomponent1.png

JavaRTcomponent2.png

Relationship between IDL data types and Java language data types

Table 3-4 shows the relationship between CORBA IDL data types and Java language data types.

Table 3-4 Relationship between data types

CORBA IDL Java language
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

Others

Tips

Configuration to Start up Eclipse automatically

    • On Windows
      • 1. Select a ".project" file from the directory of Eclipse project, and right-click to select [Properties]from the shown context menu.
        Note: you can select any Eclipse project.
        SelectProject.png
        Fig.3-22 Selecting a ".project" file
      • 2. Click [Change] button which is in the right of "Program" line center at "General" tab in the displayed "Properties" dialog.
        Property.png
        Fig.3-23 Changing an association with an application
      • 3. Click [Browse] button lower in "Open With" dialog.
        SelectApplication.png
        Fig.3-24 Selecting Eclipse
      • 4. A dialog to select a file is shown, Go to the directory in which Eclipse you want to start up automatically existes, and select "Eclipse.exe".
      • 5. Press [OK] to close "Open With" dialog and "Properties" dialog.
        ::Once you set up as above, you can double-click a ".project" file to run the version of Eclipse you specified automatically.
        |
        ::Note You must notice that the version you specified is always started up, even if several versions of Eclipse are installed.
        |
        Note When the project you clicked is not included in the workspace set up for the starting Eclipse, the project does not appear in Package Explorer of the Eclipse. In this case, you need to import the target project to the workspace, or switch to another workspace.


        --On Linux
        You can specify a workspace with a data option when you start up Eclipse.
         > eclipse ?data /home/devel/OpenRTM/workspace
        Please specify the workspace which contains the target project.


        Acknowledgments

OpenRTM-aist Java was developed using the following libraries. We would like to extend out thanks to anyone who was involved in the designing and developing of these projects.
Apache Commons CLI 1.1
This product includes software developed by The Apache Software Foundation (http://www.apache.org/ ).

Development of RT Component(OpenCV Components 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"にリネームしてから解凍して下さい。

Development of RT Component (NXTway case)


はじめに

  • 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 component

LEGO Mindstorms NXT RT-Component

LEGO Mindstorms NXT is a collection of LEGO components for creating robots. It includes 3 motors, 4 types of sensor and an intelligent brick, the NXT brick, for connecting and controlling them. NXT is connected via USB or Bluetooth to a PC, from which it can be directly controlled and programmed. It can also be loaded with user-created programs for operation independent of a PC.

This article will describe how to create an RT-Component (RTC) for the intelligent brick on a PC and, using other components, control connected motors and read connected sensors. With a single component for the NXT brick, controlling motors and reading sensors using existing RT-Components is simple.

Preparing SD Card

はじめに

ここでは、LEGO Mindstorms EV3 上で OpenRTM-aist とそのコンポーネントを動作させるための実行環境のインストールについて説明します。

ここでは、openrtm.org が提供する OpenRTM-aist 入りの OSイメージをダウンロードし、各種セットアップについて説明します。 EV3上で OpenRTM を使用できるようにするまでの大まかな手順は以下の通りです。

  • SDカードに OSイメージを書き込む
  • OS の基本的なセットアップ
  • コンポーネントの実行テスト

SD カード

EV3 には micro SD カードスロットが一つあり、ここに起動した OS を書き込んだ micro SD カードを差し込むと、任意の OS を起動することができます。

用意する SDカードは 2GB以上 32GB以下のも micro SDカード になります。mini SD や SDカードは刺さりませんのでご注意ください。 また、書き込むイメージは約2GB程度ありますので、最低で2GBの容量が必要となります。EV3 は 32GBより大きい SDXC仕様の SDカードには対応していませんので、注意してください。

  • 用意するSDカード
    • micro SDカード
    • 2GB以上、32GB以下

OSイメージのダウンロード

EV3上 での OpenRTM-aist の実行には ev3dev という OS を使用します。

以下のサイトから ev3-ev3dev-jessie-2015-12-30.img.zip をダウンロードしてください。 名前が似たファイルが多数配布されているので間違えないようにしてください。

ev3dev OSイメージのダウンロード

ev3dev とは EV3 上で Linux のディストリビューションの1つである、Debian GNU Linuxを EV3 に搭載した EV3用の Debian ディストリビューションです。 OpenRTM-aist を動作させるには、この ev3dev を micro SDカードに書き込み、EV3 を SDカードから起動させます。

上記の ev3dev オフィシャルWebページから ev3dev の OSイメージファイルがダウンロードできますが、OpenRTM-aist などはインストールされていません。 基本的には、以下のリンクから OpenRTM-aist (C++、Python) 入りの ev3dev イメージファイルをダウンロードしてください。

サンプルコンポーネント入りのイメージ

Educator Vehicle等のサンプルコンポーネント入りのイメージです。

EV3の無線LANアダプタを交換した場合に、無線LANアクセスポイントモードが正常に動作しない場合があります。 その場合は他のアクセスポイントに接続する等して、以下のコマンドを実行して70-persistent-net.rulesを編集します。 ユーザー名はrobot、パスワードはmakerでログインして操作してください。

 sudo nano /etc/udev/rules.d/70-persistent-net.rules

具体的には70-persistent-net.rulesのSUBSYSTEMから始まる行を全てコメントアウトします。

 # USB device 0x:0x (rtl8192cu)
 SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:22:cf:f6:52:a5", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan1"

イメージの展開

ダウンロードしたファイル YYYY-MM-DD-ev3dev-openrtm.zip を展開してください。 YYYY-MM-DD-ev3dev-openrtm.img という2GB位のファイルが展開されているはずです。

Windows

ファイルを右クリックして「すべて展開」を選択すると、展開できます。

Linux

 $ unzip <イメージファイル>

で展開できます。unzip コマンドがない場合はインストールしてください。

 $ unzip 2015-08-06-ev3dev-openrtm.zip
 Archive:  2015-08-06-ev3dev-openrtm.zip
   inflating: 2015-08-06-ev3dev-openrtm.img
 $ ls -l
 合計 2321300
 -rw-rw-r-- 1 n-ando n-ando 1887436800  8月  4 21:37 2015-08-06-ev3dev-openrtm.img
 -rw-rw-r-- 1 n-ando n-ando  489565916  8月  5 10:13 2015-08-06-ev3dev-openrtm.zip

うまく展開できない場合、ダウンロードに失敗しファイルが壊れている可能性があります。壊れたファイルを削除して、再度ダウンロードしてみてください。

イメージの書き込み

展開された yyyy-mm-dd-ev3dev-openrtm.img はイメージファイルといい、ev3dev が起動するディスクの状態をディスクの最初から最後まで1バイトづつ抜き出したものです。 このファイルを SDカードに単純にコピーしても使用することはできません!!

以下に説明する方法で SDカードに書き込んでください。

イメージの書き込み (Windows)

Windows では Win32DiskImager というツールを使用することでイメージの書き込みができます。 以下のサイトからイメージデータ書き込みツール Win32DiskImager のバイナリをダウンロードします。

ダウンロードしたファイル (win32diskimager-vX.X-binary.zip ) を解凍します。

※Win32DiskImager は、2バイト文字に対応していないため、YYYY-MM-DD-ev3dev-openrtm.zip は途中のパス名に全角文字や空白が含まれていない場所に解凍してください。

Raspberry Pi で使用する SD カードを PCに挿入し、Win32DiskImager を起動します。

※SD カードはドライブとして認識されている必要があるので、事前に FAT32 形式でフォーマットしておいてください。

「Image File」に解凍したRaspbian のイメージファイル (YYYY-MM-DD-wheezy-raspbian.img)、「Drive」にSD カードのドライブを指定し、「Write」ボタンをクリックします。

win32diskimager.png
イメージデータの書き込み

以上で SD カードの準備は終了です。 書き込みが終了したら、SD カードを Raspberry Pi に設置し、電源を投入します。

イメージの書き込み (Linux)

Linux では dd コマンドを利用してイメージの読み書きができます。 dd コマンドは UNIX系の OS なら大抵デフォルトでインストールされています。

SDカードを差し込んでから、 dmesg コマンドでカーネルのメッセージを確認します。

 $ dmesg
   : 中略
 [333478.822170] sd 3:0:0:0: [sdb] Assuming drive cache: write through
 [333478.822174]  sdb: sdb1 sdb2
 [333478.839563] sd 3:0:0:0: [sdb] Assuming drive cache: write through
 [333478.839567] sd 3:0:0:0: [sdb] Attached SCSI removable disk
 [333479.094873] EXT4-fs (sdb2): mounted filesystem with ordered data mode
 [333527.658195] usb 1-1: USB disconnect, address 2

このメッセージから SDカードのデバイス名を確認します。この例では sdb が SDカードのデバイス名のようです。/dev/の下を見てみます。

 ls -al /dev/sd*
 brw-rw---- 1 root disk 8,  0 May  7 17:28 /dev/sda
 brw-rw---- 1 root disk 8,  1 May  7 17:28 /dev/sda1
 brw-rw---- 1 root disk 8,  2 May  7 17:28 /dev/sda2
 brw-rw---- 1 root disk 8,  5 May  7 17:28 /dev/sda5
 brw-rw---- 1 root disk 8, 16 May 18 14:19 /dev/sdb
 brw-rw---- 1 root disk 8, 17 May 18 14:19 /dev/sdb1
 brw-rw---- 1 root disk 8, 32 May 18 14:19 /dev/sdc

sda は大抵システムディスクなので、絶対に触ってはいけません。

ディストリビューションによっては、SDカード内にマウント可能なファイルシステムがある場合自動でマウントするケースもあるようです。 その場合、ディスクをアンマウントしてください。(Ubuntuではデスクトップにマウントしたファイルシステムのフォルダーが現れるので右クリックで取り外してください。 それ以外は umount コマンドでアンマウントします。)

ubuntu_adcard_mount.png
Ubuntu場でマウントされたSDカード(右クリックメニューで取り外すことができる)

dd if=イメージファイル of=SDカードのデバイスファイル bs=1M のようにコマンドを入力し実行します。 ただし、デバイスファイルへの書き込みは管理者(root)権限が必要ですので、sudoを使用してください。

 $ sudo dd if=2015-08-05-ev3dev-openrtm.img of=/dev/sdb bs=1M
 1850+0 records in
 1850+0 records out
 1939865600 bytes (1.9 GB) copied, 201.543 s, 9.6 MB/s

実行中は別のターミナルなどで、iostat コマンドを実行して書き込みが正しく行われているかどうか見ることができます。 (最近のディストリビューションではデフォルトでインストールされていないことがあります。debian/ubuntu では apt-get install sysstat で iostatコマンドが使えるようになります。)

 $ iostat -mx 1
  avg-cpu:  %user   %nice %system %iowait  %steal   %idle
            0.00    0.00    0.00   50.25    0.00   49.75
 
 Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await  svctm  %util
 sda               0.00     0.00    0.00    1.00     0.00     0.00     8.00     0.00    0.00   0.00   0.00
 sdb               0.00  1856.00    0.00   78.00     0.00     9.14   240.00   143.40 1855.85  12.82 100.00

sdb の項目を見ると 9.14MB/s の書き込み速度が出ていることがわかります。 class 6 のSDカードなら 6MB/sec, class 10 の SDカードなら 10MB/sec 程度の速度が出ていれば、問題なく書き込まれていると考えてよいでしょう。 書き込みが終了すると、ディストリビューションによっては自動でマウントされる場合があります。その場合、アンマウントしてから SDカードを抜いてください。

イメージの書き込み (Mac OS X)

Mac OS X も Linuxと同様 dd コマンドを利用して書き込みます。 ただし、Mac では SDカードを挿入すると自動的にマウントされてしまい、マウント中は dd コマンドで SDカードに書き込むことができないので、アンマウント (OSから取り外す) する必要があります。

SDカードを差し込むとFinderに図のように SDカードのアイコンが現れます。 アンマウントするつもりでイジェクトボタンを押さないよう気を付けてください。

sdcard_mac.png
MacにマウントされたSDカード

SDカードのボリューム名はここでは Untitled です。ボリューム名を覚えておきます。 コマンドプロンプトから df コマンドを入力すると以下のように表示されます。

 $ df -k
 Filesystem                        1024-blocks      Used Available Capacity   iused    ifree %iused  Mounted on
 /dev/disk0s2                        500000000 437664508  62079492    88% 109480125 15519873   88%   /
 devfs                                     194       194         0   100%       679        0  100%   /dev
 map -hosts                                  0         0         0   100%         0        0  100%   /net
 map auto_home                               0         0         0   100%         0        0  100%   /home
 /dev/disk1s1                            57288     18992     38296    34%       512        0  100%   /Volumes/Untitled

 一番下 ''/Volumes/Untitled'' とあるのが先ほどの SDカードのマウントポイントです。一番左の SDカードのデバイス名 /dev/disk1s1 を覚えておきます。
この SDカードを一旦アンマウント します。diskutil というコマンドを使用し diskutil umount <マウントポイント> のように入力します。

 $ diskutil umount /Volumes/Untitled
 Volume (null) on disk1s1 unmounted
 $ df -k
 Filesystem                        1024-blocks      Used Available Capacity   iused    ifree %iused  Mounted on
 /dev/disk0s2                        500000000 437664716  62079284    88% 109480177 15519821   88%   /
 devfs                                     194       194         0   100%       679        0  100%   /dev
 map -hosts                                  0         0         0   100%         0        0  100%   /net
 map auto_home                               0         0         0   100%         0        0  100%   /home

先ほどの /Volumes/Untitled が消えて、SDカードがアンマウントされていることがわかります。 次に dd コマンドを使用してイメージを書き込みます。 dd if=イメージファイル of=/dev/rdisk1 bs=1m のように入力します。 of=/dev/rdisk1 は先ほど覚えたデバイスファイル /dev/disk1s1 のうち後ろの s1 を取り、さらに disk の前に raw deviceであることを示す r を付けたデバイス名です。

このコマンドはデバイスファイルにアクセスするので管理者 (root) でなければ実行できません。sudoを使用して以下のように実行します。

 $ sudo dd if=2015-08-05-ev3dev-openrtm.img of=/dev/rdisk1 bs=1m
 1850+0 records in
 1850+0 records out
 1939865600 bytes transferred in 302.377337 secs (6415380 bytes/sec)
 $

書き込み中は、「アクティビティモニタ」で「ディスクの動作」を見ることで書き込みが正しく行われているかどうかわかります。 class 6 の SDカードなら 6MB/sec, class 10のSDカードなら 10MB/sec 程度の速度が出ていれば、問題なく書き込まれていると考えてよいでしょう。

書き込みが終了すると、自動的に再度マウントされますので、今度は Finder のイジェクトボタンを押して SDカードを抜きます。

Mindstorms NXT Setup

Mindstorms NXT Setup

To create an RTC for the NXT, you must first setup your PC and NXT. The NXT can be connected to the PC by USB or Bluetooth, but because we don't want to tie our battery-powered, portable NXT to the PC with a cable, we will use Bluetooth.

Block assembly

Using the assembled robot shown in the photo as an example, we will create the NXT RTC.

TribotBase.png

This is a simple mobile base, called "Tribot," combined with a ultrasonic sensor (the "eyes") at the front. The Tribot construction instructions can be found in the NXT "Start Here" booklet included in the NXT kit. Attach the ultrasonic sensor to this base.

Bluetooth Device Installation

All revisions of the NXT intelligent brick include Bluetooth support. PCs without Bluetooth included can use USB Bluetooth adapters like those shown in the photo to communicate with the NXT. Install such a device if necessary, including any required device drivers. Most versions of Windows from XP SP2 on include suitable default device drivers.

BluetoothDevices.png

Once a Bluetooth device has been installed, a "Bluetooth Devices" item should appear in the control panel. Clicking on this will produce the dialog shown below.

BthDialogOption.png

Select "Options," and enable device discovery and displaying the Bluetooth icon in the notification area. We will connect the NXT to the PC next, so leave this dialog open.

Connecting the NXT to the PC

The process to connect the NXT to the PC via Bluetooth is given below.

  1. Turn on the NXT
  2. Put the NXT into Bluetooth search mode
  3. Select your PC
  4. Select a channel
  5. Start the Connection Wizard from the Bluetooth Devices dialog on the PC
  6. Set a pass key
  7. Push the connect button on both the PC and NXT
  8. Restart the NXT once it has connected.

Starting the NXT

Press the central orange button on the NXT to turn on the power. A sequence of beeps will sound (if the volume is not set to zero) and the brick will turn on. The screen should look like the image below.

NXTBoot.png

If it does not, use the square button below the orange button to navigate to the "My Files" mode.

Bluetooth Device Search

In "My Files" mode, press the triangular, grey buttons on either side of the orange button to move the cursor to the "Bluetooth" option, and press the orange button.

NXTBluetooth.png

Use the grey buttons again to move the cursor to the "Search" option.

NXTBthSearch.png

Press the orange button and a search for Bluetooth devices will be conducted. The screen will appear as below while searching.

NXTBthSearching.png

Device Connection

If the PC has Bluetooth enabled and is visible to the NXT, the PC's name (as set in Windows) should appear on the screen of the NXT.

NXTBthPCfound.png

If other Bluetooth-enabled PCs are nearby, you may see more than one appear on the NXT. Use the triangular buttons to move the cursor to the correct PC name, then press the orange button. The channel selection screen will appear next. Press the orange button on the default selection. After displaying "Connecting" for a short time, the pass key input screen will appear. Press the orange button.

NXTBthPasskey.png

An information balloon should appear on the PC. Click on it to close it.

PCballoon.png

If the PC asks for a pass key, input the key that was displayed on the NXT. Once a connection is established, a dialog like that shown below will appear. To prevent other devices interfering, check "Disable discovery" before clicking "Finish."

PCConnectComp.png

Connection confirmation

Click on the "Devices" tab in the Bluetooth Devices dialog. The NXT should appear as below.

PCDevlistNXT.png

Installing NXT Python

Before creating the NXT RTC, PyBluez (a module for using Bluetooh from Python) and NXT Python (a module for controlling a NXT) must be installed.

Installing PyBlues

Download the Windows installer from the above link and run it.

Installing NXT Python

Download the zip file from the above link. NXT Python uses a setup.py script to install. If you have associated .py files with the Python interpreter, execute the script as follows from a command prompt:

 setup.py install
Otherwise, execute it similar to the line below:
 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>

Test NXT Python

Turn on the NXT and connect it to the PC. Using the samples under example/, confirm that the NXT can be controlled from the PC.

The example/ directory contains the following samples:

  • latency.py: Measures the latency in reading the sensors.
  • mary.py: Plays "Mary Had a Little Lamb."
  • message_test.py: Displays a message on the NXT's screen.
  • spin.py: Spins motors connected to ports B and C.
  • test_sensors.py: Displays all sensor values.

Motors and sensors must be connected appropriately for each test.

Making a NXT Python RTC

After completing the above preparations, the NXT should be controllable from the PC using Python.

At this point, you could use RtcTemplate to generate a NXT component template and get stuck in coding. However, while NXT Python is a clean module, please read on a little before putting NXT Python directly into an RT-Component.

As you can see from the samples, NXT Python is organised into modules for locators, motors, sensors, etc. In order to provide fine control over motors and sensors, the access method is a little complex.

We will create a class that allows us to access the many NXT Python modules through a single interface. We will use the Facade software pattern.

 The facade pattern is a software engineering design pattern commonly used with Object-oriented programming. A facade is an object that provides a simplified interface to a larger body of code, such as a class library. (Source: "Facade pattern," Wikipedia)

Making such a class has the following benefits:

  • Use it in places other than the RTC
  • Easier to debug
    • Debug the facade in isolation
    • Debugging directly in the RTC makes it difficult to determine if a problem is because of the facade or the RTC.
  • Modifications are easier
    • Even if the facade class changes, if the interface does not change then the RTC component does not need to be changed.
    • For example, if you want to add limits to some functions called get_xxx() and set_xxx(), this will only require changes in the facade.
  • Easier to use devices other than the NXT
    • For example, switching to a new version of the NXT that uses the same interface.

This is generally the case for RT-Components. Make good classes for robots and devices you wish to interact with, and even if the RTC itself changes or a new version is made, it will be easy to interact and maintenance will be greatly simplified.

Avoid creating low-level code like the following in your RTC's onExecute() method:

 ioctl(xxxx, xxxx); // UNIX direct device access
 inb(xxx);          // Direct I/O
 outb(xxx);         // Direct I/O

Ideally, create code like the following:

 onExecute(ec_id) { // Pseudocode
    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.write(sensor_data);
    } else {
       // Processing a read error
       if (fatal_error) return RTC::RTC_ERROR;
    }
    return RTC::RTC_OK;
 }

Code like this calls a function to process data from the input port and write sensor data to the output port. This sort of abstract code is ideal.

NXT Python Facade Class

NXT Python Facade Class

Nearly all functions of the NXT brick can be controlled using NXT Python. However, because utilising all functions is complex, the facade class will only focus on those functions we want to use. The main functions of the NXT are:

  • Input
    • Set motor speed
    • Control the speaker
    • Display messages
  • Output
    • Read motor encoder values
    • Read sensors (microphone, ultrasonic, touch, light)
    • Read system information
  • Other
    • Search for a NXT
    • Connect to a NXT
    • Read files from the NXT
    • Read the NXT firmware

There is little meaning to putting all of these functions into the facade class. When another function is necessary, the facade can be extended to include it. In keeping with this, the facade described here will supply the functions necessary for the robot we will control.

  • Input
    • Set motor speed: setMotors()
  • Output
    • Read motor encoders: getMotors()
    • Read sensors (microphone, ultrasonic, touch, light): getSensors()

A class made along these lines is shown below.

 #!/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):
         """
         Constructor
         Connect to a NXT brick, control motors and sensors, and reset odometry.
         """
         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):
         """
         Close the connection to the NXT.
         """
         self.sock.close()
 
     def resetPosition(self, relative = 0):
         """
         Reset the NXT motor encoders.
         """
         for m in self.motors:
             m.reset_position(relative)
 
     def setMotors(self, vels):
         """
         Receive an array, set the motor power levels.
         If the length of vels is not equal to the number of motors,
         the smaller of the two values is used.
         """
         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):
         """
         Read the motor encoder angles.
         
         """
         state = []
         for m in self.motors:
             state.append(m.get_output_state())
         return state
 
     def getSensors(self):
         """
         Read the sensor values, return them in an array.
         """
         state = []
         for s in self.sensors:
             state.append(s.get_sample())
         return state
 
 
 """
 Test program
 Set a suitable motor value, read their encoders.
 Read sensor values and display them.
 """
 if __name__ == "__main__":
     import time
     nxt = NXTBrick()
     print "connected"
     
     # Motor test
     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])
 
     # Sensor test
     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)

The final section from if name == "main": is a test program. When this module is executed independently, the test will run. The module should be tested and corrected until it passes the test succesfully.

The above NXT facade class is very simple, only setting motor values, reading the encoders and reading the sensors. Trying to do everything from the start leads to a class whose purpose is difficult to understand. The class can be extended at any time, so begin with something simple that works as it should.

NXT RTC Implementation

NXT RTC Implementation

We shall now use the NXTBrick class created above to create an RTC.

  • InPort
    • Motor speed (TimedFloatSeq)
  • Outport
    • Motor position (TimedFloatSeq)
    • Sensor data (TimedFloatSeq)

Generate NXTRTC base code

Generate the NXT RTC template

We shall use RtcTemplate to generate a template component. You can use the command line tool, rtc-template, or the Eclipse-based tool, RtcTemplate, to create the component.

If using rtc-template, make a batch file containing the following (remember to adjust paths as necessary):

 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"

Executing rtc-template using a created 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.

If using RtcTemplate under Eclipse, use the following options:
  • 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

Following these instructions should create the NXTRTC.py file containing the template component.

Sample code explanation

Sample code explanation

We will now add the NXTBrick.py functionality to the generated component.

  • NXTBrick.py import: Import the NXTBrick.py file to get access to the NXTBrick class. The file extension is not necessary in the import statement.

 import NXTBrick

  • Implement onInitialize(self): In onInitialize(), instantiate the NXTBrick class. If this causes an error, return RTC_ERROR and the component will transition to the end state.
            # create NXTBrick object
            try:
                self._nxtbrick = NXTBrick.NXTBrick()
            except:
                print "NXTBrick create failed."""
                return RTC.RTC_ERROR
  • Implement onActivated(self, ec_id) and onDeactivated(self, ec_id): In onActivated() and onDeactivated(), the NXTBrick class's resetPosition() method should be called.
            self._nxtbrick.resetPosition() 
  • Implement onExecute(self, ec_id): The following steps should be performed by onExecute():
    • Read speeds from the data InPort
    • Based on configuration values, which port of the NXT the speed values are to be sent to should be set.
    • Call the setMotors() method of the NXTBrick class to set motor speeds.
    • Call the getSensors() method of the NXTBrick class to read the ultrasonic sensor data, and write this to an OutPort.
    • Call the getMotors() method of the NXTBrick class to get the motor encoder angles, and write this to an OutPort.

Code that implements the above functionality is shown below:

 #!/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()

Sample code explanation (Callback object usage example)

This sample code adds a callback, OnWrite, to the above sample. When data is written to the InPort's buffer, the motor's speeds will be set immediately.

  • Callback class
    Create a callback class as below.

 # @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_)

This class receives a NXTBrick instance and a configuration parameter map in its constructor.

  • Callback class registration
    Use the setOnWrite() method to register a CallBackClass instance with the component.

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

After this call to setOnWrite(), whenever data is written to the InPort, CallBackClass.call() will be called.

Sample code that implements the complete component using a callback class is shown below.

 #!/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()
 

In the first example, the motor output, sensor reading and motor encoder reading are all done in a single synchronous loop. By using a callback, the motor output is done asynchronously when data arrives.

NXT RTC Test

We will now test our NXT RT-Component.

Start the name server

The name server must be started to connect components together. If you have installed the Windows C++ version of OpenRTM-aist, the Start Menu will contain an entry for this: OpenRTM-aist->C++->examples->Start Naming Service.

Write an rtc.conf file

Put the following into a file named "rtc.conf":

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

Ensure the value of corba.nameservers matches the address of the name server you want to use. In this case, a name server running on localhost is used. Copy this file to the directories of components you want to use. Other than NXTRTC, we will also use:
  • TkJoystickComp
  • TkMotorPosComp
  • TkSliderMonitorComp

Start RtcLink

Start RtcLink. Once it has started, connect to your name server and open the System Diagram Editor.

Start Components

Execute the following components:
  • NXTRTC
  • TkJoystickComp
  • TkMotorPosComp
  • TkSliderMonitorComp As each is executed, it will appear in the name service view of RtcLink.

TkJoystickComp

TkJoyStickComp is a graphical virtual joystick component (see image, below). The centre circle can be dragged like a joystick to send X/Y values out its upper OutPort. The lower OutPort outputs values suitable for controlling a differential drive robot, such as the Tribot.

TkJoystick.png

TkMotorComp

TkMotorComp is a GUI-based component for displaying motor angles received at its InPort. It can be used to monitor the rotation of wheels. Connect the angle output port of NXTRTC to its input port to display the angle of the Tribot's wheels. The angle will change even if you turn the wheels by hand.

TkMotor.png

TkSliderMonitorComp

This component displays values received at its input port in a GUI using sliders. Connect the sensor output of the NXTRTC component to it to monitor the NXT's sensor values.

Connect Components

After executing all components, we will connect them together. Drag each component from the name service view into the system diagram editor window. Click and drag between the ports you want to connect together. The screenshot below shows one example of connecting some components.

RtcLink.png

Using these components, confirm that the NXTRTC component functions correctly.

Make your own components to control the NXT brick

You have successfully made the NXT into an RT-Component. Now, make new components using your own logic to control the NXT. You can make new components using Python, C++ or Java. No matter what language you use, you can connect your new components with the NXTRTC component created here.

Using GUI Tool Kit for RTCs

はじめに

Python のサンプルとして付属している GUIジョイスティック
http://svn.openrtm.org/OpenRTM-aist-Python/trunk/OpenRTM-aist-Python/OpenRTM_aist/examples/TkJoyStick/TkJoyStickComp.py

移動ロボットのシンプルなシミュレーターのサンプル
http://svn.openrtm.org/OpenRTM-aist-Python/trunk/OpenRTM-aist-Python/OpenRTM_aist/examples/MobileRobotCanvas/TkMobileRobotSimulator.py

などが一例です。

TkJoystickComp の例

TkJoystickComp のサンプルの main関数ですが、以下のようになっています。

 def main():
   tkJoyCanvas = tkjoystick.TkJoystick() # GUIオブジェクトを作成
   tkJoyCanvas.master.title("TkJoystick")
   mgr = OpenRTM_aist.Manager.init(sys.argv) # RTCマネージャを作成
   mgr.activateManager() #マネージャを活性化
 
   # コンポーネントの登録
   profile = OpenRTM_aist.Properties(defaults_str=tkjoystick_spec)
   mgr.registerFactory(profile,
                       TkJoyStick,
                       OpenRTM_aist.Delete)
   # コンポーネントの生成
   comp = mgr.createComponent("TkJoyStick")
 
   # このコンポーネントには set_pos というメンバ関数があり、
   # GUIジョイスティックの座標をコンポーネントに渡すために使用される。
   # TkJoystickのGUI の update イベントが呼ばれるときに、
   # こいつを呼ぶようにコールバックとしてセットする。
   tkJoyCanvas.set_on_update(comp.set_pos)
   mgr.runManager(True) # マネージャの runManager の引数に True を渡して non-block モードでマネージャを起動
   tkJoyCanvas.mainloop() # PythonのGUI のメインループに入る。
 
 if __name__ == "__main__":
   main()

ポイント

コンポーネントの中から GUI のメインループ関数を呼んだり、GUI オブジェクトを生成したりはしないほうが無難

GUI ツールキットの中には、mainスレッドから呼ばなければならないものや、 同一のスレッドから呼ばれることを前提としている関数などを持つものがある。RTC は onInitialize/onFinalize を呼ぶスレッドと、onExecute/onActivated/onDeactivated その他を呼ぶスレッドは異なります。また、onExecute 他の関数は複数のスレッドから同時に呼ばれる可能性すらあります。GUI スレッドと RTC とは別に扱ったほうがトラブルは少ないでしょう。

GUI と RTC のデータのやり取りは、RTC にそのための関数を追加することで対応

    • 上記のような理由から、GUI とコンポーネントは切り離したほうがよいのですが、GUI と RTC とデータのやり取りをしなければならない時に困ります。通常 RTC にデータのやり取りをするためのインターフェースを別途継承させて、createComponent で取得したコンポーネントへのポインタを取得して GUI からデータを取得したり、データを与えたりします。
       class IMyInterface
       {
       public:
           virtual int getData() = 0;
           virtual void setData(double x, double y) = 0;
       };
       
       class MyComponent
        : public IMyInterface,
          public RTC::DataFlowComponentBase
       {
        : コンポーネントの定義
       public:
           virtual int getData()
           {
               coil::Guard guard(m_outdatalock);
               return m_outdata;
            }
          virtual void setData(double x, double y)
           {
               coil::Guard guard(m_indatalock);
               m_indata.x = x;
               m_indata.y = y;
           }
       };
      このようにコンポーネントを実装しておいてから、
        RTObject_impl* rtobj = mgr.createComponent("MyComponent");
        IMyInterface* comp = dynamic_cast<IMyInterface*>(rtobj);
        if (comp == 0) { abort(); }
        mgr.runManager(true);
        gui.mainloop();
      そして、GUIのコントロール内で
        std::cout << "out data: " << comp->getData() << std::endl; // データを取得
        comp->setData(1.0, 2.0); // データを入力
      のようにRTCとデータのやり取りをします。

RTC と GUI のコントロールやウィジェットを1対1対応にしておくとわかりやすい

ただし、GUI コントロールと RTC はそれぞれ生成方法が異なるので、別々に生成して、上述したように RTC に実装したデータ入出力関数を通じて GUI コントロールと結びつけたほうがよいでしょう。

 
移動ロボットのシンプルなシミュレーターのサンプルでは、移動ロボットオブジェクトとそれに対応する RTC を動的に増やしたり減らしたりしています。
http://svn.openrtm.org/OpenRTM-aist-Python/trunk/OpenRTM-aist-Python/OpenRTM_aist/examples/MobileRobotCanvas/TkMobileRobotSimulator.py

LEGO Mindstorms EV3 use case

LEGO Mindstorms EV3 は LEGO の Mindstorms シリーズの新しいパッケージです。EV3のメインのコントローラーは、Linux が標準搭載され、様々な言語でロボットの開発が可能になりました。

また、NXT では外部との無線通信が基本的に Bluetooth のみでしたが、EV3 では USBインターフェースが搭載され、無線LAN の USBアダプタを挿すことで無線LANなどで外部と通信することも可能になりました。

搭載される OS が Linux になったことで、これまでよりもさらに柔軟に、かつ高度なロボット開発が可能になりました。

このドキュメントでは、LEGO Mindstorms EV3 上に OpenRTM-aist を搭載し、ロボット制御に活用する方法、コンポーネントの開発方法を紹介します。

仕様

EV3 の外観を以下に示します。

ev3.png
LEGO Mindstorms EV3

EV3のコンピュータは以下の仕様になっています。

LEGO Mindstorms EV3 仕様
プロセッサ ARM9 300MHz
メモリ(ROM) 16MB Flash
メモリ(RAM) 64MB RAM
OS Linuxベース
ディスプレイ 178 x 128 pixels
出力ポート 4個
入力ポート 4個
アナログ
デジタル 460.8kbit/s
USB通信速度 High Speed (480Mbps)
USBインターフェース EV3同士の連結可能 (最大4台)
Wi-Fi通信ドングル利用可能
SDカードスロット Micro SDカード 32GBまでサポート
スマートデバイス接続 iOS, Android, Windows
ユーザーインターフェース 6ボタン, イルミネーション機能
プログラムサイズ (ライントレースの場合) 0.950KB
センサー通信性能 1000 回/秒, 1ms
データロギング 最大 1,000サンプリング/秒
Bluetooth通信 最大7台のスレーブと接続可能
動力 リチャージブルバッテリー または、単3電池 6本

なお、EV3では、リチャージブルバッテリーが同梱されています。スペックは下記のとおりです。

電池の種類 リチウムイオン
容量 2050mAh
NXT DCバッテリとの互換性 なし
NXT DCアダプタでの充電 可能
単三電池で動かした場合との比較 単三電池を利用した場合より充電式 DCバッテリーのほうが長く動く。
充電時間 4時間(フル充電の場合)

このBookの概要

このBookでは OpenRTM-aist で RTコンポーネントを開発・実行するための環境構築方法、便利に使うためのノウハウ、移動ロボットの制御や IO の利用方法などを解説します。


Tutorial (EV3)

このページでは RTM講習会での EV3 操作手順を説明します。
実習では以下の Educator Vehicle 改を制御します。


/jp/node/6038

LEGO Mindstorms EV3 は LEGO の Mindstorms シリーズの新しいパッケージです。EV3 のメインのコントローラーは、Linux が標準搭載され、様々な言語でロボットの開発が可能になりました。

仕様

LEGO Mindstorms EV3 仕様
プロセッサ ARM9 300MHz
メモリ(ROM) 16MB Flash
メモリ(RAM) 64MB RAM
OS Linuxベース
ディスプレイ 178 x 128 pixels
出力ポート 4個
入力ポート 4個
アナログ
デジタル 460.8kbit/s
USB通信速度 High Speed (480Mbps)
USBインターフェース EV3同士の連結可能 (最大4台)
Wi-Fi通信ドングル利用可能
SDカードスロット Micro SDカード 32GBまでサポート
スマートデバイス接続 iOS、Android、Windows
ユーザーインターフェース 6ボタン, イルミネーション機能
プログラムサイズ (ライントレースの場合) 0.950KB
センサー通信性能 1000 回/秒、1ms
データロギング 最大 1,000サンプリング/秒
Bluetooth通信 最大7台のスレーブと接続可能
動力 リチャージブルバッテリー または、単3電池 6本

デバイス

EV3 には以下のデバイスが付属しています。

ジャイロセンサー
45505_GyroSensor.jpg
確度モード: 精度 +/- 3°
角速度モード: 最大 440 deg/sec
サンプリングレート 1,000 Hz
カラーセンサー
45506_color.jpg
計測: 赤色光の反射光、 周囲の明るさ、色
検出カラー数: 8色 (無色、黒、青、緑、黄、赤、白、茶)
サンプリングレート 1,000 Hz
距離 約1mm~18mm(アフレル調査値)
タッチセンサー
45507_TouchSensor.jpg
オン (1), オフ (0)
スイッチ可動域: 約4mm
超音波センサー
45504_UltrasonicSensor.jpg
距離計測可能範囲: 3cmから250cm
距離計測精度: +/- 1 cm
前面電飾: 点灯:超音波発信中、 点滅:超音波観測中
EV3 Lモーター
45502_LargeMotor.jpg
フィードバック: 1°単位
回転数: 160から170RPM
定格トルク: 0.21 N・m (30oz*in)
停動トルク: 0.42 N・m (60oz*in)
重さ: 76 g
EV3 Mモーター
45503_MediumMotor.jpg
フィードバック 1°単位
回転数: 240から250RPM
定格トルク: 0.08 N・m (11oz*in)
停動トルク: 0.12 N・m (17oz*in)
重さ: 36 g

ダウンロード

最初にPC側で使用する RTC 等をダウンロードしてください。

ZIPファイルを Lhaplus 等で展開してください。

EV3 の組み立て方

EV3 は分解した状態で参加者に配ります。 組み立て方は以下の通りです。

※超音波センサー、カラーセンサー、ジャイロセンサーについては、講習で使用しないため取り付ける必要はありません。 以下の※の作業については、時間が余った人が実施してください。

まずは土台部分を取り出してください。


s_DSC00463.JPG

最初にMモーターにケーブル(25cm)を接続します※。


s_DSC00446.JPG

次に EV3 本体を取り付けます。 Mモーターにケーブルを接続した場合は、ケーブルが左側の隙間から出るようにしてください。


s_DSC00448.JPG s_DSC00450.JPG



右側のタッチセンサーを取り付けてください。


s_DSC00451.JPG s_DSC00452.JPG



超音波センサーを取り付けてください※。


s_DSC00454.JPG

ケーブルを接続してください。 必須なのは車輪駆動用のLモーター右、Lモーター左、タッチセンサーだけです。

Lモーター右 ポート C 25cmケーブル
Lモーター左 ポート B 25cmケーブル
Mモーター※ ポートA 25cmケーブル
タッチセンサー右 ポート 3 35cmケーブル
タッチセンサー左 ポート 1 35cmケーブル
超音波センサー※ ポート 4 50cmケーブル
ジャイロセンサー※ ポート 2 25cmケーブル

ケーブルは EV3 の上下に A~D と 1~4 のポートがあるのでそこにケーブルを接続します。


s_DSC00.JPG s_DSC00471.JPG




s_DSC00455.JPG s_DSC00456.JPG



左右にパーツを取り付けます※。 Lモーター右、Lモーター左、Mモーター、タッチセンサー右、タッチセンサー左のケーブルを挟むようにして取り付けてください※。
Lモーター右、タッチセンサー右のケーブルは右側から、Lモーター左、Mモーター、タッチセンサー左は左側から通してください※。


s_DSC00457.JPG


s_DSC00459.JPG

これでとりあえず完成ですが、余裕のある人はジャイロセンサーを取り付けてみてください※。


s_DSC00460.JPG s_DSC00461.JPG



電源の入れ方/切り方

電源の入れ方

中央のボタンを押せば電源が投入されます。


ev3_on.jpg

電源の切り方

EV3 の電源を切る場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Power Off」を選択してください。


ev3_off.jpg


s_DSC01033.JPG

再起動

再起動する場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Reboot」を選択してください。

リセット

ev3dev の起動が途中で停止する場合には、中央ボタン、戻るボタン(左上)、左ボタンを同時押ししてください。画面が消えたら戻るボタンを離すと再起動します。


ev3_reset.jpg

EV3 への接続

EV3 へは原則として無線LANで接続するようにしてください

無線LANアクセスポイントへの接続

まずは EV3 の中央のスイッチを押して電源を投入してください。
ここで電源を投入する前に無線LANアダプタを取り付けておいてください。

以下の作業でスクリプトを実行するとアクセスポイントが起動します。

EV3 の操作画面から「File Browser」を上下ボタンで選択して中央のボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
 [File Browser               > ]
  Device Browser             >
  Wireless and Networks      > 
  Battery                    >
  Open Roberta Lab           >
  About                      >
 ------------------------------

次に scripts を選択して中央ボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot
 ------------------------------
 [scripts                     ]
 ・・
 ・・
 ------------------------------

次の画面から start_ap.sh を選択して中央ボタンを押すとスクリプトが起動します。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot/scripts
 ------------------------------
 ../
 Component/
 ・・
 [start_ap.sh                 ]
 ------------------------------

しばらくすると無線LANアクセスポイントが起動するので、指定の SSID のアクセスポイントに接続してください。 SSID、パスワードは EV3 に貼り付けたテープに記載してあります。


tutorial_ev3_irex26.png

アクセスポイントへの接続方法は以下のページを参考にしてください。

まず右下のネットワークアイコンをクリックしてください。


/ja/node/6042

次に一覧から ev3_***を選択してください。


tu_ev3_11.png

パスワードを入力してください。


/ja/node/6042

USBケーブルでの接続

以下の作業は有線で接続する場合の作業なので、無線で接続する場合は不要です。

付属の USBケーブルで EV3 と PC を接続してください。


s_DSC00467.JPG

中央のボタンを押して電源を投入してください。

ここで EV3 の電源を投入する前に無線LANアダプタは取り外しておいてください。
以下の画面が表示されていれば ev3dev の起動に成功していますが、起動途中で停止した場合は この手順 で再起動してください。


s_DSC00470.JPG

デバイスマネージャで「EV3+ev3dev」が「その他のデバイス」の下にある場合は正しく機能していないので、以下の手順でデバイスソフトウェアの更新を行ってください。

まずはコントロールパネルからデバイスマネージャを開いてください。


tu_ev3_17.png


tu_ev3_18.png

開いたら EV3+ev3dev を右クリックして「ドライバソフトウェアの更新」を選択してください。
ここで EV3+ev3dev がネットワークアダプターの下にある場合は正常に認識されているので以下の作業は不要です。


tu_ev3_1.png

「コンピューターを参照してドライバー ソフトウェアを検索します」を選択してください。


tu_ev3_3.png

「コンピューター上のデバイス ドライバーの一覧から選択します」を選択してください。


tu_ev3_4.png

ネットワーク アダプターを選択して次へをクリックしてください。


tu_ev3_5.png

製造元は「Microsoft Corporation」、ネットワークアダプタは「Remote RNDIS Compatible Device」を選択して次へをクリックしてください。


tu_ev3_9.png

ドライバー更新警告が出た場合は「はい」を選択してください。


tu_ev3_7.png

正常に更新されたら閉じるを選択してください。


tu_ev3_8.png

事前準備

このページ の手順に従ってネームサーバー、RTシステムエディタを起動してください。 予めネームサーバーを起動してある場合は再起動してください。

またネットワークインターフェースが2つ以上ある場合に通信に失敗する可能性があるため、有線で接続した場合は他のネットワークデバイスを無効にしてからネームサーバーを起動してください。


/ja/node/6042


/ja/node/6042

ネームサーバー追加

続いて RTシステムエディタのネームサーバー追加ボタンで192.168.0.1(無線LANで接続する場合は192.168.11.1)を追加してください。


/jp/node/6042 tu_ev3_25.png



するとEducatorVehicle0という RTC が見えるようになります。

EducatorVehicle は EV3 の走行速度の入力、センサーのデータの出力等を行うためのコンポーネントです。


 /ja/node/6030

EducatorVehicle
InPort
名前 データ型 説明
velocity2D RTC::TimedVelocity2D 目標速度
angle RTC::TimedDouble Mモーターの角度
lcd RTC::TimedString LCDに表示する画像ファイル名
sound RTC::TimedString 出力する音声
OutPort
名前 データ型 説明
odometry RTC::TimedPose2D 現在の位置・姿勢
ultrasonic RTC::RangeData 超音波センサーで計測した距離
gyro RTC::TimedDouble ジャイロセンサーで計測した角度
color RTC::TimedString カラーセンサーで計測した色
light_reflect RTC::TimedDouble カラーセンサーで計測した反射光の強さ
touch RTC::TimedBoolean タッチセンサーのオンオフ。右側が0番目の要素、左側が1番目の要素
コンフィギュレーションパラメーター
名前 デフォルト値 説明
wheelRadius 0.028 車輪の半径
wheelDistance 0.054 タイヤ間距離の1/2
medium_motor_speed 1.6 Mモーターの速度

TimedVelocity2D型について

TimedVelocity2D型は以下のように定義されています。

     struct Velocity2D
     {
         double vx;
         double vy;
         double va;
     };

     struct TimedVelocity2D
     {
         Time tm;
         Velocity2D data;
     };

vx、vy、va はロボット中心座標系での速度を表しています。


tu_ev3_19.png

vx は X方向の速度、vy は Y方向の速度、va は Z軸周りの角速度です。

Educator Vehicle のように2個の車輪が左右に取り付けられているロボットの場合、横滑りしないと仮定すると vy は0になります。

vx、va を指定することでロボットの操作を行います。

音声の出力

sound による音声の入力には以下のコマンドを利用できます。

  • beep
    beep と入力するとビープ音が鳴ります。
  • tone
    以下のように tone、周波数、時間と入力すると指定周波数の音を指定ミリ秒数だけ鳴らします。

 tone,100,1000

  • それ以外
    それ以外は指定文字列を発音します

LCD に表示する画像について

LCD で表示する画像は付属資料の software/saveBinaryImage/EXE/saveBinaryImage.exeで変換したものを利用してください。
画像ファイルを saveBinaryImage.exe にドラッグ・アンド・ドロップすれば変換できます。

サンプルコンポーネント起動

付属資料のstart_component_ev3.batを起動してください。 ※OpenRTM-aist Python版をインストールしていない場合、もしくはインストールに失敗している場合は個別に実行ファイル入りのUSBメモリーを配布いたしますので、その中のstart_component_ev3_exe.batを利用してください。

すると以下の2つの RTC が起動します。

tu_ev3_24.png

動作確認

まずはジョイスティックで EV3 を操作してみます。

RTシステムエディタで EducatorVehicle、FloatSeqToVelocity、TkJoyStick を以下のように接続します。


tutorial_ev3_16.png

そして RTC をアクティブ化するとジョイスティックで EV3 の操作ができるようになります。


tutorial_ev3_21.png

自作の RTC で制御

まずは FloatSeqToVelocity の out と EducatorVehicle の target_velocity_in のコネクタを切断してください。

tutorial_ev3_17.png

FloatSeqToVelocity と EducatorVehicle の間に自作の RTC を接続して、タッチセンサーがオンになった場合に停止して音を鳴らすようにします。

ひな形コードの作成

RTC ビルダを起動してください。


/jp/node/6038

起動したら新規にプロジェクトを作成します。


/jp/node/6038

プロジェクト名は TestEV3CPP(TestEV3Py)にします。

以下のように設定を行ってください。 C++、もしくは Python で作成します。

基本
モジュール名 TestEV3CPP、もしくはTestEV3Py
アクティビティ
有効アクション onInitialize、onExecute、onActivated、onDeactivated
データポート
InPort
名前 データ型 説明
velocity_in RTC::TimedVelocity2D 入力目標速度
touch RTC::TimedBooleanSeq タッチセンサーのオンオフ
OutPort
名前 データ型 説明
velocity_out RTC::TimedVelocity2D 出力目標速度
sound RTC::TimedString 音声
コンフィギュレーション
名前 説明
sound_output string タッチセンサーが ON の時に発する音声。デフォルト値は beep
言語・環境
言語 C++、もしくはPython

[コード生成] ボタンを押したらコードが生成されます。


tutorial_raspimouse10.png

プロジェクト生成

コードが生成できたら C++ の場合は CMake で Visual Studio のプロジェクト(Ubuntu の場合は Code::Blocks)を生成してください。

まず CMake (cmake-gui) を起動します。

  • Windows 7

/jp/node/6038

  • Windows 8.1


/jp/node/6038

起動したらソースコードのディレクトリー、ビルドを行うディレクトリーに以下を指定します。 括弧内は eclipse の作業ディレクトリーを C:\workspace にした場合の例です。

Where is the source code RTCBuilder で生成したコードのフォルダー(C:\workspace\TestEV3CPP)
Where to build the binaries RTCBuilder で生成したコードのフォルダーの下に作成したbuildフォルダー(C:\workspace\TestEV3CPP\build)


tutorial_ev3_24.png

[Configure] ボタン→ [Generate] ボタンをクリックするとVisual Studioのプロジェクトが生成されます。

ソースコードの編集

build ディレクトリーの TestEV3CPP.sln を開いてください。

次にコードの編集を行います。

Pythonの場合はまず変数の初期化部分を修正してください。

  • TestEV3Py.py

     def __init__(self, manager):
         #self._d_velocity_in = RTC.TimedVelocity2D(*velocity_in_arg)
         self._d_velocity_in = RTC.TimedVelocity2D(RTC.Time(0,0),RTC.Velocity2D(0,0,0))

         #self._d_distance_sensor = RTC.TimedShortSeq(*distance_sensor_arg)
         self._d_distance_sensor = RTC.TimedShortSeq(RTC.Time(0,0),[])

         #self._d_velocity_out = RTC.TimedVelocity2D(*velocity_out_arg)
         self._d_velocity_out = RTC.TimedVelocity2D(RTC.Time(0,0),RTC.Velocity2D(0,0,0))

         #self._d_buzzer = RTC.TimedShort(*buzzer_arg)
         self._d_buzzer = RTC.TimedShort(RTC.Time(0,0),0)

まずは onExecute で入力速度をそのまま出力するコードを書いてみます。
C++の場合は以下のようになります。
isNew関数で新規の入力データが存在するかを確認して、read関数で変数(m_velocity_in)に格納します。 そして m_velocity_out に出力データを格納して write関数を呼び出すとデータが送信されます。

  • src/TestEV3CPP.cpp

     if (m_velocity_inIn.isNew())
     {
         m_velocity_inIn.read();
         //入力速度をそのまま出力
         m_velocity_out.data.vx = m_velocity_in.data.vx;
         m_velocity_out.data.vy = m_velocity_in.data.vy;
         m_velocity_out.data.va = m_velocity_in.data.va;
         setTimestamp(m_velocity_out);
         m_velocity_outOut.write();
 
 
     }

Pythonの場合は以下のようになります。

  • TestEV3Py.py

         if self._velocity_inIn.isNew():
             data = self._velocity_inIn.read()
             #入力速度をそのまま出力する
             self._d_velocity_out.data.vx = data.data.vx
             self._d_velocity_out.data.vy = data.data.vy
             self._d_velocity_out.data.va = data.data.va
             OpenRTM_aist.setTimestamp(self._d_velocity_out)
             self._velocity_outOut.write()

次にタッチセンサーがオンの場合に停止する処理を記述します。
常にタッチセンサーのデータが入力されるとは限らないので、センサーのデータを格納する変数を宣言します。
C++の場合は TestEV3CPP.h に記述します。

  • include/TestEV3CPP/TestEV3CPP.h

  private:
      bool m_last_sensor_data[2];


Pythonの場合はコンストラクタに記述します。

  • TestEV3Py.py

     def __init__(self, manager):
         OpenRTM_aist.DataFlowComponentBase.__init__(self, manager)
 
         self._last_sensor_data = [False, False]

次に onExecute に停止する処理を記述します。
C++の場合は以下のようになっています。
まずインポート touch に isNew 関数で新規にデータが入力されたかを確認して、入力されている場合は read関数で読み込みます。そして変数 m_last_sensor_data に格納します。
そしてインポート velocity_in で受信したデータの vx が 0 以上の場合には前進しているため障害物に接触するかもしれないと判定して、タッチセンサーがオンの場合は停止してブザーを鳴らします。

  • src/TestEV3CPP.cpp

 RTC::ReturnCode_t TestEV3CPP::onExecute(RTC::UniqueId ec_id)
 {
     //データを新規に受信した場合に、データをm_last_sensor_dataを格納する
     if (m_touchIn.isNew())
     {
         m_touchIn.read();
         if (m_touch.data.length() == 2)
         {
             for (int i = 0; i < 2; i++)
             {
                 //タッチセンサがOFFからONになった時に音を鳴らす
                 if (!m_last_sensor_data[i] && m_touch.data[i])
                 {
                     m_sound.data = m_sound_output.c_str();
                     setTimestamp(m_sound);
                     m_soundOut.write();
 
 
                 }
                 m_last_sensor_data[i] = m_touch.data[i];
             }
         }
     }
     if (m_velocity_inIn.isNew())
     {
         m_velocity_inIn.read();
         //vxが0以上(前進)のときのみ停止するか判定する
         if (m_velocity_in.data.vx > 0)
         {
             for (int i = 0; i < 2; i++)
             {
                 //タッチセンサがONの時に停止する
                 if (m_last_sensor_data[i])
                 {
                     //停止する
                     m_velocity_out.data.vx = 0;
                     m_velocity_out.data.vy = 0;
                     m_velocity_out.data.va = 0;
                     setTimestamp(m_velocity_out);
                     m_velocity_outOut.write();
 
 
                     return RTC::RTC_OK;
                 }
             }
         }
         //入力速度をそのまま出力
         m_velocity_out.data.vx = m_velocity_in.data.vx;
         m_velocity_out.data.vy = m_velocity_in.data.vy;
         m_velocity_out.data.va = m_velocity_in.data.va;
         setTimestamp(m_velocity_out);
         m_velocity_outOut.write();

   return RTC::RTC_OK;
 }

Pythonの場合は以下のようになっています。

  • TestEV3Py.py

     def onExecute(self, ec_id):
         #データを新規に受信した場合に、データをm_last_sensor_dataを格納する
         if self._touchIn.isNew():
             data = self._touchIn.read()
             if len(data.data) == 2:
                 for i in range(2):
                     #タッチセンサがOFFからONになった時に音を鳴らす
                     if not self._last_sensor_data[i] and data.data[i]:
                         self._d_sound.data = self._sound_output[0]
                         OpenRTM_aist.setTimestamp(self._d_sound)
                         self._soundOut.write()
                 self._last_sensor_data = data.data[:]
 
         if self._velocity_inIn.isNew():
             data = self._velocity_inIn.read()
             #vxが0以上(前進)のときのみ停止するか判定する
             if data.data.vx > 0:
                 for d in self._last_sensor_data:
                     #タッチセンサーがONの時に停止する
                     if d:
                         #停止する
                         self._d_velocity_out.data.vx = 0
                         self._d_velocity_out.data.vy = 0
                         self._d_velocity_out.data.va = 0
                         OpenRTM_aist.setTimestamp(self._d_velocity_out)
                         self._velocity_outOut.write()
                         
                         return RTC.RTC_OK
 
             #入力速度をそのまま出力する
             self._d_velocity_out.data.vx = data.data.vx
             self._d_velocity_out.data.vy = data.data.vy
             self._d_velocity_out.data.va = data.data.va
             OpenRTM_aist.setTimestamp(self._d_velocity_out)
             self._velocity_outOut.write()
         return RTC.RTC_OK

コードの編集が終わったら C++ の場合はビルドしてください。
ビルドに成功すると build\src\Release(Debug) に TestEV3CPPComp.exe が生成されます。

動作確認

TestEV3CPPComp.exe (TestEV3CPPComp.py) をダブルクリックして起動してください。
TestEV3CPP(TestEV3Py)を以下のように接続してください。


tutorial_ev3_18.png

最後に RTC をアクティブ化して動作確認してください。

RTシステム保存

RTシステムを保存する場合は System Diagram 上で右クリックして Save As... を選択してください。


tutorial_ev3_20.png


/jp/node/6038


RTシステム復元

復元する場合は Open and Restore を選択して、先ほど保存したファイルを選択してください。


/ja/node/6042

RTC 終了

RTC を終了する場合は RTシステムエディタ上で RTC を exit してください。


tutorial_ev3_19.png

補足

スクリプトファイルについて

EV3 のボタン操作で File Brower を選択すると/home/robot以下のディレクトリーの操作ができます。
scripts フォルダー内のシェルスクリプトを実行することで以下の操作ができます。

スクリプトファイル名 内容
run_rtcs.sh RTC を起動する
stop_rtcs.sh RTC を終了する

Tutorial (EV3)

このページでは RTM講習会での EV3 操作手順を説明します。
実習では以下の Educator Vehicle 改を制御します。


/jp/node/6038

LEGO Mindstorms EV3 は LEGO の Mindstorms シリーズの新しいパッケージです。EV3 のメインのコントローラーは、Linux が標準搭載され、様々な言語でロボットの開発が可能になりました。

仕様

LEGO Mindstorms EV3 仕様
プロセッサ ARM9 300MHz
メモリ(ROM) 16MB Flash
メモリ(RAM) 64MB RAM
OS Linuxベース
ディスプレイ 178 x 128 pixels
出力ポート 4個
入力ポート 4個
アナログ
デジタル 460.8kbit/s
USB通信速度 High Speed (480Mbps)
USBインターフェース EV3同士の連結可能 (最大4台)
Wi-Fi通信ドングル利用可能
SDカードスロット Micro SDカード 32GBまでサポート
スマートデバイス接続 iOS、Android、Windows
ユーザーインターフェース 6ボタン, イルミネーション機能
プログラムサイズ (ライントレースの場合) 0.950KB
センサー通信性能 1000 回/秒、1ms
データロギング 最大 1,000サンプリング/秒
Bluetooth通信 最大7台のスレーブと接続可能
動力 リチャージブルバッテリー または、単3電池 6本

デバイス

EV3 には以下のデバイスが付属しています。

ジャイロセンサー
45505_GyroSensor.jpg
確度モード: 精度 +/- 3°
角速度モード: 最大 440 deg/sec
サンプリングレート 1,000 Hz
カラーセンサー
45506_color.jpg
計測: 赤色光の反射光、 周囲の明るさ、色
検出カラー数: 8色 (無色、黒、青、緑、黄、赤、白、茶)
サンプリングレート 1,000 Hz
距離 約1mm~18mm(アフレル調査値)
タッチセンサー
45507_TouchSensor.jpg
オン (1), オフ (0)
スイッチ可動域: 約4mm
超音波センサー
45504_UltrasonicSensor.jpg
距離計測可能範囲: 3cmから250cm
距離計測精度: +/- 1 cm
前面電飾: 点灯:超音波発信中、 点滅:超音波観測中
EV3 Lモーター
45502_LargeMotor.jpg
フィードバック: 1°単位
回転数: 160から170RPM
定格トルク: 0.21 N・m (30oz*in)
停動トルク: 0.42 N・m (60oz*in)
重さ: 76 g
EV3 Mモーター
45503_MediumMotor.jpg
フィードバック 1°単位
回転数: 240から250RPM
定格トルク: 0.08 N・m (11oz*in)
停動トルク: 0.12 N・m (17oz*in)
重さ: 36 g

ダウンロード

最初にPC側で使用する RTC 等をダウンロードしてください。

ZIPファイルを Lhaplus 等で展開してください。

EV3 の組み立て方

EV3 は分解した状態で参加者に配ります。 組み立て方は以下の通りです。

※超音波センサー、カラーセンサー、ジャイロセンサーについては、講習で使用しないため取り付ける必要はありません。 以下の※の作業については、時間が余った人が実施してください。

まずは土台部分を取り出してください。


s_DSC00463.JPG

最初にMモーターにケーブル(25cm)を接続します※。


s_DSC00446.JPG

次に EV3 本体を取り付けます。 Mモーターにケーブルを接続した場合は、ケーブルが左側の隙間から出るようにしてください。


s_DSC00448.JPG s_DSC00450.JPG



右側のタッチセンサーを取り付けてください。


s_DSC00451.JPG s_DSC00452.JPG



超音波センサーを取り付けてください※。


s_DSC00454.JPG

ケーブルを接続してください。 必須なのは車輪駆動用のLモーター右、Lモーター左、タッチセンサーだけです。

Lモーター右 ポート C 25cmケーブル
Lモーター左 ポート B 25cmケーブル
Mモーター※ ポートA 25cmケーブル
タッチセンサー右 ポート 3 35cmケーブル
タッチセンサー左 ポート 1 35cmケーブル
超音波センサー※ ポート 4 50cmケーブル
ジャイロセンサー※ ポート 2 25cmケーブル

ケーブルは EV3 の上下に A~D と 1~4 のポートがあるのでそこにケーブルを接続します。


s_DSC00.JPG s_DSC00471.JPG




s_DSC00455.JPG s_DSC00456.JPG



左右にパーツを取り付けます※。 Lモーター右、Lモーター左、Mモーター、タッチセンサー右、タッチセンサー左のケーブルを挟むようにして取り付けてください※。
Lモーター右、タッチセンサー右のケーブルは右側から、Lモーター左、Mモーター、タッチセンサー左は左側から通してください※。


s_DSC00457.JPG


s_DSC00459.JPG

これでとりあえず完成ですが、余裕のある人はジャイロセンサーを取り付けてみてください※。


s_DSC00460.JPG s_DSC00461.JPG



電源の入れ方/切り方

電源の入れ方

中央のボタンを押せば電源が投入されます。


ev3_on.jpg

電源の切り方

EV3 の電源を切る場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Power Off」を選択してください。


ev3_off.jpg


s_DSC01033.JPG

再起動

再起動する場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Reboot」を選択してください。

リセット

ev3dev の起動が途中で停止する場合には、中央ボタン、戻るボタン(左上)、左ボタンを同時押ししてください。画面が消えたら戻るボタンを離すと再起動します。


ev3_reset.jpg

EV3 への接続

EV3 へは原則として無線LANで接続するようにしてください

無線LANアクセスポイントへの接続

まずは EV3 の中央のスイッチを押して電源を投入してください。
ここで電源を投入する前に無線LANアダプタを取り付けておいてください。

以下の作業でスクリプトを実行するとアクセスポイントが起動します。

EV3 の操作画面から「File Browser」を上下ボタンで選択して中央のボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
 [File Browser               > ]
  Device Browser             >
  Wireless and Networks      > 
  Battery                    >
  Open Roberta Lab           >
  About                      >
 ------------------------------

次に scripts を選択して中央ボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot
 ------------------------------
 [scripts                     ]
 ・・
 ・・
 ------------------------------

次の画面から start_ap.sh を選択して中央ボタンを押すとスクリプトが起動します。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot/scripts
 ------------------------------
 ../
 Component/
 ・・
 [start_ap.sh                 ]
 ------------------------------

しばらくすると無線LANアクセスポイントが起動するので、指定の SSID のアクセスポイントに接続してください。 SSID、パスワードは EV3 に貼り付けたテープに記載してあります。


tutorial_ev3_irex26.png

アクセスポイントへの接続方法は以下のページを参考にしてください。

まず右下のネットワークアイコンをクリックしてください。


/ja/node/6042

次に一覧から ev3_***を選択してください。


tu_ev3_11.png

パスワードを入力してください。


/ja/node/6042

USBケーブルでの接続

以下の作業は有線で接続する場合の作業なので、無線で接続する場合は不要です。

付属の USBケーブルで EV3 と PC を接続してください。


s_DSC00467.JPG

中央のボタンを押して電源を投入してください。

ここで EV3 の電源を投入する前に無線LANアダプタは取り外しておいてください。
以下の画面が表示されていれば ev3dev の起動に成功していますが、起動途中で停止した場合は この手順 で再起動してください。


s_DSC00470.JPG

デバイスマネージャで「EV3+ev3dev」が「その他のデバイス」の下にある場合は正しく機能していないので、以下の手順でデバイスソフトウェアの更新を行ってください。

まずはコントロールパネルからデバイスマネージャを開いてください。


tu_ev3_17.png


tu_ev3_18.png

開いたら EV3+ev3dev を右クリックして「ドライバソフトウェアの更新」を選択してください。
ここで EV3+ev3dev がネットワークアダプターの下にある場合は正常に認識されているので以下の作業は不要です。


tu_ev3_1.png

「コンピューターを参照してドライバー ソフトウェアを検索します」を選択してください。


tu_ev3_3.png

「コンピューター上のデバイス ドライバーの一覧から選択します」を選択してください。


tu_ev3_4.png

ネットワーク アダプターを選択して次へをクリックしてください。


tu_ev3_5.png

製造元は「Microsoft Corporation」、ネットワークアダプタは「Remote RNDIS Compatible Device」を選択して次へをクリックしてください。


tu_ev3_9.png

ドライバー更新警告が出た場合は「はい」を選択してください。


tu_ev3_7.png

正常に更新されたら閉じるを選択してください。


tu_ev3_8.png

事前準備

このページ の手順に従ってネームサーバー、RTシステムエディタを起動してください。 予めネームサーバーを起動してある場合は再起動してください。

またネットワークインターフェースが2つ以上ある場合に通信に失敗する可能性があるため、有線で接続した場合は他のネットワークデバイスを無効にしてからネームサーバーを起動してください。


/ja/node/6042


/ja/node/6042

ネームサーバー追加

続いて RTシステムエディタのネームサーバー追加ボタンで192.168.0.1(無線LANで接続する場合は192.168.11.1)を追加してください。


/jp/node/6042 tu_ev3_25.png



するとEducatorVehicle0という RTC が見えるようになります。

EducatorVehicle は EV3 の走行速度の入力、センサーのデータの出力等を行うためのコンポーネントです。


 /ja/node/6030

EducatorVehicle
InPort
名前 データ型 説明
velocity2D RTC::TimedVelocity2D 目標速度
angle RTC::TimedDouble Mモーターの角度
lcd RTC::TimedString LCDに表示する画像ファイル名
sound RTC::TimedString 出力する音声
OutPort
名前 データ型 説明
odometry RTC::TimedPose2D 現在の位置・姿勢
ultrasonic RTC::RangeData 超音波センサーで計測した距離
gyro RTC::TimedDouble ジャイロセンサーで計測した角度
color RTC::TimedString カラーセンサーで計測した色
light_reflect RTC::TimedDouble カラーセンサーで計測した反射光の強さ
touch RTC::TimedBoolean タッチセンサーのオンオフ。右側が0番目の要素、左側が1番目の要素
コンフィギュレーションパラメーター
名前 デフォルト値 説明
wheelRadius 0.028 車輪の半径
wheelDistance 0.054 タイヤ間距離の1/2
medium_motor_speed 1.6 Mモーターの速度

TimedVelocity2D型について

TimedVelocity2D型は以下のように定義されています。

     struct Velocity2D
     {
         double vx;
         double vy;
         double va;
     };

     struct TimedVelocity2D
     {
         Time tm;
         Velocity2D data;
     };

vx、vy、va はロボット中心座標系での速度を表しています。


tu_ev3_19.png

vx は X方向の速度、vy は Y方向の速度、va は Z軸周りの角速度です。

Educator Vehicle のように2個の車輪が左右に取り付けられているロボットの場合、横滑りしないと仮定すると vy は0になります。

vx、va を指定することでロボットの操作を行います。

音声の出力

sound による音声の入力には以下のコマンドを利用できます。

  • beep
    beep と入力するとビープ音が鳴ります。
  • tone
    以下のように tone、周波数、時間と入力すると指定周波数の音を指定ミリ秒数だけ鳴らします。

 tone,100,1000

  • それ以外
    それ以外は指定文字列を発音します

LCD に表示する画像について

LCD で表示する画像は付属資料の software/saveBinaryImage/EXE/saveBinaryImage.exeで変換したものを利用してください。
画像ファイルを saveBinaryImage.exe にドラッグ・アンド・ドロップすれば変換できます。

サンプルコンポーネント起動

付属資料のstart_component_ev3.batを起動してください。 ※OpenRTM-aist Python版をインストールしていない場合、もしくはインストールに失敗している場合は個別に実行ファイル入りのUSBメモリーを配布いたしますので、その中のstart_component_ev3_exe.batを利用してください。

すると以下の2つの RTC が起動します。

tu_ev3_24.png

動作確認

まずはジョイスティックで EV3 を操作してみます。

RTシステムエディタで EducatorVehicle、FloatSeqToVelocity、TkJoyStick を以下のように接続します。


tutorial_ev3_16.png

そして RTC をアクティブ化するとジョイスティックで EV3 の操作ができるようになります。


tutorial_ev3_21.png

自作の RTC で制御

まずは FloatSeqToVelocity の out と EducatorVehicle の target_velocity_in のコネクタを切断してください。

tutorial_ev3_17.png

FloatSeqToVelocity と EducatorVehicle の間に自作の RTC を接続して、タッチセンサーがオンになった場合に停止して音を鳴らすようにします。

ひな形コードの作成

RTC ビルダを起動してください。


/jp/node/6038

起動したら新規にプロジェクトを作成します。


/jp/node/6038

プロジェクト名は TestEV3CPP(TestEV3Py)にします。

以下のように設定を行ってください。 C++、もしくは Python で作成します。

基本
モジュール名 TestEV3CPP、もしくはTestEV3Py
アクティビティ
有効アクション onInitialize、onExecute、onActivated、onDeactivated
データポート
InPort
名前 データ型 説明
velocity_in RTC::TimedVelocity2D 入力目標速度
touch RTC::TimedBooleanSeq タッチセンサーのオンオフ
OutPort
名前 データ型 説明
velocity_out RTC::TimedVelocity2D 出力目標速度
sound RTC::TimedString 音声
コンフィギュレーション
名前 説明
sound_output string タッチセンサーが ON の時に発する音声。デフォルト値は beep
言語・環境
言語 C++、もしくはPython

[コード生成] ボタンを押したらコードが生成されます。


tutorial_raspimouse10.png

プロジェクト生成

コードが生成できたら C++ の場合は CMake で Visual Studio のプロジェクト(Ubuntu の場合は Code::Blocks)を生成してください。

まず CMake (cmake-gui) を起動します。

  • Windows 7

/jp/node/6038

  • Windows 8.1


/jp/node/6038

起動したらソースコードのディレクトリー、ビルドを行うディレクトリーに以下を指定します。 括弧内は eclipse の作業ディレクトリーを C:\workspace にした場合の例です。

Where is the source code RTCBuilder で生成したコードのフォルダー(C:\workspace\TestEV3CPP)
Where to build the binaries RTCBuilder で生成したコードのフォルダーの下に作成したbuildフォルダー(C:\workspace\TestEV3CPP\build)


tutorial_ev3_24.png

[Configure] ボタン→ [Generate] ボタンをクリックするとVisual Studioのプロジェクトが生成されます。

ソースコードの編集

build ディレクトリーの TestEV3CPP.sln を開いてください。

次にコードの編集を行います。

Pythonの場合はまず変数の初期化部分を修正してください。

  • TestEV3Py.py

     def __init__(self, manager):
         #self._d_velocity_in = RTC.TimedVelocity2D(*velocity_in_arg)
         self._d_velocity_in = RTC.TimedVelocity2D(RTC.Time(0,0),RTC.Velocity2D(0,0,0))

         #self._d_distance_sensor = RTC.TimedShortSeq(*distance_sensor_arg)
         self._d_distance_sensor = RTC.TimedShortSeq(RTC.Time(0,0),[])

         #self._d_velocity_out = RTC.TimedVelocity2D(*velocity_out_arg)
         self._d_velocity_out = RTC.TimedVelocity2D(RTC.Time(0,0),RTC.Velocity2D(0,0,0))

         #self._d_buzzer = RTC.TimedShort(*buzzer_arg)
         self._d_buzzer = RTC.TimedShort(RTC.Time(0,0),0)

まずは onExecute で入力速度をそのまま出力するコードを書いてみます。
C++の場合は以下のようになります。
isNew関数で新規の入力データが存在するかを確認して、read関数で変数(m_velocity_in)に格納します。 そして m_velocity_out に出力データを格納して write関数を呼び出すとデータが送信されます。

  • src/TestEV3CPP.cpp

     if (m_velocity_inIn.isNew())
     {
         m_velocity_inIn.read();
         //入力速度をそのまま出力
         m_velocity_out.data.vx = m_velocity_in.data.vx;
         m_velocity_out.data.vy = m_velocity_in.data.vy;
         m_velocity_out.data.va = m_velocity_in.data.va;
         setTimestamp(m_velocity_out);
         m_velocity_outOut.write();
 
 
     }

Pythonの場合は以下のようになります。

  • TestEV3Py.py

         if self._velocity_inIn.isNew():
             data = self._velocity_inIn.read()
             #入力速度をそのまま出力する
             self._d_velocity_out.data.vx = data.data.vx
             self._d_velocity_out.data.vy = data.data.vy
             self._d_velocity_out.data.va = data.data.va
             OpenRTM_aist.setTimestamp(self._d_velocity_out)
             self._velocity_outOut.write()

次にタッチセンサーがオンの場合に停止する処理を記述します。
常にタッチセンサーのデータが入力されるとは限らないので、センサーのデータを格納する変数を宣言します。
C++の場合は TestEV3CPP.h に記述します。

  • include/TestEV3CPP/TestEV3CPP.h

  private:
      bool m_last_sensor_data[2];


Pythonの場合はコンストラクタに記述します。

  • TestEV3Py.py

     def __init__(self, manager):
         OpenRTM_aist.DataFlowComponentBase.__init__(self, manager)
 
         self._last_sensor_data = [False, False]

次に onExecute に停止する処理を記述します。
C++の場合は以下のようになっています。
まずインポート touch に isNew 関数で新規にデータが入力されたかを確認して、入力されている場合は read関数で読み込みます。そして変数 m_last_sensor_data に格納します。
そしてインポート velocity_in で受信したデータの vx が 0 以上の場合には前進しているため障害物に接触するかもしれないと判定して、タッチセンサーがオンの場合は停止してブザーを鳴らします。

  • src/TestEV3CPP.cpp

 RTC::ReturnCode_t TestEV3CPP::onExecute(RTC::UniqueId ec_id)
 {
     //データを新規に受信した場合に、データをm_last_sensor_dataを格納する
     if (m_touchIn.isNew())
     {
         m_touchIn.read();
         if (m_touch.data.length() == 2)
         {
             for (int i = 0; i < 2; i++)
             {
                 //タッチセンサがOFFからONになった時に音を鳴らす
                 if (!m_last_sensor_data[i] && m_touch.data[i])
                 {
                     m_sound.data = m_sound_output.c_str();
                     setTimestamp(m_sound);
                     m_soundOut.write();
 
 
                 }
                 m_last_sensor_data[i] = m_touch.data[i];
             }
         }
     }
     if (m_velocity_inIn.isNew())
     {
         m_velocity_inIn.read();
         //vxが0以上(前進)のときのみ停止するか判定する
         if (m_velocity_in.data.vx > 0)
         {
             for (int i = 0; i < 2; i++)
             {
                 //タッチセンサがONの時に停止する
                 if (m_last_sensor_data[i])
                 {
                     //停止する
                     m_velocity_out.data.vx = 0;
                     m_velocity_out.data.vy = 0;
                     m_velocity_out.data.va = 0;
                     setTimestamp(m_velocity_out);
                     m_velocity_outOut.write();
 
 
                     return RTC::RTC_OK;
                 }
             }
         }
         //入力速度をそのまま出力
         m_velocity_out.data.vx = m_velocity_in.data.vx;
         m_velocity_out.data.vy = m_velocity_in.data.vy;
         m_velocity_out.data.va = m_velocity_in.data.va;
         setTimestamp(m_velocity_out);
         m_velocity_outOut.write();

   return RTC::RTC_OK;
 }

Pythonの場合は以下のようになっています。

  • TestEV3Py.py

     def onExecute(self, ec_id):
         #データを新規に受信した場合に、データをm_last_sensor_dataを格納する
         if self._touchIn.isNew():
             data = self._touchIn.read()
             if len(data.data) == 2:
                 for i in range(2):
                     #タッチセンサがOFFからONになった時に音を鳴らす
                     if not self._last_sensor_data[i] and data.data[i]:
                         self._d_sound.data = self._sound_output[0]
                         OpenRTM_aist.setTimestamp(self._d_sound)
                         self._soundOut.write()
                 self._last_sensor_data = data.data[:]
 
         if self._velocity_inIn.isNew():
             data = self._velocity_inIn.read()
             #vxが0以上(前進)のときのみ停止するか判定する
             if data.data.vx > 0:
                 for d in self._last_sensor_data:
                     #タッチセンサーがONの時に停止する
                     if d:
                         #停止する
                         self._d_velocity_out.data.vx = 0
                         self._d_velocity_out.data.vy = 0
                         self._d_velocity_out.data.va = 0
                         OpenRTM_aist.setTimestamp(self._d_velocity_out)
                         self._velocity_outOut.write()
                         
                         return RTC.RTC_OK
 
             #入力速度をそのまま出力する
             self._d_velocity_out.data.vx = data.data.vx
             self._d_velocity_out.data.vy = data.data.vy
             self._d_velocity_out.data.va = data.data.va
             OpenRTM_aist.setTimestamp(self._d_velocity_out)
             self._velocity_outOut.write()
         return RTC.RTC_OK

コードの編集が終わったら C++ の場合はビルドしてください。
ビルドに成功すると build\src\Release(Debug) に TestEV3CPPComp.exe が生成されます。

動作確認

TestEV3CPPComp.exe (TestEV3CPPComp.py) をダブルクリックして起動してください。
TestEV3CPP(TestEV3Py)を以下のように接続してください。


tutorial_ev3_18.png

最後に RTC をアクティブ化して動作確認してください。

RTシステム保存

RTシステムを保存する場合は System Diagram 上で右クリックして Save As... を選択してください。


tutorial_ev3_20.png


/jp/node/6038


RTシステム復元

復元する場合は Open and Restore を選択して、先ほど保存したファイルを選択してください。


/ja/node/6042

RTC 終了

RTC を終了する場合は RTシステムエディタ上で RTC を exit してください。


tutorial_ev3_19.png

補足

スクリプトファイルについて

EV3 のボタン操作で File Brower を選択すると/home/robot以下のディレクトリーの操作ができます。
scripts フォルダー内のシェルスクリプトを実行することで以下の操作ができます。

スクリプトファイル名 内容
run_rtcs.sh RTC を起動する
stop_rtcs.sh RTC を終了する

Setup EV3/ev3dev

電池・無線LAN・SDカードのセット

電池

EV3 は単3電池6本、または専用のバッテリーパックで動作します。単3電池6本、もしくは専用のバッテリーをセットしてください。

EV3_with_AAAbattery.png
単3電池

EV3_with_battery.png
EV3専用バッテリーパック

無線LAN

USB接続の無線LANインターフェースをを用意します。EV3 は PCと有線接続して、PC経由でインターネットに接続することもできますが、ここでは無線LAN接続を前提として説明します。

最近の無線LANドングルであれば、大抵のものが使用可能なはずですが、一例として以下の無線LANドングルは動作確認ができています。

  • BUFFALO WLI-UC-GNM2 Wireless LAN Adapter
  • BUFFALO WLI-UC-GN Wireless LAN Adapter
  • PLANEX GW-USMicro300
    wlan_dongle.png
    無線LANドングルの例

その他の接続方法に関しては、ev3dev の Webページをご覧ください。

SDカードの挿入

無線LANドングルと共に、EV3 のコントローラの側面に ev3dev イメージを書き込んだ SDカードを以下のように挿入してください。

ev3_wlan_sdcard.png
EV3の側面に、無線LANドングルと SDカードを挿入

ev3dev の起動

SDカードを挿入し、電源ボタン(十字キーボタンの中央の濃いグレーのボタン)を押して EV3 に電源を投入すると、以下のような起動画面が表示され、十字キーのあたりの LED が点滅します。

ev3dev_screen_booting.png
ev3dev 起動画面

暫く(1分前後)して起動が完了すると、以下のような初期画面が表示されます。

ev3dev_screen.png
起動直後の画面

ssh ログイン

ネットワークへの接続

起動直後の EV3 は、まだネットワークにつながっていません。 初期画面の状態から、十字キーで、Wireless and Networks を選択し、決定ボタン(中央の濃いグレーのボタン)を押します。

 ------------------------------
                       V [8.12>
 ------------------------------
  File Brower                >
  Device Browser             >
 [Wireless and Networks      > ]
  Battery                    >
  About                      >
 ------------------------------

選択すると、以下のような画面になります。

 
 ------------------------------
      Wireless and Network
 ------------------------------
        Status: Offline
 ------------------------------
 Bluetooth                    >
 USB                          >
 Wifi                         >
 All Network Connections      >
 Tethering                    >
 Offline Mode                □
 ------------------------------

WiFi を選択すると、以下の画面に遷移します。

 ------------------------------
           WiFi
 ------------------------------
 Powered                      □
 Start Scan
            Networks
 ------------------------------
 [* MyWirelessNetwork   ?? ]
 
 
 ------------------------------

Powered を選択し、電源をONにし、スキャンして現れた SSID のうち接続したい ID を選択します。

 ------------------------------
      MyWirelessNetwork
 ------------------------------
 Status:                 Online
 Signal                     83%
 Security        WPA/2 PSK, WPS
 IP Address:
 [    Connect   ]
 [      Network Connection     ]

Connect を選択し決定ボタンを押すと、KEYを入力するダイアログが現れます。再度、決定ボタンを押すと、以下のようなキーボードが現れるので、KEY を入力します。

 [_                           ]
 [ABC] [abc] [123] [!@# ] [INS]
 [Q][W][E][R][T][Y][U][I][O][P]
 [A][S][D][F][G][H][J][K][L][ ]
 [ ][Z][X][C][V][B][N][M][ ][ ]
 [ Accept ]          [ Cancel ]

KEY を入力後、Accept を選択し決定ボタンを押すと、先どのダイアログに KEY が入力された状態で表示されるので、再度 Accept を押します。 暫くすると、指定した無線LANアクセスポイントにつながるはずです。何度か戻るボタン(画面の左下のボタン)を押すと初期画面に戻ります。 左上に割り当てらえた IPアドレスが表示されているはずです。

 --------------------------
 192.168.11.3          V [8.12>
 --------------------------
  File Brower                >
  Device Browser             >
 [Wireless and Networks      > ]
  Battery                    >
  About                      >
 
  
 --------------------------

USBケーブルでの接続

無線LANが何らかの理由で使用できない場合は USBケーブルで接続することもできます。

EV3 と PCを付属の USBケーブルで接続してください。

/ja/node/6041

ev3dev の初期画面で Wireless and Networks を選択してください。 次に All Network Connections を選択します。

 ------------------------------
      Wireless and Network
 ------------------------------
        Status: Offline
 ------------------------------
 Bluetooth                    >
 Wifi                         >
 All Network Connections      > ]
 Tethering                    >
 Offline Mode                □
 ------------------------------

Wiredを選択します。

 ------------------------------
      All Network Connections
 ------------------------------
 Wired                        ψ ]
 ------------------------------

Connectを選択すると接続します。

 ------------------------------
             Wired
 ------------------------------
        Status: Offline
 ------------------------------
 Connect                        ]
 Connect automatically       □
 IPv4                         >
 DNS                          >
 ENET                         >
 ------------------------------

テザリングの設定

EV3 で USBテザリングの設定を行う手順を説明します。 まず ev3dev の初期画面で Wireless and Networks を選択してください。

次に Tehering を選択します。

 ------------------------------
      Wireless and Network
 ------------------------------
        Status: Offline
 ------------------------------
 Bluetooth                    >
 Wifi                         >
 All Network Connections      >
 Tethering                    > ]
 Offline Mode                □
 ------------------------------

Gadget をオンにすれば完了です。

 ------------------------------
           Tethering
 ------------------------------
 Bluetooth                   □
 Gadget                      ■
 Network Info                 >
 ------------------------------

ログイン

EV3 に割り当てられた IPアドレスに sshで接続します。デフォルトでは、ev3dev は以下の ID とパスワードが設定されています。

ID robot
Password maker

Windows では、TeraTerm などのターミナルソフトウェアを利用します。 Linux などでは、ターミナル画面から、

 $ ssh robot@<IPアドレス>

として、ログインします。ログインすると、以下のような画面が表示されるはずです。

              _____     _
    _____   _|___ /  __| | _____   __
   / _ \ \ / / |_ \ / _` |/ _ \ \ / /
  |  __/\ V / ___) | (_| |  __/\ V /
   \___| \_/ |____/ \__,_|\___| \_/
 
 Debian jessie on LEGO MINDSTORMS EV3!
 
 The programs included with the Debian GNU/Linux system are free software;
 the exact distribution terms for each program are described in the
 individual files in /usr/share/doc/*/copyright.
 
 Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
 permitted by applicable law.
 Last login: Tue Aug  4 01:34:12 2015 from openrtm.org
 root@ev3dev:~#

TeraTerm のインストール(参考)

Windows から EV3 に ssh 経由でログインするためには、ssh クライアントをインストールする必要があります。 Windows で利用可能なクライアントは多数ありますが、ここでは Tera Term を紹介します。

こちらから、TeraTerm をダウンロード、インストールしてください。

teraterm_connect.png
TeraTerm による接続

TeraTerm をインストール後、起動すると接続ダイアログが現れるので、先ほど設定したホスト名+.local を「ホスト」のテキストボックスに入力しOKを押します。

設定

無線LANの設定

上記で行ったEV3 の無線LAN設定は、再起動すると消えてしまいます。起動後に自動で無線LAN に接続するためには、EV3 にログインして無線LANの接続設定を行う必要があります。

/etc/wpa_supplicant/wpa_supplicant.conf の編集

次に、無線LAN の ESSID とキーを登録します。

 # cd /etc/wpa_supplicant
 # wpa_passphrase ESSID pass >> wpa_supplicant.conf

SSID には無線LANの ESSID、pass には無線LANのキーを入力します。リダイレクトの際、> ではなく >> (追記)を使用するよう注意してください。 結果は以下のようになっていると思います。

 ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
 update_config=1
 network={
         ssid="OpenRTM"
         #psk="4332221111"
         psk=142914b76be167767055ff945898baaaf83c42b3ad3b99afb0ae531e8fb15e5e
 }

通常は、これだけで接続できるはずです。ただし、無線LANアクセスポイントの設定によっては、追加の設定が必要になるかもしれません。 以下に、一例を示します。

 ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
 update_config=1
 network={
         ssid="OpenRTM"
         proto=WPA2
         key_mgmt=WPA-PSK
         pairwise=TKIP CCMP
         group=TKIP CCMP
         #psk="4332221111"
         psk=142914b76be167767055ff945898baaaf83c42b3ad3b99afb0ae531e8fb15e5e
 }

最後に、インターフェースを初期化します。

 # ifdown wlan0 ; ifup wlan0
 Internet Systems Consortium DHCP Client 4.2.2
 Copyright 2004-2011 Internet Systems Consortium.
 All rights reserved.
 For info, please visit https://www.isc.org/software/dhcp/
  : 中略
 DHCPREQUEST on wlan0 to 255.255.255.255 port 67
 DHCPOFFER from 192.168.11.1
 DHCPACK from 192.168.11.1
 bound to 192.168.11.26 -- renewal in 34810 seconds.

実際には、いったん無線LANインターフェースがOFFになるので、このような表示は見えません。また、場合によっては、ssh 接続が切断される可能性があるので、その場合は、再度接続してみてください。

最後に、設定が正しく行われているか確認するため、再起動してみます。

 # reboot

EV3 がシャットダウンされ、再び起動画面が表示されます。しばらくすると、初期画面が表示され、設定が正しく行われていれば、画面の左上に IPアドレスが表示されるはずです。

ホスト名でリモート接続する

EV3 に ssh でリモートログインで操作する場合、上記のように無線LAN接続後、画面左上に表示される IPアドレスにssh接続することができます。 IPアドレスは DHCP で割り当てられているので、接続の度に代わる可能性があり、そのたびに異なる IPアドレスで接続しなければならないので不便です。

ev3dev には avahi という Bonjour互換のサービスがインストールしてあります。 Bonjour は Apple が提唱するネットワーク上のサービスを自動的に検索して利用できるようにするためのサービスです。 avahiを使うと、DHCP で IPアドレスを割り振っている EV3 に対してもホスト名でアクセスすることができるようになります。

ホスト名の設定

ev3dev ではデフォルトで ev3dev というホスト名が設定してあります。他の avahi や Bonjour がインストールしてあるマシンからは、ev3dev.local という .local がついたホスト名でアクセスできます。

EV3 が複数台ネットワーク上にある場合には、他のホスト名と衝突しないホスト名を選び設定する必要があります。

 $ sudo vi /etc/hostname

/etc/hostname の1行目にホスト名を記載します。デフォルトでは ev3dev となっていますので、好みの名前に設定してください。 。

avahi-daemon のインストール

Linux ホストから EV3 へアクセスする場合、avahiというサービスをインストールする必要があります。 最近のLinuxディストリビューションでは、たいていデフォルトでこのサービスがインストールされていますが、もしインストールされていない場合は、以下のように avahi デーモンをインストールします。(debian系のディストリビューションの場合)

 $ sudo apt-get update
 $ sudo apt-get install avahi-daemon

EV に対して ping を打ってみます。ホスト名の後に .local を付けた名前を使います。

 $ ping ev3dev.local

これで ping が返ってくれば、avahi がほぼ正しく設定されていることになります。

Bonjour のインストール (Windowsのみ)

PC から EV3 にアクセスするためには、PC側にも avahi か Bonjour がインストールされている必要があります。

Windows ではデフォルトでは Bonjour はインストールされていません。 最も簡単に Bonjour を導入する方法は iTunes をインストールすることです。

どうしても iTunes をインストールしたくない場合は、アーカイバアプリケーションなどで、ダウンロードした iTunesSetup.exe を展開すると BonjourSetup.exe を抽出することもできます。

また、以下の Apple Bonjour 印刷サービスにも Bonjour が同梱されています。(iTunesに同梱されているものよりバージョンが若干古いようです。)

現在 Applie では Bonjour for Windows 単体としては配布は行なっていませんが、かつて配布していたものを再配布しているサイトも幾つかあります。(ただし、古いバージョンしか入手できないようです。) 以下は Appleサイト以外の Bonjourダウンロードサイトです。自己責任でご利用ください。

Bonjour がうまく機能しない場合

ファイヤウォールが動作している場合、Bonjour がうまく機能しないことがあります。 その場合、UDPポート5353を開放するかファイヤウォールをOFFにしてください。

Executing Sample Components

OpenRTM-aistのインストール

上述のリンクから ev3dev のイメージで EV3 を起動した場合、すでに OpenRTM-aist (C++、Python版) がインストールされています。 その場合は、以下の OpenRTM-aist のインストールはスキップして、サンプルコンポーネントの実行から進めてください。

自分で ev3dev.org からイメージをダウンロードした場合は、OpenRTM-aist のパッケージを apt-get 等でインストールします。

sources.list の編集

openrtm.org をパッケージリポジトリとして追加するために、/etc/sources.list を編集します。

 # vi /etc/apt/sources.list

のように、vi で /etc/apt/sources.list を開き、

 deb http://ftp.debian.org/debian jessie main contrib non-free
 deb http://ev3dev.org/debian jessie main
 deb http://openrtm.org/pub/Linux/debian jessie main ← この行を追加

この例のように、最下行に openrtm.org のリポジトリを追加します。

その後、

 # apt-get update
として、パッケージリポジトリのデーターベースを更新します。 EV3は遅いので、パッケージデータベースの更新もかなり時間がかかります。

OpenRTM-aist パッケージのインストール

以上で、openrtm.org のパッケージリポジトリにアクセスできるようになりましたので、以下のようにしてパッケージをインストールします。

 # apt-get install libomniorb4-dev omniidl
 # apt-get install openrtm-aist openrtm-aist-dev openrtm-aist-example python-yaml
 # apt-get install gcc g++ make uuid-dev
 # apt-get install python-omniorb
 # apt-get install openrtm-aist-python openrtm-aist-python-example

パッケージのインストールにもかなりの時間がかかりますので気長に待ちます。途中でバッテリーが切れないように、アダプタに接続したまま作業することをお勧めします。

OpenRTM-aistのサンプルの実行

ConsoleIn-ConsoleOut (C++)

C++のサンプルコンポーネントを動作させて、OpenRTM-aistが正しくインストールできているかどうかを確認します。

EV3上で ConsoleIn を実行したうえで、PC上で ConsoleOut を実行して相互に接続し、PCから入力した数字が EV3上で表示できるかどうかを確認してみます。

ConsoleIn の起動

まず、EV3上で、ネームサービスと ConsoleIn を起動します。 omniorb-nameserver というパッケージがインストールされているはずですので、すでにシステムのサービスとして起動していますが、ネットワークがらみのトラブルを避けるためにも、rtm-naming というコマンドで起動させた方が良いでしょう。

Windows などでは TeraTerm などのターミナルソフトウェア、Linux ではコンソールから EV3 に ssh でログインします。

ログインしたら、まず rtm-naming を起動します。途中で既存のネームサーバを落とすかどうか聞いてきますので y と答えて進みます。

              _____     _
    _____   _|___ /  __| | _____   __
   / _ \ \ / / |_ \ / _` |/ _ \ \ / /
  |  __/\ V / ___) | (_| |  __/\ V /
   \___| \_/ |____/ \__,_|\___| \_/
 
 Debian jessie on LEGO MINDSTORMS EV3!
 :中略
 # rtm-naming
 Starting omniORB omniNames: ev3dev:2809
 omniORB: Failed to bind to address 0.0.0.0 port 2809. Address in use?
 omniORB: Error: Unable to create an endpoint of this description: giop:tcp::2809
 :中略
 and start omniNames by rtm-naming? (y/N)y ← yを入力
 Stopping omniNames by /etc/init.d/omniorb4-nameserver.
 [ ok ] Stopping omniorb4-nameserver (via systemctl): omniorb4-nameserver.servic.
 Starting omniORB omniNames: ev3dev:2809
 
 Wed Aug  5 11:04:01 2015:
 
 Starting omniNames for the first time.
 Wrote initial log file.
 Read log file successfully.
 Root context is IOR:010000002b00000049444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f6e746578744578743a312e30000001000000000000007400000001010200100000003139322e3136382e3132382e31303400f90a00000b0000004e616d6553657276696365000300000000000000080000000100000000545441010000001c000000010000000100010001000000010001050901010001000000090101000354544108000000a1edc1550100028f
 Checkpointing Phase 1: Prepare.
 Checkpointing Phase 2: Commit.
 Checkpointing completed.
 omniNames properly started
 root@ev3dev:~#

次に、ConsoleInComp を起動します。

 root@ev3dev:~# /usr/share/openrtm-1.1/example/ConsoleInComp
 Creating a component: "ConsoleIn"....succeed.
 =================================================
  Component Profile
 -------------------------------------------------
 InstanceID:     ConsoleIn0
 :中略
 port.outport.dataport:
 =================================================
 =================================================
 Port0 (name): ConsoleIn0.out
 -------------------------------------------------
 - properties -
 port.port_type: DataOutPort
 dataport.data_type: IDL:RTC/TimedLong:1.0
 dataport.subscription_type: flush,new,periodic
 dataport.dataflow_type: push,pull
 dataport.interface_type: corba_cdr
 -------------------------------------------------
 
ConsoleIn_cxx.png
EV3上で実行されたConsoleInComp

ConsoleOut の起動

PC上で ConsoleOut を起動します。Windowsであれば、ネームサーバ、RTSystemEditor、ConsoleIn をそれぞれ以下のように起動します。

  • ネームサーバの起動
    • 「スタート」>「OpenRTM-aist x.y」>「Tools」>「Start C++ Naming Service」
  • RTSystemEditorの起動
    • 「スタート」>「OpenRTM-aist x.y」>「Tools」>「RTSystemEditor」
  • ConsoleInの起動
    • 「スタート」>「OpenRTM-aist x.y」>「C++」>「Components」>「Examples」>「ConsoleOutComp.exe」

Linux であれば、ネームサービス、eclipse、ConsoleInComp を以下のように起動します。

 $ rtm-naming
 $ <eclipseの起動> &
 $ /usr/share/openrtm-1.1/example/ConsoleOutComp
 :中略
 naming.names: ubuntu1404.host_cxt/ConsoleOut0.rtc
 config_file:
 port.inport.dataport:
 port.inport.in:
 =================================================
 =================================================
 Port0 (name): ConsoleOut0.in
 -------------------------------------------------
 - properties -
 port.port_type: DataInPort
 dataport.data_type: IDL:RTC/TimedLong:1.0
 dataport.subscription_type: Any
 dataport.dataflow_type: push,pull
 dataport.interface_type: corba_cdr
 -------------------------------------------------

OpenRTP (eclipse) または、RTSystemEditotr (RPC版) から、EV3 で起動したネームサーバと PC で起動したネームサーバーにそれぞれ接続します。

それぞれのネームサーバに ConsoleIn (EV3のネームサーバー) と ConsoleOut (PC側のネームサーバー) が現れるはずので、それぞれエディタにドラッグアンドドロップして、InPort と OutPort を接続、Activate します。

ConsoleInOut_rtse01.png
RTSystemEditor上でConsoleInとConsoleOutを接続

ConsoleIn の方から数値を入力し、ConsoleOut の方で表示されれば、テストは成功です。

ConsoleIn-ConsoleOut (Python)

今度は、上記とは逆に、ConsoleIn を PC で、ConsoleOut を EV3 上で実行し接続してみます。

PC上では、以下のように ConsoleOut を起動します。。

  • ConsoleOutの起動
    • 「スタート」>「OpenRTM-aist x.y」>「Python」>「Components」>「Examples」>「ConsoleOutComp.exe」
      ConsoleIn_py.png
      PC上で実行した ConsoleIn (Python版)

次に、EV3上で、以下のように ConsoleOut.py を起動します。

 # python /usr/share/openrtm-1.1/example/python/SimpleIO/ConsoleOut.py
 ------------------------------
 Listener:        ON_CONNECT
 Profile::name:   ConsoleIn0.out_ConsoleOut0.in
 Profile::id:     246bf2c0-3b68-11e5-91a9-005056c00008
 ------------------------------
ConsoleOut_py.png
EV3 で実行した ConsoleOut (Python版)

RTSystemEditor上で、ConsoleIn と ConsoleOut を接続し、Activate します。 ConsoleIn側で数値を入力し、ConsoleOut側で表示されれば、テストは成功です。

ConsoleInOutpy_rtse01.png
RTSystemEditor上で ConsoleIn と ConsoleOut を接続

その他の組み合わせとして、C++ と Python のコンポーネントを起動して接続してみる、他のサンプルコンポーネントを起動して通信させてみるなど試してみてください。

Making Development Environment

EV3 および ev3dev 上で動作するコンポーネントを開発するには、EV3上で開発する方法と、クロス開発環境で開発する方法があります。

ただし、EV3 はメモリが64MB、CPUクロックが300MHz程度と、今どきの開発環境としてはだいぶ遅いマシンですので、EV3上での開発はかなり大変です。

クロス開発というのは、異なるアーキテクチャ、つまり一般的な PCのアーキテクチャである Intel 系の CPU上で、ARM などの異なる種類で動作する実行ファイルを作成することです。

クロス開発では、コンパイル即実行という形はとれませんが、コンパイル時間が圧倒的に速いので、結果として開発効率は高くなります。

Ubuntu のインストール

開発環境は Linux のディストリビューションの1つである Ubuntu (14.04 LTS) を使用します。

こちらの URL から ISOイメージをダウンロードして OS をインストールしてください。

PC のそのままインストールして使用することもできますし、バーチャルマシンとしてインストールして使用しても構いません。

バーチャルマシン環境としては、主に以下のようなものがあります。

これらの詳細な使用方法については、それぞれの Webサイトもしくは、解説の Webページを検索してみてください。

brickstrap

ここからは、Ubuntu14.04 がインストールされたものとして、話を進めます。 まず、brickstrap というツールをインストールします。brickstrap は上述の ev3dev の OSイメージを作成するためのツールですが、クロス開発を行うためのツールとしても利用可能です。

brickstrap のインストール

brickstap は下記の github 上で開発が行われています。

まずは、brickstrap をインストールします。通常の debian リポジトリには存在しないコマンドですので、パッケージリポジトリに ev3dev.org を追加しています。

 $ sudo apt-key adv --keyserver pgp.mit.edu --recv-keys 2B210565
 $ sudo apt-add-repository "deb http://archive.ev3dev.org/ubuntu trusty main"
 $ sudo apt-get update
 $ sudo apt-get install brickstrap

イメージ作成のための準備

まずは supermin appliance を作成するために以下のコマンドを実行します。

 $ sudo update-guestfs-appliance
 $ sudo usermod -a -G kvm <username>
 $ sudo chmod +r /boot/vmlinuz*

開発環境の作成

ここまでできたら、いよいよ開発環境を作成します。特定のディレクトリーに、EV3 に搭載するシステムのファイルシステムを模擬したものを作成します。 場所としては、自分のホームディレクトリー以下の適当な場所が良いでしょう。ここでは、ホームディレクトリー以下の work ディレクトリーに作成します。

 $ cd ~
 $ mkdir work
 $ cd work

以下のコマンドでファイルシステムの作成~イメージの作成を行います。

 $ brickstrap -b ev3-ev3dev-jessie -d ev3-ev3dev-work all

このコマンドの実行には10分から20分かかります。 実際に行われていることは、ev3dev の Linux システムに必要なコマンド群、パッケージなどを仮想的な root ディレクトリー ev3-ev3dev-work 以下にインストールし、それを SDカードに書き込み可能なイメージファイルとして構成する作業です。

終了後には、ev3-ev3dev-work.tar と ev3-ev3dev-work.img というファイルシステムのアーカイブとイメージファイルが作成されているはずです。 この .img ファイルを SDカードに書き込むと EV3 で ev3dev を起動できます。

proot を入れ替える

ubuntu14.04 x86_64 では、brickstrap 内部で使われている proot のバージョンが古く、一部のユーザーIDやグループIDを扱う関数が実行できないため、apt-get でエラーが発生します。 これを避けるために、バージョン4.0以降の proot を使用します。

ここから、x86_64用バイナリをダウンロードし、当該マシンにコピーします。

 $ wget http://portable.proot.me/proot-x86_64
 $ sudo mv /usr/bin/proot /usr/bin/proot_3.0.2
 & sudo mv proot /usr/bin/
 $ chmod 755 /usr/bin/proot

シェルを起動する

以上で、クロス開発環境ができましたので、OpenRTM-aist をコンパイル・インストールして、さらに RTコンポーネントを開発します。 brickstrap を利用すると、あたかも EV3 上でパッケージのインストールやソースコードのコンパイルをしているように振る舞うモードを利用することができます。

以下のコマンドを入力します。

user@host:~/work$ brickstrap -b ev3-ev3dev-jessie -d ev3-ev3dev-work shell

すると、コマンドプロンプトが # となり、上述のモードとなります。

OpenRTM-aist のコンパイル

OpenRTM を自分でコンパイル・インストールする場合には、以下の手順に従ってください。ビルド済みのパッケージを利用する場合は、次の章へ移動してください。

まず、OpenRTM-aist をコンパイルします。OpenRTM-aist のビルドに必要なパッケージをインストールします。

 # apt-get update
 # apt-get install libomniorb4-dev omniidl
 # apt-get install gcc g++ make uuid-dev libboost-filesystem-dev
 # apt-get install doxygen
 # apt-get install build-essential debhelper devscripts
 # apt-get install subversion texlive texlive-lang-cjk xdvik-ja python-yaml
 # apt-get install wget

次に、OpenRTM-aist のソースコードをダウンロードします。

 # cd /home
 # wget http://tmp.openrtm.org/pub/OpenRTM-aist/cxx/x.y.z/OpenRTM-aist-x.y.z.tar.gz
 # tar xvzf OpenRTM-aist-x.y.z.tar.gz
 # cd OpenRTM-aist
 # ./configure --prefix=/usr
 # cd packages
 # make

これで、packages の下に OpenRTM-aist のパッケージが生成されます。 その後は、

 # dpkg -i openrtm-aist-*

のようにして、パッケージをインストールしてください。 OpenRTM-aist-Python についても同様にソースコードをダウンロードして、コンパイル(正確には、パッケージ作成)します。

 # wget http://tmp.openrtm.org/pub/OpenRTM-aist/python/x.y.z/OpenRTM-aist-Python-x.y.z-RELEASE.zip
 # cd OpenRTM-aist-Python
 # cd packages
 # make

C++版同様に package ディレクトリー以下に作成されたパッケージが作成されます。 インストールは、

 # dpkg -i openrtm-aist-python-*

のように行います。

OpenRTM-aist のインストール

上述のように、OpenRTM-aist を自分でソースからインストールせずに、事前にコンパイル済みのパッケージをインストールしても構いません。 ev3dev に OpenRTM-aist をインストールする方法について述べた、上述の方法とほぼ同様です。

sources.list の編集

openrtm.org をパッケージリポジトリとして追加するために、/etc/sources.list を編集します。

 # vi /etc/apt/sources.list

のように、vi で /etc/apt/sources.list を開き、

 deb http://ftp.debian.org/debian jessie main contrib non-free
 deb http://ev3dev.org/debian jessie main
 deb http://openrtm.org/pub/Linux/debian jessie main ← この行を追加

この例のように、最下行に openrtm.org のリポジトリを追加します。

その後、

 # apt-get update
として、パッケージリポジトリのデーターベースを更新します。 EV3 は遅いので、パッケージデータベースの更新もかなり時間がかかります。

OpenRTM-aist パッケージのインストール

以上で、openrtm.org のパッケージリポジトリにアクセスできるようになりましたので、以下のようにしてパッケージをインストールします。

 # apt-get install openrtm-aist openrtm-aist-dev openrtm-aist-example
 # apt-get install openrtm-aist-python openrtm-aist-python-example

EV3 Device Use

LEGO Mindstorms のセンサーとモーター

LEGO Mindstorms EV3 には標準で、以下のようなモーター・センサーが付属しています。

  • ジャイロセンサー x1
  • カラーセンサー x1
  • タッチセンサー x2
  • 超音波距離センサー x1
  • モーター(L)x2
  • モーター(M)x1

センサー類は、プッシュスイッチは ON/OFF 値を取得可能で、超音波センサー、ジャイロセンサーは連続値を出力可能、カラーセンサーはいくつかの色を認識することもできます。 モーターは2種類(L、M)あり、それぞれ PWM(トルク)制御、速度制御、位置制御が可能で、これらの制御のゲイン等も変更できます。 以下に、EV3 のセンサーとモーターの使用を表示します。

ジャイロセンサー
45505_GyroSensor.jpg
確度モード: 精度 +/- 3°
角速度モード: 最大 440 deg/sec
サンプリングレート 1,000 Hz
カラーセンサー
45506_color.jpg
計測: 赤色光の反射光、周囲の明るさ、色
検出カラー数: 8色 (無色、黒、青、緑、黄、赤、白、茶)
サンプリングレート 1,000 Hz
距離 約1mm~18mm(アフレル調査値)
タッチセンサー
45507_TouchSensor.jpg
オン (1)、オフ (0)
スイッチ可動域: 約4mm
超音波センサー
45504_UltrasonicSensor.jpg
距離計測可能範囲: 3cmから250cm
距離計測精度: +/- 1 cm
前面電飾: 点灯:超音波発信中、点滅:超音波観測中
EV3 Lモーター
45502_LargeMotor.jpg
フィードバック: 1°単位
回転数: 160から170RPM
定格トルク: 0.21 N・m (30oz*in)
停動トルク: 0.42 N・m (60oz*in)
重さ: 76 g
EV3 Mモーター
45503_MediumMotor.jpg
フィードバック 1°単位
回転数: 240から250RPM
定格トルク: 0.08 N・m (11oz*in)
停動トルク: 0.12 N・m (17oz*in)
重さ: 36 g

sysfs 経由でのアクセス

ev3dev からこれらのデバイスにアクセスするには、sysfs という仕組みを経由してアクセスします。 sysfs は /sys 以下にある疑似的なファイルシステムで、ここに存在するファイルに値を書き込んだり読み込んだりすることで、モーターの制御やセンサーデータへのアクセスが可能になります。

sysfs 経由でのデバイスのアクセス方法に関しては、以下の ev3dev のドキュメントに詳細が記載されています。

具体的には、以下のようにアクセスします。

モーターの制御例

モーターを連続回転するには、ポートAにモーター(L/Mどちらでもよい)をコマンドラインから以下のように入力します。

 # echo 50 > /sys/class/tacho-motor/motor0/duty_cycle_sp
 # echo run-forever > /sys/class/tacho-motor/motor0/command

現在の位置を表示させるにはこのように入力します。止めるには Ctrl+C

 # while true; do echo -en "\033[0G$(cat /sys/class/tacho-motor/motor0/speed)   "; done
回転を止めるには、以下のように入力します。

 # echo stop > /sys/class/tacho-motor/motor0/command

センサーの例

センサーなども同様のアクセス法で利用できます。 カラーセンサーをポート1に接続します。カラーセンサーの先1cm位のところに赤いものを置いてください。 以下のようなコマンドを入力すると、5と表示されます。

 # echo "COL-COLOR" > /sys/class/lego-sensor/sensor1/mode
 # cat /sys/class/lego-sensor/sensor1/value1
 5

カラーセンサーには、いくつかのモードがあり、上ではカラー検出モードを使用しました。このモードでは、以下の8つの色 (ただし、0は何もないことを意味する) を認識します。

番号 0 1 2 3 4 5 6 7

コマンドラインからは以上のようにアクセスできますが、プログラムからこれらの操作をしようとすると非常に面倒な手続きが必要になります。

プログラムからデバイスにアクセスためのアクセスライブラリがありますので、これを利用します。

python-ev3dev use

ev3dev-lang を利用した EV3 デバイスへのアクセス

ev3dev-lang は EV3 のモーターやセンサーにアクセスするためのライブラリです。以下の4つの言語で利用することができます。

  • C++
  • Python
  • JavaScript
  • Lua

ここでは、Python と C++ でのアクセス例を示します。

python-ev3dev のインストール

ev3dev-lang の Python版 (Pythonバインディング) を (クロス開発環境ではなく) EV3 にインストールします。 以下のコマンドを入力すると、python-ev3dev がシステムにインストールされ、Python から利用できるようになります。

 # cd ~
 # mkdir work
 # d work
 # apt-get install libboost-python1.55.0 python-setuptools python-pil
 # wget https://github.com/rhempel/ev3dev-lang-python/archive/0.6.0.zip
 # unzip 0.6.0.zip
 # cd ev3dev-lang-python-0.6.0/
 # python setup.py install

python-ev3dev からのデバイスアクセス

python-ev3dev を使ってモーターを回してみます。まず、適当なディレクトリーに移動し(先ほどの python-ev3dev をインストールする時のディレクトリーで実行するとエラーになります) python をインタラクティブモードで起動します。

 # cd 
 # python
 Python 2.7.9 (default, Mar  1 2015, 13:52:09)
 [GCC 4.9.2] on linux2
 Type "help", "copyright", "credits" or "license" for more information.
 >>> import ev3dev.ev3 as ev3     (ev3dev モジュールをインポートする)
 >>> m = ev3.LargeMotor() (モーターオブジェクトをインスタンス化)
 >>> m.connected (接続しているかを確認)
 True
 >>> m.run_forever(duty_cycle_sp=50) (デューティー比50%でモータを連続回転)
 >>> m.stop() (モーターを停止)

モーターの停止モードには、'break'、'coast'、'hold' の3種類があります。デフォルトは 'coast' に設定してあり、m.stop() 実行時も、惰性で若干動いたと思います。これを、'hold' にして、stop 実行時にピタッと止めます。

 >>> m.set_attr_string(None, "stop_command", ev3.Motor.STOP_COMMAND_HOLD)
 >>> m.get_attr_string(None, "stop_command")[1]
 >>> m.run_forever(duty_cycle_sp=50)
 >>> m.stop()

以上のように、Python からモータを比較的容易に制御出来ます。

次にセンサーを利用してみます。ポート2に超音波センサーをつなぎます。引き続き Python のインタラクティブモードから以下のように入力します。 for 文で100秒間センサの値(mm)を表示し続けます。途中でやめたいときは Ctrl+C を押してループを抜けます。

 >>> s = ev3.UltrasonicSensor()
 >>> s.distance_centimeters()
 118
 >>> import time
 >>> for i in range(0,100):
 ...     print s.distance_centimeters()
 ...     time.sleep(1)
 ..
 118
 114
 :中略
 2382
 2366
 326
 76
 ^CTraceback (most recent call last):
   File "<stdin>", line 3, in <module>
 KeyboardInterrupt
 >>>

python-ev3dev を知る

python-ev3dev の様々なクラス・関数のチュートリアル、リファレンスマニュアルは以下のページにあります。

また、Python では、モジュールやオブジェクトに対して dir() 関数を呼ぶと、利用可能な変数や関数のリストを見ることができます(変数か関数かの区別は dir() だけではわかりません)。

 >>> dir(ev3)
 ['Button', 'ButtonBase', 'ButtonEVIO', 'ColorSensor', 'DcMotor', 'Device', 'FbMem', 
 'FirgelliL12100Motor', 'FirgelliL1250Motor', 'GyroSensor', 'I2cSensor', 'INPUT_1', 'INPUT_2', 
 'INPUT_3', 'INPUT_4', 'INPUT_AUTO', 'Image', 'ImageDraw', 'InfraredSensor', 'LargeMotor', 
 'Led', 'Leds', 'LegoPort', 'LightSensor', 'MediumMotor', 'Motor', 'NxtMotor', 'OUTPUT_A', 
 'OUTPUT_AUTO', 'OUTPUT_B', 'OUTPUT_C', 'OUTPUT_D', 'Popen', 'PowerSupply', 'RemoteControl', 
 'Screen', 'Sensor', 'ServoMotor', 'Sound', 'SoundSensor', 'TouchSensor', 'UltrasonicSensor', 
 '__builtins__', '__doc__', '__file__', '__loader__', '__name__', '__package__', 'abspath', 
 'array', 'ctypes', 'fcntl', 'fnmatch', 'list_device_names', 'list_devices', 'list_motors', 
 'mmap', 'numbers', 'os', 'pack', 're', 'stat', 'unpack']
 >>>

たとえば、ev3dev.ev3 モジュールには Sound という変数か関数があります。 run_forever() という関数があり、Sound() 関数を呼んでインスタンスを生成してみます。以下の例でわかるように、引数は不要だったようです。

 >>> s = ev3.Sound()
 >>> dir(s)
 ['__doc__', '__module__', 'beep', 'play', 'speak', 'tone']

生成したオブジェクト s に対して dir(s) を実行してみます。すると、このオブジェクトで呼び出し可能そうな関数群が分かります。 試しに、speak を呼び出してみます。

 >>> s.speak("Hello RT-Middleware World")

EV3 から声が出るはずです。 実際には、関数の引数に何が必要なのか、どのようなデータ型を渡す必要があるのか、など詳細を知らないと呼び出せない関数もありますが、この方法でおおよその使い方を把握することができます。詳しい関数の呼び出し方を知りたい場合には、リファレンスマニュアルを参照してください。

Using EV3 Device Binding C++ Library

ev3dev-lang の利用

ev3dev-lang には C++ や Python、Lua などの EV3 デバイスへのバインディングライブラリが含まれています。 以下では、C++ から利用できる、ev3dev バインディングの導入と使い方について説明します。

ev3dev-lang の取得

以下の作業は、クロス開発環境にて行います。既に、上述の方法でクロス開発環境が導入済みであるとして進めます。

まず、github から ev3dev-lang をクローニングします。クローニング先は、クロス開発環境内の適当な場所 (ここでは /home/openrtm/work の下) にします。

 $ cd ev3-ev3dev-work/home/
 $ mkdir -p openrtm/work
 $ cd openrtm/work
 $ git clone https://github.com/ddemidov/ev3dev-lang-cpp
 Cloning into 'ev3dev-lang-cpp'...
 remote: Counting objects: 1733, done.
 remote: Total 1733 (delta 0), reused 0 (delta 0), pack-reused 1733
 Receiving objects: 100% (1733/1733), 510.41 KiB | 310.00 KiB/s, done.
 Resolving deltas: 100% (1150/1150), done.
 Checking connectivity... done.
 $ ls
 ev3dev-lang-cpp
 $ cd  ev3dev-lang-cpp
 $ git checkout c0829011bf668bb77e55049a7f2f396b0b452f41

ev3dev-lang のビルド

次に、コンパイルを行います。まず、作業ディレクトリー work に入り、brickstrap のクロス開発モードに入ります。

 $ cd ~/work
 $ brickstrap -b ev3-ev3dev-jessie -d ev3-ev3dev-work shell

先ほど github からクローニングしたディレクトリに移動し、make します。

 # cd /home/openrtm/work/ev3dev-lang-cpp/
 # mkdir build
 # cd build
 # cmake .. -DEV3DEV_PLATFORM=EV3
 # make

コンパイルが終了すると build の下にスタティックリンクライブラリ (libev3dev.a)、demos の下にはいくつかのサンプルアプリケーションが生成されます。

サンプルプログラム

サンプルプログラムの内容は以下の通りです。 標準セットの構成で使用できる産プロプログラムは、button-test、ev3dev-lang-demo、ev3dev-lang-test で drive-test、remote_control-test には IRセンサーと IRビーコンが必要になります。

button-test EV3 インテリジェントブロック(本体)のボタン(十字、中央、キャンセル)の ON/OFF を表示するサンプル。
drive-test モード1: IRセンサーを距離センサーとして利用し、障害物がなければ直進、障害物があればターンする自動動作を繰り返す。モード2: IRビーコンを用いた赤外線による遠隔操作モード。(標準セットにはIRセンサー、IRビーコンが付属していません。)
ev3dev-lang-demo メニュー形式で、センサー、モーター、LED、ボタン、サウンド、バッテリー、LCD等のデバイスの様々な機能にアクセスできるでもプログラム。
ev3dev-lang-test libev3dev のテスト用プログラム。センサー、モーター、LEDなどの機能を一通りアクセスし、libev3dev の機能をテストするプログラム。
remote_control-test IRセンサーと IRビーコンを用いたリモートコントロールのでもプログラム。

サンプルロプログラムの EV3 へのコピー

実際に EV3 で動作させるために、サンプルプログラムを EV3 にコピーします。EV3 で ev3dev が起動してネットワークにつながっていることを確認してください。クロス開発環境側から以下のようにしてコピーします。

 # cd demos ← ev3dev-lang-cpp/demos に移動
 # scp * ev3dev.local:/tmp/ ← ファイルを ev3.local (EV3) の /tmp/ にコピー

サンプルプログラムの実行

そのままターミナルから ev3dev.local にログインしてサンプルプログラムを実行します。

 # ssh ev3dev.local
 login: root
 password: r00tme
 # cd /tmp
 # ./ev3dev-lang-demo
 *** main menu ***
 
 (s)ensors
 (m)otors
 (l)eds
 (b)uttons
 s(o)und
 b(a)ttery
 l(c)d
 (q)uit
 
 Choice: 

自作のプログラムのビルド

自作のプログラムをビルドする際は ev3dev.h のインクルードと libev3dev.a とのリンクする必要があります。 以下は CMakeLists.txt の一例です。

 cmake_minimum_required(VERSION 2.8)
 
 set(ev3dev_dir /home/ev3dev-lang-cpp)
 set(ev3dev_lib ${ev3dev_dir}/build/libev3dev.a)
 
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
 
 project( testEV3 )
 add_executable(testEV3 main.cpp)
 include_directories(${ev3dev_dir})
 target_link_libraries(testEV3 ${ev3dev_lib} pthread)

Making RTC for EV3 (Python)

移動ロボットの組み立て

EV3 の標準セットを購入すると、移動ロボット (Educator Vehicle) の作成方法のマニュアルが1冊ついてくるはずです。 なければ、以下の URL からダウンロードすることができます。この移動ロボットを例にとり、移動ロボットRTC を作成してみます。

まずは、このマニュアルに従って、移動ロボット (Educator Vehicle) を組み立てます。

それぞれのモーター、センサーはそれぞれ以下のように取り付けます。

モーター右 ポート C
モーター左 ポート B
モーター(M) ポートA
タッチセンサー右 ポート 3
タッチセンサー左 ポート 1
超音波センサー ポート 4
ジャイロセンサー ポート 2

移動ロボットの運動学

さて、上記のコンポーネントは指令値として2次元速度指令を受け取りますが、実際にモータを制御する際には、モーターの角速度指令値を計算しモーターに命令しなければなりません。

移動ロボットの運動学については、東北学院大学の熊谷先生のページが参考になります。

座標系としては、自律移動機能共通インターフェース仕様書に従って、

ロボット進行方向をX軸とした右手系を想定する。速度指令は座標系にならって、(v_x、v_y、v_a)とする。独立二輪駆動式の移動ロボットなので、v_y は常に0となり、実質的に v_x および v_a を指定することになる。

さて、車輪の角速度を math16.png , math17.png として、車輪の半径を r とすると、各車輪の接地点での速度 v_r、v_l はそれぞれ


math3.png
math4.png

となる。また、回転中心からロボット中心の距離を ρ とすると、以下の式が成り立つ。


math5.png

一方、中心から車輪までの距離(トレッドの1/2)を d とすると、


math0.png
math1.png

となる。以上の式から、速度指令(v_x、v_y、v_a)の時に実際に与えるべき左右のモーターの角速度は以下の通りになります。


math6.png
math7.png

Educator Vehicle の車輪の直径 (2r)、トレッド (2d) はそれぞれ

車輪の直径 2r 56mm (0.056m)
車輪の直径 r 28mm (0.028m)
トレッド幅 2d 118.5mm (0.1185m)
トレッド幅 d 59.25mm (0.05925m)
車輪の幅 28.5mm (0.0285m)

なので、

math8.png
math9.png

となる。

自己位置推定(オドメトリ)

次に、ロボットの自己位置同定の方法について考えます。 ロボットの移動速度・角速度を積分することで任意の時点の位置・姿勢を得ることができます。速度 v_x、角速度 v_a とすると、(x、y、θ) の微小変位と v_x、v_a との関係を直線近似すると、


math10.png
math11.png
math12.png

となる。(より厳密には円弧近似する方法もあるが、ここでは簡単に直線近似とした。) これを実際のロボット制御時に、計算機上で求める場合、サンプリング周期 Δt [s] として、

math13.png
math14.png
math15.png

のように求められる。

RTC のひな形作成

作成する RTC の設計

作成する RTC を設計します。

作成する RTC の仕様をいかに示します。RTCBuilder で必要事項を入力し、ひな形コードを生成します。

基本タブ
プロファイル名 名称・指定
モジュール名 EducatorVehicle
バージョン 任意
ベンダ名 任意
カテゴリ Mobilerobot
アクティビティ
有効アクション onInitialize、onActivated、onDeactivated、onExecute
データポート (InPort)
ポート名 意味
velocity2D RTC::TimedVelocity2D 速度指令 (v_x、v_y、v_θ) [m/s、m/s、rad/s]
angle RTC::TimedDouble
データポート (OutPort)
ポート名 意味
odometry RTC::TimedPose2D 現在の位置・姿勢(角度) (x、y、θ) [m、m、rad]
ultrasonic RTC::RangeData 超音波センサーをレンジセンサーと仮定し、要素1の距離データを格納
gyro RTC::TimedDouble ジャイロセンサーを TimedDouble [rad] にて出力
color RTC::TimedString カラーセンサーの値を色名 (none、black、white、blue、green、red、yellow、brown) で出力
touch RTC::TimedBooleanSeq タッチセンサーの値をBoolean[2] で出力
コンフィギュレーション
ポート名 意味
wheelRadius double タイヤの半径 [m]
wheelDistance double タイヤ間距離の1/2 [m]

Operating EV3 Device

モーター

速度制御

速度を制御するためには以下のデバイスファイルに"on"と書き込む必要があります。

 echo on > /sys/class/tacho-motor/motor0/speed_regulation

起動時には off になっているので速度制御はできません。 off の場合は duty_cycle_sp でデューティー比を設定できます。

そして以下のファイルに目標速度を書き込みます。

 echo 50 > /sys/class/tacho-motor/motor0/speed_sp

ここで指定する値は1秒間に何カウント動かすかという値なので、以下のデバイスファイルから取得する値を角速度に掛けて変換してください。 ※モーターM、モーターLの場合は count_per_rot で取得できる値が360なので変換しなくても影響はありません。

 cat /sys/class/tacho-motor/motor0/count_per_rot

後は以下のコマンドを入力すると回転を開始します。

 echo run-forever > /sys/class/tacho-motor/motor0/command

現在の角速度を取得するためには以下のデバイスファイルを使用します。

 cat /sys/class/tacho-motor/motor0/speed

以上の手順を ev3dev-lang-cpp を利用して記述すると以下のようになります。

 ev3dev::large_motor lm = ev3dev::large_motor();
 lm.set_speed_regulation_enabled("on");
 lm.set_speed_sp(50*lm.count_per_rot());
 lm.run_forever();
 std::cout << lm.speed()/lm.count_per_rot() << std::endl;

ev3dev-lang-python を利用して記述すると以下のようになります。

 lm = ev3.LargeMotor()
 lm.set_attr_string("speed_regulation", "on")
 lm.speed_sp = 50*lm.count_per_rot
 lm.run_forever()
 print lm.speed/lm.count_per_rot

一定時間動作させる

モーターを一定時間動作させるには、まず以下のデバイスファイルで時間を設定してください。

 echo 2000 > /sys/class/tacho-motor/motor0/time_sp

そして以下のコマンドで動作を開始します。

 echo run-timed > /sys/class/tacho-motor/motor0/command

位置制御

位置制御を行うためにはまず duty_cycle_sp (speed_regulation が on の場合は speed_sp) を設定しておく必要があります。

 echo 50 > /sys/class/tacho-motor/motor0/speed_sp

次に以下のデバイスファイルで位置を設定します。

 echo 100 > /sys/class/tacho-motor/motor1/position_sp

この位置もカウント数で指定するため、count_per_rot で変換してください。

以下のコマンドで動作を開始します。

 echo run-to-abs-pos > /sys/class/tacho-motor/motor1/command

現在の位置からの相対的な角度を指定したい場合は以下のコマンドを利用します。

 echo run-to-rel-pos > /sys/class/tacho-motor/motor1/command

現在の位置は以下のデバイスファイルから取得できます。

 cat /sys/class/tacho-motor/motor1/position

以上の手順を ev3dev-lang-cpp を利用して記述すると以下のようになります。

 ev3dev::large_motor lm = ev3dev::large_motor();
 lm.set_speed_regulation_enabled("on");
 lm.set_speed_sp(50*lm.count_per_rot());
 lm.set_position_sp(100*lm.count_per_rot());
 lm.run_to_abs_pos(100*lm.count_per_rot());
 #lm.run_to_rel_pos(100*lm.count_per_rot());

ev3dev-lang-python を利用して記述すると以下のようになります。

 lm = ev3.LargeMotor()
 lm.set_attr_string(None, "speed_regulation", "on")
 lm.speed_sp = 50*lm.count_per_rot
 lm.run_to_abs_pos()
 #lm.run_to_rel_pos()

センサーの C++、Python による操作

カラーセンサーで反射光の強さ取得

  • C++
     ev3dev::color_sensor cs = ev3dev::color_sensor();
     std::cout << cs.reflected_light_intensity() << std::endl;
  • Python
     cs = ev3.ColorSensor()
     print cs.reflected_light_intensity()

タッチセンサーのオンオフ取得

  • C++
     ev3dev::touch_sensor ts = ev3dev::touch_sensor();
     std::cout << ts.is_pressed() << std::endl;
  • Python
     cs = ev3.ColorSensor()
     print cs.reflected_light_intensity()

ジャイロセンサーで角度取得

  • C++
     ev3dev::gyro_sensor gs = ev3dev::gyro_sensor();
     std::cout << gs.angle() << std::endl;
  • Python
     gs = ev3.GyroSensor()
     print gs.angle()

LCD の操作

  • C++
     ev3dev::lcd lcd = ev3dev::lcd();
     unsigned char *fb = lcd.frame_buffer();
     for(int i=0;i < 3072;i++)
     {
         if(i%24 < 12)fb[i] = 0x00;
         else fb[i] = 0xff;
     }
  • Python
     lcd = ev3.Screen()
     lcd.mmap.seek(os.SEEK_SET)
     lcd.mmap.write(chr(0xff)*3072)
     for i in range(lcd.fix_info.smem_len):
         if i%24 < 12:
             lcd.mmap.seek(os.SEEK_SET)
             lcd.mmap.seek(i)
             lcd.mmap.write_byte(chr(0xff))

Installing RTCs for Educator Vehicle (EV3)

このページでは Educator Vehicle を操作するための各RTC のインストール方法について説明します。

EducatorVehicle

EducatorVehicle は EV3 の走行速度の入力、センサーのデータの出力等を行うためのコンポーネントです。

クロス環境で以下のコマンドを入力してください。

 git clone https://github.com/Nobu19800/EducatorVehicle
 cd EducatorVehicle
 cmake .
 make

生成された src/EducatorVehicleComp を EV3 に転送してください。

EducatorVehicle
InPort
名前 データ型 説明
velocity2D RTC::TimedVelocity2D 目標速度
angle RTC::TimedDouble モーターMの角度
lcd RTC::CameraImage LCDに表示する画像データ
sound RTC::TimedString 出力する音声
OutPort
名前 データ型 説明
odometry RTC::TimedPose2D 現在の位置・姿勢
current_vel RTC::TimedVelocity2D 現在の速度・角速度
ultrasonic RTC::RangeData 超音波センサーで計測した距離
gyro RTC::TimedDouble ジャイロセンサーで計測した角度
color RTC::TimedString カラーセンサーで計測した色
light_reflect RTC::TimedDouble カラーセンサーで計測した反射光の強さ
touch RTC::TimedBoolean タッチセンサーのオンオフ。右側が0番目の要素、左側が1番目の要素
コンフィギュレーションパラメーター
名前 デフォルト値 説明
wheelRadius 0.028 車輪の半径
wheelDistance 0.054 タイヤ間距離の1/2
medium_motor_speed 1.6 モーターMの速度

simulator_ev3_2.png

タッチセンサーの出力

touch で入力したデータの0番目が右側(3番ポート)のタッチセンサー、1番目が左側(1番ポート)のタッチセンサーに対応しています。

音声の出力

sound による音声の入力には以下のコマンドを利用できます。

  • beep
    beep と入力するとビープ音が鳴ります。
  • tone
    以下のように tone、周波数、時間と入力すると指定周波数の音を指定ミリ秒数だけ鳴らします。

 tone,100,1000

  • それ以外
    それ以外は指定文字列を発音します

LCD に表示する画像について

画像ファイルを saveBinaryImage.exe にドラッグ・アンド・ドロップすれば変換できます。

LDC で表示する画像データは、以下のコンポーネントで画像データの変換を行えば入力可能です。 Windows で使用する場合は、release フォルダーの ImageConversionLCDComp.exe で起動できます。

ImageConversionLCD
InPort
名前 データ型 説明
out RTC::CameraImage 変換前の画像データ
OutPort
名前 データ型 説明
out RTC::CameraImage 変換後の画像データ

ImageConversionLCD.png

例えば、OpenRTM-aist 付属のサンプルコンポーネント OpenCVCamera と接続すれば、EV3 にカメラ画像を表示させることができます。

system_lcd.png
s_DSC00796.JPG

あるいは、指定の画像を入力するということもできます。

s_DSC00797.JPGs_DSC00798.JPG

ControlEducatorVehicle

ControlEducatorVehicle を用いることによって以下の移動ロボット(Educator Vehicle 改)の制御ができます。組み立て方は このページ を参考にしてください。

  • タッチセンサーに障害物が当たった時に回避する
  • カラーセンサーで地面の反射光を計測して一定以下になったら停止する
  • 超音波センサーで地面までの距離を計測して一定上になったら停止して超音波センサーを回転させて走行可能な地面を探索する

超音波センサーを使用しない場合は Educator Vehicle の制御にも使用できます。

クロス環境で以下のコマンドを入力してください。

 git clone https://github.com/Nobu19800/ControlEducatorVehicle
 cd ControlEducatorVehicle
 cmake .
 make

生成された src/ControlEducatorVehicleComp を EV3 に転送してください。

ControlEducatorVehicle
InPort
名前 データ型 説明
target_velocity_in RTC::TimedVelocity2D 目標速度
current_pose RTC::TimedPose2D 現在位置・姿勢
ultrasonic RTC::TimedRangeData 超音波センサーで計測した距離
light_reflect RTC::TimedDouble カラーセンサーで計測した反射光の強さ
touch TimedBoolean タッチセンサーのオンオフ
OutPort
名前 データ型 説明
target_velocity_out RTC::TimedVelocity2D 補正後の目標速度
angle RTC::TimedDouble モーターMの角度
コンフィギュレーションパラメーター
名前 デフォルト値 説明
sensor_height 0.2 走行できる地面があると判定する超音波センサーの計測値
back_speed 0.1 後退運動をする速さ
back_time 1.0 後退運動する時間
rotate_speed 0.8 回転運動をする速さ
rotate_time 2.0 回転運動する時間
medium_motor_range 1.6 モーターMの動作範囲

ControlEducatorVehicle.png

タッチセンサーの入力による回避運動

ev3_4.png

タッチセンサーがオンになった場合、まず③のように back_speed で指定した速さで back_time で指定した時間だけ後退します。 そして④のように rotate_speed で指定した角速度で rotate_time で指定した時間だけ回転します。 回転する方向はオンになったタッチセンサーと逆方向です。

超音波センサーによる地面までの距離計測

超音波センサーで地面までの距離を計測して停止、超音波センサーを回転させて走行可能な地面を探索する操作を実行するためには、EV3 の Education Vehicle を組み立ててさらに超音波センサーを回転させるように取り付ける必要があります。

動作手順は以下のようになっています。

ev3_sensor.png

机の上などを走行している場合で、②のようぬ机の端まで移動したとします。 超音波センサーで計測した距離が sensor_height 以上になった場合、一旦停止します。 そして③のように超音波センサーを右側に90度回転させて地面までの高さが sensor_height 以下かを判定します。 sensor_height 以上だった場合は④のように左方向に180度回転させて地面までの高さが sensor_height 以下かを判定します。 sensor_height 以下だった場合は、超音波センサーを向けた方向に Education Vehicle を回転させます。

一括インストール

上記の RTC を一括でインストールします。 以下のコマンドを入力してください。

 git clone https://github.com/Nobu19800/EducatorVehicle_script_ev3dev
 cd EducatorVehicle_script_ev3dev
 sh Component/install_rtc.sh

Sample RTSystem Execution

事前準備

Windows

RTC のインストール

サンプルの RTシステムの一部で以下の RTC を使用するためインストールしてください。

DirectInputRTC

ジョイスティックで操作するためのRTCです。 以下のサイトからインストーラーをダウンロードして実行してください。

JoystickToVelocity

DirectInputRTC のアウトポートからの pos からの出力 (TimedLongSeq型) を TimedVelocity2D型に変換する RTC です。 以下のサイトからインストーラーをダウンロードして実行してください。

FloatSeqToVelocity

FloatSeqToVelocity は OpenRTM-aist-Python のサンプルTkJoyStick の出力 (TimedFloatSeq型) をTimeVelocity2D型でに変換してアウトポートから出力する RTC です。

ここ からダウンロードしてください。

EducatorVehicle
InPort
名前 データ型 説明
in TimeFloatSeq 変換前のデータ
OutPort
名前 データ型 説明
in TimedVelocity2D 変換後のデータ
コンフィギュレーションパラメーター
名前 デフォルト値 説明
rotation_by_position -0.02 ジョイスティックのX座標に対する回転角速度
velocity_by_position 0.002 ジョイスティックのY座標に対する直進速度

ev3_10.png

スクリプトファイル

RTC の起動、RTシステムの復元を自動化するためのスクリプトファイルです。 ここ からダウンロードしてください。

ネームサーバー、RTシステムエディタの起動

Windows

まず最初に Windows でネームサーバー、RTシステムエディタを起動してください。 詳しい手順は このページ を参考にしてください。

ev3dev

EV3 の 一括インストールの項目 でダウンロードしたファイルの中に rtc.conf があるので、rtc.conf の以下の ppp.pp.pp.ppp の部分を Windows側の IPアドレスに変更してください。

 corba.nameservers: ppp.pp.pp.ppp

これで起動したコンポーネントが Windows のネームサーバーに登録されるようになります。

Windows で IPアドレスを確認するには以下のコマンドを入力します。

 ipconfig

RTC の起動

Windows

Windows側の RTC は スクリプトファイルの項目 でダウンロードしたファイルの中に start_component.bat というバッチファイルがあるのでそれを実行すれば以下の RTC が起動します。 ※64bit版 Windows、32bit版 OpenRTM-aist を対象にしています。32bit版 Windows の場合は start_component_32.bat、64bit版 OpenRTM-aist を利用する場合は start_component_64.bat を起動してください。 ※Python のインストールしたディレクトリーにパスが通っていない場合、TkJoyStick は起動できません。お手数ですが このページ の手順を参考にして手動で起動してください。

ev3dev

一括インストールの項目 でダウンロードしたフォルダーの中の start_rtc.sh を実行することで起動できます。

 cd RaspberryPiMouseRTSystem_script_Raspbian
 sh start_rtc.sh

これで以下の RTC が起動します。

 ControlEducatorVehicle0
 EducatorVehicle0

RTシステムの復元、開始

Windows側でスクリプトファイルの項目 でダウンロードしたファイルの JoystickControlEV3 フォルダー内の JoystickControlEV3_resurrect.bat を実行してください。 これでデータポートの接続、コンフィギュレーションパラメーターの設定などが行われます。 次に JoystickControlEV3_activate.bat を実行すると RTC をアクティブ化します。 JoystickControlEV3 はジョイスティックコンポーネントから EV3 を操作する RTシステムです。

非アクティブにする際は JoystickControlEV3_stop.bat を起動してください。 JoystickControlEV3_teardown.bat を起動するとポートの接続を切断します。

サンプルの詳細

JoystickControlEV3 以外のサンプルも ****_resurrect.bat で RTシステム復元、****_activate.bat でアクティブ化、****_stop.bat で非アクティブ化、****_teardown.bat でポートの切断ができます。

ファイル名 内容
****_resurrect.bat RTシステム復元
****_activate.bat アクティブ化
****_stop.bat 非アクティブ化
****_teardown.bat ポートの切断

GamePadSimpleControlEV3

このサンプルはゲームパッドのアナログスティックで傾けた方向に Educator Vehicle を操作する RTシステムです。 Windows側の PC にゲームパッドを接続してから起動してください。

ev3_9.png

JoystickSimpleControlEV3

このサンプルは OpenRTM-aist-Python のサンプルコンポーネント TkJoyStick で傾けた方向に Educator Vehicle を操作する RTシステムです。

ev3_8.png

GamePadControlEV3

このサンプルはゲームパッドのアナログスティックで傾けた方向に Educator Vehicle を操作することに加えて、タッチセンサー、超音波センサー、カラーセンサーを利用した制御 を行う RTシステムです。

ev3_6.png

JoystickControlEV3

このサンプルは OpenRTM-aist-Python のサンプルコンポーネント TkJoyStick で傾けた方向に Educator Vehicle を操作することに加えて、タッチセンサー、超音波センサー、カラーセンサーを利用した制御 を行う RTシステムです。

ev3_7.png

Control by Original RTCs

初心者用課題

ひな形コードの作成

新規作成した RTC を EducatorVehicle と接続して制御するまでの手順を説明します。

まずは Windows、Ubuntu上で このページ の手順に従って RTC を作成してください。 RTC の仕様は以下のように入力します。

基本
モジュール名 EV3SampleCPP、もしくはEV3SamplePy
アクティビティ
有効アクション onInitialize、onExecute、onActivated、onDeactivated
データポート
InPort
名前 データ型 説明
touch RTC::TimedBooleanSeq タッチセンサーのオンオフ
OutPort
名前 データ型 説明
target_velocity RTC::TimedVelocity2D 目標速度
sound RTC::TimedString 音声
コンフィギュレーション
名前 説明
forward_velocity double 直進速度、デフォルト値は0.0、制約条件に-0.20<=x<=0.20、Widgetはslider、Stepは0.02
rotate_velocity double 回転速度、デフォルト値は0.0、制約条件に-3.1<=x<=3.1、Widgetはslider、Stepは0.2
言語・環境
言語 C++、もしくは Python

この RTC ではコンフィギュレーションパラメーター forward_velocity、rotate_velocity に入力した速度を target_velocity から EducatorVehicle に送信します。 さらに touch でタッチセンサーのオンオフを取得して、オンになった場合は停止して sound から発声する音を指令します。

※RTC は EV3上で動作させることを前提にしていますが、動作確認や講習会での利用には Windows や Ubuntu上で動作させても問題はないので、その場合はコードの編集をする前に CMake で Visual Studio、もしくは Code::Blocks のプロジェクトを生成しておくことをお勧めします。 CMake でプロジェクト生成からビルドまでの手順は以下のページに記載してあります。

コードの編集

まずは onExecute 関数を編集します。

以下はforward_velocity、rotate_velocityの 値を target_velocity から送信するコードです。

C++(src/EV3SampleCPP.cpp)

 RTC::ReturnCode_t EV3SampleCPP::onExecute(RTC::UniqueId ec_id)
 {
     //コンフィギュレーションパラメータで設定した速度を送信する
     m_target_velocity.data.vx = m_forward_velocity;
     m_target_velocity.data.va = m_rotate_velocity;
     setTimestamp(m_target_velocity);
     m_target_velocityOut.write();
   return RTC::RTC_OK;
 }

Python(EV3SamplePy.py)

     def onExecute(self, ec_id):
         #コンフィギュレーションパラメータで設定した速度を送信する
         self._d_target_velocity.data.vx = self._forward_velocity[0]
         self._d_target_velocity.data.vy = 0
         self._d_target_velocity.data.va = self._rotate_velocity[0]
         OpenRTM_aist.setTimestamp(self._d_target_velocity)
         self._target_velocityOut.write()

一応、非アクティブ状態の時は停止するように onDeactivated で速度0を送信するようにします。

C++(src/EV3SampleCPP.cpp)

 RTC::ReturnCode_t EV3SampleCPP::onDeactivated(RTC::UniqueId ec_id)
 {
     //停止する
     m_target_velocity.data.vx = 0;
     m_target_velocity.data.vy = 0;
     m_target_velocity.data.va = 0;
     setTimestamp(m_target_velocity);
     m_target_velocityOut.write();
 
   return RTC::RTC_OK;
 }

Python(EV3SamplePy.py)

     def onDeactivated(self, ec_id):
         #停止する
         self._d_target_velocity.data.vx = 0
         self._d_target_velocity.data.vy = 0
         self._d_target_velocity.data.va = 0
         OpenRTM_aist.setTimestamp(self._d_target_velocity)
         self._target_velocityOut.write()
         return RTC.RTC_OK

共通インターフェース仕様書では進行方向をX軸正方向にしているため、Velocity2D型のvxに直進速度、vaに回転速度を入力します。

Pythonではさらにコンストラクタの以下の部分を修正してください。

Python(EV3SamplePy.py)

         #self._d_target_velocity = RTC.TimedVelocity2D(*target_velocity_arg)
         self._d_target_velocity = RTC.TimedVelocity2D(RTC.Time(0,0),RTC.Velocity2D(0,0,0))

         #self._d_sound = RTC.TimedString(*sound_arg)
         self._d_sound = RTC.TimedString(RTC.Time(0,0),[])

※ここまでの作業だけでも Educator Vehicle の動作は可能なので、面倒ならば以下の手順は飛ばしてもらっても大丈夫です。

次にタッチセンサーが ON になった時に停止する処理を書きます。 ここで touch に onExecute が呼び出された時に新規に受信したデータが必ず存在するとは限らないので、タッチセンサーのデータを一時的に格納しておく変数を宣言しておきます。

C++(include/EV3SampleCPP/EV3SampleCPP.h)

 private:
      bool m_last_sensor_data[2];    /*センサーのデータを格納する変数*/

Python(EV3SamplePy.py)

     def __init__(self, manager):
         OpenRTM_aist.DataFlowComponentBase.__init__(self, manager)
 
         #タッチセンサーのデータを格納する変数
         self._last_sensor_data = [False, False]

次に onExecute にタッチセンサーがオンの場合に停止、音声を出力する処理を追加します。

C++(src/EV3SampleCPP.cpp)

 RTC::ReturnCode_t EV3SampleCPP::onExecute(RTC::UniqueId ec_id)
 {
     //ここから
     //データを新規に受信した場合に、データを m_last_sensor_data を格納する
     if (m_touchIn.isNew())
     {
         m_touchIn.read();
         if (m_touch.data.length() == 2)
         {
             for (int i = 0; i < 2; i++)
             {
                 //タッチセンサーがOFFからONになった時に音を鳴らす
                 if (!m_last_sensor_data[i] && m_touch.data[i])
                 {
                     m_sound.data = "beep";
                     setTimestamp(m_sound);
                     m_soundOut.write();
 
                     
                 }
                 m_last_sensor_data[i] = m_touch.data[i];
             }
         }
     }
     //タッチセンサーがONの時に前進しないようにする
     if (m_forward_velocity > 0)
     {
         for (int i = 0; i < 2; i++)
         {
             if (m_last_sensor_data[i])
             {
                 m_target_velocity.data.vx = 0;
                 m_target_velocity.data.vy = 0;
                 m_target_velocity.data.va = 0;
                 setTimestamp(m_target_velocity);
                 m_target_velocityOut.write();
                 return RTC::RTC_OK;
             }
         }
     }
     //ここまで
     //コンフィギュレーションパラメーターで設定した速度を送信する
     m_target_velocity.data.vx = m_forward_velocity;
  (以下略)

Python(EV3SamplePy.py)

    def onExecute(self, ec_id):
         #ここから
         #データを新規に受信した場合に、データをm_last_sensor_dataを格納する
         if self._touchIn.isNew():
             data = self._touchIn.read()
             if len(data.data) == 2:
                 for i in range(2):
                     #タッチセンサーがOFFからONになった時に音を鳴らす
                     if not self._last_sensor_data[i] and data.data[i]:
                         self._d_sound.data = "beep"
                         OpenRTM_aist.setTimestamp(self._d_sound)
                         self._soundOut.write()
                 self._last_sensor_data = data.data[:]
 
 
         #タッチセンサーが ON の時に前進しないようにする
         if self._forward_velocity[0] > 0:
             for d in self._last_sensor_data:
                 if d:
                     self._d_target_velocity.data.vx = 0
                     self._d_target_velocity.data.vy = 0
                     self._d_target_velocity.data.va = 0
                     OpenRTM_aist.setTimestamp(self._d_target_velocity)
                     self._target_velocityOut.write()
                     return RTC.RTC_OK
 
 
        #ここまで
        #コンフィギュレーションパラメーターで設定した速度を送信する
        self._d_target_velocity.data.vx = self._forward_velocity[0]
 (以下略)

ビルド

ソースコードの編集が終了したらC++の場合はビルドを行います。 EV3上で動作させる場合はクロスコンパイル環境でビルドを行ってください。 ソースコードのあるディレクトリに移動して以下のコマンドを入力すればビルドできます。

 cd EV3SampleCPP
 cmake .
 make

※Visual Studio、もしくは Code::Blocks で編集している場合は GUI上の操作でビルドを行ってください。

生成された src/EV3SampleCPPComp を EV3 に転送してください。 ※Windows、Ubuntu で動作させる場合は転送しないでください。

 sftp robot@<IPアドレス>
 sftp> put EV3SampleCPP/src/EV3SampleCPPComp

Python版もEV3SamplePy.py を転送してください。

 sftp robot@<IPアドレス>
 sftp> put EV3SamplePy/EV3SamplePy.py

cmake がインストールされていない場合は以下のコマンドを入力してください。

 sudo apt-get install cmake

RTC 起動

ビルドが完了したら RTC を起動します。 以下のコマンドで起動できます。

C++

 EV3SampleCPPComp&

Python

 python EV3SamplePy.py&

※Windows上で RTC を起動する場合は EV3SampleCPPComp.exe (EV3SamplePy.py) をダブルクリックしてください。

次に EducatorVehicle を起動させます。 以下のコマンドで起動してください。

 Compomnents/EducatorVehicleComp&

動作確認

まずは動作確認のためにデータポートの接続を行います。 Windows側で RTシステムエディタを起動してください。 最初に EV3 で起動しているネームサーバーを RTシステムエディタのビューに追加します。 ネームサーバー追加ボタンを押下して EV3 の IPアドレスを入力してください。

rpm11.png

ネームサーバーを追加したらデータポートを以下のように接続してください。

ev3_sample.png

ポートの接続
EV3SampleCPP0(EV3SamplePy0) EducatorVehicle0
target_velocity velocity2D
touch touch
sound sound

そして RTC をアクティブ化すると動作を開始します。

まずは EV3SampleCPP(EV3SamplePy) のコンフィギュレーションパラメーターを変更する事で Educator Vehicle を操作してみます。

RTシステムエディタで EV3SampleCPP0(EV3SamplePy0) を選択して、Configuration View の編集を選択してください。

ev3_conf.png

すると以下のウインドウが起動します。

ev3_conf2.png

forward_velocity と rotate_velocity のスライダーによりEV3を操作できます。

次にタッチセンサーがオンになった場合に停止するかを確認してみます。 タッチセンサーに物体を接触させて停止するかを確認してください。 停止した時に音が鳴るかも確認してください。

応用(中級者用課題)

ここからはさらにタッチセンサーがオンになった場合に後退→回転のより障害物を回避する動作を実装します。 タッチセンサーのデータが入力された時に以下の処理を実行することで回避運動を行います。

flowChart_4.png

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

新たに以下のコンフィギュレーションパラメーターを設定します。

コンフィギュレーション
名前 説明
back_speed double 後退速度、デフォルト値は0.1
back_time double 後退運動の時間、デフォルト値は1.0
rotate_speed double 回転速度、デフォルト値は0.8
rotate_time double 回転運動の時間、デフォルト値は2.0

関数の実装

新たに以下の関数を実装します。

関数名 内容 引数
stop_robot 停止する なし
back_move 後退する なし
rotate_move 回転する dir(回転方向)

stop_robot 関数

stop_robot関数は以下のようになっています。 目標速度を0に設定して送信します。

 void ControlEducatorVehicle::stop_robot()
 {
     m_target_velocity_out.data.vx = 0;
     m_target_velocity_out.data.vy = 0;
     m_target_velocity_out.data.va = 0;
     setTimestamp(m_target_velocity_out);
     m_target_velocity_outOut.write();
 }

back_move 関数

back_move 関数は以下のようになっています。 後退するための速度を出力後、sleep 関数で指定時間だけ待つことによりコンフィギュレーションパラメータ back_speed の 速度でback_time の時間だけ後退する運動を実現しています。

 void ControlEducatorVehicle::back_move()
 {
     //後退運動を指令
     m_target_velocity_out.data.vx = -m_back_speed;
     m_target_velocity_out.data.vy = 0;
     m_target_velocity_out.data.va = 0;
     setTimestamp(m_target_velocity_out);
     m_target_velocity_outOut.write();
     
     //一定時間待つ
     double sec, usec;
     usec = modf(m_back_time, &sec);
     
     coil::TimeValue ts((int)sec, (int)(usec*1000000.0));
     coil::sleep(ts);
 
     //一旦停止
     stop_robot();
 }

※onExecute 関数内で sleep 関数などで待機すると、その間は RTシステムエディタ等による操作ができなくなるためあまり望ましい実装の方法ではありません。 ただ今回の場合は実行コンテキストを止めずに処理を実装すると処理が複雑化するためこのように実装しています。

stop_robot 関数

rotate_move 関数は以下のようになっています。 後退運動と同じく、コンフィギュレーションパラメーター rotate_speed の速度で rotate_time の時間だけ回転運動する処理を実装しています。

 void ControlEducatorVehicle::rotate_move(bool dir)
 {
     //回転運動を指令
     m_target_velocity_out.data.vx = 0;
     m_target_velocity_out.data.vy = 0;
     //左回転
     if (dir)m_target_velocity_out.data.va = m_rotate_speed;
     //右回転
     else m_target_velocity_out.data.va = -m_rotate_speed;
     setTimestamp(m_target_velocity_out);
     m_target_velocity_outOut.write();
     
     //一定時間待つ
     double sec, usec;
     usec = modf(m_rotate_time, &sec);
     coil::TimeValue ts((int)sec, (int)(usec*1000000.0));
     coil::sleep(ts);
 
     //一旦停止
     stop_robot();
 }

onExecute 関数の実装

最後にフローチャートのように、タッチセンサーのデータ入力時に回避運動を行う処理を実装すれば完成です。

 RTC::ReturnCode_t ControlEducatorVehicle::onExecute(RTC::UniqueId ec_id)
 {
     //目標速度を変数に格納
     if (m_target_velocity_inIn.isNew())
     {
         m_target_velocity_inIn.read();
         vx = m_target_velocity_in.data.vx;
         vy = m_target_velocity_in.data.vy;
         va = m_target_velocity_in.data.va;
     }
     //タッチセンサーの処理
     if (m_touchIn.isNew())
     {
         while(m_touchIn.isNew())m_touchIn.read();
         if (m_touch.data.length() >= 2)
         {
             touch_r = m_touch.data[0];
             touch_l = m_touch.data[1];
             //前進しているときのみ
             if (vx > 0)
             {
                 //左タッチセンサー
                 if (m_touch.data[0])
                 {
                     停止→後退→右回転
                     stop_robot();
                     back_move();
                     rotate_move(true);
 
                 }
                 //右タッチセンサー
                 else if (m_touch.data[1])
                 {
                     停止→後退→左回転
                     stop_robot();
                     back_move();
                     rotate_move(false);
 
                 }
 
             }
         }
 
     }
 
     m_target_velocity_out.data.vx = vx;
     m_target_velocity_out.data.vy = vy;
     m_target_velocity_out.data.va = va;
 
     
     
     setTimestamp(m_target_velocity_out);
     //目標速度送信
     m_target_velocity_outOut.write();
     
     return RTC::RTC_OK;
 }

発展(上級者用課題)

ここでは超音波センサーにより地面までの距離を検知して滑落を回避する RTC を作成します。

 /ja/node/6030/

超音波センサーで検知した地面までの高さが一定以上の場合に、以下の処理を実行します。

flowChart2.png

データポート、コンフィギュレーションパラメーター

以下のデータポート、コンフィギュレーションパラメーターを追加してください。

データポート
InPort
名前 データ型 説明
ultrasonic RTC::RangeData 超音波センサーをレンジセンサーと仮定し、要素1の距離データを格納
current_pose RTC::TimedPose2D 現在の位置・姿勢
OutPort
名前 データ型 説明
angle RTC::TimedDouble モーターMの角度
コンフィギュレーション
名前 説明
sensor_height double 接地可能と判定する地面までの高さ、デフォルト値は0.20
medium_motor_range double Mモーターの動作範囲、デフォルト値は1.6

関数の実装

接地可能な地面を超音波センサーで調べる search_ground 関数と、ロボットを指定した角度だけ回転させる turn_move 関数を実装します。

 bool ControlEducatorVehicle::search_ground(double &a)
 {
     //Mモーターを右回転
     m_angle.data = (-1)*m_medium_motor_range;
     setTimestamp(m_angle);
     m_angleOut.write();
     coil::TimeValue ts(2, 0);
     coil::sleep(ts);
    
     //地面までの距離が一定以下かを判定
     if (m_ultrasonicIn.isNew())
     {
         while(m_ultrasonicIn.isNew())m_ultrasonicIn.read();
         if (m_ultrasonic.ranges.length() >= 1)
         {
             
             if (m_ultrasonic.ranges[0] < m_sensor_height)
             {
                 //一定以下の場合は接地可能と判定して、接地可能な地面を検知した方向を変数に格納
                 a = m_medium_motor_range;
                 return true;
             }
         }
     }
     
     //Mモーターを左回転
     m_angle.data = (-1)*-m_medium_motor_range;
     setTimestamp(m_angle);
     m_angleOut.write();
     ts = coil::TimeValue(4, 0);
     coil::sleep(ts);
 
     //地面までの距離が一定以下かを判定
     if (m_ultrasonicIn.isNew())
     {
         while(m_ultrasonicIn.isNew())m_ultrasonicIn.read();
         if (m_ultrasonic.ranges.length() >= 1)
         {
             
             if (m_ultrasonic.ranges[0] < m_sensor_height)
             {
                 //一定以下の場合は接地可能と判定して、接地可能な地面を検知した方向を変数に格納
                 a = -m_medium_motor_range;
                 return true;
             }
                 
         }
     }
     
 
     m_angle.data = 0;
     setTimestamp(m_angle);
     m_angleOut.write();
     
 
     
     return false;
 }

 void ControlEducatorVehicle::turn_move(double a)
 {
 
     int max_count = 100;
     coil::TimeValue ts(0,10000);
 
     //現在の姿勢を取得する
     double spos = 0;
     for (int i = 0; i < max_count; i++)
     {
         if (m_current_poseIn.isNew())
         {
             m_current_poseIn.read();
             spos = m_current_pose.data.heading;
             break;
         }
         coil::sleep(ts);
         //一定時間内にデータを取得できなかった場合は回転運動を中止する
         if (i == max_count - 1)return;
     }
     max_count = 1000;
 
     //回転運動を開始する
     double data_new_count = 0;
     double max_data_new_count = 100;
     for (int i = 0; i < max_count; i++)
     {
         if (m_current_poseIn.isNew())
         {
             while(m_current_poseIn.isNew())m_current_poseIn.read();
 
             //目標姿勢角との差に定数を掛けた値を目標速度とする
             double pos = m_current_pose.data.heading - spos;
             double k = 1;
             m_target_velocity_out.data.vx = 0;
             m_target_velocity_out.data.vy = 0;
 
 
             double diff = (a - pos);
             double vela = k * diff;
             if(vela > m_rotate_speed)vela = m_rotate_speed;
             if(vela < -m_rotate_speed)vela = -m_rotate_speed;
 
             //目標速度出力
             m_target_velocity_out.data.va = vela;
 
             setTimestamp(m_target_velocity_out);
             m_target_velocity_outOut.write();
             
         
             
             
             //目標姿勢角との差が一定以下の場合は停止して処理終了
             if(sqrt(pow(diff,2)) < 0.03)
             {
                 stop_robot();
                 return;
             }
 
             data_new_count = 0;
         }
         else
         {
             //一定時間データが取得できなかった場合は処理終了
             data_new_count += 1;
             if(data_new_count > max_data_new_count)
             {
                 stop_robot();
                 return;
             }
             coil::sleep(ts);
         }
 
 
     }
     stop_robot();
     
 }

onExecute 関数

最後に onExecute 関数を以下のように実装します。

 RTC::ReturnCode_t ControlEducatorVehicle::onExecute(RTC::UniqueId ec_id)
 {
     //目標速度を変数に格納
     if (m_target_velocity_inIn.isNew())
     {
         m_target_velocity_inIn.read();
         vx = m_target_velocity_in.data.vx;
         vy = m_target_velocity_in.data.vy;
         va = m_target_velocity_in.data.va;
     }
     
     //超音波センサーのデータ入力時の処理開始
     if (m_ultrasonicIn.isNew())
     {
         while(m_ultrasonicIn.isNew())m_ultrasonicIn.read();
         if (m_ultrasonic.ranges.length() >= 1)
         {
             range = m_ultrasonic.ranges[0];
             //前進しているかの判定
             if (vx > 0)
             {
                 //超音波センサーで検知した地面までの高さが一定以上かの判定
                 if (m_ultrasonic.ranges[0] > m_sensor_height)
                 {
                     //停止フラグをtrueにする
                     //(次の超音波センサーデータ入力時に距離が一定以下と判定した場合は停止フラグをfalseにする)
                     stop_flag = true;
                     //一旦停止
                     stop_robot();
                     //Mモーターの角度を0に設定
                     m_angle.data = 0;
                     setTimestamp(m_angle);
                     m_angleOut.write();
                      //Mモーターを回転させて接地可能な地面を検知
                     double a = 0;
                     bool ret = search_ground(a);
                     //Mモーターの角度を0に設定
                     m_angle.data = 0;
                     setTimestamp(m_angle);
                     m_angleOut.write();
                     
 
                     
                     //接地可能な地面が見つかった場合は回転運動を行う
                     if (ret)
                     {
                         turn_move(a);
                     }
                     coil::TimeValue ts(2, 0);
                     coil::sleep(ts);
                     
                 }
                 //超音波センサーで検知した地面までの高さが一定以下の場合は停止フラグを false にする
                 else
                 {
                     stop_flag = false;
                 }
 
             }
         }
     }
 
     
     //停止フラグが true の場合は停止する
     if (stop_flag)
     {
         m_target_velocity_out.data.vx = 0;
         m_target_velocity_out.data.vy = 0;
         m_target_velocity_out.data.va = 0;
     }
     //停止フラグがfalseの場合は目標速度をそのまま出力
     else
     {
         m_target_velocity_out.data.vx = vx;
         m_target_velocity_out.data.vy = vy;
         m_target_velocity_out.data.va = va;
     }
     
     
     
     setTimestamp(m_target_velocity_out);
     m_target_velocity_outOut.write();
     
   return RTC::RTC_OK;
 }

RTシステムの保存について

RTシステムの保存については RTシステムエディタ上でもできるのですが、rtshell により RTシステムの復元を自動化することができます。 RTシステムエディタでポートの接続、コンフィギュレーションパラメーターを設定後、rtcryo コマンドを実行してください。

 rtcryo localhost -o testSystem.rtsys

これで、testSystem.rtsys というファイルにRTシステムの情報が保存されます。

ただし今回の課題のように複数のネームサーバーに登録された RTC を使用する場合、以下のように複数のネームサーバーを指定する必要があります。

 rtcryo localhost 192.168.11.1 -o testSystem.rtsys

保存した RTシステムを復元するためには rtresurrect コマンドを使用します。

 rtresurrect testSystem.rtsys

RTCをアクティブ化するには rtstart コマンドを使用します。

 rtstart testSystem.rtsys

RTCの非アクティブ化はrtstopコマンドを使用します。

 rtstop testSystem.rtsys

ポートを切断する場合は rtteardownコマンドを使用します。

 rtteardown testSystem.rtsys

注意点として、RTC はデフォルトの設定だとネームサーバーにはホスト名 .host_cxt 以下に登録されます。 ホスト名は環境によって違うので、この手順で RTシステムを保存しても他の環境では再現できないという事になります。 このため、rtc.conf を編集してネームサーバーの Root 直下に登録するようにすると、他の環境でも RTシステムの復元ができるようになります。

 naming.formats: %n.rtc

Procedure to make EV3 work as Wifi Access Point

ここでは EV3 をアクセスポイント化するまでの手順を説明します。

Raspberry Pi とほとんど手順は同じなので、詳しいことは以下のサイトなどを参考にしてください。

http://hara.jpn.com/_default/ja/Topics/RasPiE3839EE382A6E382B9E38292E784A1E7B79ALANE382A2E382AFE382BBE382B9E3839DE382A4E383B3E38388E58C96.html

Realtek 製品対応の hostapd のコンパイル

無線LANアダプタは GW-USNANO2A を使います。 まず上記のサイトにもあるように 8192CU は hostapd にはデフォルトで対応していないので、クロス環境で以下のコマンドを入力してコンパイルを行います。

 wget http://12244.wpc.azureedge.net/8012244/drivers/rtdrivers/cn/wlan/0001-RTL8188C_8192C_USB_linux_v4.0.2_9000.20130911.zip
 unzip 0001-RTL8188C_8192C_USB_linux_v4.0.2_9000.20130911.zip
 cd RTL8188C_8192C_USB_linux_v4.0.2_9000.20130911/wpa_supplicant_hostapd/
 tar zxvf wpa_supplicant_hostapd-0.8_rtw_r7475.20130812.tar.gz
 cd wpa_supplicant_hostapd-0.8_rtw_r7475.20130812/hostapd/
 make

コンパイルが終了したら、hostapdをEV3 に転送してください。

 sftp robot@<IPアドレス>
 put hostapd

各種設定

固定IPアドレス

/etc/network/interfaces に以下の記述を追加してください。

 auto lo
 
 iface lo inet loopback
 
 iface eth0 inet dhcp
 
 auto wlan0
 iface wlan0 inet static
 address 192.168.11.1
 netmask 255.255.255.0

hostapd のインストール

以下のコマンドで hostapd をインストールしてください。

 sudo apt-get install hostapd

インストールが完了したら、先ほど転送した hostapdを/usr/sbin にコピーしてください。

 cp hostapd /usr/sbin/hostapd

/etc/hostapd/hostapd.conf は以下のように記述します。 ssid、wpa_passphrase は適宜変更してください。

 interface=wlan0
 driver=rtl871xdrv
 ssid=ev3_0
 hw_mode=g
 channel=6
 macaddr_acl=0
 auth_algs=1
 ignore_broadcast_ssid=0
 wpa=2
 wpa_passphrase=ev3atx9fr
 wpa_key_mgmt=WPA-PSK
 wpa_pairwise=TKIP
 rsn_pairwise=CCMP

/etc/default/hostapd を以下のように変更します。

 #DAEMON_CONF=""
 →
 DAEMON_CONF="/etc/hostapd/hostapd.conf"

ネットワークを再起動します。

 sudo /etc/init.d/networking restart

以下のコマンドで hostapd を起動します。

 sudo /usr/sbin/hostapd /etc/hostapd/hostapd.conf -dd

これで他の PC から設定した SSID が見えるようにはなったはずです。

DHCP サーバーの設定

以下のコマンドで DHCP サーバーをインストールします。

 sudo apt-get install isc-dhcp-server

/etc/dhcp/dhcpd.conf を以下のように修正してください。

 # option definitions common to all supported networks...
 #option domain-name "example.org";
 #option domain-name-servers ns1.example.org, ns2.example.org;
 
 ping-check true;
 
 #default-lease-time 600;
 #max-lease-time 7200;
 
 authoritative;
 
 subnet 192.168.11.0 netmask 255.255.255.0 {
 option routers 192.168.11.1;
 option broadcast-address 192.168.11.255;
 option subnet-mask 255.255.255.0;
 option domain-name "local";
 option domain-name-servers 8.8.8.8,8.8.4.4;
 default-lease-time 600;
 max-lease-time 7200;
 range 192.168.11.101 192.168.11.199;
 }

/etc/default/isc-dhcp-server を以下のように修正します。

 INTERFACES="wlan0"

起動スクリプトの設定

最後の rc.local に以下の記述を追加します。

 ifdown wlan0
 ifup wlan0
 service hostapd start
 service isc-dhcp-server start

再起動する前に このページの手順で無線LANを設定している場合は、Wireless and Networks→Wifi→Powered を OFF に設定してください。また Wireless and Networks→Offline Mode を ON にしてください。

これで全ての手順が終了したので、再起動して接続できるかを確認してください。 失敗する場合は有線で EV3 と接続して設定が正しく行われているかを確認してください。

ボタン操作でアクセスポイントを起動する手順

EV3 のボタン操作でアクセスポイントを起動する手順を説明します。

まず /home/robot に以下のスクリプトファイル(ここでは start_ap.sh) を作成してください。

 #!/bin/sh
 sudo ifdown wlan0
 sudo ifup wlan0
 sudo service hostapd start
 sudo service isc-dhcp-server start

以下のコマンドで権限を変更してください。

 chmod +x start_ap.sh

このファイルを EV3 のボタン操作で File Browser→start_ap.sh で起動するのですが、EV3 のボタン操作で sudo 実行時のパスワードが入力できないので sudo をパスワードなしで実行できるように設定します。

以下のコマンドを入力してください。

 sudo visudo

そして以下の記述を追加してください。

 robot ALL=(ALL)     ALL
 %robot ALL=(ALL)     NOPASSWD: ALL

これで準備完了です。EV3 のボタン操作で File Browser→start_ap.sh を起動してください。

How to Use TETRIX

概要

TETRIX は Pitsco Education が販売している LEGO Mindstroms の拡張製品です。 アルミ製フレーム、高トルクのモーター等がセットに含まれており、本格的なロボットの製作が可能になります。

仕様

  • DCモーター
電圧 12V
静止トルク 2.1N・m
回転数 152rpm
  • RCサーボ
名前 HS-485HB(HITEC)
電圧 6V
静止トルク 0.59N・m
回転数 56rpm

モータードライバ

TETRIX ベースセットには DCモータードライバ (DC Motor Controller for TETRIX)、RCサーボドライバ (Servo Controller for TETRIX) が付属しています。

これらのモータードライバと EV3 を接続することで DCモーター及び RCサーボの制御が可能になります。

接続方法

以下のように接続してください。

s_DSC00776.JPG

接続してモータードライバの電源が ON の状態で EV3 の電源を投入してください。

操作方法

EV3 とモータードライバの通信は I2C により行います。

接続に成功している場合、/dev以下にi2c-3、i2c-4、i2c-5、i2c-6 のいずれかのデバイスファイルが追加されているはずです。 後ろの値がポート番号に2を足した値になっています。例えばポート1に接続した場合は i2c-3 になります。 以下で ev3dev上で C++、及び Python によるモータードライバの制御方法について述べます。

まずは I2C 通信に必要なファイルをインクルードしてください。

  • C++
     #include <fcntl.h>
     #include <sys/ioctl.h>
     #include <linux/i2c-dev.h>
  • Python
     import smbus

I2C の通信を開始します。

  • C++
     int fd; = open("/dev/i2c-3", O_RDWR);
  • Python
     sm = smbus.SMBus(3)

モータードライバのアドレスは 0x01 に設定されますが、デイジーチェインで接続した場合 0x02~0x08 に設定されます。 上記の接続の場合、DCモータードライバは 0x01 で RCサーボドライバは 0x02 に設定されているはずです。 まずは DCモータードライバを操作してみます。

DCモータードライバ

制御モードを設定します。 モーター1 は 0x44、モーター2 は 0x47 のレジスタに以下の値を書き込むことで制御モードが設定可能です。 ※TETRIX ベースセットにはエンコーダーは付属していないので速度制御、位置制御モードを使用する場合は別途購入する必要があります。

入力値 制御モード
0b00 PWM
0b01 速度制御
0b10 位置制御
0b11 エンコーダーリセット

PWM モードに設定します。

  • C++
     ioctl(fd, I2C_SLAVE, 0x01)
     unsigned char buf[2] = {0x44, 0x00};
     write(fd, buf, 2);
  • Python
     sm.write_i2c_block_data(0x01, 0x44, [0x00])

最後に PWM の幅を設定します。 モーター1は 0x45、モーター2 は 0x46 のレジスタに書き込むことにより設定可能です。 PWM は正回転するばあいは 1~127 の値、逆回転する場合は -127~-1 の値で設定できます。 ただし、0 の場合はフロートモードで停止、128 の場合はブレーキモードで停止します。

  • C++
     unsigned char buf[2] = {0x45, 0x30};
     write(fd, buf, 2);
  • Python
     sm.write_i2c_block_data(0x01, 0x45, [0x30])

RCサーボドライバ

RCサーボドライバは最大6個の RCサーボを制御可能であり、0x42~0x47 のレジスタに目標角度を書き込むことで制御できます。 パルス幅は 0.75ms~2.25ms で設定可能です。

  • C++
     ioctl(fd, I2C_SLAVE, 0x02)
     unsigned char buf[2] = {0x42, 0x60};
     write(fd, buf, 2);
  • Python
     sm.write_i2c_block_data(0x02, 0x42, [0x60])

応用例

以下は DCモーターを利用した乗り物の作成例です。

s_DSC00777.JPG

取っ手部分にLモーターが2個取り付けられており、Lモーターを回転させることで車体の制御が可能になっています。

tetrix_device.png

使用した RTC は以下の通りです。

TetrixVehicle

上記の乗り物を制御するためのコンポーネントです。

TetrixVehicle
InPort
名前 データ型 説明
target_velocity RTC::TimedVelocity2D 目標速度
update_position RTC::TimedPose2D 位置の再設定
OutPort
名前 データ型 説明
position RTC::TimedPose2D 現在位置
コンフィギュレーションパラメータ-
名前 デフォルト値 説明
wheelRadius 0.04 車輪の半径
wheelDistance 0.34 車輪間の距離
portNum 1 ポート番号
rot_dir_left_motor 1 左車輪の回転方向
rot_dir_righteft_motor -1 右車輪の回転方向
GearRatio 3.0 ギア比

VehicleController

Lモーターの角度から車体の目標速度を出力するコンポーネントです。

TetrixVehicle
OutPort
名前 データ型 説明
out RTC::TimedVelocity2D 目標速度
コンフィギュレーションパラメーター
名前 デフォルト値 説明
rotation_by_angle -0.6 根元のモーターの角度に対する目標角速度の変化量
velocity_by_angle -0.1 先端のモーターの角度に対する目標速度の変化量

How to Use Educator Vehicle Simulator

このページでは Educator Vehicle 改 のシミュレーター RTC の仕様、利用方法について説明します。

ev32.png

仕様

simulator_ev3_1.png

EV3Simulator
InPort
名前 データ型 説明
velocity2D RTC::TimedVelocity2D 目標速度
angle RTC::TimedDouble Mモーターの角度
pos_update RTC::TimedPose2D 現在位置更新
OutPort
名前 データ型 説明
odometry RTC::TimedPose2D 現在の位置
current_vel RTC::TimedVelocity2D 現在の速度
ultrasonic RTC::RangeData 超音波センサーで計測した距離
gyro RTC::TimedDouble ジャイロセンサーで計測した角度
light_reflect RTC::TimedDouble カラーセンサーで計測した反射光の強さ
touch RTC::TimedBooleanSeq タッチセンサーのオンオフ。右側が0番目の要素、左側が1番目の要素
コンフィギュレーションパラメーター
名前 デフォルト値 説明
medium_motor_speed 1.6 モーターMの速度
blocksConfigFile None 障害物の配置設定ファイルの名前
touchSensorOnLength 0.003 タッチセンサーをオンと判定する押し込んだ距離
lightReflectThreshold 0.1 カラーセンサーから物体までの距離がこの値以下になると255を出力
plane_exist 0 1の時は新たに地面作成
plane_x 0 地面の位置(X)
plane_y 0 地面の位置(Y)
plane_z 0 カラーセンサーから物体までの距離がこの値以下になると255を出力
plane_lx 1.0 地面の長さ
plane_ly 1.0 地面の幅
plane_lz 1.0 地面の高さ
draw_time 0.01 描画の周期
sampling_time -1 シミュレーションの刻み幅。負の値に設定した場合は実行コンテキストの周期で設定

使用方法

以下からダウンロードできます。

展開したフォルダーの EXEフォルダー内に実行ファイル (EV3SimulatorComp.exe) があります。 この EXEファイルを実行すると RTC が起動します。

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

障害物の設定ファイル

blocksConfigFile というパラメーターで障害物の配置を設定する CSVファイルを指定できます。 サンプルとして test.csv というファイルを用意してあります。

このファイルに位置、角度、サイズを記述してください。

位置(X) 位置(Y) 位置(Z) 長さ(L) 幅(W) 高さ(H) 角度(θ)
0.3 0.0 0.0 0.1 1.0 0.3 0.0

 /ja/node/6198
 /ja/node/6198

ブロックは何個でも設定可能です。

地面の設定

Educator Vehicle 改 は超音波センサーにより設置可能な地面の有無を検知して回避する運動が可能になっています。 この制御シミュレーションのために、地面を新たに作成するコンフィギュレーションパラメーターを用意してあります。

plane_existを1に設定後、地面の位置、サイズを設定してください。

Tutorial (RTM Seminar)

Tutorial (EV3, Windows, Part 2)

はじめに

このページではシミュレーター上の Educator Vehicleを操作するためのコンポーネントの作成手順を説明します。 Educator VehicleはレゴマインドストームEV3の組み立て例の一つです。

ev32_2.png

資料のダウンロード

まずは資料をダウンロードしてください。

ZIPファイルは Lhaplus 等で展開してください。

インターネットに接続できない環境で講習会を実施している場合がありますので、その場合は配布のUSBメモリーに入れてあります。

付属のシミュレータで以下の Educator Vehicle 改のシミュレーションができます。

/jp/node/6038

Lモーター、Mモーターの制御だけではなく、タッチセンサー、ジャイロセンサー、超音波センサーのシミュレーションも可能になっています。

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

  • RobotController コンポーネント

EV3Simulator コンポーネントと接続してシミュレーター上のロボットを操作するためのコンポーネントです。

RobotController コンポーネントの作成

GUI(スライダー)によりシミュレーター上のロボットの操作を行い、タッチセンサーがオンの時には自動的に停止するコンポーネントの作成を行います。

tutorial_ev3_irex1.png

作成手順

作成手順は以下の通りです。

  • 開発環境の確認
  • コンポーネントの仕様を決める
  • RTC Builderによるソースコードのひな形の作成
  • ソースコードの編集
  • コンポーネントの動作確認

開発環境の確認

以下の環境を想定しています。

コンポーネントの仕様

RobotController は目標速度を出力するアウトポート、センサー値を入力するインポート、目標速度を設定するコンフィギュレーションパラメーターを持っています。

コンポーネント名称 RobotController
InPort
ポート名 in
TimedBooleanSeq
説明 センサー値
OutPort
ポート名 out
TimedVelocity2D
説明 目標速度
Configuration
パラメーター名 speed_x
double
デフォルト値 0.0
制約 -1.0<x<1.0
Widget slider
Step 0.01
説明 直進速度の設定
Configuration
パラメーター名 speed_r
double
デフォルト値 0.0
制約 -2.0<x<2.0
Widget slider
Step 0.01
説明 回転速度の設定

TimedVelocity2D 型について

2次元平面上の移動ロボットの移動速度を格納するデータ型である TimedVelocity2D 型を使用します。

     struct Velocity2D
     {
           /// Velocity along the x axis in metres per second.
           double vx;
           /// Velocity along the y axis in metres per second.
           double vy;
           /// Yaw velocity in radians per second.
           double va;
     };
 
 
     struct TimedVelocity2D
     {
           Time tm;
           Velocity2D data;
     };

このデータ型にはX軸方向の速度vx、Y軸方向の速度vy、Z軸周りの回転速度vaが格納できます。

vxvyvaはロボット中心座標系での速度を表しています。


tutorial_ev3_irex3.png

vxはX方向の速度、vyはY方向の速度、vaはZ軸周りの角速度です。

Educator Vehicle改のように2個の車輪が左右に取り付けられているロボットの場合、横滑りしないと仮定するとvyは0になります。

直進速度vx、回転速度vaを指定することでロボットの操作を行います。

タッチセンサーについて


tutorial_ev3_irex2.png

RobotController コンポーネントのひな型の生成

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

RTCBuilder の起動

Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダーの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。

  • C:\workspace

まずは Eclipse を起動します。 Windows 8.1の場合は「スタート」>「アプリビュー(右下矢印)」>「OpenRTM-aist 1.1.2」>「OpenRTP」をクリックすると起動できます。

最初にワークスペースの場所を尋ねられますので、上記のワークスペースを指定してください。

/ja/node/6026

すると、以下のようなWelcomeページが表示されます。


/ja/node/6026
Eclipse の初期起動時の画面

Welcomeページはいまは必要ないので左上の「×」ボタンをクリックして閉じてください。

右上の [Open Perspective] ボタンをクリックしてください。

/ja/node/6026
パースペクティブの切り替え

「RTC Builder」を選択することで、RTCBuilder が起動します。メニューバーに「カナヅチとRT」の RTCBuilder のアイコンが表示されます。

/ja/node/6026
パースペクティブの選択

新規プロジェクトの作成

RobotController コンポーネントを作成するために、RTC Builder で新規プロジェクトを作成する必要があります。

左上の [Open New RTCBuilder Editor] のアイコンをクリックしてください。

/ja/node/6057
RTC Builder 用プロジェクトの作成

「プロジェクト名」欄に作成するプロジェクト名 (ここでは RobotController) を入力して [終了] ボタンをクリックします。

RT-Component-BuilderProject_1.png

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

PackageExplolrer_1.png

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

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

RTC.xml が生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし起動しない場合はパッケージエクスプローラーの RTC.xml をダブルクリックしてください。

/ja/node/6026

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

まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた RobotController コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。

  • モジュール名: RobotController
  • モジュール概要: 任意(Robot Controller component)
  • バージョン: 任意(1.0.0)
  • ベンダ名: 任意
  • モジュールカテゴリ: 任意(Controller)


Basic_1.png
基本情報の入力


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

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


Activity_1.png
アクティビティコールバックの選択


さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションで、そのままで結構です。


  • InPort Profile:
    • ポート名: in
    • データ型: TimedBooleanSeq

  • OutPort Profile:
    • ポート名: out
    • データ型: TimedVelocity2D


DataPort_1.png
データポート情報の入力


次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。 制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUI で値の変更を行うためのものです。

直進速度 speed_x、回転速度 speed_r はスライダーのより操作できるようにします。


  • speed_x
    • 名称: speed_x
    • データ型: double
    • デフォルト値: 0.0
    • 制約条件: -1.0<x<1.0
    • Widget: slider
    • Step: 0.01
  • speed_r
    • 名称: speed_r
    • データ型: double
    • デフォルト値: 0.0
    • 制約条件: -2.0<x<2.0
    • Widget: slider
    • Step: 0.01


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


次に、「言語・環境」タブを選択し、プログラミング言語を選択します。 ここでは、C++(言語)を選択します。なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。

Language_1.png
プログラミング言語の選択


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


Generate_1.png
雛型の生成(Generate)


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

CMake によるビルドに必要なファイルの生成

RTC Builder で生成したコードの中には CMake でビルドに必要な各種ファイルを生成するための CMakeLists.txt が含まれています。 CMake を利用することにより CMakeLists.txt からVisual Studio のプロジェクトファイル、ソリューションファイル、もしくは Makefile 等を自動生成できます。

CMake(cmake-gui) の操作

CMake を利用してビルド環境の Configure を行います。 まずは CMake(cmake-gui) を起動してください。「スタート」>「アプリビュー(右下矢印)」>「CMake 3.7.2」>「CMake (cmake-gui)」をクリックすると起動できます。

CMakeGUI0_1.png
CMake GUI の起動とディレクトリーの指定

画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所 (CMakeList.txtがある場所) と、ビルドディレクトリーを指定します。

  • Where is the soruce code
  • Where to build the binaries

ソースコードの場所は RobotController コンポーネントのソースが生成された場所で CMakeList.txt が存在するディレクトリーです。 デフォルトでは <ワークスペースディレクトリー>/RobotController になります。

このディレクトリーはエクスプローラから cmake-gui にドラックアンドドロップすると手入力しなくても設定されます。

ビルドディレクトリーとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。 場所は任意ですが、この場合 <ワークスペースディレクトリー>/RobotController/build のように分かりやすい名前をつけた RobotController のサブディレクトリーを指定することをお勧めします。

Where is the soruce code C:\workspace\RobotController
Where to build the binaries C:\workspace\RobotController\build

指定したら、下の [Configure] ボタンをクリックします。すると下図のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回は Visual Studio 15 2017 とします。Visual Studio 2013や Visual Studio 2019を利用している方はそれぞれ変更してください。 またプラットフォームにはx64を設定します。32bit版をインストールしている場合はWin32を選択してください。

/ja/node/6310
生成するプロジェクトの種類の指定

ダイアログで [Finish] ボタンをクリックすると Configure が始まります。 問題がなければ下部のログウインドウに「Configuring done」と出力されますので、続けて [Generate] ボタンをクリックします。 「Generating done」と出ればプロジェクトファイル・ソリューションファイル等の出力が完了します。

なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。

ヘッダ、ソースの編集

次に先ほど指定した build ディレクトリーの中の RobotController.sln をダブルクリックして Visual Studio を起動します。

※cmake-gui の新しいバージョンでは cmake-gui 上のボタンをクリックすることで起動できます。


cmake_gui.png

ヘッダ (include/RobotController/RobotController.h) およびソースコード (src/RobotController.cpp) をそれぞれ編集します。 Visual Studio のソリューションエクスプローラから RobotController.h、RobotController.cpp をクリックすることで編集画面が開きます。
/ja/node/6026

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

RobotController コンポーネントでは、コンフィギュレーションパラメーター(speed_x、speed_y)をスライダーで操作しその値を目標速度としてアウトポート(out)から出力します。 インポート(in) から入力された値を変数に格納して、その値が一定以上の場合は停止するようにします。


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

RCRTC_State_1.png
アクティビティ処理の概要


ヘッダファイル (RobotController.h) の編集

センサー値を一時的に格納する変数 sensor_data を宣言します。

   private:
     bool sensor_data[2];           //センサー値を一時格納する変数

ソースファイル (RobotController.cpp) の編集

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

 RTC::ReturnCode_t RobotController::onActivated(RTC::UniqueId ec_id)
 {
     //センサー値初期化
     for (int i = 0; i < 2; i++)
     {
         sensor_data[i] = false;
     }
 
     return RTC::RTC_OK;
 }

 RTC::ReturnCode_t RobotController::onDeactivated(RTC::UniqueId ec_id)
 {
          //ロボットを停止する
          m_out.data.vx = 0;
          m_out.data.va = 0;
          m_outOut.write();
  
          return RTC::RTC_OK;
 }

 RTC::ReturnCode_t RobotController::onExecute(RTC::UniqueId ec_id)
 {
     //入力データの存在確認
     if (m_inIn.isNew())
     {
         //入力データ読み込み
         m_inIn.read();
         for (int i = 0; i < m_in.data.length(); i++)
         {
             //入力データ格納
             if (i < 2)
             {
                 sensor_data[i] = m_in.data[i];
             }
         }
     }
 
     //前進するときのみ停止するかを判定
     if (m_speed_x > 0)
     {
         for (int i = 0; i < 2; i++)
         {
             //タッチセンサのオンオフを判定
             if (sensor_data[i] == true)
             {
                 //タッチセンサがオンの場合は停止
                 m_out.data.vx = 0;
                 m_out.data.va = 0;
                 m_outOut.write();
                 return RTC::RTC_OK;
             }
         }
     }
 
     //すべてのタッチセンサがオフの場合はコンフィギュレーションパラメーターの値で操作
     m_out.data.vx = m_speed_x;
     m_out.data.va = m_speed_r;
     m_outOut.write();
  }

Visual Studio によるビルド

ビルドの実行

Visual Studioの [ビルド] >「ソリューションのビルド」を選択してビルドを行います。


/ja/node/6026
ビルドの実行


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

作成した RobotController をシミュレーターコンポーネントと接続して動作確認を行います。

以下より EV3Simulator コンポーネントをダウンロードしてください。

ZIPファイルは Lhaplus 等で展開してください。

インターネットに接続できない環境で講習会を実施している場合がありますので、その場合は配布のUSBメモリーに入れてあります。

NameService の起動

コンポーネントの参照を登録するためのネームサービスを起動します。


「スタート」>「アプリビュー(右下矢印)」>「OpenRTM-aist 1.1.2」の順に辿り、「Start Naming Service」をクリックしてください。

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

RobotController コンポーネントの起動

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

RobotController\build\src\Debug(もしくは、Release)フォルダーの RobotControllerComp.exe ファイルを実行してください。

シミュレーターコンポーネントの起動

このコンポーネントは先ほどダウンロードしたファイル(RTM_Tutorial_iREX2017.zip)を展開したフォルダーの EXE/EV3SimulatorComp.exe を実行すると起動します。

コンポーネントの接続

下図のように、RTSystemEditor にて RobotController コンポーネント、EV3Simulator コンポーネントを接続します。 システムダイアグラムは左上のOpen New System Editorボタンで表示できます。

tutorial_ev3_irex9_2.png
コンポーネントの接続

コンポーネントのActivate

RTSystemEditor の上部にあります [All Activate] というアイコンをクリックし、全てのコンポーネントをアクティブ化します。 正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。


tutorial_ev3_irex10.png
コンポーネントのアクティブ化


動作確認

下図のようにコンフィギュレーションビューの [編集] ボタンからコンフィギュレーションを変更することができます。


tutorial_ev3_irex6.png

スライダーを操作してシミュレーター上のEducator Vehicle改の操作ができるかを確認してください。


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


正常に動作している場合は、開始し位置から直進した場合に壁の前で停止します。


tutorial_ev3_irex18.png


正常に動作していない場合は、壁に接触後にそのまま前進を続けます。


tutorial_ev3_irex17.png


実機での動作確認

講習会で EV3実機を用意している場合は実機での動作確認が可能です。

手順は以下の通りです。

  • Educator Vehicle改の組立て
  • EV3アクセスポイントの起動
  • EV3のアクセスポイントに接続
  • ポートの接続
  • コンポーネントのアクティブ化

Educator Vehicle改の組立て

EV3 は分解した状態で参加者に配ります。 組み立て方は以下の通りです。

ただし、Mモーター、超音波センサー、カラーセンサー、ジャイロセンサーについては、講習で使用しないため取り付ける必要はありません。 以下の※の作業については、時間が余った人が実施してください。

まずは土台部分を取り出してください。


s_DSC00463.JPG

最初にMモーターにケーブル(15cm)を接続します※。


s_DSC00446.JPG

次に EV3 本体を取り付けます。 Mモーターにケーブルを接続した場合は、ケーブルが左側の隙間から出るようにしてください。


s_DSC00448.JPG s_DSC00450.JPG



右側のタッチセンサーを取り付けてください。


s_DSC00451.JPG s_DSC00452.JPG



超音波センサーを取り付けてください※。


s_DSC00454.JPG

ケーブルを接続してください。 必須なのは車輪駆動用のLモーター右、Lモーター左、タッチセンサーだけです。

Lモーター右 ポート C 25cmケーブル
Lモーター左 ポート B 25cmケーブル
Mモーター※ ポートA 25cmケーブル
タッチセンサー右 ポート 3 35cmケーブル
タッチセンサー左 ポート 1 35cmケーブル
超音波センサー※ ポート 4 50cmケーブル
ジャイロセンサー※ ポート 2 25cmケーブル

ケーブルは EV3 の上下に A~D と 1~4 のポートがあるのでそこにケーブルを接続します。


s_DSC00.JPG s_DSC00471.JPG




s_DSC00455.JPG s_DSC00456.JPG



左右にパーツを取り付けます※。 Lモーター右、Lモーター左、Mモーター、タッチセンサー右、タッチセンサー左のケーブルを挟むようにして取り付けてください※。
Lモーター右、タッチセンサー右のケーブルは右側から、Lモーター左、Mモーター、タッチセンサー左は左側から通してください※。


s_DSC00457.JPG


s_DSC00459.JPG

これでとりあえず完成ですが、余裕のある人はジャイロセンサーを取り付けてみてください※。


s_DSC00460.JPG s_DSC00461.JPG



電源の入れ方/切り方

電源の入れ方

中央のボタンを押せば電源が投入されます。


ev3_on.jpg

電源の切り方

EV3 の電源を切る場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Power Off」を選択してください。


ev3_off.jpg


s_DSC01033.JPG

再起動

再起動する場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Reboot」を選択してください。

リセット

ev3dev の起動が途中で停止する場合には、中央ボタン、戻るボタン(左上)、左ボタンを同時押ししてください。画面が消えたら戻るボタンを離すと再起動します。


ev3_reset.jpg

アクセスポイントの設定

EV3 の操作画面から「File Browser」を上下ボタンで選択して中央のボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
 [File Browser               > ]
  Device Browser             >
  Wireless and Networks      > 
  Battery                    >
  Open Roberta Lab           >
  About                      >
 ------------------------------

次に scripts を選択して中央ボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot
 ------------------------------
 [scripts                     ]
 ・・
 ・・
 ------------------------------

次の画面から start_ap.sh を選択して中央ボタンを押すとスクリプトが起動します。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot/scripts
 ------------------------------
 ../
 Component/
 ・・
 [start_ap.sh                 ]
 ------------------------------

しばらくすると無線LANアクセスポイントが起動するので、指定の SSID のアクセスポイントに接続してください。 SSID、パスワードは EV3 に貼り付けたテープに記載してあります。


tutorial_ev3_irex26.png

アクセスポイントに接続

アクセスポイントへの接続方法は以下のページを参考にしてください。

SSID、パスワードは EV3に貼り付けたシールに記載してあります。

まず右下のネットワークアイコンをクリックしてください。


/ja/node/6042

次に一覧から ev3_*** を選択してください。


/ja/node/6042

パスワードを入力してください。


/ja/node/6042

※ネットワークが切り替わった場合にネームサーバーへのコンポーネントの登録やポートの接続が失敗する場合があるのでネームサーバ、コンポーネントを一旦全て終了してください。 ネットワーク切り替え後に起動した場合には問題ないので、終了させる必要はありません。

ネームサーバー追加

続いてRTシステムエディタの [ネームサーバー追加] ボタンで 192.168.0.1 を追加してください。


tutorial_raspimouse0.png tutorial_ev3_irex12.png



するとEducatorVehicle0という RTC が見えるようになります。

ポートの接続

RTシステムエディタで EducatorVehicle、RobotController コンポーネントを以下のように接続します。

tutorial_ev3_irex11.png

アクティブ化

そして RTC をアクティブ化すると EV3の操作ができるようになります。

Tutorial (EV3, Ubuntu, Part 2)

はじめに

このページではシミュレーター上の Educator Vehicleを操作するためのコンポーネントの作成手順を説明します。 Educator VehicleはレゴマインドストームEV3の組み立て例の一つです。

ev32_2.png

資料のダウンロード

まずは資料をダウンロードしてください。

 git clone https://github.com/OpenRTM/RTM_Tutorial_EV3

付属のシミュレータで以下の Educator Vehicle 改のシミュレーションができます。

/jp/node/6038

Lモーター、Mモーターの制御だけではなく、タッチセンサー、ジャイロセンサー、超音波センサーのシミュレーションも可能になっています。

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

  • RobotController コンポーネント

EV3Simulator コンポーネントと接続してシミュレーター上のロボットを操作するためのコンポーネントです。

RobotController コンポーネントの作成

GUI(スライダー)によりシミュレーター上のロボットの操作を行い、タッチセンサーがオンの時には自動的に停止するコンポーネントの作成を行います。

tutorial_ev3_irex1.png

作成手順

作成手順は以下の通りです。

  • 開発環境の確認
  • コンポーネントの仕様を決める
  • RTC Builderによるソースコードのひな形の作成
  • ソースコードの編集
  • コンポーネントの動作確認

開発環境の確認

Linux (ここでは Ubuntu 16.04 を仮定) 上に開発環境を構築します。

OpenRTM-aistのインストール

インストールスクリプトでインストールします。

 $ wget http://svn.openrtm.org/OpenRTM-aist/trunk/OpenRTM-aist/build/pkg_install_ubuntu.sh
 $ sudo sh pkg_install_ubuntu.sh

OpenRTP のインストール

こちらのURL から Linux版の OpenRTP (コンポーネント開発ツール、システム開発ツール統合環境) をダウンロード、インストールします。 OepnRTP の実行には Java も必要となりますので default-jre パッケージをインストールします。

 $ apt-get install default-jre
 $ wget http://openrtm.org/pub/openrtp/packages/1.1.2.v20160526/eclipse442-openrtp112v20160526-ja-linux-gtk-x86_64.tar.gz
 $ tar xvzf eclipse442-openrtp112v20160526-ja-linux-gtk-x86_64.tar.gz

eclipse起動後、RTSystemEditor でネームサーバに接続できない場合があります。その場合、/etc/hosts の localhost の行に自ホスト名を追記してください。

 $ hostname
 ubuntu1404 ← ホスト名は ubuntu1404
 $ sudo vi /etc/hosts

 127.0.0.1       localhost
 を以下のように変更
 127.0.0.1       localhost ubuntu1404

CMake のインストール

 $ sudo apt-get install cmake cmake-gui

Code::Blocks のインストール

Code::Blocks は C/C++ に対応した統合開発環境です。 以下のコマンドでインストールできます。

 $ sudo apt-get install codeblocks

最新版を入手したい場合は以下のコマンドを入力します。

 $ sudo add-apt-repository ppa:damien-moore/codeblocks-stable
 $ sudo apt-get update
 $ sudo apt-get install codeblocks

EV3Simulator コンポーネント

シミュレーターコンポーネントについては手動でビルドを行います。 以下のコマンドを入力してください。

 $ wget https://raw.githubusercontent.com/Nobu19800/RTM_Tutorial_iREX2017/master/script/install_ev3_simulator.sh
 $ sudo sh install_ev3_simulator.sh

インターネットに接続できない環境で講習会を実施している場合がありますので、その場合は配布の USBメモリー内のスクリプトを起動してください。

 $ sudo sh install_ev3_simulator_usb.sh

コンポーネントの仕様

RobotController は目標速度を出力するアウトポート、センサー値を入力するインポート、目標速度を設定するコンフィギュレーションパラメーターを持っています。

コンポーネント名称 RobotController
InPort
ポート名 in
TimedBooleanSeq
説明 センサー値
OutPort
ポート名 out
TimedVelocity2D
説明 目標速度
Configuration
パラメーター名 speed_x
double
デフォルト値 0.0
制約 -1.0<x<1.0
Widget slider
Step 0.01
説明 直進速度の設定
Configuration
パラメーター名 speed_r
double
デフォルト値 0.0
制約 -2.0<x<2.0
Widget slider
Step 0.01
説明 回転速度の設定

TimedVelocity2D 型について

2次元平面上の移動ロボットの移動速度を格納するデータ型である TimedVelocity2D 型を使用します。

     struct Velocity2D
     {
           /// Velocity along the x axis in metres per second.
           double vx;
           /// Velocity along the y axis in metres per second.
           double vy;
           /// Yaw velocity in radians per second.
           double va;
     };
 
 
     struct TimedVelocity2D
     {
           Time tm;
           Velocity2D data;
     };

このデータ型にはX軸方向の速度vx、Y軸方向の速度vy、Z軸周りの回転速度vaが格納できます。

vxvyvaはロボット中心座標系での速度を表しています。


tutorial_ev3_irex3.png

vxはX方向の速度、vyはY方向の速度、vaはZ軸周りの角速度です。

Educator Vehicle改のように2個の車輪が左右に取り付けられているロボットの場合、横滑りしないと仮定するとvyは0になります。

直進速度vx、回転速度vaを指定することでロボットの操作を行います。

タッチセンサーについて


tutorial_ev3_irex2.png

RobotController コンポーネントのひな型の生成

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

RTCBuilder の起動

Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダーの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。

  • /home/ユーザー名/workspace

まずは Eclipse を起動します。 OpenRTP を展開したディレクトリーに移動して以下のコマンドを入力します。

 $ ./openrtp

最初にワークスペースの場所を尋ねられますので、上記のワークスペースを指定してください。

/ja/node/6058

すると、以下のような Welcome ページが表示されます。


/ja/node/6026
Eclipse の初期起動時の画面

Welcome ページはいまは必要ないので左上の「×」ボタンをクリックして閉じてください。

右上の [Open Perspective] ボタンをクリックしてください。

/ja/node/6026
パースペクティブの切り替え

「RTC Builder」を選択することで、RTCBuilderが起動します。メニューバーに「カナヅチとRT」の RTCBuilder のアイコンが現れます。

/ja/node/6026
パースペクティブの選択

新規プロジェクトの作成

RobotController コンポーネントを作成するために、RTC Builder で新規プロジェクトを作成する必要があります。

左上の [Open New RTCBuilder Editor] のアイコンをクリックしてください。

/ja/node/6057
RTC Builder 用プロジェクトの作成

「プロジェクト名」欄に作成するプロジェクト名 (ここでは RobotController) を入力して [終了] をクリックします。

/ja/node/6310

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

/ja/node/6310

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

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

RTC.xmlが生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし起動しない場合はパッケージエクスプローラーの RTC.xml をダブルクリックしてください。

/ja/node/6026

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

まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた RobotController コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。

  • モジュール名: RobotController
  • モジュール概要: 任意(Robot Controller component)
  • バージョン: 任意(1.0.0)
  • ベンダ名: 任意
  • モジュールカテゴリ: 任意(Controller)


/ja/node/6310
基本情報の入力


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

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


/ja/node/6310
アクティビティコールバックの選択


さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションで、そのままで結構です。


  • InPort Profile:
    • ポート名: in
    • データ型: TimedBooleanSeq

  • OutPort Profile:
    • ポート名: out
    • データ型: TimedVelocity2D


/ja/node/6310
データポート情報の入力


次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。 制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUI で値の変更を行うためのものです。

直進速度 speed_x、回転速度 speed_r はスライダーのより操作できるようにします。


  • speed_x
    • 名称: speed_x
    • データ型: double
    • デフォルト値: 0.0
    • 制約条件: -1.0<x<1.0
    • Widget: slider
    • Step: 0.01
  • speed_r
    • 名称: speed_r
    • データ型: double
    • デフォルト値: 0.0
    • 制約条件: -2.0<x<2.0
    • Widget: slider
    • Step: 0.01


/ja/node/6310
コンフィグレーション情報の入力


次に、「言語・環境」タブを選択し、プログラミング言語を選択します。 ここでは、C++(言語) を選択します。なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。

/ja/node/6310
プログラミング言語の選択


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


/ja/node/6310
雛型の生成(Generate)


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

CMake によるビルドに必要なファイルの生成

RTC Builder で生成したコードの中には CMake でビルドに必要な各種ファイルを生成するための CMakeLists.txt が含まれています。 CMake を利用することにより CMakeLists.txt から Visual Studio のプロジェクトファイル、ソリューションファイル、もしくは Makefile 等を自動生成できます。

CMake(cmake-gui) の操作

CMake を利用してビルド環境の Configure を行います。 まずは CMake(cmake-gui) を起動してください。

 $ cmake-gui
CMakeGUI0_2_ubuntu.png
CMake GUI の起動とディレクトリーの指定

画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所 (CMakeList.txt がある場所) と、ビルドディレクトリーを指定します。

  • Where is the soruce code
  • Where to build the binaries

ソースコードの場所は RobotController コンポーネントのソースが生成された場所で CMakeList.txt が存在するディレクトリーです。 デフォルトでは <ワークスペースディレクトリー>/RobotController になります。

このディレクトリーはエクスプローラから cmake-gui にドラックアンドドロップすると手入力しなくても設定されます。

ビルドディレクトリーとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。 場所は任意ですが、この場合 <ワークスペースディレクトリー>/RobotController/build のように分かりやすい名前をつけた RobotController のサブディレクトリーを指定することをお勧めします。

Where is the soruce code /home/ユーザー名/RobotController
Where to build the binaries /home/ユーザー名/RobotController\build

指定したら、下の [Configure] ボタンをクリックします。すると下図のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回は CodeBlocks - Unix Makefiles を指定します。 Code::Blocks を使わない場合は Unix Makefiles を使ってください。

/ja/node/6026
生成するプロジェクトの種類の指定

また cmake-gui を使用しない場合は以下のコマンドでファイルを生成できます。

 $ mkdir build
 $ cd build
 $ cmake .. -G "CodeBlocks - Unix Makefiles"

ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と出力されますので、続けて [Generate] ボタンをクリックします。 「Generating done」と出ればプロジェクトファイル・ソリューションファイル等の出力が完了します。

なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] を選択して、キャッシュを削除してから Configure からやり直してください。

ヘッダ、ソースの編集

次に先ほど指定した build ディレクトリーの中の RobotController.cbp をダブルクリックして Visual Studio 2013 を起動します。

ヘッダ (include/RobotController/RobotController.h) およびソースコード (src/RobotController.cpp) をそれぞれ編集します。 Code::BlocksのProjectsからRobotController.h、RobotController.cpp をクリックすることで編集画面が開きます。

codeblocks0_2.png

64bitの環境の場合に Code::Blocks の動作が不安定になることがあります。 その場合は code completion というプラグインを無効化すると動作することがあります。

「Plugins」>「Manage plugins...」を選択します。

/ja/node/6057/

「code completion」を選択して [Disable] ボタンをクリックします。

/ja/node/6057/

動作しないときはこの手順を試してください。

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

RobotController コンポーネントでは、コンフィギュレーションパラメーター(speed_x、speed_y)をスライダーで操作しその値を目標速度としてアウトポート(out)から出力します。 インポート(in)から入力された値を変数に格納して、その値が一定以上の場合は停止するようにします。


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

/ja/node/6310/
アクティビティ処理の概要


ヘッダファイル (RobotController.h) の編集

センサー値を一時的に格納する変数 sensor_data を宣言します。

   private:
     bool sensor_data[2];           //センサー値を一時格納する変数

ソースファイル (RobotController.cpp) の編集

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

 RTC::ReturnCode_t RobotController::onActivated(RTC::UniqueId ec_id)
 {
     //センサー値初期化
     for (int i = 0; i < 2; i++)
     {
         sensor_data[i] = false;
     }
 
     return RTC::RTC_OK;
 }

 RTC::ReturnCode_t RobotController::onDeactivated(RTC::UniqueId ec_id)
 {
          //ロボットを停止する
          m_out.data.vx = 0;
          m_out.data.va = 0;
          m_outOut.write();
  
          return RTC::RTC_OK;
 }

 RTC::ReturnCode_t RobotController::onExecute(RTC::UniqueId ec_id)
 {
     //入力データの存在確認
     if (m_inIn.isNew())
     {
         //入力データ読み込み
         m_inIn.read();
         for (int i = 0; i < m_in.data.length(); i++)
         {
             //入力データ格納
             if (i < 2)
             {
                 sensor_data[i] = m_in.data[i];
             }
         }
     }
 
     //前進するときのみ停止するかを判定
     if (m_speed_x > 0)
     {
         for (int i = 0; i < 2; i++)
         {
             //タッチセンサのオンオフを判定
             if (sensor_data[i] == true)
             {
                 //タッチセンサがオンの場合は停止
                 m_out.data.vx = 0;
                 m_out.data.va = 0;
                 m_outOut.write();
                 return RTC::RTC_OK;
             }
         }
     }
 
     //すべてのタッチセンサがオフの場合はコンフィギュレーションパラメーターの値で操作
     m_out.data.vx = m_speed_x;
     m_out.data.va = m_speed_r;
     m_outOut.write();
  }

Code::Blocks によるビルド

ビルドの実行

Code::Blocksの [ビルド] ボタンをクリックしてビルドを行います。


codeblocks_build_2.png
ビルドの実行


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

作成した RobotController をシミュレーターコンポーネントと接続して動作確認を行います。

以下より EV3Simulator コンポーネントをダウンロードしてください。

インターネットに接続できない環境で講習会を実施している場合がありますので、その場合は配布のUSBメモリーに入れてあります。

NameService の起動

コンポーネントの参照を登録するためのネームサービスを起動します。


 $ rtm-naming

RobotController コンポーネントの起動

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

RobotController\build\srcフォルダーの RobotControllerComp ファイルを実行してください。

 $ RobotControllerComp

シミュレーターコンポーネントの起動

EV3SimulatorComp コンポーネントをインストールしたディレクトリーに移動後、下記のコマンドにて起動できます。

 $ src/EV3SimulatorComp

コンポーネントの接続

下図のように、RTSystemEditor にて RobotController コンポーネント、EV3Simulator コンポーネントを接続します。 システムダイアグラムは左上のOpen New System Editorボタンで表示できます。

tutorial_ev3_irex9_2.png
コンポーネントの接続

コンポーネントのActivate

RTSystemEditor の上部にあります [All Activate] というアイコンをクリックし、全てのコンポーネントをアクティブ化します。 正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。


tutorial_ev3_irex10.png
コンポーネントのアクティブ化


動作確認

下図のようにコンフィギュレーションビューの [編集] ボタンからコンフィギュレーションを変更することができます。


tutorial_ev3_irex6.png

スライダーを操作してシミュレーター上のEducator Vehicle改の操作ができるかを確認してください。


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


正常に動作している場合は、開始し位置から直進した場合に壁の前で停止します。


tutorial_ev3_irex18.png


正常に動作していない場合は、壁に接触後にそのまま前進を続けます。


tutorial_ev3_irex17.png


実機での動作確認

講習会で EV3実機を用意している場合は実機での動作確認が可能です。

手順は以下の通りです。

  • Educator Vehicle改の組立て
  • EV3アクセスポイントの起動
  • EV3のアクセスポイントに接続
  • ポートの接続
  • コンポーネントのアクティブ化

Educator Vehicle改の組立て

EV3 は分解した状態で参加者に配ります。 組み立て方は以下の通りです。

ただし、超音波センサー、カラーセンサー、ジャイロセンサーについては、講習で使用しないため取り付ける必要はありません。 以下の※の作業については、時間が余った人が実施してください。

まずは土台部分を取り出してください。


s_DSC00463.JPG

最初にMモーターにケーブル(15cm)を接続します※。


s_DSC00446.JPG

次に EV3 本体を取り付けます。 Mモーターにケーブルを接続した場合は、ケーブルが左側の隙間から出るようにしてください。


s_DSC00448.JPG s_DSC00450.JPG



右側のタッチセンサーを取り付けてください。


s_DSC00451.JPG s_DSC00452.JPG



超音波センサーを取り付けてください※。


s_DSC00454.JPG

ケーブルを接続してください。 必須なのは車輪駆動用のLモーター右、Lモーター左、タッチセンサーだけです。

Lモーター右 ポート C 25cmケーブル
Lモーター左 ポート B 25cmケーブル
Mモーター※ ポートA 25cmケーブル
タッチセンサー右 ポート 3 35cmケーブル
タッチセンサー左 ポート 1 35cmケーブル
超音波センサー※ ポート 4 50cmケーブル
ジャイロセンサー※ ポート 2 25cmケーブル

ケーブルは EV3 の上下に A~D と 1~4 のポートがあるのでそこにケーブルを接続します。


s_DSC00.JPG s_DSC00471.JPG




s_DSC00455.JPG s_DSC00456.JPG



左右にパーツを取り付けます※。 Lモーター右、Lモーター左、Mモーター、タッチセンサー右、タッチセンサー左のケーブルを挟むようにして取り付けてください※。
Lモーター右、タッチセンサー右のケーブルは右側から、Lモーター左、Mモーター、タッチセンサー左は左側から通してください※。


s_DSC00457.JPG


s_DSC00459.JPG

これでとりあえず完成ですが、余裕のある人はジャイロセンサーを取り付けてみてください※。


s_DSC00460.JPG s_DSC00461.JPG



電源の入れ方/切り方

電源の入れ方

中央のボタンを押せば電源が投入されます。


ev3_on.jpg

電源の切り方

EV3 の電源を切る場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Power Off」を選択してください。


ev3_off.jpg


s_DSC01033.JPG

再起動

再起動する場合は最初の画面で EV3 本体の左上の戻るボタンを押して「Reboot」を選択してください。

リセット

ev3dev の起動が途中で停止する場合には、中央ボタン、戻るボタン(左上)、左ボタンを同時押ししてください。画面が消えたら戻るボタンを離すと再起動します。


ev3_reset.jpg

アクセスポイントの設定

EV3 の操作画面から「File Browser」を上下ボタンで選択して中央のボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
 [File Browser               > ]
  Device Browser             >
  Wireless and Networks      > 
  Battery                    >
  Open Roberta Lab           >
  About                      >
 ------------------------------

次に scripts を選択して中央ボタンを押してください。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot
 ------------------------------
 [scripts                     ]
 ・・
 ・・
 ------------------------------

次の画面から start_ap.sh を選択して中央ボタンを押すとスクリプトが起動します。

 ------------------------------
 192.168.0.1
 ------------------------------
         File Browser
 ------------------------------
 /home/robot/scripts
 ------------------------------
 ../
 Component/
 ・・
 [start_ap.sh                 ]
 ------------------------------

しばらくすると無線LANアクセスポイントが起動するので、指定の SSID のアクセスポイントに接続してください。 SSID、パスワードは EV3 に貼り付けたテープに記載してあります。


tutorial_ev3_irex26.png

アクセスポイントに接続

SSID、パスワードは EV3に貼り付けたシールに記載してあります。

※ネットワークが切り替わった場合にネームサーバーへのコンポーネントの登録やポートの接続が失敗する場合があるのでネームサーバ、コンポーネントを一旦全て終了してください。 ネットワーク切り替え後に起動した場合には問題ないので、終了させる必要はありません。

ネームサーバー追加

続いてRTシステムエディタの [ネームサーバー追加] ボタンで 192.168.0.1 を追加してください。


tutorial_raspimouse0.png tutorial_ev3_irex12.png



するとEducatorVehicle0という RTC が見えるようになります。

ポートの接続

RTシステムエディタで EducatorVehicle、RobotController コンポーネントを以下のように接続します。

tutorial_ev3_irex11.png

アクティブ化

そして RTC をアクティブ化すると EV3の操作ができるようになります。

Tutorial (EV3, Part 3)

このページでは2台のEV3を連携したRTシステムの構築を行います。

1台目のEV3をアクセスポイントとして、ノートPCと2台目のEV3をアクセスポイントに接続します。

※EV3(1台目)は奇数番号のものを配布します。EV3のシールに記載された番号を確認してください。 EV3(2台目)はEV3(1台目)の次の番号のものを配布します。(例:EV3(1台目):7、EV3(2台目):8)


tutorial_ev3_irex23.png

EV3(2台目)の組立て

第二部の手順に従って2台目のEducator Vehicleを組み立ててください。

EV3との接続

ノートPCとEV3(1台目)の接続

第二部の、実機での動作確認まで完了してください。 この時点でノートPCとアクセスポイントのEV3が接続されているはずです。


tutorial_ev3_irex24.png

EV3(1台目)とEV3(2台目)の接続

まずはEV3(2台目)の電源を投入してください。 起動後にEV3(1台目)に自動接続します。 自動接続できた場合は、EV3の画面左上にIPアドレスが表示されます。 IPアドレスは192.168.11.yyyが表示されます。


tutorial_ev3_irex25.png

他のIPアドレスが表示されている場合は、配布したEV3の番号が違う可能性があるため確認してください。

ネームサーバー、RTCの起動

EV3(2台目)の画面上の操作でネームサーバーとRTCを起動します。

EV3 の操作画面から「File Browser」→「scripts」を選択してください。

ネームサーバー、RTCはstart_rtcs.shのスクリプトを実行することで起動します。

 ------------------------------
 192.168.11.yyy
 ------------------------------
         File Browser
 ------------------------------
 /home/robot/scripts
 ------------------------------
 ../
 Component/
 ・・
 [start_rtcs.sh                 ]
 ------------------------------


tutorial_ev3_irex32.png

ネームサーバー追加

RTシステムエディタから、192.168.11.yyyのネームサーバーに接続してください。


tutorial_raspimouse0.png tutorial_ev3_irex22.png



この時点でRTシステムエディタのネームサービスビューにはlocalhost、192.168.0.1、192.168.11.yyyのネームサーバーが登録されています。 192.168.11.yyyのネームサーバーに登録されているRTCの名前はEducatorVehicle1となります。


tutorial_ev3_irex30.png

  • localhost
    • RobotController0
  • 192.168.0.1
    • EducatorVehicle0
  • 192.168.11.yyy
    • EducatorVehicle1

動作確認

EducatorVehicle0(192.168.0.1)とEducatorVehicle1(192.168.11.yyy)をシステムダイアグラム上で接続してください。 EducatorVehicle1の現在の速度出力をEducatorVehicle0の目標速度入力に接続することで、EV3(2台目)の動きにEV3(1台目)が追従するようになります。


tutorial_ev3_irex31.png

RTCをアクティベートして2台目のEducator Vehicleの車輪を転がすと、1台目のEducator Vehicleがそれに合わせて動作します。


tutorial_ev3_irex28.png

自由課題

これで実習は一通り終了ですが、時間が余っている場合は以下のような課題に挑戦してみてください。

  • EV3(2台目)のタッチセンサのオンオフでEV3(1台目)を操作

ジョイスティックコンポーネントはOpenRTM-aist Python版のサンプルにあります(TkJoyStickComp.py)。 TkJoyStickComp.pyのアウトポートのデータ型はTimedFloatSeq型であるため、TimedVelocity2D型に変換するRTCを作成する必要があります。

EducatorVehicleRTCのsoundという名前のインポートに文字列(TimedString型)を入力すると、EV3が発声します。

文字列(const char*)をデータポートで出力する際はCORBA::string_dup関数で文字列をコピーする必要があります。

 m_out.data= CORBA::string_dup("abc");

  • 各種センサの利用(カラーセンサ、超音波センサ、ジャイロセンサ)

How to build Educator Vehicle (Revised)

このページではレゴマインドストーム EV3 で以下の車輪移動ロボット (Educator Vehicle 改) を組み立てる手順を説明します。



s_DSC00443.JPG


s_DSC00440.JPG


Educator Vehicle との共通部分

車輪移動ロボットの組立て

まずは Educator Vehicleの説明書 の最初の車輪移動ロボットを組み立てる章の45番まで組み立ててください。 ただし8番と20番の作業は必要ないので飛ばしてください。

またジャイロセンサーを取り付ける章(98ページ)を参考にしてジャイロセンサーを取り付けてください。



s_DSC00374.JPG


モーターMの取り付け

モーターMにパーツを取り付けます。 Educator Vehicle の説明書のモーターMを取り付ける章(78ページ)の2~5、10番作業を行ってください。



s_DSC00370.JPG


そして18番の作業を行ってモーターMを移動ロボットに取り付けてください。



s_DSC00375.JPG


タッチセンサーへのパーツ取り付け

タッチセンサーにパーツを取り付けます。 Educator Vehicle の説明書のタッチセンサーを取り付ける章(61ページ)の1~3番の作業を行ってください。



s_DSC00372.JPG


独自部分

ここからの作業に必要な部品は以下の通りです。



s_DSC00369.JPG


土台部分組立て



s_DSC00376.JPG




s_DSC00386.JPG




s_DSC00387.JPG




s_DSC00388.JPG




s_DSC00389.JPG




s_DSC00390.JPG s_DSC00391.JPG





s_DSC00393.JPG s_DSC00394.JPG





s_DSC00395.JPG s_DSC00396.JPG





s_DSC00397.JPG




s_DSC00398.JPG




s_DSC00399.JPG




s_DSC00400.JPG




s_DSC00401.JPG s_DSC00402.JPG



タッチセンサーの取り付け



s_DSC00403.JPG s_DSC00404.JPG





s_DSC00405.JPG




s_DSC00406.JPG




s_DSC00407.JPG




s_DSC00408.JPG


超音波センサーの取り付け



s_DSC00409.JPG




s_DSC00410.JPG




s_DSC00411.JPG




s_DSC00412.JPG




s_DSC00415.JPG




s_DSC00416.JPG




s_DSC00417.JPG




s_DSC00418.JPG




s_DSC00419.JPG




s_DSC00422.JPG




s_DSC00424.JPG




カラーセンサの取り付け

s_DSC00425.JPG




s_DSC00426.JPG




s_DSC00427.JPG




s_DSC00428.JPG


ケーブルの接続

モーター右 ポート C 25cmケーブル
モーター左 ポート B 25cmケーブル
モーター(M) ポートA 25cmケーブル
タッチセンサー右 ポート 3 35cmケーブル
タッチセンサー左 ポート 1 35cmケーブル
超音波センサー ポート 4 50cmケーブル
ジャイロセンサー ポート 2 25cmケーブル

※カラーセンサーを利用する際は超音波センサーかタッチセンサーのケーブルを外して使用してください。



s_DSC00432.JPG




s_DSC00433.JPG




s_DSC00434.JPG


Controlling GRobo on Chorenoid Simulator By Using LeapMotion

このケーススタディでは、LeapMotion センサーを利用して Choreonoid シミュレーターで機動中の GRobo を制御します。まずは準備されたコンポーネント(デモシステム及びサンプルコンポーネント)で動作確認をします。それから、自作コンポーネントでシミュレーター上のロボットを制御します。

リソースと事前準備

以下のアーカイブをダウンロードし解凍してください。

robomec2015_openrtm_tutorial_part3.zip 2015/05/20更新

アーカイブの内容にある主なるファイルは以下のとおりです。

  • Demo/: デモシステムのコンポーネント及び Choreonoid
    • rtc_handle.bat: デモシステムを機動するためのバッチファイル
  • src/: 準備されたコンポーネントのソース
    • LeapMotion_RobotControl_Sample: サンプルコンポーネントのソース
  • LeapMotionGRoboControlSampleComp.exe: サンプルコンポーネントのバイナリー(x64、Visual Studio 2013用)
  • online_tutorial/:この Webページ

【必須】以下のページから「Windows Download」をクリックして、LeapMotion のドライバーをダウンロードしてインストールしてください。

https://www.leapmotion.com/setup/windows

デモシステム動作確認

ここでは、デモシステムを機動し動作を確認します。以下の手順にしたがってデモシステムを起動させてください。

  1. スタートメニューの「Start Naming Service」でネームサーバーを機動する(OpenRTMのドキュメントに参照してください)
  2. スタートメニューから「RTSystemEditorRCP」または「OpenRTP 1.1.1」を起動する
  3. Demo/rtc_handle.batを実行してデモシステムのランチャーを起動する
    start_launcher.png
    rtchandle_launcher.png
  4. RtcHandle で以下のボタンをクリックしてコンポーネントを起動する
    1. CNoid_G
    2. RobotDemo
    3. LeapRTC
    4. LeapMotion
      launch_demo_components.png
      rtchandle_launched_components.png
  5. 「getRtcList」をクリックする
    rtchandle_getrtclist_button.png
    rtchandle_getrtclist_result.png
  6. 「Load」をクリックして「LeapDemo.data」を選択する
    rtchandle_load_button.png
    rtchandle_load_button_after.png
  7. 「ConnectAll」ボタンをクリックしてコンポーネントを接続するとコンポーネントは自動的に以下のように接続される。
    rtchandle_connectall_button.png
    rtsysed_demo_connected.png
  8. 「ActivateAll」をクリックしてデモを起動してシステムを起動する
    rtchandle_activateall_button.png
    rtsysed_demo_activated.png
  9. LeapRTCCompのウィンドーが選択された状態で LeapMotionの上で手を動かすと、「LeapMotion」ウィンドーでデータが現れ、コマンドによってシミュレーター上のロボットが動く
    demo_moving_robot.png

サンプルコンポーネントの動作確認

ここでは、デモシステムの中でロボットを制御するコンポーネント「GRobotDemo」の代わりにサンプルコンポーネントを使って、シミュレーター上のロボットを制御します。

以下の手順を始める前に、まずはデモシステムを上述の手順で起動してください。

  1. 「LeapMotionGRoboControlSampleComp」をコンパイルする
    1. CMakeを起動する
    2. CMake上で、サンプルコンポーネントのソースダイレクトリー(解凍された場所のsrc/LeapMotion_RobotControl_Sample)をソースコードとして指定する
      cmake_select_source.png
    3. CMake上で、buildディレクトリーを、ソースディレクトリーの中の「build」ディレクトリーに指定する
      cmake_select_build.png
    4. 「Configure」ボタンをクリックする
      cmake_configure_button.png
    5. Generatorを選択する。以下のは、Visual Studio 2013でx64用のバイナリーの場合である。自分がインストールした OpenRTM バージョンに合わせた Generator を選択する。
      cmake_select_generator.png
    6. 「Generate」ボタンをクリックしてプロジェクトを生成する
      cmake_generate_button.png
    7. 「src/LeapMotion_RobotControl_Sample/build」にある「LeapMotionGRoboControlSample.sln」をVisual Studioで開いて、「ALL_BUILD」を右クリックメニューし、コンパイルする
      sample_component_project.png
      sample_component_compile.png
    8. 以下の用にコンポーネントのバイナリーができた
      sample_component_binary.png
  2. 「LeapMotionGRoboControlSampleComp」を起動してロボットを制御する
    1. LeapMotionGRoboControlSampleComp.exeを実行する
    2. RTSystemEditorでGRobotDemoコンポーネントのコネクションを削除する
      grobotdemo_delete_connections.png
    3. RTSystemEditorでLeapMotionGRoboControlSampleCompを以下の用につなげて、コンポーネントを activate する
      • LeapMotionのeSEAT0.hands_outポートからLeapMotionGRoboControlSample0の.hand_positionsへ
      • LeapMotionGRoboControlSample0.hand_positionsからRobotMotion0の.targetAngleへ
        sample_component_connect.png
    4. LeapRTCCompのウィンドーが選択された状態でLeapMotionの上で手を動かすとシミュレーター上のロボットの手が自分の手に合って動く
      sample_component_follow_hands.png

自作コンポーネントを作る

ここでは、ロボット制御コンポーネントを作成する手順を説明します。まずは RTCBuilder を利用してソースのスタブを生成します。それから自由にソースを編集してLeapMotionからのデータに対してシミュレータ上のロボットを制御するアルゴリズムを実装します。

  1. RTCBuilderを起動する
  2. 新しい RTCBuilder プロジェクトを作る(プロジェクト名、コンポーネント名などを自由に選択してください)
    rtcbuilder_new_project.png
    rtcbuilder_project_type.png
  3. アクティビティでonActivatedonDeactivated及びonExecuteを選択する
  4. 以下のデータポートを作成する
種類 ポート名 データ型
入力ポート hand_positions RTC::TimedFloatSeq
出力ポート command RTC::TimedString
出力ポート target_angles RTC::TimedShortSeq
rtcbuilder_create_ports.png
  1. サービスポートはないのでタブをスキップする
  2. コンフィグレーションはないのでタブをスキップする
  3. ソースコードの言語をC++に設定する
    rtcbuilder_select_language.png
  4. コンポーネントソースのスタブを生成する
    rtcbuilder_generate_source.png

以上の準備ができたら、CMake と Visual Studio を利用してコンポーネントのソースを編集してコンパイルします。生成されたソースの中に「include/<コンポーネント名>/<コンポーネントト名>.h」「src/<コンポーネント名>.cpp」を開いて、以下の編集を行ってください。

ヘッダーファイルのクラス定義で以下のプライベート変数を追加してください。

  bool m_rightUp;
  bool m_leftUp;

.cppファイルで、12行目で以下の関数を追加してください。

  double minmax(double a, double max, double min)
  {
    if (a > max)
    {
      return max;
    }
    else if (min > a)
    {
      return min;
    }
    return a;
  }

.cppファイルで、コンストラクタで以下の行を追加してください。

  m_rightUp = false;
  m_leftUp = false;

.cppファイルで、onExecute メソッドで以下のソースをペストしてください。

  if (m_hand_positionsIn.isNew())
  {
    m_hand_positionsIn.read();
    // Hand position data is received as an array of floats:
    // [right_hand_x, right_hand_y, right_hand_z, left_hand_x, left_hand_y, left_hand_z]
    // Where the x axis runs left to right across the LeapMotion sensor, the y axis is
    // verticle, and the z axis is front to back across the LeapMotion sensor.
    // So if you move your hand up and down above the sensor, the y axis will change.
    // If you move your hand towards the screen, the z axis will change.
    if (m_hand_positions.data.length() == 6)
    {
      // Calculate a command based on the hand positions
      // Right hand
      if (m_hand_positions.data[1] > 15 && !m_rightUp)
      {
        // Raise the hand
        if (m_leftUp)
        {
          m_command.data = CORBA::string_dup("rightup2");
        }
        else
        {
          m_command.data = CORBA::string_dup("rightup1");
        }
        m_rightUp = true;
        std::cout << "Right hand up\n";
        // Write the output port
        m_commandOut.write();
      }
      else if (m_hand_positions.data[1] < -15 && m_rightUp)
      {
        // Lower the hand
        if (m_leftUp)
        {
          m_command.data = CORBA::string_dup("rightdown2");
        }
        else
        {
          m_command.data = CORBA::string_dup("rightdown1");
        }
        m_rightUp = false;
        std::cout << "Right hand down\n";
        // Write the output po