Nextage Open チュートリアル

The code is open source, and available on github.

はじめに

安全について

NEXTAGE OPEN ロボットを動作させる前に必ず次の事項を確認してください.

  • ロボットが適切に設置されている
  • 周辺に動作に必要な空間が確保されている
  • 緊急停止スイッチがいつでも押せる状態になっている Emergency Stop Switch

クイックスタート

コンピュータの起動

  1. 台座の下部にキャスターを上下に動かすノブがあります. このノブを反時計回りに回してロボットを床に固定します.
  2. AC電源プラグは台座から出ています.電源につないでください.
  3. キーボードとマウス,ディスプレイモニタをUIコントロールボックスに接続してください. UIコントロールボックスは2つあるコンピュータのうち上側のコンピュータです. UI Control Box - Keyboard/Mouse UI Control Box - Monitor
  4. AC電源を入れてください.コントローラボックスのメインスイッチが赤く点灯します. Controller Box - Main Switch
  5. スイッチボックスをロボットの後ろ側にあるコネクタに接続してください. Switch Box Connection
  6. ロボットの後ろ側にある緑色のボタンを押してください.ビープ音が1分ほど鳴ります. ロボット胸部にある4つのLEDライトが全て点滅します. Green Button LED Lights on Chest
  7. ロボットの後ろ側にある青色のボタンを押してください. ロボットの胸部にある緑色と白色のLEDライトのみが点滅していたら, ロボットの動作準備が整った状態となっています. Blue Button

ソフトウェアのアップデート

出荷状態のロボットにおいては Ubuntu 側のソフトウェアに最新のバージョンがインストールされていない場合があります.

UIコントロールボックスのターミナルを開いて次の1行のコマンドを実行するとアップデートされます. 本コマンド実行の際には別途通知されているルートパスワードが必要です.

$ sudo apt-get update && sudo apt-get dist-upgrade

ROS サーバの起動

rtm_ros_bridge の実行

UIコントロールボックスのターミナルで下記のコマンドを実行して ROS ソフトウェアを実行します.

$ roslaunch hironx_ros_bridge hironx_ros_bridge_real.launch    (HIRO)

$ roslaunch nextage_ros_bridge nextage_ros_bridge_real.launch    (NEXTAGE OPEN)

デフォルト状態の NEXTAGE OPEN ではホスト名 nextage でアクセスすることができ, それは nextage_ros_bridge_real.launch 内に既に記述されています.

ホスト名を変更した場合には下記コマンドの%HOSTNAME% の部分を変更したホスト名に置き換え,明示的にコマンドを実行してください.

$ roslaunch hironx_ros_bridge hironx_ros_bridge_real.launch nameserver:=%HOSTNAME%   (HIRO)

$ roslaunch nextage_ros_bridge nextage_ros_bridge_real.launch nameserver:=%HOSTNAME%   (NEXTAGE OPEN)

このプログラムはプログラムからロボットを操作するときには実行された状態である必要があります.

ROS プロセスの確認

rtm_ros_bridge を実行しているターミナルとは別のターミナルを開いてください. 新しいターミナルで次のコマンドを実行することで動作しているROSノードを確認することができます.

$ rosnode list
/diagnostic_aggregator
/hrpsys_profile
/hrpsys_ros_diagnostics
/hrpsys_state_publisher
/rosout

Hironx Dashboard によるロボットの初期化

rqt での Hironx Dashboard の起動

Hironx Dashboard は rqt 上で動きますのでターミナルから rqt を起動します.

$ rqt

rqt の Plugins で Hironx Dashboard を選択します.

Hironx Dashboard

ロボットのキャリブレーション

Hironx Dashboard からロボットの関節角度のエンコーダのキャリブレーションを行います.

  • 注意-1: スイッチボックスの緊急停止スイッチをいつでも押せる状態にしてください.
  • 注意-2: ロボットが動きます.

Hironx Dashboard の [ Joint Calibration ] ボタンを押してください.

次のようにロボットが動きます.

FROM:
NXO Off Pose

TO:
NXO Init Pose Factory

ロボットを初期姿勢にする

ロボットの各関節を初期姿勢にします.

  • 注意: ロボットの全関節が動きます.(初期姿勢から外れいてる場合)

Hironx Dahsboard の [ Goto init pose ] ボタンを押してください.

MoveIt! での動作の実行

ROS の GUI も備えたモーションプランニングフレームワークの MoveIt! を用いてロボットを動かしてみます.

MoveIt! の起動

MoveIt! を起動します.新しくターミナルを開いて次のコマンドを実行してください.

$ roslaunch hironx_moveit_config moveit_planning_execution.launch   (HIRO)

$ roslaunch nextage_moveit_config moveit_planning_execution.launch  (NEXTAGE OPEN)

MoveIt! Window

MoveIt! での動作計画と実行

MoveIt! 内に表示されているロボットモデルの手先に矢印や球で表現されているマーカがあります. これは InteractiveMarker と呼ばれるもので,これで手先の位置と姿勢を指定します.

手先姿勢を変更する前に次の準備を実行してください.

  1. Planning タブ Querry の Select Start State: < current > で [ Update ] ボタンをクリック
  2. Planning タブ Querry の Select Goal State: < current > で [ Update ] ボタンをクリック

InteractiveMarker をマウスでドラッグして少し移動させてみます. 色違いの腕が表示されていることと思います. 現在のロボットの姿勢と InteractiveMarker で指定した目標姿勢が表示されています.

MoveIt! InteractiveMarker

Planning タブ内の [ Plan ] ボタンをクリックしてください. MoveIt! 内においてアニメーションで腕が目標姿勢になるように動作計画が表示されていることと思います.

アニメーション表示された動作計画で実機ロボット周囲の状況も含めて干渉など問題がないようでしたら, 実際にロボットを目標姿勢になるように動かしてみます.

  • 注意: ロボットが動きます.

Planning タブ内の [ Plan and Execute ] ボタンをクリックしてください.

MoveIt! Plan and Execute Done

MoveIt! で動作計画したように実際にロボットが動いたことと思います.

他の目標姿勢にも動かしてみます. ここでは MoveIt! にランダムな姿勢を生成させます.

  • 注意: 生成された目標姿勢に対するロボット周辺の状況を確認しながら下記の手順を進めてください.
  1. Planning タブ Querry の Select Start State: で < random valid > を選択
  2. Select Start State: [ Update ] ボタンをクリック(再クリックで再生成)
  3. Commands の [ Plan ] ボタンをクリック → 動作計画の確認
  4. ロボット動作環境などの安全確認
  5. Commands の [ Plan and Execute ] ボタンをクリック → 動作の実行

MoveIt! - Selecto Goal Random Valid

MoveIt! での動作計画と実行の手順まとめ

  1. Planning タブ Querry の Select Start State: < current > で [ Update ] ボタンをクリック
  2. Planning タブ Querry の Select Goal State: < current > で [ Update ] ボタンをクリック
  3. MoveIt! 上の InteractiveMarker でロボットの手をドラックして目標姿勢に移動
  4. Planning タブ Commands の [ Plan ] ボタンをクリックして動作計画を確認
  5. ロボットの動作による周辺環境との干渉などの問題がないことを確認
  6. Planning タブ Commands の [ Plan and Execute ] ボタンをクリック

ロボットのシャットダウン処理

ロボットをサーボオフ姿勢にする

ロボットの各関節をサーボオフ姿勢にしてサーボ切ります.

  • 注意: ロボットが動きます.

Hironx Dahsboard の [ Goto init pose ] ボタンを押してください.

これによりロボットの各関節がサーボオフ姿勢になり,サーボが切れます.

全プログラムの終了

実行しているプログラムを全て終了します. 全てのターミナルで Ctrl-c により終了してください.

QNX の停止

Ubuntu デスクトップ上にある NextageOpenSupervisor ツールを用いて QNX コンピュータのシャットダウンを行ってください.

Ubuntu の終了

コマンド $ sudo shutdown now などで Ubuntu を終了します.

  • 注意: 不在時もコンピュータをオンにしておく場合は製造元の指示に従ってください.

動力学シミュレーション

RTM の動力学シミュレーション - hrpsys-simulator

ロボットの核となる機能は OpenRTM というフレームワーク上で動いています. この OpenRTM をベースとした hrpsys-simulator と呼ばれるシミュレータでロボットの機能を仮想的に実現することができます. このシミュレータは ROS が提供する高層レイヤを使用しなくても利用できます.

特別な目的ではないプログラムを実行する多くの場合においてこのシミュレータで十分です.

シミュレータの実行

実際のロボットを「模倣する」シミュレータを起動します. roslaunch ではなく rtmlaunch であることに注意して次のコマンドを実行してください.

$ rtmlaunch hironx_ros_bridge hironx_ros_bridge_simulation.launch   (HIRO)

$ rtmlaunch nextage_ros_bridge nextage_ros_bridge_simulation.launch (NEXTAGE OPEN)

この launch ファイルは主に次の2つを実行します.

まずはシミュレータに仮想ロボットを読み込みます.

$ rtmlaunch hironx_ros_bridge hironx_startup.launch

このシミュレーションのロボットは OpenRTM ベースのソフトウェア上のみで実行されています.

次に ROS 経由でこのロボットを動作させるために ROS と OpenRTM の2つの空間を結ぶ「橋」をつくります.

$ roslaunch hironx_ros_bridge hironx_ros_bridge.launch    (HIRO)

$ roslaunch nextage_ros_bridge nextage_ros_bridge.launch    (NEXTAGE OPEN)

次のようなコマンドプロンプトが表示されていたら,シミュレーションが正常に動作しています.

[ INFO] [1375160303.399785831, 483.554999999]: [HrpsysSeqStateROSBridge0] @onExecutece 0 is working at 201[Hz]
[ INFO] [1375160304.408500642, 484.544999999]: [HrpsysSeqStateROSBridge0] @onExecutece 0 is working at 198[Hz]

そして hrpsys simulator viewer が次のように表示されます.

hrpsys viewer

MoveIt! の起動とシミュレーションロボットの操作

ロボット実機と同じように MoveIt! を起動してシミュレーション上のロボットを操作することができます.

$ roslaunch hironx_moveit_config moveit_planning_execution.launch   (HIRO)

$ roslaunch nextage_moveit_config moveit_planning_execution.launch  (NEXTAGE OPEN)

MoveIt! 内での操作も実機のロボットとシミュレーション上のロボットでは同じです. InteractiveMarker を動かし,動作計画をし,実行します.

ROS の動力学シミュレーション - Gazebo

NEXTAGE OPEN の Gazebo シミュレーションを起動します.

  • メモ: ROS / Gazebo でのシミュレーションのため rtm_ros_bridge は不要
$ roslaunch nextage_gazebo nextage_world.launch

Gazebo nextage_world に セスナ機,カフェのテーブル,建設用のバレル,HUSKYロボットオブジェクトをインサートした図

Gazebo - nextage_world with inserting lots of objects

ロボットハードウェアの概要

ロボット諸元

詳しくはカワダロボティクスのサイト内にある NEXTAGE 製品仕様を参照してください.

座標系

HIRO / NEXTAGE OPEN の座標系は下の図のようになっています.

ベース座標は腰基部中心に設定されています.

  • ベース座標
    • X軸: 正 - 前方 / 負 - 後方
    • Y軸: 正 - 左側 / 負 - 右側
    • Z軸: 正 - 上方 / 負 - 下方

Nextage Open - Coordinate Sys / Base WAIST

各フレームの座標系は次のようになっています.

  • 各座標軸と色表記
    • X軸: Red
    • Y軸: Green
    • Z軸: Blue

Nextage Open - Coordinate Sys / All Frames

コンピュータとソフトウェアの概要

制御用コンピュータ

カワダロボティクスの HIRO および NEXTAGE OPEN ロボットシステムには次の2つのコンピュータがあります.

  • コントロールボックス(QNXコンピュータ)
    • ロボットを制御するコンピュータ
    • QNX: 実時間オペレーティングシステム
    • デフォルトではソフトウェアのインストール・改変は不可
    • 保守・アップデートは可能
  • UIコントロールボックス(Ubuntuコンピュータ/ビジョンPC)
    • 作業を行うコンピュータ
    • Ubuntu
    • 高次レイヤソフトウェアの実行

開発用コンピュータ

HIRO および NEXTAGE OPEN のシステムでは2台の制御用コンピュータの他に 開発用コンピュータも使用することができます.

  • 開発用コンピュータ要求仕様
    • Intel i5 以上のプロセッサ
    • 4GB 以上のメモリ
    • 15GB 以上のディスク空き容量
    • Ubuntu 14.04
    • ROS Indigo
    • QNXコンピュータにイーサネット経由で接続(ロボット所有者のみ)

ソフトウェアの概要

下に HIRO / NEXTAGE OPEN の QNX と Ubuntu のソフトウェアコンポーネント構成図を示します. ユーザーは ROS と RTM(hrpsys) のどちらを利用してもロボットを制御するソフトウェアをプログラムすることができます.

Software Components Diagram

API の概要

HIRO / NEXTAGE OPEN で使用する API をシステムの観点で大別すると RTM と ROS の2種があり, ROS API でも ROS インタフェース と MoveIt! インタフェースに分けることができます.

  • RTM API on hrpsys
  • ROS API
    • ROS インタフェース
    • MoveIt! インタフェース

それぞれの API が Python や C++ などのプログラミング言語で利用することができます.

下図にそれら API の構成図を示します.

API Diagram

各 API の利用法

各 API の利用法や使い分けについては次を参考にしてください.

  • RTM ベースのインタフェースの方が実装されている機能が多い
    • HIRONX ロボットが元々 OpenRTM 上で動作しており,それに対して RTM プロセスと ROS で書かれたプログラムを繋ぐ hrpsys_ros_bridge を作った経緯のため
  • キャリブレーションやサーボの ON/OFF などの基本的な操作は RTM インタフェースを使用
  • 全ての API は同一プログラム言語で書かれた1つのプログラムファイル内で混成利用可能

保守・管理

Ubuntuソフトウェアのアップデート

Ubuntu 上の全てのソフトウェアをアップデートする場合は次のコマンドを実行してください.

$ sudo apt-get update && sudo apt-get dist-upgrade

HIRO / NEXTAGE OPEN のソフトウェアのみをアップデートする場合は次のコマンドを実行してください.

$ sudo apt-get update && sudo apt-get ros-indigo-rtmros-nextage ros-indigo-rtmros-hironx

QNX での作業

QNX GUIツール - NextageOpenSupervisor

QNX に関する日常的な作業のうち次のものは カワダロボティクスから提供されている GUI ツール NextageOpenSupervisor を用いて操作することができます.

  • コントロールボックス(QNXコンピュータ)のシャットダウン処理
  • アップデート

このツールは UIコンピュータ(Ubuntuコンピュータ/ビジョンPC)に nxouser アカウントでログオンするとデスクトップ上にアイコンがあります.

QNXのコマンド操作

本作業は QNX にログオンできることを前提としたものです. 次のコマンドで QNX にSSH接続とログオンを行います.

YOURHOST$ ssh -l %QNX_YOUR_USER% %IPADDR_YOUR_QNX%
QNX シャットダウンコマンド
QNX$ su -c '/opt/grx/bin/shutdown.sh'
QNX リブートコマンド
QNX$ su -c '/bin/shutdown'

ログファイルの定期メンテナンス

QNXのログファイル

QNX のログファイルは /opt/jsk/var/log にあります.

  • Nameserver.log
  • Modelloader.log
  • rtcd.log

これらのログファイルは自動的に生成されますが,自動的には削除されません.

ログファイルの圧縮・削除

ディスクスペースにおいてログファイルはすぐに何ギガバイトにもなってしまします. これらのログファイルは自動的に削除される仕組みにはなっておりませんので, 時おり /opt/jsk/var/log 下のログファイルを削除することをお勧めします.

ログファイルの削除はスクリプトの実行もしくはリモートログインによる手動操作で行うことができます.

スクリプト操作によるログファイルの圧縮
# Remove all raw .log files to free disk space. Same .zip file will be kept in the log folder.

$ rosrun hironx_ros_bridge qnx_fetch_log.sh nextage qnx_nxo_user archive
手動でのログファイルの削除

QNX コンピュータログオンできる場合においては SSH 接続をして, ディレクトリ /opt/jsk/var/log 下にあるログファイルを削除することができます.

ディスク空き容量の確認

QNX コンピュータログオンできる場合においては SSH 接続をして, ディスクの空き容量を確認することができます.

QNX$ df -h

ディスク空き容量の例

/dev/hd0t179 7.8G 7.1G 725M 92% /
/dev/hd0t178 229G 22G 207G 10% /opt/
/dev/hd0 466G 466G 0 100%

QNXでのインストールやソフトウェア利用

HIRO / NEXTAGE OPEN ソフトウェアはすべて公開されていますが, それは HIRO / NEXTAGE OPEN でダウンロードしてただ実行するだけで済むということを意味していません. それらのソフトウェアは QNX オペレーティングシステム上で動いているコントローラボックスで ビルド・コンパイルする必要があります. QNX 上ででコンパイルするには,地域のベンダーから購入できる開発者ライセンスが必要です.

QNX に必要なソフトウェアをインストールする方法も開示されていません. QNX は開発者ライセンス以外にも,その運用者は運用認定が必要もしくは十分に運用に精通している必要がある商用ソフトウェアです. また,QNX では頑強なパッケージングインフラストラクチャ(ROSのようなもの)がないため, インストール作業は非常に長い手作業となる可能性があり,エラーが発生しやすくなります.

しかし,次のような場合には QNX 内での作業が必要となります.

  • 以前のクローズドソースの GRX コントローラに戻す必要があるとき
  • デバッグ時に Ubuntu ソフトウェアの API で見られるログだけでは原因が不明な場合に QNX 上のログファイルを見るとき

このような場合には製造元またはソフトウェアサービスプロバイダからログオンアカウント情報を入手することができます.

開発用ワークステーションのセットアップ

Debian バイナリパッケージからのインストール

Debian バイナリパッケージからソフトウェアのインストールを行います.

ROS および HIRO / NEXTAGE OPEN ソフトウェアのインストール

ROS Indigo および HIRO,NEXTAGE OPEN のパッケージをインストールします. ターミナルを開いて次のコマンドを実行してください..

$ sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu trusty main" > /etc/apt/sources.list.d/ros-latest.list'
$ sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net --recv-key0xB01FA116
$ sudo apt-get update && sudo apt-get install ros-indigo-rtmros-nextage ros-indigo-rtmros-hironx

インストールの最後に setup.bash を読み込み,ROS の環境を設定します.

$ source /opt/ros/indigo/setup.bash

これは新しくターミナルを立ち上げて ROS を使用する前に毎回必要になります. 下記のように .bashrc ファイルに設定を加えて ターミナル起動時に setup.bash を自動で実行し ROS 環境になるようにしておくと便利です.

$ echo "source /opt/ros/indigo/setup.bash" >> ~/.bashrc
  • 注意: 上記コマンドの >>> にしてしまうと元々あった .bashrc 内の設定が消えてしまうので気をつけてください.

HIRO / NEXTAGE OPEN ソフトウェアのみのインストール

ROS Indigo が既にインストールされていて HIRO / NEXTAGE OPEN のソフトウェアのみをインストールする必要がある場合は次の1行だけ実行してください.

$ sudo apt-get update && sudo apt-get install ros-indigo-rtmros-nextage ros-indigo-rtmros-hironx

ネットワーク設定

ホスト名の設定(オプション)

デフォルトの HIRO / NEXTAGE OPEN のソフトウェアでは QNX コンピュータのホスト名を通信の際に使います. ホスト名の設定をオペレーティングシステム上で行っておくと便利です.

  • 次の行を /etc/hosts に追加
# For accessing QNX for NEXTAGE Open
192.168.128.10 nextage
  • IP アドレスはユーザの環境により上記のものとは異なる場合があります.
  • ping を打って nextage から返ってくることを確認してください.
  • シミュレータを利用するために上記の設定を変更する必要はありません.
  • 192.168.128.x セグメントを使用するネットワークアプリケーションを使用している場合を除き, この設定はネットワークの使用には有害ではありません.
  • 既知の OpenRTM の問題 https://github.com/start-jsk/rtmros_hironx/issues/33 を回避するため ネットワーク接続に eth0 が使用されていることを確認してください.

ワークスペースのセットアップ

HIRO / NEXTAGE OPEN のプログラムコードを作成・ビルドする必要がない場合は 本項目のインストール手順は不要です.

下記のセットアップ手順ではワークスペース名を catkin_ws (catkin workspace) としていますが 他の名前・フォルダ名でも大丈夫です.

$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws
$ wstool init src
$ wstool merge -t src https://raw.githubusercontent.com/tork-a/rtmros_nextage/indigo-devel/hironxo.rosinstall
$ wstool update -t src
$ source /opt/ros/indigo/setup.bash
$ rosdep update && rosdep install -r -y --from-paths src --ignore-src
$ catkin build
$ source devel/setup.bash

ターミナルを開くたびに source ~/catkin_ws/devel/setup.bash を実行したくない場合は .bashrc に設定します.

$ echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
  • 注意: 上記コマンドの >>> にしてしまうと元々あった .bashrc 内の設定が消えてしまうので気をつけてください.

トラブルシューティング

ソフトウェア情報の確認

コントローラボックス(QNX)の hrpsys のバージョンの確認

Python インタフェースから次のコマンドを実行して コントローラボックス(QNXコンピュータ)で動作している hrpsys のバージョンを確認してください.

In [1]: robot.hrpsys_version
Out[1]: '315.2.8

コントローラボックスにログオンできる場合にはリモート接続をして 次のコマンドを実行して確認することもできます.

  • 注意: バージョン 315.2.0 以降にて可能
$ cd /opt/jsk/lib
$ ls -l RobotHardware.so  
$ strings RobotHardware.so | grep ^[0-9]*\\.[0-9]*\\.[0-9]*$
315.2.0

RTM ステートのモニタリング

ロボットホスト上で動作しているRTコンポーネントのリストを取得するには rtls を利用します.

  • 注意: 最後のスラッシュ / を忘れずに入力してください.
$ rtls %HOST_ROBOT%:15005/

シミュレーションの場合の例

$ rtls localhost:15005/

Nextage Open の場合の例(ホスト名をユーザの環境に合わせて変更してください)

$ rtls nextage:15005/

rtls の実行例(シミュレーション)

$ rtls localhost:15005/
robotuser-PC.host_cxt/               StateHolderServiceROSBridge.rtc
fk.rtc                              DataLoggerServiceROSBridge.rtc
longfloor(Robot)0.rtc               ImageSensorROSBridge_HandLeft.rtc
sh.rtc                              HiroNX(Robot)0.rtc
ImageSensorROSBridge_HeadRight.rtc  seq.rtc
HrpsysJointTrajectoryBridge0.rtc    log.rtc
HGcontroller0.rtc                   sc.rtc
ModelLoader                         el.rtc
CollisionDetector0.rtc              ImageSensorROSBridge_HandRight.rtc
ImageSensorROSBridge_HeadLeft.rtc   HrpsysSeqStateROSBridge0.rtc
SequencePlayerServiceROSBridge.rtc  ForwardKinematicsServiceROSBridge.rtc
ic.rtc                              rmfo.rtc

rtls で得られたリストの各RTコンポーネントの情報を得るには rtcat を利用します.

$ rtls %HOST_ROBOT%:15005/%CONPONENT_NAME%

rtcat の実行例(シミュレーション)

$ rtcat localhost:15005/fk.rtc
fk.rtc  Active
  Category       example
  Description    forward kinematics component
  Instance name  fk
  Type name      ForwardKinematics
  Vendor         AIST
  Version        315.14.0
  Parent         
  Type           Monolithic
 +Extra properties
+Execution Context 0
+DataInPort: q
+DataInPort: sensorRpy
+DataInPort: qRef
+DataInPort: basePosRef
+DataInPort: baseRpyRef
+CorbaPort: ForwardKinematicsService

$ rtcat localhost:15005/CollisionDetector0.rtc
CollisionDetector0.rtc  Active
  Category       example
  Description    collisoin detector component
  Instance name  CollisionDetector0
  Type name      CollisionDetector
  Vendor         AIST
  Version        315.14.0
  Parent         
  Type           Monolithic
 +Extra properties
+Execution Context 0
+DataInPort: qRef
+DataInPort: qCurrent
+DataInPort: servoStateIn
+DataOutPort: q
+DataOutPort: beepCommand
+CorbaPort: CollisionDetectorService

$ rtcat localhost:15005/ImageSensorROSBridge_HeadRight.rtc
ImageSensorROSBridge_HeadRight.rtc  Active
  Category       example
  Description    openrhp image - ros bridge
  Instance name  ImageSensorROSBridge_HeadRight
  Type name      ImageSensorROSBridge
  Vendor         Kei Okada
  Version        1.0
  Parent         
  Type           Monolithic
 +Extra properties
+Execution Context 0
+DataInPort: image
+DataInPort: timedImage
robotuser@robotuser-PC:~$

ROS ステートのモニタリング

ROS 環境の確認

ROS のバージョンの確認
$ rosversion -d
ROS に関する環境変数の確認
$ env | grep ROS

rqt を用いたモニタリング

ROS の rqt は開発時におけるデータの可視化に大変役立つツールセットです. ここでは HIRO / NEXTAGE OPEN において特に便利ないくつかのツールを紹介します.

まず rqt を起動します.

$ rqt

次に “Plugins” をクリックして以下のプラグインを選択します.

下図のようなウィンドウが表示されるはずです.

  • 注意: この画像はロボットモニタの診断が無効なシミュレータでキャプチャされたため,失効ステータスが表示されています.

rqt Window

QNXでのトラブルシューティング

HIRO / NEXTAGE OPEN の QNX 内プロセスの状態を見るリモート監視ツールがないため, その状態を見るためには QNX コンピュータ上のログファイルの内容を見る必要があります.

ログの回収

QNX のログファイルは /opt/jsk/var/log にあります.

  • Nameserver.log
    • OpenRTM または CORBA に関係したログの多くが記載
  • Modelloader.log
    • OpenHRP3 に関するログ
  • rtcd.log
    • hrpsys のRTコンポーネントに関連したログ

これらのログファイルは次のいずれかの方法で取得することができます.

【推奨】 ログファイルの zip ボールを取得するスクリプトを実行する
  • 注意: rtmros_hironx 1.1.25 以降で利用可能
# Simplest

$ rosrun hironx_ros_bridge qnx_fetch_log.sh nextage qnx_nxo_user
:
$ ls
opt_jsk_var_logs_20170602-020236.zip

# Fetch only files generated after certain date. "1" can be anything except "archive"

$ rosrun hironx_ros_bridge qnx_fetch_log.sh nextage qnx_nxo_user 1 2017-01-11
【代替】 QNX にリモート接続する

QNX コンピュータに SSH 接続をして, ディレクトリ /opt/jsk/var/log 下にあるログファイルにアクセスしてください.

ログのチェック

ロボット胸部の4つ全てのランプが電源投入後も点滅し続けている場合

電源投入後正常起動した場合はロボット胸部の緑と白の LED ライトだけが点滅する状態となります. この場合においてもまだロボット胸部の4つのランプが全て点滅し続けている場合は QNX のログを確認してください.

/opt/jsk/var/log/rtcd.log が次のようになってるかを確認してください.

Logger::Logger: streambuf address = 0x805fc70
hrpExecutionContext is registered
pdgains.file_name: /opt/jsk/etc/HIRONX/hrprtc/PDgains.sav
dof = 15
open_iob - shmif instance at 0x80b3f58
the number of gyros = 0
the number of accelerometers = 0
the number of force sensors = 0
period = 5[ms], priority = 49

/opt/jsk/var/log/Nameserver.log が次のようになってるかを確認してください.

Sat Jan 24 10:55:33 2015:

Starting omniNames for the first time.
Wrote initial log file.
Read log file successfully.
Root context is IOR:010000002b00000049444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f6e74
6578744578743a312e300000010000000000000070000000010102000d0000003139322e3136382e312e313600009d3a0b00
00004e616d6553657276696365000300000000000000080000000100000000545441010000001c0000000100000001000100
0100000001000105090101000100000009010100035454410800000095fbc2540100001b
Checkpointing Phase 1: Prepare.
Checkpointing Phase 2: Commit.
Checkpointing completed.

Sat Jan 24 11:10:33 2015:

Checkpointing Phase 1: Prepare.
Checkpointing Phase 2: Commit.
Checkpointing completed.

/opt/jsk/var/log/ModelLoader.log が次のようになっているかを確認してください.

ready
loading /opt/jsk/etc/HIRONX/model/main.wrl
Humanoid node
Joint nodeWAIST
  Segment node WAIST_Link
  Joint nodeCHEST_JOINT0
    Segment node CHEST_JOINT0_Link
    Joint nodeHEAD_JOINT0
      Segment node HEAD_JOINT0_Link
      Joint nodeHEAD_JOINT1
        Segment node HEAD_JOINT1_Link
        VisionSensorCAMERA_HEAD_R
        VisionSensorCAMERA_HEAD_L
    Joint nodeRARM_JOINT0
      Segment node RARM_JOINT0_Link
      Joint nodeRARM_JOINT1
        Segment node RARM_JOINT1_Link
        Joint nodeRARM_JOINT2
          Segment node RARM_JOINT2_Link
          Joint nodeRARM_JOINT3
            Segment node RARM_JOINT3_Link
            Joint nodeRARM_JOINT4
              Segment node RARM_JOINT4_Link
              Joint nodeRARM_JOINT5
                Segment node RARM_JOINT5_Link
    Joint nodeLARM_JOINT0
      Segment node LARM_JOINT0_Link
      Joint nodeLARM_JOINT1
        Segment node LARM_JOINT1_Link
        Joint nodeLARM_JOINT2
          Segment node LARM_JOINT2_Link
          Joint nodeLARM_JOINT3
            Segment node LARM_JOINT3_Link
            Joint nodeLARM_JOINT4
              Segment node LARM_JOINT4_Link
              Joint nodeLARM_JOINT5
                Segment node LARM_JOINT5_Link
The model was successfully loaded !

すべてのログが上記のような表示でしたらログに関しては正常で, 他に何か複雑なことが起こっている可能性があります. サポートにお問い合わせください.

チュートリアル: Python インタフェース

RTM Python インタフェース

インタラクティブモードでの操作

rtm_ros_bridge の起動

rtm_ros_bridge を起動します.シミュレーションの場合は不要です.

$ roslaunch hironx_ros_bridge hironx_ros_bridge_real.launch nameserver:=%HOSTNAME%    (HIRO)

$ roslaunch nextage_ros_bridge nextage_ros_bridge_real.launch nameserver:=%HOSTNAME%  (NEXTAGE OPEN)
iPython インタフェースの起動

HIRO での iPython インタフェースの起動 ホスト名は各ロボットの設定に応じて変更してください.

ipython -i `rospack find hironx_ros_bridge`/scripts/hironx.py     (Simulation)

ipython -i `rospack find hironx_ros_bridge`/scripts/hironx.py  -- --host hiro014    (Real robot example)

NEXTAGE OPEN での iPython インタフェースの起動

ipython -i `rospack find nextage_ros_bridge`/script/nextage.py     (Simulation)

ipython -i `rospack find nextage_ros_bridge`/script/nextage.py  -- --host nextage    (Real Robot Example)

他のオプションも必要であれば指定することができます.

$ ipython -i `rospack find hironx_ros_bridge`/scripts/hironx.py -- --modelfile /opt/jsk/etc/HIRONX/model/main.wrl --host hiro014 --port 15005

$ ipython -i `rospack find nextage_ros_bridge`/script/nextage.py -- --modelfile /opt/jsk/etc/HIRONX/model/main.wrl --host nextage101 --port 15005

リモート接続する場合は --robot 引数を使用してロボットのインスタンス名を指定する必要があります. 例えば次のように指定して実行します.

$ ipython -i `rospack find hironx_ros_bridge`/scripts/hironx.py -- --host nxo_simulation_host --robot "HiroNX(Robot)0"

iPython インタフェース初期化時に行っていることは次のようになっています.

  • RTCManager と RobotHardware の検出
    • RTCManager: RT コンポーネントを起動する OpenRTM のクラス
    • RobotHardware: ロボットハードウェアとのインタフェースである hrpsys に定義されているRTコンポーネント
  • hrpsys コントローラを動作させるのに必要な RT コンポーネントへの接続とアクティベーション
  • ロガーの開始
  • Joint Groups への割当て(HIRO/NEXTAGE OPEN 固有)
よく使う RTM Python インタフェースコマンド

ロボット実機操作において特有なコマンドを紹介します.

  • 注意-1: コマンドを実行するとロボットが動きます.
  • 注意-2: 緊急停止スイッチをいつでも押せる状態にしておいてください.

ロボット実機のキャリブレーションを行うコマンドです.

ipython>>> robot.checkEncoders()
  • ロボット実機のキャリブレーションがなされていない場合にのみ実行されます.
    • ほとんどの場合においてロボット胸部の緑のロボットステートインジケータライトが点滅します.

ロボットを初期姿勢するコマンドです.

ipython>>> robot.goInitial()

作業終了姿勢に移行するコマンドです.終了姿勢に移行した後にサーボが切れます.

ipython>>> robot.goOffPose()
  • システムの再起動・シャットダウン時の前にはこのコマンドを実行してください.

サーボを入れるコマンドです.

ipython>>> robot.servoOn()
  • 次の動作を行ってサーボが切れた状態のときには手動でサーボを入れる必要があります.
    • goOffPose を実行したとき
    • リレースイッチを押したとき(この場合はまず servoOff を行う必要があります)
    • ロボットに異常動作を与えて緊急停止したとき
  • 現状の物理的関節角度にて角度指示値を設定します.
    • goActual() を内部的に呼び出しています.

サーボを切るコマンドです.

ipython>>> robot.servoOff()
  • 明示的にサーボを切るのではなくリレースイッチを押した場合にはこのコマンドを実行してください.
RTM Python インタフェースの利用

iPython の初期化時にロボットのクライアントインタフェースクラスである HIRONX/NextageClient が iPython ターミナル上で robot ととしてインスタンス化されます.

robot において何ができるのかを見てみます.

In : robot.

上記のように iPython ターミナル上で入力したのに tab キーを押すと選択可能なものが表示されます.

robot.Groups                            robot.getCurrentRPY                     robot.rh_svc
robot.HandClose                         robot.getCurrentRotation                robot.saveLog
robot.HandGroups                        robot.getJointAngles                    robot.sc
robot.HandOpen                          robot.getRTCInstanceList                robot.sc_svc
robot.InitialPose                       robot.getRTCList                        robot.sensors
robot.OffPose                           robot.getReferencePose                  robot.seq
robot.RtcList                           robot.getReferencePosition              robot.seq_svc
robot.abc                               robot.getReferenceRPY                   robot.servoOff
robot.activateComps                     robot.getReferenceRotation              robot.servoOn
robot.afs                               robot.getSensors                        robot.setHandEffort
robot.checkEncoders                     robot.goActual                          robot.setHandJointAngles
robot.clearLog                          robot.goInitial                         robot.setHandWidth
robot.co                                robot.goOffPose                         robot.setJointAngle
robot.co_svc                            robot.hand_width2angles                 robot.setJointAngles
robot.configurator_name                 robot.hgc                               robot.setJointAnglesOfGroup
robot.connectComps                      robot.ic                                robot.setSelfGroups
robot.connectLoggerPort                 robot.init                              robot.setTargetPose
robot.createComp                        robot.isCalibDone                       robot.setupLogger
robot.createComps                       robot.isServoOn                         robot.sh
robot.el                                robot.kf                                robot.sh_svc
robot.el_svc                            robot.lengthDigitalInput                robot.simulation_mode
robot.ep_svc                            robot.lengthDigitalOutput               robot.st
robot.findModelLoader                   robot.liftRobotUp                       robot.stOff
robot.fk                                robot.loadPattern                       robot.tf
robot.fk_svc                            robot.log                               robot.vs
robot.flat2Groups                       robot.log_svc                           robot.waitForModelLoader
robot.getActualState                    robot.moveHand                          robot.waitForRTCManagerAndRoboHardware
robot.getBodyInfo                       robot.ms                                robot.waitInterpolation
robot.getCurrentPose                    robot.readDigitalInput                  robot.waitInterpolationOfGroup
robot.getCurrentPosition                robot.rh                                robot.writeDigitalOutput

Groups のリストとそのメンバを見てみます.

In : robot.Groups
Out:
[['torso', ['CHEST_JOINT0']],
 ['head', ['HEAD_JOINT0', 'HEAD_JOINT1']],
 ['rarm',
  ['RARM_JOINT0',
   'RARM_JOINT1',
   'RARM_JOINT2',
   'RARM_JOINT3',
   'RARM_JOINT4',
   'RARM_JOINT5']],
 ['larm',
  ['LARM_JOINT0',
   'LARM_JOINT1',
   'LARM_JOINT2',
   'LARM_JOINT3',
   'LARM_JOINT4',
   'LARM_JOINT5']]]

スクリプトインタラプタとしての iPython の利点はそこから API の情報を得られることにあります.

例えば,ロボットの現在の姿勢を知りたいがそのコマンドを知らないような場合でも, まず少し推測してからタブ補完を利用することで次のように情報を得ることができます.

In : robot.getCurrent
robot.getCurrentPose      robot.getCurrentPosition  robot.getCurrentRPY       robot.getCurrentRotation

getCurren に対して4つの選択肢があり, この内 getCurrentPose が意図するもののコマンドらしいことが分ります. さらにそのメソッドの引数を知る必要がある場合にはコマンドの最後に ? を入力します.

In : robot.getCurrentPose?
Type:       instancemethod
Base Class: <type 'instancemethod'>
String Form:<bound method HIRONX.getCurrentPose of <__main__.HIRONX instance at 0x1f39758>>
Namespace:  Interactive
File:       /opt/ros/hydro/lib/python2.7/dist-packages/hrpsys_config.py
Definition: robot.getCurrentPose(self, lname)
Docstring:  <no docstring>

ここで getCurrentPoselname(link name の略)を受け取ることが分ります. よって次のように実行します.

In: robot.getCurrentPose('RARM_JOINT0')
Out:
[0.912826202314136,
 -0.4083482880688395,
 0.0,
 0.0,
 0.39443415756662026,
 0.8817224037285941,
 -0.25881904510252074,
 -0.145,
 0.1056883139872261,
 0.2362568060275051,
 0.9659258262890683,
 0.370296,
 0.0,
 0.0,
 0.0,
 1.0]

この getCurrentPose は指示したリンクの回転行列と位置の値を1次元のリストとして戻します.

位置だけを知りたい場合には他の次の方法で取得することもできます.

In: robot.getCurrent
robot.getCurrentPose      robot.getCurrentPosition  robot.getCurrentRPY       robot.getCurrentRotation

In : robot.getCurrentPosition('RARM_JOINT0')
Out: [0.0, -0.145, 0.370296]

hrpsys においては位置ベクトルは次のように対応した3つの要素 [x, y, z] で表されます.

  • x: 前
  • y: 左
  • z: 上

次に腕を動かしてみます. まず初期姿勢まで動かします.

In : robot.goInitial()

目標姿勢を設定するにはどうすれば良いかを調べます.

In : robot.setTargetPose?
Type:       instancemethod
Base Class: <type 'instancemethod'>
String Form:<bound method HIRONX.setTargetPose of <__main__.HIRONX instance at 0x333b758>>
Namespace:  Interactive
File:       /opt/ros/hydro/lib/python2.7/dist-packages/hrpsys/hrpsys_config.py
Definition: robot.setTargetPose(self, gname, pos, rpy, tm)
Docstring:  <no docstring>

gnamejoint group の名前です. posrpy はリスト形式です.

目標姿勢を指定する前に現在のロボットの姿勢を変数に格納します.

In : pos = robot.getCurrentPosition('RARM_JOINT5')
In : rpy = robot.getReferenceRPY('RARM_JOINT5')
In : tm = 3

ロボットの姿勢はおそらく下図のようになっていることと思います.

HIRO Current Pose

それでは目標位置を現在の姿勢から少し変えて指示して,そこへの動作を実行させてみましょう.

In : pos[2] = 0.1

In : robot.setTargetPose('rarm', pos, rpy, tm)
Out: True

次の図のように右腕の手先が指定した位置へと移動したことと思います.

  • 注意: 下図は MoveIt! が実行されているときにキャプチャしたもので,MoveIt! 由来の黄緑色で表示されている開始姿勢の腕はここのチュートリアルでは関係しませんので無視してください.

HIRO setTargetPose

ロボットでの作業が終了したら終了姿勢にしてください.

In : robot.goOffPose()

HIRO goOffPose

hrpsys-based API のソースとドキュメント

hrpsys-based API は次のリンク先にソースとドキュメントがあります.

RTM Python インタフェースプログラミング

Python を用いた HIRO / NEXTAGE OPEN のプログラミングは1つの統合されたインタフェースによって行うことができます. インタフェースの名称はロボットによりそれぞれ異なります.

  • HIRONX : HIRO / HIRONX ユーザ
  • NextageClient : NEXTAGE OPEN ユーザ

本項では “HIRONX” を使用しますが NEXTAGE OPEN ユーザは NextageClient インタフェースを用いて同様のことを行うことができます.

サンプルコード - Acceptance Test (RTM)

Acceptance Test (RTM) のコードを参考に RTM Python インタフェースプログタミング の方法を見たあとにコードを実行します.

acceptancetest_rtm.py

まずこのサンプルは下図のような2段階の依存関係を持っています.

AcceptanceTest_Hiro Dependency

このサンプルコードの動作は AcceptanceTest_Hiro クラスに記述されていて HIRONX クラスに接続する AcceptancetestRTM クラスを利用しています.

AcceptancetestRTM について見てみます.コードの全体は次のようになっています.

1 # -*- coding: utf-8 -*-
2
3 # Software License Agreement (BSD License)
4 #
5 # Copyright (c) 2014, TORK (Tokyo Opensource Robotics Kyokai Association)
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
10 # are met:
11 #
12 #  * Redistributions of source code must retain the above copyright
13 #    notice, this list of conditions and the following disclaimer.
14 #  * Redistributions in binary form must reproduce the above
15 #    copyright notice, this list of conditions and the following
16 #    disclaimer in the documentation and/or other materials provided
17 #    with the distribution.
18 #  * Neither the name of TORK. nor the
19 #    names of its contributors may be used to endorse or promote products
20 #    derived from this software without specific prior written permission.
21 #
22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 # POSSIBILITY OF SUCH DAMAGE.
34
35 import time
36
37 from hironx_ros_bridge.constant import Constant
38 from hironx_ros_bridge.testutil.abst_acceptancetest import AbstAcceptanceTest
39
40
41 class AcceptanceTestRTM(AbstAcceptanceTest):
42
43     def __init__(self, robot_client):
44         '''
45         @type robot_client: hironx_ros_bridge.hironx_client.HIRONX
46         '''
47         self._robotclient = robot_client
48
49     def go_initpos(self):
50         self._robotclient.goInitial()
51
52     def set_joint_angles(self, joint_group, joint_angles, msg_tasktitle=None,
53                          task_duration=7.0, do_wait=True):
54         '''
55         @see: AbstAcceptanceTest.set_joint_angles
56         '''
57         print("== RTM; {} ==".format(msg_tasktitle))
58         self._robotclient.setJointAnglesOfGroup(
59                          joint_group, joint_angles, task_duration, do_wait)
60
61     def set_pose(self, joint_group, pose, rpy, msg_tasktitle,
62                       task_duration=7.0, do_wait=True, ref_frame_name=None):
63
64         print("== RTM; {} ==".format(msg_tasktitle))
65         self._robotclient.setTargetPose(joint_group, pose, rpy, task_duration,
66                                         ref_frame_name)
67         if do_wait:
68             self._robotclient.waitInterpolationOfGroup(joint_group)
69
70     def set_pose_relative(
71                         self, joint_group, dx=0, dy=0, dz=0, dr=0, dp=0, dw=0,
72                         msg_tasktitle=None, task_duration=7.0, do_wait=True):
73         if joint_group == Constant.GRNAME_LEFT_ARM:
74             eef = 'LARM_JOINT5'
75         elif joint_group == Constant.GRNAME_RIGHT_ARM:
76             eef = 'RARM_JOINT5'
77
78         print("== RTM; {} ==".format(msg_tasktitle))
79         self._robotclient.setTargetPoseRelative(
80                                     joint_group, eef, dx, dy, dz, dr, dp, dw,
81                                     task_duration, do_wait)
82
83     def _run_tests_hrpsys(self):
84         '''
85         @deprecated: This method remains as a reference. This used to function
86                      when being called directly from ipython commandline and
87                      now replaced by optimized codes.
88         '''
89         _TIME_SETTARGETP_L = 3
90         _TIME_SETTARGETP_R = 2
91         _TIME_BW_TESTS = 5
92
93         self.robot.goInitial()
94
95         # === TASK-1 ===
96         # L arm setTargetPose
97         _POS_L_INIT = self.robot.getCurrentPosition('LARM_JOINT5')
98         _POS_L_INIT[2] += 0.8
99         _RPY_L_INIT = self.robot.getCurrentRPY('LARM_JOINT5')
100         self.robot.setTargetPose('larm', _POS_L_INIT, _RPY_L_INIT, _TIME_SETTARGETP_L)
101         self.robot.waitInterpolationOfGroup('larm')
102
103         # R arm setTargetPose
104         _POS_R_INIT = self.robot.getCurrentPosition('RARM_JOINT5')
105         _POS_R_INIT[2] -= 0.07
106         _RPY_R_INIT = self.robot.getCurrentRPY('RARM_JOINT5')
107         self.robot.setTargetPose('rarm', _POS_R_INIT, _RPY_R_INIT, _TIME_SETTARGETP_R)
108         self.robot.waitInterpolationOfGroup('rarm')
109         time.sleep(_TIME_BW_TESTS)
110
111         # === TASK-2 ===
112         self.robot.goInitial()
113         # Both arm setTargetPose
114         _Z_SETTARGETP_L = 0.08
115         _Z_SETTARGETP_R = 0.08
116         self.robot.setTargetPoseRelative('larm', 'LARM_JOINT5',
117                                          dz=_Z_SETTARGETP_L,
118                                          tm=_TIME_SETTARGETP_L, wait=False)
119         self.robot.setTargetPoseRelative('rarm', 'RARM_JOINT5',
120                                          dz=_Z_SETTARGETP_R,
121                                          tm=_TIME_SETTARGETP_R, wait=False)
122
123         # === TASK-3 ===
124         # Head toward down
125         _TIME_HEAD = 5
126         self.robot.setTargetPoseRelative('head', 'HEAD_JOINT0', dp=0.1, tm=_TIME_HEAD)
127         self.robot.waitInterpolationOfGroup('head')
128         # Head toward up
129         self.robot.setTargetPoseRelative('head', 'HEAD_JOINT0', dp=-0.2, tm=_TIME_HEAD)
130         self.robot.waitInterpolationOfGroup('head')
131         # See left by position
132         self.robot.setJointAnglesOfGroup('head', [50, 10], 2, wait=True)
133         # See right by position
134         self.robot.setJointAnglesOfGroup('head', [-50, -10], 2, wait=True)
135         # Set back face to the starting pose w/o wait.
136         self.robot.setJointAnglesOfGroup( 'head', [0, 0], 2, wait=False)
137
138         # === TASK-4 ===
139         # 0.1mm increment is not working for some reason.
140         self.robot.goInitial()
141         # Move by iterating 0.1mm at cartesian space
142         _TIME_CARTESIAN = 0.1
143         _INCREMENT_MIN = 0.0001
144         for i in range(300):
145             self.robot.setTargetPoseRelative('larm', 'LARM_JOINT5',
146                                              dy=_INCREMENT_MIN,
147                                              tm=_TIME_CARTESIAN)
148             self.robot.setTargetPoseRelative('rarm', 'RARM_JOINT5',
149                                              dy=_INCREMENT_MIN,
150                                              tm=_TIME_CARTESIAN)
151             print('{}th move'.format(i))
152
153         self.robot.goInitial()
154         # === TASK-5 ===
155         # Turn torso
156         _TORSO_ANGLE = 120
157         _TIME_TORSO_R = 7
158         self.robot.setJointAnglesOfGroup('torso', [_TORSO_ANGLE], _TIME_TORSO_R, wait=True)
159         self.robot.waitInterpolationOfGroup('torso')
160         self.robot.setJointAnglesOfGroup('torso', [-_TORSO_ANGLE], 10, wait=True)
161  
162         self.robot.goInitial()
163
164         # === TASK-6.1 ===
165         # Overwrite previous command, for torso using setJointAnglesOfGroup
166         self.robot.setJointAnglesOfGroup('torso', [_TORSO_ANGLE], _TIME_TORSO_R,
167                                          wait=False)
168         time.sleep(1)
169         self.robot.setJointAnglesOfGroup('torso', [-_TORSO_ANGLE], 10, wait=True)
170
171         self.robot.goInitial(5)
172
173         # === TASK-6.2 ===
174         # Overwrite previous command, for arms using setTargetPose
175         _X_EEF_OVERWRITE = 0.05
176         _Z_EEF_OVERWRITE = 0.1
177         _TIME_EEF_OVERWRITE = 7
178         _POS_L_INIT[0] += _X_EEF_OVERWRITE
179         _POS_L_INIT[2] += _Z_EEF_OVERWRITE
180         self.robot.setTargetPose('larm', _POS_L_INIT, _RPY_L_INIT, _TIME_EEF_OVERWRITE)
181         self.robot.waitInterpolationOfGroup('larm')
182         # Trying to raise rarm to the same level of larm.
183         _POS_R_INIT[0] += _X_EEF_OVERWRITE
184         _POS_R_INIT[2] += _Z_EEF_OVERWRITE
185         self.robot.setTargetPose('rarm', _POS_R_INIT, _RPY_R_INIT, _TIME_EEF_OVERWRITE)
186         self.robot.waitInterpolationOfGroup('rarm')
187         time.sleep(3)
188         # Stop rarm
189         self.robot.clearOfGroup('rarm')  # Movement should stop here.
190
191         # === TASK-7.1 ===
192         # Cover wide workspace.
193         _TIME_COVER_WORKSPACE = 3
194         # Close to the max width the robot can spread arms with the hand kept
195         # at table level.
196         _POS_L_X_NEAR_Y_FAR = [0.32552812002303166, 0.47428609880442024, 1.0376656470275407]
197         _RPY_L_X_NEAR_Y_FAR = (-3.07491977663752, -1.5690249316560323, 3.074732073335767)
198         _POS_R_X_NEAR_Y_FAR = [0.32556456455769633, -0.47239119592815987, 1.0476131608682244]
199         _RPY_R_X_NEAR_Y_FAR = (3.072515432213872, -1.5690200270375372, -3.072326882451363)
200
201         # Close to the farthest distance the robot can reach, with the hand kept
202         # at table level.
203         _POS_L_X_FAR_Y_FAR = [0.47548142379781055, 0.17430276793604782, 1.0376878025614884]
204         _RPY_L_X_FAR_Y_FAR = (-3.075954857224205, -1.5690261926181046, 3.0757659493049574)
205         _POS_R_X_FAR_Y_FAR = [0.4755337947019357, -0.17242322190721648, 1.0476395479774052]
206         _RPY_R_X_FAR_Y_FAR = (3.0715850722714944, -1.5690204449882248, -3.071395243174742)
207         self.robot.setTargetPose('larm', _POS_L_X_NEAR_Y_FAR, _RPY_L_X_NEAR_Y_FAR, _TIME_COVER_WORKSPACE)
208         self.robot.setTargetPose('rarm', _POS_R_X_NEAR_Y_FAR, _RPY_R_X_NEAR_Y_FAR, _TIME_COVER_WORKSPACE)
209         self.robot.waitInterpolationOfGroup('larm')
210         self.robot.waitInterpolationOfGroup('rarm')
211         time.sleep(3)
212         self.robot.setTargetPose('larm', _POS_L_X_FAR_Y_FAR, _RPY_L_X_FAR_Y_FAR, _TIME_COVER_WORKSPACE)
213         self.robot.setTargetPose('rarm', _POS_R_X_FAR_Y_FAR, _RPY_R_X_FAR_Y_FAR, _TIME_COVER_WORKSPACE)
214         self.robot.waitInterpolationOfGroup('larm')
215         self.robot.waitInterpolationOfGroup('rarm')
216
217         self.robot.goInitial()

タスクの内容を少し見てみます.

37 from hironx_ros_bridge.constant import Constant
38 from hironx_ros_bridge.testutil.abst_acceptancetest import AbstAcceptanceTest

RTM API の HIRONX もしくは NextageClient クラスがインポートされていません.しかし,次の部分を見ると...

43 def __init__(self, robot_client):
44     '''
45     @type robot_client: hironx_ros_bridge.hironx_client.HIRONX
46     '''
47     self._robotclient = robot_client

コンストラクタが HIRONX を受け取り,メンバー self._robotclient に格納していることが分ります. 以後は self._robotclient が HIRONX クラスインスタンスを参照することとなります.

コードの残りの部分は基本的な操作を行っているだけです.

例えば次のような部分です. 簡潔に全ての関節が初期姿勢になるように記述されています.

49 def go_initpos(self):
50     self._robotclient.goInitial()
後々,自分のコードを開発してゆくうちに, 各クラスとメソッドの API ドキュメントを見てオプションを知りたくなるかもしれません. このチュートリアルでは詳しく説明しませんので,ぜひご自身で探索してみてください. 例えば goInitial メソッドには次のオプションがあります.
def hironx_ros_bridge.hironx_client.HIRONX.goInitial(self, tm = 7, wait = True, init_pose_type = 0)

更に例を挙げると次の部分です.

52     def set_joint_angles(self, joint_group, joint_angles, msg_tasktitle=None,
53                          task_duration=7.0, do_wait=True):
54         '''
55         @see: AbstAcceptanceTest.set_joint_angles
56         '''
57         print("== RTM; {} ==".format(msg_tasktitle))
58         self._robotclient.setJointAnglesOfGroup(
59                          joint_group, joint_angles, task_duration, do_wait)

こちらも簡潔に HIRONX のメソッドを呼び出すだけで, 多くのことがすでに HIRONX 内で処理されているので, このような簡単な方法でコードを書くことができます.

サンプルコードの実行

新しい iPython ターミナルを開きます. 少なくとも3つのターミナルが開いた状態になっています. 次のように iPython ターミナルを起動します. シミュレーション環境の場合はそれに合わせて起動してください.

$ ipython -i `rospack find hironx_ros_bridge`/scripts/acceptancetest_hironx.py -- --host %HOSTNAME%

次のコマンドで RTM インタフェースを通じてタスクが実行されます.

  • 注意: ロボットが動きます.
IN [1]: acceptance.run_tests_rtm()

次のように記述することでタスクの逐次実行もできます.

IN [1]: acceptance.run_tests_rtm(do_wait_input=True)

iPython インタフェースを終了するときは Ctrl-d にてエスケーブします.

サンプルコード - 円を描く

ロボットのエンドエフェクタで円を描くサンプルコードを下記に示します.

  • 注意: 本コードを改変する場合はまずシミュレーションでその動作を確認してから実機で動作させてください.

[NEXTAGE OPEN] Robots hands drawing circles: https://www.youtube.com/watch?v=OVae1xa5Rak

変数 robot は何らかの方法でユーザの HIRONX/NextageClient クラスのインスタンスに置き換える必要があります.

def circle_eef(radius=0.01, eef='larm', step_degree=5, ccw=True, duration=0.1):
    '''
    Moves the designated eef point-by-point so that the trajectory as a whole draws a circle.

    Currently this only works on the Y-Z plane of *ARM_JOINT5 joint.
    And it's the most intuitive when eef maintains a "goInitial" pose where circle gets drawn on robot's X-Y plane
    (see the wiki for the robot's coordinate if you're confused http://wiki.ros.org/rtmros_nextage/Tutorials/Programming#HiroNXO_3D_model_coordination).

    Points on the circular trajectory is based on a standard equation https://en.wikipedia.org/wiki/Circle#Equations

    @param radius: (Unit: meter) Radius of the circle to be drawn.
    @param step_degree: Angle in degree each iteration increments.
    @param ccw: counter clock-wise.
    @param duration: Time for each iteration to be completed.
    '''
    goal_deg = GOAL_DEGREE = 360
    start_deg = 0
    if eef == 'larm':
        joint_eef = 'LARM_JOINT5'
    elif eef == 'rarm':
        joint_eef = 'RARM_JOINT5'
    eef_pos = robot.getCurrentPosition(joint_eef)
    eef_rpy = robot.getCurrentRPY(joint_eef)
    print('eef_pos={}'.format(eef_pos))
    X0 = eef_pos[0]
    Y0 = eef_pos[1]
    ORIGIN_x = X0
    ORIGIN_y = Y0 - radius
    print('ORIGIN_x={} ORIGIN_y={}'.format(ORIGIN_x, ORIGIN_y))
    i = 0
    for theta in range(start_deg, goal_deg, step_degree):
        if not ccw:
            theta = -theta
        x = ORIGIN_x + radius*math.sin(math.radians(theta))  # x-axis in robot's eef space is y in x-y graph
        y = ORIGIN_y + radius*math.cos(math.radians(theta))
        eef_pos[0] = x
        eef_pos[1] = y
        print('#{}th theta={} x={} y={} X0={} Y0={}'.format(i, theta, x, y, X0, Y0))
        robot.setTargetPose(eef, eef_pos, eef_rpy, duration)
        robot.waitInterpolation()
        i += 1
使用場面に応じたプログラミング

デフォルトではいくつかの HIRONX / NextageClient クラスのコマンドは 動作終了を待ってから次の動作を開始します. またいくつかのコマンドはそのように待たないものもあります. それは各メソッドが受け取る引数から明確になる実装に依存します. wait 引数を持つメソッドは待機するかどうかを指定できます. それ以外のものは特に API ドキュメントに記述の無いかぎり待機の可否を指定できません.

次は “待機” の特性を持つメソッドの例です.

70     def set_pose_relative(
71                         self, joint_group, dx=0, dy=0, dz=0, dr=0, dp=0, dw=0,
72                         msg_tasktitle=None, task_duration=7.0, do_wait=True):
73         if joint_group == Constant.GRNAME_LEFT_ARM:
74             eef = 'LARM_JOINT5'
75         elif joint_group == Constant.GRNAME_RIGHT_ARM:
76             eef = 'RARM_JOINT5'
77
78         print("== RTM; {} ==".format(msg_tasktitle))
79         self._robotclient.setTargetPoseRelative(
80                                     joint_group, eef, dx, dy, dz, dr, dp, dw,
81                                     task_duration, do_wait)

一般にはデフォルトでは wait=True とした方が安全となるでしょう.

次の setTargetPose は待機・中断の信号は受け取りません. この場合においては HrpsysConfiguratorwaitInterpolationOfGroup() を呼び出して対処することができます. HrpSysConfiguratorHIRONX の親クラスです.

61     def set_pose(self, joint_group, pose, rpy, msg_tasktitle,
62                       task_duration=7.0, do_wait=True, ref_frame_name=None):
63
64         print("== RTM; {} ==".format(msg_tasktitle))
65         self._robotclient.setTargetPose(joint_group, pose, rpy, task_duration,
66                                         ref_frame_name)
67         if do_wait:
68             self._robotclient.waitInterpolationOfGroup(joint_group)
連続した軌道座標指示による動作

次のようにすることで連続した軌道座標指示による動作を行うことができます.

hcf.playPatternOfGroup('LARM',
                        [[0.010,0.0,-1.745,-0.265,0.164,0.06],
                         [0.010,-0.2,-2.045,-0.265,0.164,0.06],
                         [0.010,-0.4,-2.245,-0.265,0.164,0.06],
                         [0.010,-0.6,-2.445,-0.265,0.164,0.06],
                         [0.010,-0.8,-2.645,-0.265,0.164,0.06]],
                        [1,1,1,1,1])

hcfrobot など Python インタフェースを起動した状況に応じて変更してください.

hrpsys 315.6.0 以降では setJointAnglesSequenceOfGroup も利用することができます.

相対姿勢指示による動作

エンドエフェクタのフレームや関節を現在の姿勢からの相対姿勢を指示して動作させるのも HIRONX インタフェースの setTargetPoseRelative を用いることで簡単に行うことができます.

下記の [1] では torso を 3[s] かけて 0.1[rad] 回転させます. [2] では右腕のエンドエフェクタフレームを 3[s] かけて前方に 0.1[m] 移動させます.

In [1]: robot.setTargetPoseRelative('torso', 'CHEST_JOINT0', dw=0.1, tm=3)

In [2]: robot.setTargetPoseRelative('rarm', 'RARM_JOINT5', dx=0.1, tm=3)
ユーザ Python コードの作成

ここまでの RTM Python インタフェースのチュートリアルでは hironx.pynextage.py といったスクリプトを実行していました.

クライアントインタフェースクラスのメソッドを利用して アプリケーションモジュールを作成する方法を考えてみます. そのモジュールを your_nxo_sample.py と名付け, それをシミュレーションなり実機なりで動作させるものとします.

  1. まず1つの方法として nextage.py を複製して NextageClient クラスがインスタンス化される行を書き換えます. 他の NextageClient クラスがアクセスする部分も消したり書き換えたりします.
  2. 上記の方法はあまりスマートで簡潔ではありません. それは大部分のコードを複製するというのはソフトウェア開発においては悪い実践法であるからです. 例えば hrpsys_tools/hrpsys_tools_config.py は この目的に役立つことを意図して作られています.

デジタルI/O の利用(NEXTAGE OPEN)

本項はデジタルI/O(DIO)が備わっている NEXTAGE OPEN でのみ有効です.

DIO 操作で利用できるメソッドの全体については利用可能なメソッドが集約されている API ドキュメント, 特にデフォルトで利用可能なメソッドが集められている NextageClient クラスを参照してください。

RTM クライアントからのインタラクティブな操作

まず RTM クライアントを実行し,ネームサーバー内部(つまり QNX コントローラボックス上)のロボットに接続する必要があります.

$ ipython -i `rospack find nextage_ros_bridge`/script/nextage.py -- --host %HOST%

そうすると _hands オブジェクトから全ての DIO メソッドにアクセスすることができます. iPython ターミナルで表示させると次のようになります.

In [6]: robot._hands.h
robot._hands.handlight_both     robot._hands.handtool_l_attach  robot._hands.handtool_r_eject
robot._hands.handlight_l        robot._hands.handtool_l_eject   
robot._hands.handlight_r        robot._hands.handtool_r_attach  

In [6]: robot._hands.g
robot._hands.gripper_l_close  robot._hands.gripper_r_close  
robot._hands.gripper_l_open   robot._hands.gripper_r_open   

In [6]: robot._hands.a
robot._hands.airhand_l_drawin   robot._hands.airhand_l_release  robot._hands.airhand_r_keep
robot._hands.airhand_l_keep     robot._hands.airhand_r_drawin   robot._hands.airhand_r_release

ハンドツールを接続・取り外しする場合は次のように行います.

他のよく使われるメソッドについてはその名前自体から大体の機能は類推できるかと思います.

  • グリッパの開閉: gripper_l_open, gripper_r_open, gripper_l_close, gripper_r_close
  • ライトの点灯等: handlight_both, handlight_l, handlight_r

サンプルとしてハンドDIOのシステムテストツールも参考にしてください.

2014年8月よりも前に出荷された NEXTAGE OPEN での DIO の利用

2014年8月よりも前に出荷された NEXTAGE OPEN で DIO を利用する場合は DIO のバージョンを指定する必要があります.

$ ipython -i `rospack find nextage_ros_bridge`/script/nextage.py -- --host %HOST% --dio_ver 0.4.2

NextageClient インスタンスに DIO を指定する場合には set_hand_version メソッドにて引数 0.4.2 を与えて指定してください. このバージョン番号は固定で,変更する必要はありません.

ROS Python インタフェース

インタラクティブモードでの操作

rtm_ros_bridge の起動

rtm_ros_bridge を起動します.シミュレーションンの場合は不要です.

$ roslaunch hironx_ros_bridge hironx_ros_bridge_real.launch nameserver:=%HOSTNAME%    (HIRO)

$ roslaunch nextage_ros_bridge nextage_ros_bridge_real.launch nameserver:=%HOSTNAME%  (NEXTAGE OPEN)

ROSのノードが動作しているかを確認してみます.

$ rosnode list
/diagnostic_aggregator
/hrpsys_profile
/hrpsys_ros_diagnostics
/hrpsys_state_publisher
/rosout
iPython ターミナルの実行

インタラクティブにロボットを操作できるように iPython インタラクティブコンソールを実行します.

$ ipython -i `rospack find hironx_ros_bridge`/scripts/hironx.py -- --host hiro014     (HIRO)

$ ipython -i `rospack find nextage_ros_bridge`/script/nextage.py -- --host nextage101  (NEXTAGE OPEN)

:  (same initialization step as simulation)

[hrpsys.py]  initialized successfully

シミュレーションの場合は引数は不要です.

$ ipython -i `rospack find hironx_ros_bridge`/scripts/hironx.py

$ ipython -i `rospack find nextage_ros_bridge`/script/nextage.py

これらは RTM インタフェースの時と同じです. つまり hironx.pynextage.py を実行したときに それらが RTM クライアントと ROS クライアントの両方を起動るということです.

iPython コンソールを起動したときに次のようなエラーが出ることがあります. これは MoveIt! のサービスが起動していないという内容のエラーです. ROS クライアントメソッドのいくつかは MoveIt! が無くても動作しますので ここではこのエラーを無視しても大丈夫です.

:
[hrpsys.py]  initialized successfully
[INFO] [WallTime: 1410918126.749067] [206.795000] Joint names; Torso: ['CHEST_JOINT0']
        Head: ['HEAD_JOINT0', 'HEAD_JOINT1']
        L-Arm: ['LARM_JOINT0', 'LARM_JOINT1', 'LARM_JOINT2', 'LARM_JOINT3', 'LARM_JOINT4', 'LARM_JOINT5']
        R-Arm: ['RARM_JOINT0', 'RARM_JOINT1', 'RARM_JOINT2', 'RARM_JOINT3', 'RARM_JOINT4', 'RARM_JOINT5']
[ERROR] [1410918130.900627643, 210.289999999]: Robot semantic description not found. Did you forget to define or remap '/robot_description_semantic'?
[ INFO] [1410918130.900757547, 210.289999999]: Loading robot model 'HiroNX'...
[ INFO] [1410918130.900794422, 210.289999999]: No root joint specified. Assuming fixed joint
[FATAL] [1410918131.991522557, 211.249999999]: Group 'left_arm' was not found.
[ERROR] [WallTime: 1410918132.006310] [211.285000] Group 'left_arm' was not found.
Make sure you've launched MoveGroup (e.g. by launching moveit_planning_execution.launch)
ROS Python インタフェースコマンド

実行している iPython コンソールから ROS Python インタフェースのコマンドを見てみます. 次のように ros. と入力し TAB キーを押します.

In [1]: ros.
ros.Joint                        ros.get_joint                    ros.go_init
ros.Link                         ros.get_joint_names              ros.go_offpose
ros.get_current_state            ros.get_link                     ros.has_group
ros.get_current_variable_values  ros.get_link_names               ros.set_joint_angles_deg
ros.get_default_owner_group      ros.get_planning_frame           ros.set_joint_angles_rad
ros.get_group                    ros.get_root_link                ros.set_pose
ros.get_group_names              ros.goInitial             

ROS_Clientros というオブジェクトから利用することができます.

ROS_Client は MoveIt! から RobotCommander を継承するしていて, その派生クラスから多くのメソッドが上記のように見えているのです. 加えて goInitial,go_init, go_offpose, set_pose などの ROS_Client 用にいくつかのメソッドが実装されています. これらのメソッドはすべて RTM バージョン の Hironx/NEXTAGE OPEN Python インターフェイスと同等のものです.

次のコマンドでロボットの全関節が初期姿勢に移行します.

  • 注意-1: コマンドを実行するとロボットが動きます.
  • 注意-2: 緊急停止スイッチをいつでも押せる状態にしておいてください.
In [1]: ros.go_init()
[INFO] [WallTime: 1410918153.591171] [226.790000] *** go_init begins ***
[INFO] [WallTime: 1410918165.419528] [233.825000] wait_for_result for the joint group rarm = True
[INFO] [WallTime: 1410918165.423372] [233.825000] [positions: [0.010471975511965976, 0.0, -1.7453292519943295, -0.26529004630313807, 0.16406094968746698, -0.05585053606381855]
velocities: []
accelerations: []
effort: []
time_from_start:
  secs: 7
  nsecs: 0]

iPython の機能を使うと RTM の場合と同様に各 ROS Python インタフェースコマンドのAPIドキュメントを見ることができます.

In [2]: ros.go_init?
Type:       instancemethod
Base Class: <type 'instancemethod'>
String Form:<bound method ROS_Client.go_init of <hironx_ros_bridge.ros_client.ROS_Client object at 0x49b7210>>
Namespace:  Interactive
File:       /home/rosnoodle/cws_hiro_nxo/src/start-jsk/rtmros_hironx/hironx_ros_bridge/src/hironx_ros_bridge/ros_client.py
Definition: ros.go_init(self, task_duration=7.0)
Docstring:
Init positions are taken from HIRONX.
TODO: Need to add factory position too that's so convenient when
      working with the manufacturer.
@type task_duration: float

RTM Python インタフェースとの互換性により, 同等の機能を持つメソッドの名前が違っていても(goInitial)同じでもどちらでも利用することができます.

IN [3]: ros.goInitial?
Type:       instancemethod
String Form:<bound method ROS_Client.goInitial of <hironx_ros_bridge.ros_client.ROS_Client object at 0x7f23458f0c50>>
File:       /home/n130s/link/ROS/indigo_trusty/cws_hironxo/src/start-jsk/rtmros_hironx/hironx_ros_bridge/src/hironx_ros_bridge/ros_client.py
Definition: ros.goInitial(self, init_pose_type=0, task_duration=7.0)
Docstring:
This method internally calls self.go_init.

This method exists solely because of compatibility purpose with
hironx_ros_bridge.hironx_client.HIRONX.goInitial, which
holds a method "goInitial".

@param init_pose_type:
       0: default init pose (specified as _InitialPose)
       1: factory init pose (specified as _InitialPose_Factory)

ROS Python インタフェースプログラミング

サンプルコード - Acceptance Test (ROS)

“RTM Python インタフェースプログラミング” の “サンプルコード - Acceptance Test (RTM)” におけるプログラムと同様のサンプルコードを見てみます. このプログラムは RTM クライアントで行ったことと同じタスクを ROS クライアントで行っています.

acceptancetest_ros.py

ROS の Python サンプルコード acceptancetest_ros.py は次のようになっています.

1 # -*- coding: utf-8 -*-
2
3 # Software License Agreement (BSD License)
4 #
5 # Copyright (c) 2014, TORK (Tokyo Opensource Robotics Kyokai Association)
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
10 # are met:
11 #
12 #  * Redistributions of source code must retain the above copyright
13 #    notice, this list of conditions and the following disclaimer.
14 #  * Redistributions in binary form must reproduce the above
15 #    copyright notice, this list of conditions and the following
16 #    disclaimer in the documentation and/or other materials provided
17 #    with the distribution.
18 #  * Neither the name of TORK. nor the
19 #    names of its contributors may be used to endorse or promote products
20 #    derived from this software without specific prior written permission.
21 #
22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 # POSSIBILITY OF SUCH DAMAGE.
34
35 import rospy
36
37 from hironx_ros_bridge.testutil.abst_acceptancetest import AbstAcceptanceTest
38
39
40 class AcceptanceTestROS(AbstAcceptanceTest):
41
42     def __init__(self, robot_client):
43         '''
44         @type robot_client: hironx_ros_bridge.ros_client.ROS_Client
45         '''
46         self._robotclient = robot_client
47
48     def go_initpos(self, default_task_duration=7.0):
49         self._robotclient.go_init(default_task_duration)
50
51     def set_joint_angles(self, joint_group, joint_angles, msg_tasktitle=None,
52                          task_duration=7.0, do_wait=True):
53         '''
54         @see: AbstAcceptanceTest.move_armbyarm_impl
55         '''
56         rospy.loginfo("'''{}'''".format(msg_tasktitle))
57         self._robotclient.set_joint_angles_deg(
58                          joint_group, joint_angles, task_duration, do_wait)
59
60     def set_pose(self, joint_group, posision, rpy, msg_tasktitle=None,
61                  task_duration=7.0, do_wait=True, ref_frame_name=None):
62         '''
63         @see: AbstAcceptanceTest.set_pose
64         '''
65         rospy.loginfo('ROS {}'.format(msg_tasktitle))
66         self._robotclient.set_pose(joint_group, posision, rpy, task_duration,
67                                    do_wait, ref_frame_name)
68
69     def set_pose_relative(
70                         self, joint_group, dx=0, dy=0, dz=0, dr=0, dp=0, dw=0,
71                         msg_tasktitle=None, task_duration=7.0, do_wait=True):
72         rospy.logerr('AcceptanceTestROS; set_pose_relative is not implemented yet')
73         pass

最初に ROS_Client クラスのインスタンス化が必要ですが, それは上記のコードの外側,acceptancetest_hironx.py 内で次のように行われています.

self._robotclient = ROS_Client()

上記のコードに戻ります. 初期姿勢へ動くのは簡潔に1行で書かれています.

48     def go_initpos(self, default_task_duration=7.0):
49         self._robotclient.go_init(default_task_duration)

各関節を角度指示を与えるのも1行で書かれています.

51 def set_joint_angles(self, joint_group, joint_angles, msg_tasktitle=None, task_duration=7.0, do_wait=True):
:
57    self._robotclient.set_joint_angles_deg(joint_group, joint_angles, task_duration, do_wait)

直交座標系における姿勢指示も行うことができます. (このコマンドでは MoveIt! サービスを利用しています)

60 def set_pose(self, joint_group, posision, rpy, msg_tasktitle=None, task_duration=7.0, do_wait=True, ref_frame_name=None):
:
66     self._robotclient.set_pose(joint_group, posision, rpy, task_duration, do_wait, ref_frame_name)
サンプルコードの実行

iPython コンソールから ROS クライアントを利用した acceptance.run_tests_ros() を実行して どのように動作しているかを確認してみます.

  • 注意: ロボットが動きます.
$ ipython -i `rospack find hironx_ros_bridge`/scripts/acceptancetest_hironx.py -- --host %HOSTNAME%
:
IN [1]: acceptance.run_tests_ros()

チュートリアル: MoveIt! GUI

MoveIt! - ROS の動作計画ソフトウェア

ROS の動作計画アプリケーションの代表的なソフトウェアパッケージが MoveIt! です. HIRO / NEXTAGE OPEN においても rtm_ros_bridge がありますので MoveIt! の動作計画機能を利用することができます.

MoveIt! GUI の操作

基本的な操作は “クイックスタート” の “MoveIt! での動作の実行” を参照してください.

左腕・両腕の動作計画への切り替え

HIRO / NEXTAGE OPEN において MoveIt! を起動したときは デフォルトで右手の InteractiveMarker が操作できる状態になっています. これを左手を操作して左腕の動作計画を作成するには,次の手順にて MoveIt! の設定を変更します.

  1. 子ウィンドウ Displays 内の MotionPlanning → Plannning Request → Planning Group で left_arm を選択
  2. 右手と同様に InteractiveMarker を操作して [ Plan and Execute ]

MoveIt! - left_arm

HIRO / NEXTAGE OPEN の MoveIt! では両腕での動作計画もできます.
MoveIt! の GUI: RViz 上では 次のように選択します.

  1. Displays 内の MotionPlanning → Plannning Request → Planning Group で botharms を選択
  2. 各腕の InteractiveMarker を操作して [ Plan and Execute ]

MoveIt! RViz - Both Arms

動作計画のループアニメーションと軌跡の表示

MoveIt! にて動作計画が作成されたときにはその動作アニメーションが1度表示されます. 動作計画をより確認しやすくするためにその動作アニメーションの繰り返し表示や その軌跡表示を行うことができます.

次の手順で MoveIt! の設定を変更します.

  1. 子ウィンドウ Displays 内の MotionPlanning → Planning Path → Loop Animation のチェックボックスを ON
  2. Loop Animation のすぐ下にある Show Trail のチェックボックスを ON

MoveIt! でのアニメーションと軌跡の表示の様子(画像はアニメーションの途中をキャプチャ)

MoveIt! - Loop Animaiton / Show Trail

動作計画上の各経過姿勢の確認

ループアニメーションでは確認しづらい動作計画上の任意の経過点の姿勢を確認するには MotionPlanning - Slider を使います.

  1. MoveIt! のメニュー Panels 内の MotionPlanning - Slider を選択
  2. 動作計画を作成した後にスライダを操作して経過点の姿勢を表示

MoveIt! - MotionPlanning - Slider

実行動作の速度・加速度の調整

MotionPlanning 内 Planning タブにある Options の

  • Velocity Scalling: 1.00
  • Acceleration Scalling: 1.00

の値を 0〜1 の間で変更して動作の実行速度と加速度をそれぞれ動的に変更することができます.

MoveIt! - Velocity Scalling

逆運動学解のない姿勢での最善解の利用

デフォルトの設定では逆運動学解のない姿勢まで InteractiveMarker を動かすと エンドエフェクタ(手先など)は解の存在するところで止まってしまいます.

MotionPlanning 子ウィンドウの Context タブ内の Kinematics 下 Allow Approximate IK Solutions のチェックを入れると, 厳密に逆運動学解がない姿勢でもそれに近い最善の解が利用されます.

MoveIt! - Allow Approximate IK Solutions

ジョイスティクの利用

HIRO / NEXTAGE OPEN にてジョイスティックを利用するためには moveit_ros_visualization パッケージがインストールされてる必要があります.

moveit_ros_visualization パッケージがインストールされていない場合には 下記手順によりインストールしてください.

$ sudo apt-get install ros-indigo-moveit-ros-visualization
OR
$ sudo apt-get install ros-indigo-moveit    # This is simpler and better

ジョイスティックのプロセスを起動するには次のコマンドを実行してください.

$ roslaunch hironx_moveit_config joystick_control.launch
OR
$ roslaunch nextage_moveit_config joystick_control.launch

NEXTAGE OPEN での IKFast の利用

NEXTAGE_OPEN では IKFast パッケージを利用することができます. MoveIt! を実行するときに引数として運動学の設定を IKFast を利用するように指定します.

$ roslaunch nextage_moveit_config moveit_planning_execution.launch kinematics_conf:=`rospack find nextage_moveit_config`/config/kinematics_ikfast.yaml

MoveIt! GUI での障害物を回避する動作計画

MoveIt! の最大の特徴の1つは障害物を回避するような動作が自動生成されるところにあります.

障害物モデルデータを利用した MoveIt! の動作計画

環境が既知とした場合に 障害物の 3D モデルデータのファイルを MoveIt! に読み込んで動作計画を行う方法を紹介します.

障害物モデルのシーンファイルの読み込み

MoveIt! 内に次の手順により障害物モデルファイルの読込みを行います.

  1. MotionPlanning 内 Scene Objects タブで [ Import From Text ] ボタンをクリック
  2. ファイル nxo_pillar.scene を選択して [ OK ] をクリック
  3. 必要であれば障害物の InteractiveMarker を操作してロボットの手の近くに移動
  4. Context タブで [ Publish Current Scene ] をクリック → 障害物がシーンに反映

Import From Text

MoveIt! - Import From Text

Publish Current Scene

MoveIt! - Publish Current Scene

シーンファイル nxo_pillar.scene の内容

nxo_pillar
* nxo_pillar
1
box
0.1 0.1 1.1
0.4 -0.2 -0.42
0 0 0 1
0 0 0 0
.
障害物を回避する動作計画とその実行

読み込んだ障害物モデルのある環境に応じた動作計画を行ってロボットを動かします.

障害物を回避する経路は自動的に生成されるので,動作計画を行う操作は障害物が無い場合と同じです. 次の操作で障害物を回避したモーションが自動生成されてロボットにその動作が出力されます.

  • 注意: ロボットが動きます.
  1. Planning タブ Querry の Select Start State: < current > で [ Update ] ボタンをクリック
  2. Planning タブ Querry の Select Goal State: < current > で [ Update ] ボタンをクリック
  3. MoveIt! 上の InteractiveMarker でロボットの手を現在の位置から障害物の反対側にドラッグ
  4. Planning タブ Commands の [ Plan and Execute ] ボタンをクリック

障害物を回避する動作計画の例

MoveIt! Planning with Pillar

障害物が無い場合の動作計画の例

MoveIt! Planning without Pillar

STL / COLLADA(DAE) モデルファイルの利用

MoveIt! には STL や COLLADA(DAE) モデルのファイルを直接読み込む機能もあります.
MoveIt! 内に次の手順により障害物モデルファイルの読込みを行います.

  1. MotionPlanning 内 Scene Objects タブで [ Import File ] ボタンをクリック
  2. ファイル tsubo.dae もしくは tsubo.stl を選択して [ OK ] をクリック
  3. 必要であれば障害物の InteractiveMarker を操作してロボットの手の近くに移動
  4. Context タブで [ Publish Current Scene ] をクリック → 障害物がシーンに反映

MoveIt! - Import DAE File (Adjusting Position)

STL / COLLADA(DAE) モデルファイルを用いた場合も シーンファイルを読み込んだ場合と同じ手順で MoveIt! で障害物を回避する動作計画を作成できます.

Motion Planning - tsubo.dae

MoveIt! tsubo.dae Motion Planning

チュートリアル: MoveIt! Python インタフェース

MoveIt! Commander

MoveIt! の動作計画などの機能は GUI(RViz)からの操作を提供しているだけではありません. そのプログラミングインタフェースである MoveIt! Commander も提供されていますので, プログラミング言語から MoveIt! の機能を利用してロボットを動かすこともできます.

MoveIt! Python インタフェース使用環境

MoveIt! の Python インタフェース MoveIt! Python Commander を使用するときの環境は次のようになっています.

  • rtm_ros_hironx: バージョン 1.1.4 以降( Hironx/Nextage Open )
  • 推奨プログラミングインタフェース: ROS_Client( RobotCommander から派生 )
  • RobotCommander は MoveIt! の Python プログラミングインターフェス

MoveIt! の Python インタフェースである moveit_commander パッケージが Ubuntu コンピュータ上にない場合はインストールする必要があります.

$ sudo apt-get install ros-%YOUR_ROS_DISTRO%-moveit-commander

MoveIt! Python インタフェースからのロボット操作

MoveIt! Python インタフェースの起動

rtm_ros_bridge の実行

rtm_ros_bridge を実行します.

$ roslaunch hironx_ros_bridge hironx_ros_bridge_real.launch nameserver:=%HOSTNAME%   (Hironx)

$ roslaunch nextage_ros_bridge nextage_ros_bridge_real.launch nameserver:=%HOSTNAME%   (NEXTAGE OPEN)
MoveIt! サーバの起動

ROS_Client クラスをベースにしたプログラムを実行するためには その “クライアント” に対する “サーバ” として MoveIt! のノードが走っている必要があります.

MoveIt! を起動します.

$ roslaunch hironx_moveit_config moveit_planning_execution.launch   (HIRO)

$ roslaunch nextage_moveit_config moveit_planning_execution.launch  (NEXTAGE OPEN)

Python での MoveIt! Commander を用いたロボット操作

Python から MoveIt! Commander を利用しているサンプルプログラムで, HIRO / NEXTAGE OPEN ロボットをどのように動作させているのかを見てみます.

サンプルコード全体を下に記載します.その後に各行について何をしているのかを見てみます.

1 #!/usr/bin/env python
2 ##########################################
3 # @file     nextage_moveit_sample.py     #
4 # @brief    Nextage Move it demo program #
5 # @author   Ryu Yamamoto                 #
6 # @date     2015/05/26                   #
7 ##########################################
8 import moveit_commander
9 import rospy
10 import geometry_msgs.msg
11
12 def main():
13         rospy.init_node("moveit_command_sender")
14
15         robot = moveit_commander.RobotCommander()
16     
17         print "=" * 10, " Robot Groups:"
18         print robot.get_group_names()
19
20         print "=" * 10, " Printing robot state"
21         print robot.get_current_state()
22         print "=" * 10
23
24         rarm = moveit_commander.MoveGroupCommander("right_arm")
25         larm = moveit_commander.MoveGroupCommander("left_arm")
26
27         print "=" * 15, " Right arm ", "=" * 15
28         print "=" * 10, " Reference frame: %s" % rarm.get_planning_frame()
29         print "=" * 10, " Reference frame: %s" % rarm.get_end_effector_link()
30     
31         print "=" * 15, " Left ight arm ", "=" * 15
32         print "=" * 10, " Reference frame: %s" % larm.get_planning_frame()
33         print "=" * 10, " Reference frame: %s" % larm.get_end_effector_link()
34
35         #Right Arm Initial Pose
36         rarm_initial_pose = rarm.get_current_pose().pose
37         print "=" * 10, " Printing Right Hand initial pose: "
38         print rarm_initial_pose
39
40         #Light Arm Initial Pose
41         larm_initial_pose = larm.get_current_pose().pose    
42         print "=" * 10, " Printing Left Hand initial pose: "
43         print larm_initial_pose
44
45         target_pose_r = geometry_msgs.msg.Pose()
46         target_pose_r.position.x = 0.325471850974-0.01
47         target_pose_r.position.y = -0.182271241593-0.3
48         target_pose_r.position.z = 0.0676272396419+0.3
49         target_pose_r.orientation.x = -0.000556712307053
50         target_pose_r.orientation.y = -0.706576742941
51         target_pose_r.orientation.z = -0.00102461782513
52         target_pose_r.orientation.w = 0.707635461636
53         rarm.set_pose_target(target_pose_r)
54
55         print "=" * 10," plan1 ..."
56         rarm.go()
57         rospy.sleep(1)
58         
59         target_pose_l = [
60                 target_pose_r.position.x,
61                 -target_pose_r.position.y,
62                 target_pose_r.position.z,
63                 target_pose_r.orientation.x,
64                 target_pose_r.orientation.y,
65                 target_pose_r.orientation.z,
66                 target_pose_r.orientation.w
67         ]
68         larm.set_pose_target(target_pose_l)
69
70         print "=" * 10," plan2 ..."
71         larm.go()
72         rospy.sleep(1)
73         
74         #Clear pose
75         rarm.clear_pose_targets()
76
77         #Right Hand
78         target_pose_r.position.x = 0.221486843301
79         target_pose_r.position.y = -0.0746407547512
80         target_pose_r.position.z = 0.642545484602
81         target_pose_r.orientation.x = 0.0669013615474
82         target_pose_r.orientation.y = -0.993519060661
83         target_pose_r.orientation.z = 0.00834224628291
84         target_pose_r.orientation.w = 0.0915122442864
85         rarm.set_pose_target(target_pose_r)
86         
87         print "=" * 10, " plan3..."
88         rarm.go()
89         rospy.sleep(1)
90
91         print "=" * 10,"Initial pose ..."
92         rarm.set_pose_target(rarm_initial_pose)
93         larm.set_pose_target(larm_initial_pose)
94         rarm.go()
95         larm.go()
96         rospy.sleep(2)
97         
98 if __name__ == '__main__':
99     try:
100         main()
101     except rospy.ROSInterruptException:
102         pass

動作計画と実行を行う Python スクリプトの主要な部分は

  1. エンドエフェクタのリンクの位置と姿勢をターゲットとして指定
  2. ターゲットの姿勢まで動作させる

という手順になります. また,そのための準備としてエンドエフェクタの姿勢などを取得しています.

Python スクリプトの各行を具体的に見ていきます.

MoveIt! の Python インタフェース は moveit_commander.MoveGroupCommander で提供されます.

import moveit_commander
import rospy
import geometry_msgs.msg

この Python スクリプトから MoveGroupCommander を使うために rospy.init_node() を呼び出して ROS ノードを実行します.

rospy.init_node("moveit_command_sender")

RobotCommander をインスタンス化します.

robot = moveit_commander.RobotCommander()

ロボットの全ての Group の名前のリストを取得,表示します.

print "=" * 10, " Robot Groups:"
print robot.get_group_names()

ロボット全体の状態を表示するとデバッグに役立ちます.

print "=" * 10, " Printing robot state"
print robot.get_current_state()

現在のロボットの各腕の姿勢を取得します.

rarm_current_pose = rarm.get_current_pose().pose
larm_current_pose = larm.get_current_pose().pose

初期姿勢オブジェクトに現在のロボットの各腕の姿勢を代入します.

rarm_initial_pose = rarm.get_current_pose().pose
print "=" * 10, " Printing Right Hand initial pose: "
print rarm_initial_pose

larm_initial_pose = larm.get_current_pose().pose
print "=" * 10, " Printing Left Hand initial pose: "
print larm_initial_pose

動作計画を行いロボットを動作させます.

Execution Plan 1

target_pose_r = geometry_msgs.msg.Pose()
target_pose_r.position.x = 0.325471850974-0.01
target_pose_r.position.y = -0.182271241593-0.3
target_pose_r.position.z = 0.0676272396419+0.3
target_pose_r.orientation.x = -0.000556712307053
target_pose_r.orientation.y = -0.706576742941
target_pose_r.orientation.z = -0.00102461782513
target_pose_r.orientation.w = 0.707635461636
rarm.set_pose_target(target_pose_r)

print "=" * 10," plan1 ..."
rarm.go()
rospy.sleep(1)

MoveIt! Commander - Execute Plan 1

Execution Plan 2

target_pose_l = [
    target_pose_r.position.x,
    -target_pose_r.position.y,
    target_pose_r.position.z,
    target_pose_r.orientation.x,
    target_pose_r.orientation.y,
    target_pose_r.orientation.z,
    target_pose_r.orientation.w
]
larm.set_pose_target(target_pose_l)

print "=" * 10," plan2 ..."
larm.go()
rospy.sleep(1)

MoveIt! Commander - Execute Plan 2

Execution Plan 3

rarm.clear_pose_targets()

#Right Hand
target_pose_r.position.x = 0.221486843301
target_pose_r.position.y = -0.0746407547512
target_pose_r.position.z = 0.642545484602
target_pose_r.orientation.x = 0.0669013615474
target_pose_r.orientation.y = -0.993519060661
target_pose_r.orientation.z = 0.00834224628291
target_pose_r.orientation.w = 0.0915122442864
rarm.set_pose_target(target_pose_r)

print "=" * 10, " plan3..."
rarm.go()
rospy.sleep(1)

MoveIt! Commander - Execute Plan 3

Go Initial

print "=" * 10,"Initial pose ..."
rarm.set_pose_target(rarm_initial_pose)
larm.set_pose_target(larm_initial_pose)
rarm.go()
larm.go()
rospy.sleep(2)

MoveIt! Commander - Go Initial

動画: “Nextage Move it! demo” https://www.youtube.com/watch?v=heKEKg3I7cQ

また,MoveIt! を表示している RViz 上でもこれらの動作計画が表示されます.

実際にサンプルプログラムを実行して, HIRO / NEXTAGE OPEN ロボットが動作計画どおりに動いているかを見てみます.

  • 注意: ロボットが動きます.

ターミナルから次のコマンドを実行してサンプルプログラムを実行します.

$ rosrun nextage_ros_bridge nextage_moveit_sample.py   (NEXTAGE OPEN)

MoveIt! Commander では更に様々なメソッドがありますので ROS Wiki の moveit_commander のページを見てください.

MoveIt! Python インタフェースでの両腕の動作計画

MoveIt! Python インタフェースでの両腕の動作計画を利用したサンプルコードを下記に記します.

両腕での動作計画サンプルコード

import moveit_commander
import rospy
from geometry_msgs.msg import Pose

def main():
        rospy.init_node("moveit_command_sender")

        botharms = moveit_commander.MoveGroupCommander("botharms")

        target_pose_r = Pose()
        target_pose_l = Pose()
        q = quaternion_from_euler(0, -math.pi/2,0)
        target_pose_r.position.x = 0.3
        target_pose_r.position.y = 0.1
        target_pose_r.position.z = 0.0
        target_pose_r.orientation.x = q[0]
        target_pose_r.orientation.y = q[1]
        target_pose_r.orientation.z = q[2]
        target_pose_r.orientation.w = q[3]
        target_pose_l.position.x = 0.3
        target_pose_l.position.y =-0.1
        target_pose_l.position.z = 0.3
        target_pose_l.orientation.x = q[0]
        target_pose_l.orientation.y = q[1]
        target_pose_l.orientation.z = q[2]
        target_pose_l.orientation.w = q[3]
        botharms.set_pose_target(target_pose_r, 'RARM_JOINT5_Link')
        botharms.set_pose_target(target_pose_l, 'LARM_JOINT5_Link')
        botharms.go()
        rospy.sleep(1)

        target_pose_r.position.x = 0.3
        target_pose_r.position.y =-0.2
        target_pose_r.position.z = 0.0
        target_pose_l.position.x = 0.3
        target_pose_l.position.y = 0.2
        target_pose_l.position.z = 0.0
        botharms.set_pose_target(target_pose_r, 'RARM_JOINT5_Link')
        botharms.set_pose_target(target_pose_l, 'LARM_JOINT5_Link')
        botharms.go()

その他の ROS_Client クラス利用法

もう少し詳しく ROS_Client クラスについて見てみます.

ここでは iPython コンソールを用います.

$ ipython -i `rospack find hironx_ros_bridge`/scripts/hironx.py                      (simulation)

$ ipython -i `rospack find hironx_ros_bridge`/scripts/hironx.py -- --host %HOSTNAME% (for real robot)

クライアントクラスの ros がインスタンス化されていることを確認します.

In [1]: ros                                                                                  
Out[1]: <hironx_ros_bridge.ros_client.ROS_Client at 0x7f0d34a00590>

初期姿勢にします.

In [2]: ros.go_init()                                                                        
[INFO] [WallTime: 1453005565.730952] [1923.265000] *** go_init begins ***                    
[INFO] [WallTime: 1453005572.749935] [1930.345000] wait_for_result for the joint group rarm = True                                                                                       
[INFO] [WallTime: 1453005572.750268] [1930.345000] [positions: [0.010471975511965976, 0.0, -1.7453292519943295, -0.26529004630313807, 0.16406094968746698, -0.05585053606381855]         
velocities: []                                                                               
accelerations: []                                                                            
effort: []                                                                                   
time_from_start:                                                                             
  secs: 7                                                                                    
  nsecs: 0]   

関節角度指令コマンド

ロボットの腕関節角度(ラジアン)の目標値指示による動作をさせるコマンドです.

In [3]: ros.set_joint_angles_rad('larm', [0.010, 0.0, -2.094, -0.265, 0.164, 0.06], duration=2, wait=True)                                                                                
[INFO] [WallTime: 1453005602.774010] [1960.440000] wait_for_result for the joint group larm = True                                                                                       

In [4]: ros.set_joint_angles_rad('larm', [0.010, 0.0, -1.745, -0.265, 0.164, 0.06], duration=2, wait=True)                                                                                
[INFO] [WallTime: 1453005606.789887] [1964.500000] wait_for_result for the joint group larm = True  
  • set_joint_angles_deg により “度/degree” 指示も可能です.
  • set_joint_angles_* メソッドは目標値の設定だけでなく,動作の実行も行います.

エンドエフェクタ姿勢指令コマンド

ロボットのエンドエフェクタ(EEF)姿勢の目標値指示による動作をさせるコマンドです.

IN [4]: ros.set_pose?
Type:       instancemethod
Definition: ros.set_pose(self, joint_group, position, rpy=None, task_duration=7.0, do_wait=True, ref_frame_name=None)
Docstring:
@deprecated: Use set_pose_target (from MoveGroupCommander) directly.
Accept pose defined by position and RPY in Cartesian format.

@type joint_group: str
@type position: [float]
@param position: x, y, z.
@type rpy: [float]
@param rpy: If None, keep the current orientation by using
            MoveGroupCommander.set_position_target. See:
             'http://moveit.ros.org/doxygen/' +
             'classmoveit__commander_1_1move__group_1_1' +
             'MoveGroupCommander.html#acfe2220fd85eeb0a971c51353e437753'
@param ref_frame_name: reference frame for target pose, i.e. "LARM_JOINT5_Link".

In [5]: ros.set_pose('larm', [0.3256221413929748, 0.18216922581330303, 0.16756590383473382], [-2.784279171494696, -1.5690964385875825, 2.899351051232168], task_duration=7, do_wait=True)                                                                                            
[INFO] [WallTime: 1453007509.751512] [3869.365000] setpose jntgr=larm mvgr=<moveit_commander.move_group.MoveGroupCommander object at 0x7f21aaa8a950> pose=[0.3256221413929748, 0.18216922581330303, 0.16756590383473383, -2.784279171494696, -1.5690964385875825, 2.899351051232168] posi=[0.3256221413929748, 0.18216922581330303, 0.16756590383473383] rpy=[-2.784279171494696, -1.5690964385875825, 2.899351051232168]                                                    

In [6]: ros.set_pose('larm', [0.3256221413929748, 0.18216922581330303, 0.06756590383473382], [-2.784279171494696, -1.5690964385875825, 2.899351051232168], task_duration=7, do_wait=True)                                                                                            
[INFO] [WallTime: 1453007518.445929] [3878.220000] setpose jntgr=larm mvgr=<moveit_commander.move_group.MoveGroupCommander object at 0x7f21aaa8a950> pose=[0.3256221413929748, 0.18216922581330303, 0.06756590383473382, -2.784279171494696, -1.5690964385875825, 2.899351051232168] posi=[0.3256221413929748, 0.18216922581330303, 0.06756590383473382] rpy=[-2.784279171494696, -1.5690964385875825, 2.899351051232168]   

tf の利用

エンドエフェクタ(EEF)の任意のフレームに対する相対姿勢を取得するために ROS の tf とそのリスナークライアントの TransformListener という強力なライブラリを使います.

次のコードは左手のリンクフレーム /LARM_JOINT5_Link の腰フレーム /WAIST に対する相対姿勢を取得する方法です.

import tf

import rospy

listener = tf.TransformListener()
try:    
    (trans, rot) = listener.lookupTransform('/WAIST', '/LARM_JOINT5_Link', rospy.Time(0))
except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):

取得された姿勢をロボットに送ります.

In [7]: pos = list(trans)
In [8]: rpy = list(tf.transformations.euler_from_quaternion(rot))
In [9]: ros.set_pose('larm', pos, rpy, task_duration=7, do_wait=True)
[INFO] [WallTime: 1453006377.256805] [2735.650000] setpose jntgr=larm mvgr=<moveit_commander.move_group.MoveGroupCommander object at 0x7f0d1d31b950> pose=[0.3255715961317417, 0.18217283734133255, 0.06760844121713444, 2.8644073530085747, -1.569564170540247, -2.7497620461224517] posi=[0.3255715961317417, 0.18217283734133255, 0.06760844121713444] rpy=[2.8644073530085747, -1.569564170540247, -2.7497620461224517]
  • 注意
    • lookupTransform 戻り値はタプル型なのに対して set_pose はリスト型を受け取る
      • 姿勢を set_pose メソッドに送る前に “タプル→リスト変換” を忘れないようにする.

tf.transformation にクォータニオンからオイラー角への変換を行うツールがあります.

NumPy ライブラリを利用することで位置や姿勢のベクトルを変更することができます.

In [7]: ros.set_pose('larm', list(numpy.array(pos) + numpy.array([0,0,0.1])), rpy, task_duration=7, do_wait=True)                                                                        
[INFO] [WallTime: 1453006471.587639] [2830.075000] setpose jntgr=larm mvgr=<moveit_commander.move_group.MoveGroupCommander object at 0x7f0d1d31b950> pose=[0.32557159613174169, 0.18217283734133255, 0.16760844121713445, 2.8644073530085747, -1.569564170540247, -2.7497620461224517] posi=[0.32557159613174169, 0.18217283734133255, 0.16760844121713445] rpy=[2.8644073530085747, -1.569564170540247, -2.7497620461224517]
  • メモ
    • 認識プロセスによって検出されたオブジェクト姿勢からエンドエフェクタの目標姿勢を算出するために lookupTransform を利用することもできます.
      • 例)ar_track_alvar などを用いて AR マーカオブジェクトのカメラからの相対位置を取得した場合
    • 上記のコマンドでは HIRO / NEXTAGE OPEN のデフォルトのベースフレームである /WAIST フレームに対する相対姿勢を見ているので,必然的に Move Group の get_current_pose の結果と同じになります.
      • 使用目的に応じて参照フレームを変更してください.

チュートリアル: 3次元認識

ロボット搭載カメラの設定

NEXTAGE OPEN のカメラ

NEXTAGE OPEN(HIRO ではない)はデフォルトで頭部に2つ,左右ハンドに1つずつで 合計4つの IDS Ueye カメラが搭載されています. それらのカメラの操作は ueye_cam パッケージにて行われ,基本的にそれは

  • nextage_ros_bridge/launch/nextage_ueye_stereo.launch
  • nextage_ros_bridge/launch/hands_ueye.launch

内にてまとめられています.

  • 注意: HIRO のカメラについて
    • 本項で記述されている内容は HIRO に搭載されているカメラに関するものではありません.
    • HIRO のカメラを動作させる方法は本項で説明されている内容と異なる可能性があります.

ユーザ独自のカメラの利用について

デフォルトで搭載されている以外のカメラも ROS のドライバがあるものについては使用することができます. 既に ROS のドライバが存在しているカメラの情報は次のリンク先で知ることができます.

ユーザによっては Kinect や Xtion カメラをロボット頭部に搭載して利用したりしています. そのような場合には tf の確認や MoveIt! で衝突判定を行うのであれば カメラの 3D モデルなどを構築してロボットモデルを更新する必要があります.

ヘッドカメラの設定

Ueye カメラノードの実行

まずロボット頭部のカメラが頭部に設置されていることを確認してください. ステレオカメラのノードを実行します.

$ roslaunch nextage_ros_bridge nextage_ueye_stereo.launch

このノードでは ueye_cam パッケージを利用していますので 次のように映像トピックをサブスクライブ(購読)することができます.

$ rostopic echo left/image_raw
$ rostopic echo right/image_raw

映像は rqt_image_view などの GUI ツールを使用して見ることができます.

ステレオカメラのキャリブレーション

ステレオカメラを利用する前にはキャリブレーション作業が必要です.

次のコマンドでステレオカメラのキャリブレーションを行います.

$ rosrun camera_calibration cameracalibrator.py --size 4x6 --square 0.024 --approximate=0.1 --camera_name=stereo_frame right:=/right/image_raw left:=/left/image_raw right_camera:=/right left_camera:=/left

キャリブレーション結果のデータは /.ros/camera_info 内に保存してください. このデータは消さないようにしてださい.

ハンドカメラの設定

次のコマンドにより左右ハンドに搭載されている合計2つのカメラを動作させます.

$ roslaunch nextage_ros_bridge hands_ueye.launch

ハンドカメラの映像は次のトピックにより購読できます.

$ rostopic echo /left_hand_ueye/image_raw   (left)
$ rostopic echo /right_hand_ueye/image_raw  (right)

AR マーカを使ったオブジェクト認識と動作計画

AR マーカをロボットのカメラ画像から認識します.

AR マーカ認識プログラムの実行

新しいターミナルで AR マーカの認識プログラムを起動します. ar.launch では NEXTAGE OPEN ロボットの右手 Ueye カメラの画像から AR マーカの検出をします.

$ roslaunch nextage_ros_bridge ar.launch marker_size:=4.5

ar.launch 内で認識させる AR マーカの大きさなどが書かれています.

<launch>
    <arg name="marker_size" default="5.5" />
    <arg name="max_new_marker_error" default="0.08" />
    <arg name="max_track_error" default="0.2" />
    <arg name="cam_ns" default="right_hand_ueye" />
    <arg name="cam_image_topic" default="/$(arg cam_ns)/image_raw" />
    <arg name="cam_info_topic" default="/$(arg cam_ns)/camera_info" />
    <arg name="output_frame" default="/camera" />

    <node name="ar_track_alvar" pkg="ar_track_alvar" type="individualMarkersNoKinect"
          respawn="false" output="screen"
          args="$(arg marker_size) $(arg max_new_marker_error) $(arg max_track_error) $(arg cam_image_topic) $(arg cam_info_topic) $(arg output_frame)" />
</launch>

AR マーカのサイズは次に示すところに単位センチメートルで記述されています.

    <arg name="marker_size" default="5.5" />

デフォルトの 5.5 [cm] 以外のサイズの AR マーカを認識させる場合には 次のように ar.launch 実行時に引数でサイズを指定して実行します.

$ roslaunch nextage_ros_bridge ar.launch marker_size:=4.5
  • メモ: AR マーカのサイズ = マーカの黒い部分全体の辺の長さ

MoveIt! と AR マーカの表示

MoveIt! を起動してください.

$ roslaunch nextage_moveit_config moveit_planning_execution.launch

MoveIt! でロボットのカメラ映像を表示させます.

  1. [ Add ] ボタンをクリック
  2. “rviz” → “Image” をクリックして選択 → “Image” の子ウィンドウが表示
  3. Displays → Image → Image Topic の項目で /right_hand_ueye/image_raw を選択

AR マーカが印刷された紙などを右手のカメラに映るように設置してください. 必要であればロボットの腕も動かしてください. ここでは AR 番号が ar_marker_4 でサイズが 4.5 cm としています.

次にマーカの処理結果を表示させます.

  1. [ Add ] ボタンをクリック
  2. ”rviz” → ”Marker” をクリック
  3. [ Add ] ボタンをクリック
  4. ”rviz” → ”TF” をクリック
  5. Displays → TF → Frames → All Enabled のチェックを外す
  6. Displays → TF → Frames → /ar_marker_4 のチェックを入れる

AR マーカの各番号に対応した色の四角い立体とその tf が MoveIt! / RViz 上で表示されます.

MoveIt! / RViz の設定を保存しておくと上記の追加作業が不要になるので便利です. MoveIt! / RViz の File メニューから Save Config As を選択して, 例えばファイル名 moveit-ar.rviz などの名前で設定を保存します.

MoveIt! / RViz で保存してある設定を利用するには File メニューの Open Config から設定ファイルを読み込みます.

AR マーカ認識に基づく動作計画と実行

デモンストレーションプログラム ar_demo.py は AR マーカを認識して得られた座標を基に MoveIt Commander を利用して動作計画・指示・実行などを行います.

ar_demo.py のコードは次のようになっています.

#!/usr/bin/env python  

import rospy
import tf
import geometry_msgs.msg
import sys

from moveit_commander import MoveGroupCommander
from tf.transformations import *
import math

def kbhit():
    import select
    return sys.stdin in select.select([sys.stdin], [], [], 0)[0]

if __name__ == '__main__':
    rospy.init_node('ar_pose_commander', anonymous=True)
    group = MoveGroupCommander("right_arm")

    base_frame_id = '/WAIST'
    ar_marker_id = '/ar_marker_4'

    pub = rospy.Publisher('target_pose', geometry_msgs.msg.PoseStamped)
    listener = tf.TransformListener()

    rate = rospy.Rate(10.0)
    pose_st_target = None
    while not rospy.is_shutdown() and not kbhit():
        try:
            now = rospy.Time(0)
            (trans,quat) = listener.lookupTransform(base_frame_id, ar_marker_id, now)
            quat = quaternion_multiply(quat, quaternion_about_axis(math.pi/2, (1,0,0)))
            quat = quaternion_multiply(quat, quaternion_about_axis(math.pi/2, (0,0,1)))
            pose_st_target = geometry_msgs.msg.PoseStamped()
            pose_st_target.pose.position.x = trans[0]
            pose_st_target.pose.position.y = trans[1]
            pose_st_target.pose.position.z = trans[2]
            pose_st_target.pose.orientation.x = quat[0]
            pose_st_target.pose.orientation.y = quat[1]
            pose_st_target.pose.orientation.z = quat[2]
            pose_st_target.pose.orientation.w = quat[3]
            pose_st_target.header.frame_id = base_frame_id
            pose_st_target.header.stamp = now
        except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException) as e:
            rospy.logwarn(e)

        if pose_st_target:
            pub.publish(pose_st_target)
            rospy.loginfo(trans)

        rate.sleep()

    if raw_input() == 'q':
        sys.exit(1)

    # move to a point above ar marker
    pose_st_target.pose.position.z += 0.3
    rospy.loginfo("set target to {}".format(pose_st_target.pose))
    group.set_pose_target(pose_st_target.pose)
    plan = group.plan()
    rospy.loginfo("plan is {}".format(plan))
    ret = group.go()
    rospy.loginfo("executed ... {}".format(ret))

デモンストレーションコードの中から主要な行について解説します.

右手を動かします.そのために "right_arm" を引数として渡して MoveGroupCommander をインスタンス化します.

    group = MoveGroupCommander("right_arm")

基準となるフレームを /WAIST に ターゲットとする AR マーカのフレームを /ar_maker_4 として変数に格納します. AR マーカのターゲットを変えたい場合はここを変更すれば良いことになります.

    base_frame_id = '/WAIST'
    ar_marker_id = '/ar_marker_4'

サンプリング周波数 10 Hz を rate = rospy.Rate(10.0) で指定して 基準フレームとターゲットの AR マーカのフレーム間の TF を取得します. ターゲット取得ループは Enter キーが押されると抜けるようにしています.

    listener = tf.TransformListener()

    rate = rospy.Rate(10.0)
    pose_st_target = None
    while not rospy.is_shutdown() and not kbhit():
        try:
            now = rospy.Time(0)
            (trans,quat) = listener.lookupTransform(base_frame_id, ar_marker_id, now)

TF で取得された AR マーカフレームの姿勢から右腕エンドエフェクタの目標姿勢になるように クォータニオンの計算をします.

        quat = quaternion_multiply(quat, quaternion_about_axis(math.pi/2, (1,0,0)))
        quat = quaternion_multiply(quat, quaternion_about_axis(math.pi/2, (0,0,1)))

TF で取得した AR マーカフレームの位置と計算されたターゲットの姿勢を pose_st_target に格納します.

        pose_st_target = geometry_msgs.msg.PoseStamped()
        pose_st_target.pose.position.x = trans[0]
        pose_st_target.pose.position.y = trans[1]
        pose_st_target.pose.position.z = trans[2]
        pose_st_target.pose.orientation.x = quat[0]
        pose_st_target.pose.orientation.y = quat[1]
        pose_st_target.pose.orientation.z = quat[2]
        pose_st_target.pose.orientation.w = quat[3]

Enter キーが押されるとターゲット取得ループから抜けて 右腕のエンドエフェクタを AR マーカ /ar_marker_4 の真上に右手を動かします.

まず,エンドエフェクタをターゲットの 0.3 [m] 上方に動くように位置のZ座標を再計算してから, 右腕の Group に対して目標姿勢として設定します.

    pose_st_target.pose.position.z += 0.3
    group.set_pose_target(pose_st_target.pose)

次に右腕の動作計画を行い,その結果のログを表示します.

    plan = group.plan()
    rospy.loginfo("plan is {}".format(plan))

そして動作計画を実行して,その結果のログを表示します.

    ret = group.go()
    rospy.loginfo("executed ... {}".format(ret))

それでは実際に ar_demo.py を実行してみます.

$ rosrun nextage_ros_bridge ar_demo.py

コンソールに黄色い文字でワーニングが表示されている場合は座標の変換が完遂できていない場合です. 白い文字で座標変換ができている旨のメッセージが出ているときに Enter キーを押すことにより AR 認識で得られた座標データから右手の目標姿勢を定めて動作計画を行い AR マーカの上まで移動させます.

Gazebo での AR マーカ認識シミュレーション

Gazebo を用いて仮想空間内の AR マーカを認識するシミュレーションを行います. ここでは頭部カメラの画像を AR マーカ認識に使用します.

Gazebo の起動とモデルの読み込み

NEXTAGE OPEN の Gazebo シミュレーションを起動します.

  • メモ: ROS / Gazebo でのシミュレーションのため rtm_ros_bridge は不要
$ roslaunch nextage_gazebo nextage_world.launch

Gazebo の仮想空間内に次の2つのモデルを読み込みます.

  • Cafe table
  • MarkerBox-60mm

“cafe_table” モデルを Gazebo のライブラリから読み込みます.

  1. 左コラムの Insert タブを選択
  2. “http://gazeboism.org/models/” を選択して “Cafe table” をクリック
  3. ポインタに Cafe table がついてくるのでロボットの前の床でクリックして設置
  4. 上の方にある十字の矢印アイコンボタンをクリック後 Cafe table をクリック
  5. 赤緑青の座標軸をドラッグして Cafe table の位置を調整
  • メモ: Gazebo 起動直後は “http://gazeboism.org/models” が表示されずに
    “Connecting to model database...” になっているかもしれません.
    しばらくするとデータベースにつながってモデルが読めるようになります.

Gazebo - Insert Cafe table

“MarkerBox-60mm” モデルは Gazebo のライブラリに登録されていないので ユーザコンピュータの ~/.gazebo/model/ ディレクトリにモデルフォルダをコピーします. ターミナルなどを使用してコピーしてください.

$ cp -r MarkerBox-60mm/ ~/.gazebo/model/

コピーしたモデル MarkerBox-60mm を Gazebo に読み込みます.

  1. 左コラムの Insert タブを選択
  2. “/home/USERNAME/.gazebo/models” を選択して “MarkerBox-60mm” をクリック
  3. ポインタを動かしてモデルを Cafe table の下の床の上でクリックして設置
  4. 上の方にある十字の矢印アイコンボタンをクリックモデルをクリック
  5. 赤緑青の座標軸をドラッグして Cafe table 上で, ロボット頭部カメラを下に向けたときに写りそうな位置に調整

Gazebo - cafe_table and MarkerBox-60mm

カメラの画角に AR マーカが入るように頭部姿勢を動かします. ここでは rqt の Joint Trajectory Controller プラグインを利用します.

$ rqt

Plugins の Robot Tools 内 Joint trajectory controller を選択します.

rqt - Plugins / Joint trajectory controller

Joint trajectory controller 内で下記のように設定します.

  • controller manager ns : /controller_manager
  • controller : head_controller

Enable/disable ボタンをクリックして緑色になると頭部の各関節を動かせる状態になります. joints 下の各スライダを動かして頭部の姿勢を調整してください.

rqt - Joint trajectory controller / head_controller

AR プロセスと MoveIt! の起動

新しいターミナルで AR マーカの認識プログラムを起動します.

$ roslaunch nextage_ros_bridge ar_headcamera.launch sim:=true

ar_headcamera.launch

<launch>

    <arg name="marker_size" default="4.5" />
    <arg name="max_new_marker_error" default="0.08" />
    <arg name="max_track_error" default="0.2" />
    <arg name="cam_ns" default="CAMERA_HEAD_L" />
    <arg name="cam_image_topic" default="/$(arg cam_ns)/image_raw" />
    <arg name="cam_info_topic" default="/$(arg cam_ns)/camera_info" />
    <arg name="output_frame" default="/CAMERA_HEAD_L_Link" />
    <arg name="sim" default="false" />

    <node name="ar_track_alvar" pkg="ar_track_alvar" type="individualMarkersNoKinect"
          respawn="false" output="screen"
          args="$(arg marker_size) $(arg max_new_marker_error) $(arg max_track_error) $(arg cam_image_topic) $(arg cam_info_topic) $(arg output_frame)" />

    <group if="$(arg sim)">
        <node pkg="tf" type="static_transform_publisher" name="camera_head_l_link_to_frame"
              args="0 0 0 1.57080 3.14159 1.57080 /CAMERA_HEAD_L_Link /CAMERA_HEAD_L_Link_frame 100" />
        <node pkg="tf" type="static_transform_publisher" name="camera_head_r_link_to_frame"
              args="0 0 0 1.57080 3.14159 1.57080 /CAMERA_HEAD_R_Link /CAMERA_HEAD_R_Link_frame 100" />
    </group>

</launch>

あとはロボット実機で行った場合と同様に MoveIt! と ar_demo.py を実行します.

  1. MoveIt! を起動 + (オプション)RViz 設定ファイルの読み込み
  2. ar_demo.py の実行

を行います.

MoveIt! - ar_demo.py 実行前

MoveIt! - Head Camera L to ar_marker_4

MoveIt! - ar_demo.py 実行後

MoveIt! - Right Hand over ar_marker_4

Gazebo - ar_demo.py 実行後

Gazebo - Right Hand over ar_marker_4

Xtion/Kinect カメラの TF キャリブレーション

3次元深度カメラの Xtion や Kinect を利用してロボット環境情報を取得する場合において, 得られた環境空間情報とロボットとの相対位置・姿勢が分からなければ意味がありません.

そのためにはカメラがロボットに対してどのような位置・姿勢で設置されているかを知る必要があります. 具体的にはカメラを設置するリンクもしくはワールド空間に対するカメラの TF を取得します.

ロボット実機での Xtion/Kinect TF キャリブレーション

ロボットの準備

ROS を使用するので rtm_ros_bridge が起動している必要があります.

$ roslaunch nextage_ros_bridge nextage_ros_bridge_real.launch nameserver:=%HOSTNAME%

ロボット各関節のキャリブレーションを行います. 関節のキャリブレーションが終わったら一旦ロボットの姿勢を終了姿勢にしてサーボを切ります.

ここでは操作に iPython コンソールを使用しています.

$ ipython -i `rospack find nextage_ros_bridge`/script/nextage.py -- --host nextage

:
(many connection messages)
:

[hrpsys.py] initialized successfully

:

In [1]: robot.checkEncoders()
In [2]: robot.goOffPose()
In [3]: robot.servoOff()

チェッカーボードのロボットへの設置

カメラとロボットの相対位置・姿勢を定めるためにチェッカーボードをロボットに取り付けます. このときチェッカーボードと設置するロボットのリンクとの相対位置は機械設計上既知である必要があります.

  • 注意: ロボット実機への作業ですのでサーボが切れていることを確認してください.

下図のように胸部チェッカーボードもしくは腰部チェッカーボードをロボットに取り付けてください.

胸部チェッカーボード NEXTAGE OPEN - Chekcerboard on Chest / Top View NEXTAGE OPEN - Checkerboard on Chest / Bottom View

腰部チェッカーボード NEXTAGE OPEN - Checkerboard on Waist

サーボを入れる(胸部チェッカーボードの場合)

胸部チェッカーボードを使用する場合は CHEST_JOINT0 の関節角度を 0 deg(正面)で維持する必要がありますので,ロボットのサーボを入れて初期姿勢にします.

In [1]: robot.servoOn()
In [2]: robot.goInitial()

Xtion/Kinect カメラをチェッカーボードに向ける

Xtion/Kinect カメラにチェッカーボードが映るようにします. 頭部に Xtion/Kinect カメラを設置した場合には関節を制御して頭部姿勢を変えます.

下に頭部関節を動作・制御するコマンドを記します.

In [1]: robot.setJointAnglesOfGroup( 'head', [ 0.0, 45.0 ], 5.0 )
In [2]: robot.getJointAngles()

チェッカーボード検出プログラムの実行

Xtion/Kinect カメラを本作業を行っている UIコントロールボックス(Ubuntu PC/Vision PC) もしくは 開発用コンピュータ(Ubuntu)に接続してください.

チェッカーボードを検出プログラムを実行します. Kinect カメラを使用している場合は実行時に引数 launch_openni2:=false を指定してください.

Xtion カメラの場合

$ roslaunch nextage_calibration camera_checkerboard_chest.launch  #chest
or
$ roslaunch nextage_calibration camera_checkerboard_waist.launch  #waist

Kinect カメラの場合

$ roslaunch nextage_calibration camera_checkerboard_chest.launch launch_openni2:=false  #chest
or
$ roslaunch nextage_calibration camera_checkerboard_waist.launch launch_openni2:=false  #waist

Checkerboard Detector - 胸部チェッカーボード
Checkerboard Detector - Chest

Checkerboard Detector - 腰部チェッカーボード
Checkerboard Detector - Waist

RViz の起動

各機能の実行状況を確認するために RViz を起動します.

$ rviz -d `rospack find nextage_calibration`/conf/calibration.rviz

RViz - 胸部チェッカーボード
RViz - Checkerboard on Chest

RViz - 腰部チェッカーボード
RViz - Checkerboard on Chest

カメラリンクと親リンクの TF の取得

tf_echo コマンドを利用して camera_link から親リンクへの TF を取得します. ここでは camera_link から HEAD_JOINT1 への TF を表示します.

$ rosrun tf tf_echo HEAD_JOINT1_Link camera_link

tf_echo の結果の例

At time 93.641
- Translation: [0.101, -0.014, 0.199]
- Rotation: in Quaternion [-0.001, 0.704, 0.002, 0.710]
            in RPY (radian) [0.126, 1.563, 0.130]
            in RPY (degree) [7.223, 89.561, 7.477]

ロボットでの作業の終了

カメラリンクと親リンクの TF の取得ができたらロボットを終了姿勢にしてサーボを切ります.

In [1]: robot.goOffPose()
In [2]: robot.servoOff()

全ノードの終了

ロボットのサーボが切れたら全てのノードを Ctrl-C で終了します.

カメラリンク TF の適用

取得した Xtion/Kinect カメラ TF をロボット上のカメラのパラメータとして適用します. 適用は Xtion/Kinect カメラモジュール xtion_kinect.launch 起動時に引数として渡して行います.

  • 単位系
    • TF_COORD_XYZ : [m]
    • TF_COORD_RPY : [rad]

まず rtm_ros_bridge を起動します.

$ roslaunch nextage_ros_bridge nextage_ros_bridge_real.launch nameserver:=%HOSTNAME%

Xtion カメラの場合

$ roslaunch nextage_calibration xtion_kinect.launch TF_COORD_XYZ:='0.101 -0.014 0.199' TF_COORD_RPY:='0.126 1.563 0.130'

Kinect カメラの場合(launch_openni2:=false のみの違い)

$ roslaunch nextage_calibration xtion_kinect.launch TF_COORD_XYZ:='0.101 -0.014 0.199' TF_COORD_RPY:='0.126 1.563 0.130' launch_openni2:=false

Xtion カメラの画像キャリブレーション(オプション)

Xtion カメラにおいては画像のキャリブレーション情報を取得しておくことをお勧めします. 画像キャリブレーションの作業手順は下記のサイトを参照ください.

Gazebo を用いた Xtion/Kinect TF 取得シミュレーション

実機で行う Xtion/Kinect カメラの TF キャリブレーションの作業は Gazebo シミュレータ上でも同様のことを行うことができます.

Gazebo の起動

NEXTAGE OPEN ロボットモデルの Gazebo シミュレータを起動します.

$ roslaunch nextage_gazebo nextage_world.launch
  • メモ: ROS / Gazebo でのシミュレーションのため rtm_ros_bridge は不要

Gazebo 内での Kinect とチェッカーボードモデルの設置

Gazebo 起動後の NEXTAGE OPEN ロボットの初期動作が終了しましたら, Kinect と チェッカーボード のモデルを Gazebo 内に設置します.

$ roslaunch nextage_calibration gazebo_kinect_checkerboard_chest.launch   #chest
or
$ roslaunch nextage_calibration gazebo_kinect_checkerboard_waist.launch   #waist

Gazebo シミュレータ内の Kinect と胸部チェッカーボードモデルの設置
Gazebo - Kinect and Checkerboard

チェッカーボード検出プログラムの実行

実機ロボットの場合と同様に Xtion/Kinect カメラでのチェッカーボード検出プログラムを実行します.

Kinect の場合

$ roslaunch nextage_calibration camera_checkerboard_chest.launch launch_openni2:=false
or
$ roslaunch nextage_calibration camera_checkerboard_waist.launch launch_openni2:=false

Checkerboard Detector - Gazebo / Checkerboard on Chest

RViz の起動

実機ロボットの場合と同様に各種チェックのために RViz を起動します.

$ rviz -d `rospack find nextage_calibration`/conf/calibration.rviz

RViz - Checkerboard Gazebo Simulation

カメラリンクと親リンクの TF の取得

実機ロボットの場合と同様に camera_link から親リンクへの TF を取得します.

$ rosrun tf tf_echo HEAD_JOINT1_Link camera_link

tf_echo の結果の例

At time 93.641
- Translation: [0.101, -0.014, 0.199]
- Rotation: in Quaternion [-0.001, 0.704, 0.002, 0.710]
            in RPY (radian) [0.126, 1.563, 0.130]
            in RPY (degree) [7.223, 89.561, 7.477]