Skip to content

Instantly share code, notes, and snippets.

@KobayashiRui
Last active May 6, 2024 12:37
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KobayashiRui/1dd3f69b48d51ced9ff8628a2fa1bbb6 to your computer and use it in GitHub Desktop.
Save KobayashiRui/1dd3f69b48d51ced9ff8628a2fa1bbb6 to your computer and use it in GitHub Desktop.
rosやgazeboについて自分に有益なものをまとめる 参考文献 ・ROSではじめるロボットプログラミング ・プログラミングROS 参考サイト ・https://sites.google.com/site/robotlabo/time-tracker/ros/modeling_robothttp://products.rt-net.jp/micromouse/archives/3316http://wiki.ros.org/simmechanics_to_urdf/Tutorials/

#spinについて rosにおいて購読のコールバックが呼び出されるまで待つものがspinである
c++においてはspinonce()があるためそこで一度だけコールバックを呼ぶことがわかる
しかしrospyでは**spinonce()**がないためどのようにすべきか確認する

##rospyにおけるspin() spin()を呼び出すことでその関数が呼び出されるまで行動をブロックすることになる ex)

while True:
    print("hello")
    rospy.spin()

の場合,printははじめの一度しか呼ばれず、spin()の処理に入り、
callbackが呼び出される == 配信される までなにも行動ができなくなる

callbackが割り込み処理で起動するもの

!/usr/bin/env python
import rospy
from std_msgs.msg import Int32
import time
data_base = 0
def callback(msg):
    print("hello")
    print(msg.data)
    global data_base
    data_base = msg.data
    print("fin")
    time.sleep(0.1)


rospy.init_node('topic_subscriber')
sub = rospy.Subscriber('counter',Int32,callback,queue_size=1)
while not rospy.is_shutdown():
    print("database")
    time.sleep(0.5)
    print(data_base)
    time.sleep(0.5)

このコードの場合、基本がwhile内が実行され、配信された時のみcallbackが 割り込み処理として発生し、callbackの処理後、呼び出された地点から再開する

c++の場合

#include <iostream>
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "std_msgs/Int32.h"

int data_base = 0;
void messageCb( const std_msgs::Int32& msg){
    std::cout << msg.data << std::endl;
    data_base=msg.data;
}

int main(int argc, char **argv){
    ros::init(argc, argv, "listener");
    ros::NodeHandle n;
    ros::Subscriber sub = n.subscribe("counter",1,messageCb);
    ros::Rate rate(1);
    while(ros::ok()){
    std::cout << "test" << std::endl;
    std::cout << data_base << std::endl;
    std::cout << "fin" << std::endl;
    ros::spinOnce();
    rate.sleep();
    }
    return 0;
}

上記のプログラムでpythonと同じ内容のプログラムとなる

ROSとArduinoの接続

参考文献

ROS側に必要なものをダウンロード及びcatkinビルド

  1. sudo apt-get install ros-自分のROSディストロ-rosserial-arduino
  2. sudo apt-get install ros-自分のROSディストロ-rosserial
  3. catkinにてrosserialをビルドするために自分のROSワークスペースのsrcディレクトリ内に移動する
  4. srcディレクトリ内で git clone https://github.com/ros-drivers/rosserial.gitを実行
  5. ワークスペースのルートディレクトリに戻る
  6. catkin_makeを実行し、ビルドする

上記の内容でROS側の基本的な準備が終了する

ArduinoIDEにros_libをいれる

  1. Arduinoのlibrariesディレクトリに移動する
  2. librariesにros_libがすでにあった場合 rm -rf ros_libで削除する
  3. rosrun rosserial_arduino make_libraries.py . を実行する

上記の内容でArduinoの準備が終了する
ArduinoIDEを起動し、スケッチ例にros_libがあれば成功

ArduinoとROSの通信(ROS側)

  1. ROSにてrosserial.gitをいれたワークスペースにてsource deve/setup.bashを実行
  2. Arduinoと通信するために下記のコマンドを実行する
rosrun rosserial_python serial_node.py _port:=/dev/~~ _baud:=115200

注意

  • /dev/~~の部分はArduinoがつながっている部分に設定すること
  • /dev/~~にアクセスの許可がないためsudo chmod 666 /dev/~~で許可をする
  • _baudはシリアル通信の周波数帯域なのでArduionoの設定とこの数値をあわせること

ArduinoとROSの通信(Arduino側)

基本的には設定や準備は必要なく、ArduinoIDEでプログラムを書いたら終わり

プログラムにて注意する点

  • シリアル通信の帯域の設定 : 下記のコードをNodeを初期化する部分の前に入れる
nh.getHardware()->setBaud(BAUD) //BAUD:9600,115200など
  • 容量の削減 : ROSのコードをArduinoに書き込むとやたらメモリを使うときの対策
ros::NodeHandle nh;

とかいてある部分を下記のコードに変更する

typedef NodeHandle_<ArduinoHardware, 10,15,128, 256> NodeHandle;

上記のコードはPubSubの上限値とinput,outputのbufferを設定するものでデフォルトだと この設定が大きすぎるためメモリを多く使ってしまう
パラメータは Publisherの個数, Subscriberの個数, input Bufferのbyte数, output Bufferのbyte数

注意
output bufferを256より小さくすると接続に失敗する

ROSのバグ

rqt_graphのバグ

rosrun rqt_graph rqt_graphを実行したときにpythonのqtcoreがないなどの エラーがでた場合
⇒ rqt_graphコマンドからrqt_graphを起動することで対処可能

catkinについてまとめる

catkinのビルドシステムについてCMakefileやpackage.xmlについてまとめる

package.xmlについて

最小限のタグ

  • <name> : パッケージの名前
  • <version> : パッケージのバージョン
  • <description> : パッケージの説明
  • <maintainer> : メンテナンスしている人
  • <license> : パッケージのライセンス

依存関係

  • <depend> : 依存関係がビルド、エクスポート、及び実行の依存関係であることを指定する。(これが一般的に使用されるタグ : roscppやrospy,std_msgsなどがこのタグ内に入る)
  • <buildtool_depend> :
  • <build_depend> :
  • <build_export_depend> :
  • <exec_depend> :
  • <test_depend> :
  • <doc_depend> :

ROSのコマンドについて

一時的な確認などrosの機能をpythonなどのプログラムを使用せずに利用する

ROStopicのPublish

rostopic pub トピック名 型 型に入れる数値: publisherを作成する 既存のトピックでもOK

使用例

  • /test_hogehogeというトピックにInt16型で10を配信したい場合 rostopic pub /test_hogehoge std_msgs/Int16 10
  • /test_hogehogeというトピックに1HzでInt16型の10を配信したい場合 rostopic pub /test_hogehoge std_msgs/Int16 10 -r 1

#ROSのC++ rosにてc++のノードを作成した際に発生した問題について

ros.hがないとエラーがでる

原因がrosのパッケージファイル内のCMakeList.txtとpackage.xmlにある

package.xmlの確認

  • build_dependexex_dependを確認し、includeと対応しているか見る

CMakeList.txtの確認

  • find_package がincludeに対応しているか (ros/ros.hはroscpp, std_msgs/~はstd_msgsなど)
  • 上記以外にもservicesやmessageを作成している場合はそのための宣言があるため適宜確認する
  • 下記のような内容をCMakeList.txtの最後に追加したか
include_directories(include ${catkin_INCLUDE_DIRS}) //これはどれでも共通

add_executable(test src/test.cpp) 
//test=>ビルド後の実行ファイル名 src/test.cpp=>ビルドするファイルのこのCMakeList.txtからの相対パス
target_link_libraries(test ${catkin_LIBRARIES})
//test=>前の行で示したビルド後の実行ファイル名 その後ろは基本的に共通

ビルド

上記の設定を終了したら、
ワークスペースのルートディレクトリまで移動しcatkin_make

ROSとは

Install

基本ROSwikiを参照する

ワークスペース

コードを置く場所
ワークスペースは関連するROSコードを置いておくディレクトリの集合
ROSのワークスペースは複数持つことができるが、作業は一度に1つのワークスペースでしかできない

catkin

catkinはCMakeマクロとカスタムのpythonスクリプトにより、標準のCMakeのワークフロー を機能拡張したもの

ワークスペースの作成

  1. source /opt/ros/<自分のROSのバージョン>/setup.bash
    : これは最初にやる必要があるので.bashrcに追加すると良い
    この作業はROSのセットアップスクリプトをシステム全体で使用するために必要
  2. mkdir -p ~/ワークスペース名/src
  3. ワークスペース内のsrcディレクトリに移動
  4. catkin_init_workspace : このコマンドを実行するとCMakeLists.txtファイルを作成する
  5. ワークスペース内のsrcディレクトリを抜けワークスペースのディレクトリに移動
  6. catkin_make : このコマンドを実行するとbuildとdevelというファイルが作成される
    buildはC++のコードを扱っている場合catkinがその作業において生成したライブラリや 実行プログラムが保存される。develの中のsetupファイルは実行すると今いるワークスペース とその中にこれから含まれるであろうコードを扱うようにROSのシステムが設定される
  7. 上記のsetupファイルをすべて実行するためにワークスペースのディレクトリに移動
  8. source devel/setup.bash
  9. ワークスペースの完成

注意1 必ず新しいワークスペースを作成したら上記のsetup.bashを実行すること
注意2 上記のsetup.bashを実行し、ワークスペースを有効化した場合、 ノード内のpythonファイルを変更すると自動で反映されるため、上記のsetup.bashは ワークスペースを使う前には必ず実行すること

ROSパッケージ

rosソフトウェアはパッケージという単位でまとめられる
パッケージはワークスペース内のsrcディレクトリの中に置かれる
1つのパッケージの中にはCMakeLists.txtpackage.xmlが置かれている必要がある
package.xmlはパッケージ内容とcatkinがそのパッケージをどのように処理するかを説明している

パッケージの作成方法

  1. ワークスペースのsrcの中に移動
  2. catkin_create_pkg パッケージ名 依存ファイル(rospyなど) : 複数のパッケージに依存する場合は依存ファイルを後ろに列挙できる

上記のコマンドを実行するとパッケージ名で指定したディレクトリが作成され、
中にCMakeLists.txtとpackage.xmlとsrcディレクトリができる

パッケージを作成したら、pythonで書いたノードをパッケージディレクトリのsrcディレクトリに入れる

名前空間、リマッピング

同じノードを別の名前空間で起動することで名前の衝突を回避できる
2つ以上の名前空間に手が届く方法がリマッピング

roslaunch

roslaucnchはrosノードの集まりの起動を自動化するために作られたもの

roslaunchの使い方

roslaunch パッケージ 起動ファイル
roslaunchは引数にノードではなくlaunchファイルをとる
launchファイルは、ノードとそのトピックのリマッピング、パラメータを一緒に記述したxmlファイル

ex)

<launch>
  <node name="talker" pkg="rospy_tutorials"
    type="talker.py" output="screen" />
  
  <node name="listener" pkg="rospy_tutorials"
    type="listener.py" output="screen" />
</launch>

それぞれの<node>には順番に

  • そのノードのROSグラフ名を宣言した属性
  • ノードのパッケージ
  • ノードのタイプ(単にその実行ファイルのファイル名)
  • output="screen"属性

output="screen"とは
これはコンソールへの出力をファイルだけに書き出すのではなく、現在のコンソールにも 出力をするもので、デバックに使われる
デバックが完了したらこの属性は削除して問題ない

#トピック

トピックとは

ノード間での通信を行う最も一般的な方法
トピックはある定義された型をもつメッセージストリームの名前
イメージとしては通信ケーブルの名前

通信メカニズム

分散システムにおけるデータ交換でよく使われる**配信/購読(publish/subscribe)**型を利用

pubsubの仕組み

トピックを介してデータを送る前に

  1. ノードはトピックの名前とこれから送るメッセージの型をアナウンス
    => 公開(advertise)
  2. 上記の後、ノードはトピックに実際のデータを付けて送信できる
    => 配信(publish)
  3. あるトピックのメッセージを受け取りたいノードはroscoreに要求し受け取ることができる
    => 購読(subscribe)

購読するとそのトピックに配信されるすべてのメッセージがノードに届けられるようになる

注意rosでは1つのトピックを通るメッセージは常に同じデータ型でなければならない

トピックの配信方法

#!/usr/bin/env python
import rospy

from std_msgs.msg import Int32

rospy.init_node('topic_publisher')

pub = rospy.Publisher('counter',Int32)

rate = rospy.Rate(2)

count = 0
while not rospy.is_shutdown():
  pub.publish(count)
  count += 1
  rate.sleep()
  • 1行目はシバン(shebang) これを書くことでオペレーティングシステムはこのファイルが python用のファイルであり、pythonのインタープリンタに渡すべきものであることがわかる

作成したノードはプログラムとして実行しようとしているので実行権限を与える
=>chmod u+x pythonのプログラムファイル

  • 2行目はpythonで書かれるrosのノードには必ず必要

  • 4行目はトピックに送るメッセージの定義を読み込んでいる
    : 今回の場合、rosの標準メッセージパッケージのstd_msgで定義されている32ビット整数型を利用している

  • 6行目はノードの初期化

  • 8行目はPublisher関数でノードを公開

引数の説明

  • 一つ目 : トピックの名前
  • 二つ目 : そのトピックを介して送るメッセージの型の指定
  • 三つ目(queue_size) : roswikiを参照(入れないとwarningがでる)

この時点で裏ではroscoreとの接続を確立し、必要な情報を送っていて、他のノードが このトピックを購読しようとするとroscoreは配信者と購読者のリストを提供する
その後、購読しようとしたノードはこのリストを使って、それぞれのトピックに対するすべての 配信者と購読者を直接接続する

  • 10行目は配信する周期をHzで設定している
  • 13行目のrospy.is_shutdown()関数はノードがシャットダウンする準備ができたときにTrueを返し それ以外ではFalseを返す
  • 14行目で配信をするpubの部分がトピックの設定の部分で引数がその中身となる
  • 16行目でrateで設定した周期でwhileループの内側が実行されるように十分な時間スリープする

注意

4行目で新しいstd_msgsというパッケージをimportしているのでこれをpackage.xmlに追加する必要がある 今回の場合rospyは最初のパッケージ作成時にrospyを依存ファイルに指定しているとするとrospyの方はすでに記述が されているのが確認できる。そして新しものをimportする場合
ex)新しい依存パッケージstd_msgsをpackage.xmlに追加する場合

<build_depend>std_msgs</build_depend>

をpackage.xmlに追記する
上記の作業が終わったら依存==再ビルドが必要なのでそのワークスペースのルートディレクトリまで移動し

catkin_make

を実行して、package.xmlのカスタマイズ終了となる


動作確認

rostopicコマンドを使用することで現在利用可能なトピックを詳しくしることができる

使用例

  • rostopic list : 利用可能なトピックの一覧が見れる
  • rostopic -h : rostopicの引数がわかる
  • rostopic echo トピック名 : トピックを画面に出力する 上記のechoに-n 数字の引数を追加することでその数字分だけ表示して終了ということが可能
  • rostopic hz トピック名 : そのトピックが自分で設定した周期でメッセージが配信されているか確認できる
  • rostopic bw トピック名 : そのトピックで使用している帯域の情報を知ることができる
  • rostopic info トピック名 : 公開中のトピックに関する情報を知ることができる
  • rostopic type トピック名 : 指定されたトピックのメッセージ型を表示する
  • rostopic find メッセージ型 : 特定のメッセージ型を配信しているすべてのトピックを探す 上記のメッセージ型の部分には「std_msgsのInt32」の型を調べる場合std_msgs/Int32として パッケージの名前/メッセージ型というように指定する必要がある

コマンドラインからトピックにメッセージを配信する

rostopic pub トピック名 メッセージ型 メッセージ内容

トピックの購読方法

#!/usr/bin/env python

import rospy
from std_msgs.msg import Int32

def callback(msg):
    print(msg.data)

rospy.init_node('topic_subscriber')

sub = rospy.Subscriber('counter',Int32,callback)

rospy.spin()

ラッチトピック

通常のトピックの場合、メッセージが配信されたときにトピックを購読していなければ そのメッセージを受け取ることができず、次のメッセージを待たなければならない
配信者が頻繁に配信している場合は問題ないが、そうでない場合問題となる
その対策がラッチトピックである

ラッチトピックの特徴

トピックを公開するときにラッチトピックとして指定しておくと、新しい購読者は、トピックに 接続したときに最後に配信されたメッセージを自動的に受け取ることができる

ラッチトピックの配信方法

通常の配信の関数にlatch=Trueをつけるだけ
ex) pub = rospy.Publisher('counter',Int32,latch=True)

独自のメッセージ型を作成する

基本のメッセージ型
ros : C++ : python : 備考

  • bool : uint8_t : bool
  • int8 : int8_t : int
  • uint8 : uint8_t : int :uint8[]はpythonではstringとして扱われる
  • int16 : int16_t : int
  • uint16 : uint16_t : int
  • int32 : int32_t : int
  • uint32 : uint32_t : int
  • int64 : int64_t : long
  • uint64 : uint64_t : long
  • float32 : float : float
  • float64 : double : float
  • string : std::string : string
  • time : ros::Time : rospy.Time
  • duration : ros::Duration : rospy.Duration

新しいメッセージの定義

rosのメッセージはパッケージ内のmsgディレクトリにある専用のメッセージ定義ファイルに 定義する。=>追加後にcatkin_makeをすること

定義方法

ex)
ランダムな複素数を配信するメッセージを作成するためComplexという名前の新しい型を 型に対するメッセージ定義ファイルを作成する

  1. Complex.msgというファイルをパッケージのmsgディレクトリ内に作成する
  2. Complex.msgの中身に
float32 real
float32 imaginary

という値を設定する

  1. メッセージを定義したら、catkin_makeし、言語固有のコードを生成し、メッセージを使えるようにする
  2. メッセージの言語固有のコードをrosに生成させるにはビルドシステムに新しいメッセージ定義を伝える
  3. package.xmlに
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>

を追加する

  1. 次にCMakeLists.txtファイルを編集する
  2. find_package を探し最後に message_generation を追加する
    注意
    ここに他に依存するファイルを追加するstd_msgsがないとcatkin_makeでエラーがでる?
    ex)
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
  1. メッセージの実行時依存の有効を確認するため catkin_package を探し 最後に CATKIN_DEPENDS message_runtime を追加する
    ex)
catkin_package(
  CATKIN_DEPENDS message_runtime
)
  1. add_message_files を探してコメント化の#を外しFILESの下に自分の.msgファイルを追加
add_message_files(
  FILES
  自分の.msgファイルを~.msgという形で追加
)
  1. generate_messages を探し、コメント化の#を解除する
    ex)
generation_messages(
  DEPENDENCIES
  std_msgs
)
  1. 上記の作業が終わったらワークスペースのルートディレクトリに行きcatkin_makeをする
  2. ここまでで新しいメッセージ型の作成が終了する

独自の新しいメッセージ型を利用する

配信

#!/usr/bin/env python

import rospy

from my_awesome_code.msg import Complex
from random import random

rospy.init_node("message_publisher")

pub = rospy.Publisher('complex',Complex,queue_size=10)
rate = rospy.Rate(2)

while not rospy.is_shutdown():
    msg = Complex()
    msg.real = random()
    msg.imaginary = random()

    pub.publish(msg)
    rate.sleep()

購読

#!/usr/bin/env python

import rospy
from my_awesome_code.msg import Complex

def callback(msg):
    print('Real :', msg.real)
    print('Imaginary :', msg.imaginary)
    print('end')

rospy.init_node('message_subscriber')

sub = rospy.Subscriber('complex',Complex, callback)

rospy.spin()

トピックの名前がcomplexでその中を通るメッセージ型がComplexであるイメージ
Complex.realやComplex.imaginaryは完全にpythonのクラスのイメージでいける

メッセージ型の内容の見方

  • rosmsg show メッセージ型 : 上記の例の場合新しいメッセージ型の名前はComplexなのでメッセージ型の部分にComplexと入れればOK

  • rosmsg list : rosで利用可能なメッセージをすべて表示する

  • rosmsg packages : メッセージを定義しているパッケージをすべて表示する

  • rosmsg package パッケージ名 : 特定のパッケージで定義されているメッセージを表示する

注意
できるだけ新しいメッセージ型を作るのではなく既存のメッセージ型のみでどうにかしよう!!

配信者と購読者を混合する

1つのノードが配信者と購読者の両方の機能を持っていたり、複数の配信と購読が行えるものを作成できる
ex1)

#!/usr/bin/env python

import rospy
from std_msgs.msg import Int32
import time

rospy.init_node('doubler')

def callback(msg):
    doubled = Int32()
    doubled.data = msg.data * 2
    time.sleep(3)
    pub.publish(doubled)

sub = rospy.Subscriber('number',Int32, callback)
pub = rospy.Publisher('doubled',Int32,queue_size=10)

rospy.spin()

numberが配信されたときに2倍されdoubledに代入して配信する今回は配信する前にtime.sleepで3秒スリープがある

ex2)

#!/usr/bin/env python

import rospy
from std_msgs.msg import Int32

num_data = Int32()

def callback(msg):
    print("doubled :",msg.data)
    num_data.data += msg.data
    print(num_data.data)

def callback2(msg):
    print("number :",msg.data )

rospy.init_node('doubler_sub')

sub = rospy.Subscriber('number',Int32,callback2)
sub2 = rospy.Subscriber('doubled',Int32,callback)

rospy.spin()

numberが配信されたらcallback2が実行され、doubledが配信されたらcallbackが実行される
callbackではnum_dataという値があり、これはプログラムで独自にある変数でこのプログラム(ノード)を止めるまで 蓄積されリセットされない。
またInt32のデータと足し算などの計算をしたい場合はInt32に初期化しているもの同士でないとエラーがでる!!

サービス

サービスは単なる同期型リモートプロシージャーコール
あるノードから別のノードで実行される関数を呼び出すことが可能
非同期のトピックで対応しにくい、同期型のリクエスト/レスポンスのやり取りに便利

サービスの定義方法

  1. サービスコールの入力と出力を定義するためサービス定義ファイルを作成する
  2. サービスコールファイルは.srvというファイルでパッケージディレクトリのsrvディレクトリに置く
    ex) 単語をstringで渡し、単語数を返すサービスを作成する場合(ファイル名はWordCount.srv)
string words
 ---
uint32 count

3つのダッシュ記号---は入力の終わりを示している

  1. 定義ファイルを書いたらメッセージ型と同じように今回作成するサービスとやり取りする際に必要と なるコードとクラス定義をcatkin_makeを実行して生成する
  2. CMakeLists.txt内の find_package を探し、最後に message_generation を追加する
  3. rospyおよびメッセージシステムへの依存関係を反映させるためpackage.xmlに
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

を追加する

  1. サービス定義ファイルをコンパイルする必要があるかをcatkinに指示するためCMake_Lists.txtの add_service_files を探し最後に自分の.srvファイルを追加する
  2. CMakeLists.txtファイルから generate_messages関数を探しそのメッセージに必要なすべての依存関係が 含まれているか確認する=>std_msgsの型のみ使用している場合はそれが含まれていればOK
  3. 上記の作業を終えたら catkin_make で終了

サービスの確認方法

  • rossrv show サービス名 : 指定したサービスを確認できる
  • rossrv list : 使用可能なすべてのサービスを確認することができる
  • rossrv packages : サービスを提供しているすべてのパッケージを確認できる
  • rossrv package パッケージ名 : ある特定のパッケージが提供するすべてのサービスを確認できる

サービスの実装

例としてwordsに文字を入れたときにcountにその文字の単語数を返すサービスを作るとする

サーバー側(サービスの提供)

#!/usr/bin/env python

import rospy

from my_awesome_code.srv import WordCount,WordCountResponse

def count_words(request):
    return WordCountResponse(len(request.words.split()))

rospy.init_node('service_server')

service = rospy.Service('word_count',WordCount,count_words)

rospy.spin()

クライアント側(サービスを提供される)

#!/usr/bin/env python

import rospy

from my_awesome_code.srv import WordCount

import sys

rospy.init_node('Service_client')

rospy.wait_for_service('word_count')

word_counter = rospy.ServiceProxy('word_count',WordCount)

words = ' '.join(sys.argv[1:])
word_count = word_counter(words)
print words,'->',word_count.count

word_counter関数の部分の引数がサービスに与えられる値である
もし入力が複数ある場合srvファイルの上から順番に引数の左から順番に渡される
また、サービスの出力が二つの場合、上記のプログラムのword_counterは二つの値を返す

アクション

サービスは[xの値の取得]のような単純な命令だと便利だが複雑な場合良くない
また、長時間にわたるタスクにはうまく機能しない

アクションは時間のかかるゴール指向のタスクを実装するのに最も適している
アクションは非同期

アクションの流れ

  1. サービスにおけるリクエスト/レスポンスと同じようにタスクの開始にゴールを受け取る
  2. タスク完了時にリザルトを送る
  3. 上記の二つ以外に、ゴールに対する進歩状況の更新にフィードバックがある
  4. さらにゴールを取り消す仕組みも用意されている

アクションは複数のトピック(ゴール、リザルト、フィードバックなど)をどのように組み合わせて使用するか 示したもの

アクションの定義

  1. 新しいアクションを作成するためゴール、リザルト、フィードバックのメッセージフォーマットをアクション定義ファイルに定義する
  2. アクションファイルは.actionというファイルでパッケージディレクトリのactionディレクトリに置く
    ex) タイマーの自作
# part1 : ゴール
# タイマーで待ちたい時間
duration time_to_wait
 ---
# part2 : リザルト
# 実際に待った時間
duration time_elapsed
# 更新を送った回数
uint32 updates_sent
 ---
# part3 : フィードバック
#タイマー開始からの経過時間
duration time_elapsed
# 完了までの残り時間
duration time_remaining
  1. CMakeLists.txt内の find_package を探し、最後に actionlib_msgsを追加
  2. CMakeLists.txt内の add_action_filesを探し、最後に追加したい自分のactionファイルを追加する
  3. CMakeLists.txt内の generate_messagesを探し、アクションが依存するものを追加する
    基本は actionlib_msgs のみ追加でOK
  4. CMakeLists.txt内の catkin_packageを探し、catkinが依存するものとして actionlib_msgs を追加する
  5. package.xmlファイルに依存関係を反映させるため、下記をpackage.xmlに追加する
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
  1. 上記の作業を終えたらcatkin_make
  2. devel/lib/python2.7/dist-packages/パッケージ名/msg内に新しいものが生成される

アクションサーバーの実装

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rospy

import time
import actionlib
from my_awesome_code.msg import TimerAction, TimerGoal, TimerResult

def do_timer(goal):
    start_time = time.time() #現在の時刻を保存
    time.sleep(goal.time_to_wait.to_sec())
    result = TimerResult()
    result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time)
    result.updates_sent = 0
    server.set_succeeded(result)

rospy.init_node('timer_action_server')
server = actionlib.SimpleActionServer('timer',TimerAction,do_timer,False)
# 引数の意味 : サーバー名,そのサーバーが扱うアクションの型,
#コールバック関数, サーバーの自動起動を無効にするためFalse
server.start()
rospy.spin()

アクションを利用するクライアント

#! /usr/bin/env python
# -*- coding : utf-8 -*-

import rospy

import actionlib
from my_awesome_code.msg import TimerAction, TimerGoal, TimerResult

rospy.init_node('timer_action_client')
client = actionlib.SimpleActionClient('timer',TimerAction)
client.wait_for_server()
goal = TimerGoal()
goal.time_to_wait = rospy.Duration.from_sec(5.0)
client.send_goal(goal)
client.wait_for_result()
print("Time elapsed:{}".format(client.get_result().time_elapsed.to_sec()))

より複雑なアクションサーバー

ROSマスターを設定し複数のPCと通信する

複数のPCを同一ネットワークで通信する場合の設定

HOST側の設定(roscoreを実行するPC)

  1. export ROS_IP=`hostname -I` : 自分のIP
  2. export ROS_MASTER_URI=http://`hostname -I`:11311 : ROSCOREを起動するIP
  3. roscore

hostname -I : 自分のIPを表示する

CLIENT側の設定(rosmasterに接続するPC)

  1. export ROS_IP=`hostname -I` : 自分のIP
  2. export ROS_MASTER_URI=http://`hostname -I`:11311 : ROSCOREを起動するIP

仮想マシンを使用している場合の注意

NATに設定している場合は、ブリッジに変更する

名前解決の注意

CLIENT側からアクセスする場合にはHOST側の名前解決がしっかり行われているか確認する

HOSTの名前が登録されていない場合

ubuntuにて\etc\hostsファイルにアクセスし、IPと名前を登録する

ROSの環境変数を見る方法

export -p | grep ROS

PS3コントローラーの使い方(ubuntu16.04)

  1. USBでPCと接続する
  2. sudo sixpairを実行しペアリングをする
  3. USBとPCから取外す
  4. sudo sixad -sを実行しPS3の真ん中のボタンを押しBluetoothにて接続する
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment