rosやgazeboについて自分に有益なものをまとめる 参考文献 ・ROSではじめるロボットプログラミング ・プログラミングROS
参考サイト ・https://sites.google.com/site/robotlabo/time-tracker/ros/modeling_robot ・http://products.rt-net.jp/micromouse/archives/3316 ・http://wiki.ros.org/simmechanics_to_urdf/Tutorials/
rosrun rqt_graph rqt_graph
を実行したときにpythonのqtcoreがないなどの
エラーがでた場合
⇒ rqt_graph
コマンドからrqt_graphを起動することで対処可能
catkinのビルドシステムについてCMakefileや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>
:複数のPCを同一ネットワークで通信する場合の設定
export ROS_IP=`hostname -I`
: 自分のIPexport ROS_MASTER_URI=http://`hostname -I`:11311
: ROSCOREを起動するIProscore
hostname -I : 自分のIPを表示する
export ROS_IP=`hostname -I`
: 自分のIPexport ROS_MASTER_URI=http://`hostname -I`:11311
: ROSCOREを起動するIPNATに設定している場合は、ブリッジに変更する
CLIENT側からアクセスする場合にはHOST側の名前解決がしっかり行われているか確認する
ubuntuにて\etc\hosts
ファイルにアクセスし、IPと名前を登録する
export -p | grep ROS
sudo sixpair
を実行しペアリングをするsudo sixad -s
を実行しPS3の真ん中のボタンを押しBluetoothにて接続する#URDFとは
サービスは[xの値の取得]のような単純な命令だと便利だが複雑な場合良くない
また、長時間にわたるタスクにはうまく機能しない
アクションは時間のかかるゴール指向のタスクを実装するのに最も適している
アクションは非同期
アクションは複数のトピック(ゴール、リザルト、フィードバックなど)をどのように組み合わせて使用するか 示したもの
# part1 : ゴール
# タイマーで待ちたい時間
duration time_to_wait
---
# part2 : リザルト
# 実際に待った時間
duration time_elapsed
# 更新を送った回数
uint32 updates_sent
---
# part3 : フィードバック
#タイマー開始からの経過時間
duration time_elapsed
# 完了までの残り時間
duration time_remaining
find_package
を探し、最後に actionlib_msgs
を追加add_action_files
を探し、最後に追加したい自分のactionファイルを追加するgenerate_messages
を探し、アクションが依存するものを追加するactionlib_msgs
のみ追加でOKcatkin_package
を探し、catkinが依存するものとして actionlib_msgs
を追加する<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
#!/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()))
サービスは単なる同期型リモートプロシージャーコール
あるノードから別のノードで実行される関数を呼び出すことが可能
非同期のトピックで対応しにくい、同期型のリクエスト/レスポンスのやり取りに便利
string words
---
uint32 count
3つのダッシュ記号---は入力の終わりを示している
find_package
を探し、最後に message_generation
を追加する<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
を追加する
add_service_files
を探し最後に自分の.srvファイルを追加するgenerate_messages
関数を探しそのメッセージに必要なすべての依存関係が
含まれているか確認する=>std_msgsの型のみ使用している場合はそれが含まれていればOKcatkin_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は二つの値を返す
#トピック
ノード間での通信を行う最も一般的な方法
トピックはある定義された型をもつメッセージストリームの名前
イメージとしては通信ケーブルの名前
分散システムにおけるデータ交換でよく使われる配信/購読(publish/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()
作成したノードはプログラムとして実行しようとしているので実行権限を与える
=>chmod u+x pythonのプログラムファイル
2行目はpythonで書かれるrosのノードには必ず必要
6行目はノードの初期化
8行目はPublisher関数でノードを公開
この時点で裏ではroscoreとの接続を確立し、必要な情報を送っていて、他のノードが
このトピックを購読しようとするとroscoreは配信者と購読者のリストを提供する
その後、購読しようとしたノードはこのリストを使って、それぞれのトピックに対するすべての
配信者と購読者を直接接続する
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 : 備考
rosのメッセージはパッケージ内のmsgディレクトリにある専用のメッセージ定義ファイルに 定義する。=>追加後にcatkin_makeをすること
ex)
ランダムな複素数を配信するメッセージを作成するためComplexという名前の新しい型を
型に対するメッセージ定義ファイルを作成する
float32 real
float32 imaginary
という値を設定する
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
を追加する
find_package
を探し最後に message_generation
を追加するstd_msgs
がないとcatkin_makeでエラーがでる?find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
catkin_package
を探し
最後に CATKIN_DEPENDS message_runtime
を追加するcatkin_package(
CATKIN_DEPENDS message_runtime
)
add_message_files
を探してコメント化の#を外しFILESの下に自分の.msgファイルを追加add_message_files(
FILES
自分の.msgファイルを~.msgという形で追加
)
generate_messages
を探し、コメント化の#を解除するgeneration_messages(
DEPENDENCIES
std_msgs
)
#!/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に初期化しているもの同士でないとエラーがでる!!
基本ROSwikiを参照する
コードを置く場所
ワークスペースは関連するROSコードを置いておくディレクトリの集合
ROSのワークスペースは複数持つことができるが、作業は一度に1つのワークスペースでしかできない
catkinはCMakeマクロとカスタムのpythonスクリプトにより、標準のCMakeのワークフロー を機能拡張したもの
source /opt/ros/<自分のROSのバージョン>/setup.bash
これは最初にやる必要があるので.bashrcに追加すると良いmkdir -p ~/ワークスペース名/src
catkin_init_workspace
このコマンドを実行するとCMakeLists.txtファイルを作成する
catkin_make
このコマンドを実行するとbuildとdevelというファイルが作成されるsource devel/setup.bash
注意1 必ず新しいワークスペースを作成したら上記のsetup.bashを実行すること
注意2 上記のsetup.bashを実行し、ワークスペースを有効化した場合、
ノード内のpythonファイルを変更すると自動で反映されるため、上記のsetup.bashは
ワークスペースを使う前には必ず実行すること
rosソフトウェアはパッケージという単位でまとめられる
パッケージはワークスペース内のsrcディレクトリの中に置かれる
1つのパッケージの中にはCMakeLists.txtとpackage.xmlが置かれている必要がある
package.xmlはパッケージ内容とcatkinがそのパッケージをどのように処理するかを説明している
catkin_create_pkg パッケージ名 依存ファイル(rospyなど)
複数のパッケージに依存する場合は依存ファイルを後ろに列挙できる
上記のコマンドを実行するとパッケージ名で指定したディレクトリが作成され、
中にCMakeLists.txtとpackage.xmlとsrcディレクトリができる
パッケージを作成したら、pythonで書いたノードをパッケージディレクトリのsrcディレクトリに入れる
同じノードを別の名前空間で起動することで名前の衝突を回避できる
2つ以上の名前空間に手が届く方法がリマッピング
roslaucnchはrosノードの集まりの起動を自動化するために作られたもの
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>には順番に
output="screen"とは
これはコンソールへの出力をファイルだけに書き出すのではなく、現在のコンソールにも
出力をするもので、デバックに使われる
デバックが完了したらこの属性は削除して問題ない
#ROSのC++ rosにてc++のノードを作成した際に発生した問題について
原因がrosのパッケージファイル内のCMakeList.txtとpackage.xmlにある
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の機能をpythonなどのプログラムを使用せずに利用する
rostopic pub トピック名 型 型に入れる数値
: publisherを作成する 既存のトピックでもOK
rostopic pub /test_hogehoge std_msgs/Int16 10
rostopic pub /test_hogehoge std_msgs/Int16 10 -r 1
git clone https://github.com/ros-drivers/rosserial.git
を実行catkin_make
を実行し、ビルドする上記の内容でROS側の基本的な準備が終了する
rm -rf ros_lib
で削除するrosrun rosserial_arduino make_libraries.py .
を実行する上記の内容でArduinoの準備が終了する
ArduinoIDEを起動し、スケッチ例にros_libがあれば成功
source deve/setup.bash
を実行rosrun rosserial_python serial_node.py _port:=/dev/~~ _baud:=115200
注意
/dev/~~
の部分はArduinoがつながっている部分に設定すること/dev/~~
にアクセスの許可がないためsudo chmod 666 /dev/~~
で許可をする_baud
はシリアル通信の周波数帯域なのでArduionoの設定とこの数値をあわせること基本的には設定や準備は必要なく、ArduinoIDEでプログラムを書いたら終わり
nh.getHardware()->setBaud(BAUD) //BAUD:9600,115200など
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より小さくすると接続に失敗する
#spinについて
rosにおいて購読のコールバックが呼び出されるまで待つものがspinである
c++においてはspinonce()があるためそこで一度だけコールバックを呼ぶことがわかる
しかしrospyではspinonce()がないためどのようにすべきか確認する
##rospyにおけるspin() spin()を呼び出すことでその関数が呼び出されるまで行動をブロックすることになる ex)
while True:
print("hello")
rospy.spin()
の場合,printははじめの一度しか呼ばれず、spin()の処理に入り、
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の処理後、呼び出された地点から再開する
#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と同じ内容のプログラムとなる