#はじめに
このチュートリアルはROSにおける一般的な開発フローを学べます。
また、独自のメッセージ型を定義して自分のソースコードに適用する仕方を学べます。
ROSのバージョンはgroovyを対象にしていますが基本的にバージョンは違っても大丈夫だと思います。
#1. ROSのワークスペースを作る
ROSのワークスペースを作ります。以下のコマンドを入力してください。
また、ROSに関するツールを使えるようにするため、パスを通しておきます。
mkdir -p ros_ws/src
cd ros_ws
source /opt/ros/groovy/setup.bash
ここで一度makeしておきます。ROSではcatkin_makeというコマンドを使ってワークスペースのプロジェクトをビルドします。また、初回でこのコマンドを行えばワークスペースに必要なファイルやディレクトリが生成されます。
catkin_make
catkin_makeしたら以下のようなファイル階層になっていると思います。
ros_ws
|--build
|--devel
|--src
- build
- ビルドに関する設定やmakeのlogのファイルが入っています。
- devel
- 実行ファイルやmakeによって生成されたものが入っています。
- src
- ユーザがソースコードを保存する場所です。
これでワークスペースはできました。
#2. ROSパッケージを作る
ROSでは任意の機能をつめたソフトウェアの集合をパッケージと呼びます。
パッケージの雛型はcatkin_create_pkgで作成することができます。またcatkin_create_pkgのコマンド引数は以下のとおりです。
catkin_create_pkg [package_name] [depend1] [depends2] ...
コンソールに以下のコマンドを入力してください。
cd src
catkin_create_pkg comp_tutorial roscpp std_msgs
この場合、パッケージの名前はcomp_tutorial、また依存関係を持たせるパッケージはroscpp、std_msgsということになります。
ワークスペースでもう一度catkin_makeしましょう。
cd ~/ros_ws/
catkin_make
ROSのコーディングに入る前にまず、ROSの通信モデルについて説明します。
ROSではPublish/Subscribeメッセージングと呼ばれる通信モデルでノード(ソフトウェア)同士が通信します。
各要素の役割は以下とおりです。
- Message:ROSで使用するデータの呼び名。中身のデータ構造はROSにあらかじめ用意されているものか独自の定義が可能
- Topic:メッセージを分類する系統ごとに作成される論理チャネル
- Publisher:メッセージを特定のトピックへ配信するノード
- Subscriber:特定のトピックを購読対象として登録しメッセージを受信するノード
ROSではこのようにPublisherとSubscriberがtopicを介してMessageをやり取りすることでデータ通信をします。
Publish/Subscribeの特徴として、P2Pな通信であることがあげられます。これは、お互いが通信相手の情報を知る必要はなく、ノード構成ネットワークへの脱退、参加が容易になります。
つまりこれはシステムへの機能の追加・変更がとても楽に行えることを意味します。また、1対1の通信専用の方法としてROS Serviceというものもあります。
#4. ROSのコーディング
ここではROSの具体的なコーディングを行います。
##ROS message
ROSでは処理に必要なデータは基本的にメッセージとして通信を行ないます。
そのメッセージのデータ構造はさまざまなデータ型から任意のものを選択して独自のデータ構造にすることができます。
たとえば、データ型には以下のものがあります。
- int8, int16, int32, int64 (plus uint*)
- float32, float64
- string
- time, duration
- other msg files
- variable-length array[] and fixed-length array[C]
簡単なメッセージファイルを作ってみましょう。
cd ~/ros_ws/src/comp_tutorial
mkdir msg; cd msg
touch adder.msg
emacs adder.msg
メッセージファイルadder.msgの中には以下の記述をしてください。
このmsgファイルでは8bitのunsigned int型の変数を2個保有していることになります。
uint16 a
uint16 b
このメッセージファイルを元にメッセージ型を定義するヘッダファイルが生成されます。
ヘッダファイル生成の設定を行うため以下のファイルを編集してください。
cd ~/ros_ws/src/comp_tutorial
emacs CMakeLists.txt
#該当意部分がはコメントアウトして適宜修正
#7行目あたり
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
message_generation
)
#45行目あたり
## Generate messages in the 'msg' folder
add_message_files(
FILES
adder.msg
)
#66行目あたり
generate_messages(
DEPENDENCIES
std_msgs
)
catkin_makeしましょう。
cd ~/ros_ws
catkin_make
catkin_makeに成功すると以下のディレクトリにメッセージを定義したヘッダファイルが生成されます。
less ~/ros_ws/devel/include/comp_tutorial/adder.h
…
namespace comp_tutorial
{
template <class ContainerAllocator>
struct adder_
{
typedef adder_<ContainerAllocator> Type;
adder_()
: a(0)
, b(0) {
}
adder_(const ContainerAllocator& _alloc)
: a(0)
, b(0) {
}
…
ROSのノードを記述する
ROSのノードをC++言語で記述していきます。
cd ~/ros_ws/src/comp_tutorial/src
touch para_in.cpp adder.cpp
- para_in.cpp:処理に必要なパラメータをPublishするPublisher
- adder.cpp:para_inから受け取ったパラメータを元に足し算をして表示するSubscriber
###Publisherを作る
以下に示すコードがPublisherとなります。
なお、コード中のAPIの説明などはコメントによって記しています。
// ros/ros.h ROSに関する基本的なAPIのためのヘッダ
#include "ros/ros.h"
// comp_tutrial/adder.h adder.msgから生成されたメッセージを定義しているヘッダ
#include "comp_tutorial/adder.h"
int main(int argc, char **argv)
{
// 初期化のためのAPI
// このノードは"para_in"という名前であるという意味
ros::init(argc, argv, "para_in");
// ノードハンドラの宣言
ros::NodeHandle n;
//Publisherとしての定義
// n.advertise<comp_tutorial::adder>("para_input", 1000);
// comp_tutorial::adder型のメッセージをpara_inputというトピックへ配信する
//"1000"はトピックキューの最大値
ros::Publisher para_pub = n.advertise<comp_tutorial::adder>("para_input", 1000);
//1秒間に1つのメッセージをPublishする
ros::Rate loop_rate(1);
//comp_tutrial::adder型のオブジェクトを定義
//adder.msgで定義したa,bはメンバ変数としてアクセスできる
comp_tutorial::adder msg;
int count = 0;
while (ros::ok())//ノードが実行中は基本的にros::ok()=1
{
msg.a = count;
msg.b = count;
para_pub.publish(msg);//PublishのAPI
printf("a = %d b = %d \n",msg.a , msg.b );
ros::spinOnce();
loop_rate.sleep();
count++;
}
return 0;
}
Subscriberをつくる
以下に示すコードがSubscriberとなります。
なお、コード中のAPIの説明などはコメントによって記し、Publisherと同じ部分のコメントは省いています。
#include "ros/ros.h"
#include "comp_tutorial/adder.h"
// Subscribeする対象のトピックが更新されたら呼び出されるコールバック関数
// 引数にはトピックにPublishされるメッセージの型と同じ型を定義する
void chatterCallback(const comp_tutorial::adder msg)
{
int result;
result = msg.a + msg.b;
printf("a:%d + b:%d = %d\n",msg.a , msg.b, result );
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "adder");
ros::NodeHandle n;
// Subscriberとしてpara_inputというトピックがSubscribeし、トピックが更新されたときは
// chatterCallbackという名前のコールバック関数を実行する
ros::Subscriber sub = n.subscribe("para_input", 1000, chatterCallback);
// トピック更新の待ちうけを行うAPI
ros::spin();
return 0;
}
2つのファイルの記述が終わったら、パッケージをビルドするために
CMakeLists.txtを編集します。
以下のコマンドでCMakeLists.txtを編集してください。
cd ~/ros_ws/src/comp_tutorial
emacs CMakeLists.txt
#ファイルの末尾に追加
add_executable(adder src/adder.cpp)
target_link_libraries(adder ${catkin_LIBRARIES})
add_executable(para_in src/para_in.cpp)
target_link_libraries(para_in ${catkin_LIBRARIES})
編集し終わったらビルドしましょう。
cd ~/ros_ws/
catkin_make
5. ROSで作ったノードを実行してみる
ビルドが成功したら、さっそく実行してみましょう。
現在開いているコンソールnほかにもう2つのコンソールを開き以下のコマンドを上からそれぞれ入力してください。
1つ目のコンソール
ROSではroscoreというコマンドを始めに起動することでさまざまなソフトウェアをスタートすることができます。
具体的にはroscoreはネームサービスなどを行います。
cd ~/ros_ws
source devel/setup.bash
roscore
2つ目のコンソール
ROSでは各ノードの実行はrosrunというコマンドによって実行されます。
**”adder”**を起動します。
cd ~/ros_ws
source devel/setup.bash
rosrun comp_tutorial adder
3つ目のコンソール
**”para_in”**を起動します。
cd ~/ros_ws
source devel/setup.bash
rosrun comp_tutorial para_in
起動に成功したら以下のような結果が得られます。
root@localhost:~/ros_ws# rosrun comp_tutorial para_in
a = 0 b = 0
a = 1 b = 1
a = 2 b = 2
a = 3 b = 3
a = 4 b = 4
a = 5 b = 5
root@localhost:~/ros_ws# rosrun comp_tutorial adder
a:1 + b:1 = 2
a:2 + b:2 = 4
a:3 + b:3 = 6
a:4 + b:4 = 8
a:5 + b:5 = 10
#おわりに
いかがだったでしょうか。ROSのチュートリアルをもう少し簡単にわかりやすくまとめてみました。
もし何かご意見ありましたらコメントお願いします。
#参考にしたサイト
ROSの公式Wiki http://wiki.ros.org/