ROSの基本的な開発
ROSのワークスペースを作る
ROSのワークスペースを作ります。以下のコマンドを入力してください。 また、ROSに関するツールを使えるようにするため、パスを通しておきます。
mkdir -p ros_ws/src cd ros_ws source /opt/ros/indigo/setup.bash
ここで一度makeしておきます。ROSではcatkin_makeというコマンドを使ってワークスペースのプロジェクトをビルドします。また、初回でこのコマンドを行えばワークスペースに必要なファイルやディレクトリが生成されます。
catkin_make
catkin_makeしたら以下のようなファイル階層になっていると思います。
ros_ws |--build |--devel |--src
- build
- ビルドに関する設定やmakeのlogのファイルが入っています。
- devel
- 実行ファイルやmakeによって生成されたものが入っています。
- src
- ユーザがソースコードを保存する場所です。
これでワークスペースはできました。
ROSパッケージを作る
ROSでは任意の機能をつめたソフトウェアの集合をパッケージと呼びます。 パッケージの雛型はcatkin_create_pkgで作成することができます。またcatkin_create_pkgのコマンド引数は以下のとおりです。
catkin_create_pkg [package_name] [depend1] [depends2] ...
コンソールに以下のコマンドを入力してください。
cd src catkin_create_pkg image_tutorial roscpp std_msgs sensor_msgs
この場合、パッケージの名前はimage_tutorial、また依存関係を持たせるパッケージはroscpp、std_msgs,sensor_msgsということになります。 ワークスペースでもう一度catkin_makeしましょう。
cd ~/ros_ws/ catkin_make
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]
またメッセージに関するクラスが定義されているヘッダーは/opt/ros/indigo/include
内にあります。
簡単なメッセージファイルを作ってみましょう。
cd ~/ros_ws/src/image_tutorial mkdir msg; cd msg touch image.msg emacs image.msg
メッセージファイルimage.msgの中には以下の記述をしてください。 このmsgファイルでは32bitのsigned int型の変数を1個,sensor_msgs/Image型の変数を1個保有していることになります。
image.msg
int32 frameID sensor_msgs/Image img
このメッセージファイルを元にメッセージ型を定義するヘッダファイルが生成されます。 ヘッダファイル生成の設定を行うため以下のファイルを編集してください。
cd ~/ros_ws/src/image_tutorial emacs CMakeLists.txt
CMakeLists.txt
#該当意部分がはコメント解除して適宜修正 #7行目あたり find_package(catkin REQUIRED COMPONENTS roscpp std_msgs + message_generation ) #45行目あたり ## Generate messages in the 'msg' folder add_message_files( FILES - # Message1.msg - # Message2.msg + image.msg ) #66行目あたり generate_messages( DEPENDENCIES std_msgs sensor_msgs )
catkin_makeしましょう。
cd ~/ros_ws catkin_make
catkin_makeに成功すると以下のディレクトリにメッセージを定義したヘッダファイルが生成されます。
less ~/ros_ws/devel/include/image_tutorial/image.h … namespace image_tutorial { template <class ContainerAllocator> struct image_ { typedef image_<ContainerAllocator> Type; image_() : frameID(0) , img() { } image_(const ContainerAllocator& _alloc) : frameID(0) , img(_alloc) { } typedef int32_t _frameID_type; _frameID_type frameID; typedef ::sensor_msgs::Image_<ContainerAllocator> _img_type; _img_type img; …
ROSのノードを記述する
ROSのノードをC++言語で記述していきます。
cd ~/ros_ws/src/image_tutorial/src touch publisher.cpp subscriber.cpp
- publisher.cpp:処理に必要な データをPublishするPublisher
- subscriber.cpp:publisherから受け取ったデータを表示するSubscriber
Publisherを作る
以下に示すコードがPublisherとなります。
なお、コード中のAPIの説明などはコメントによって記しています。
publisher.cpp
// ros/ros.h ROSに関する基本的なAPIのためのヘッダ #include "ros/ros.h" // image_tutorial/image.h image.msgから生成されたメッセージを定義しているヘッダ #include "image_tutorial/image.h" #include <stdlib.h> #include <iostream> using namespace std; int main(int argc, char** argv) { // 初期化宣言 // このノードは"publisher"という名前であるという意味 ros::init(argc, argv, "publisher"); // ノードハンドラの宣言 ros::NodeHandle n; // Publisherとしての定義 // n.advertise<image_tutorial::image>("image_data", 1000); // image_tutorial::image型のメッセージをimage_dataというトピックへ配信する //"1000"はトピックキューの最大値 ros::Publisher pub = n.advertise<image_tutorial::image>("image_data", 1000); //1秒間に1回の間隔でループする ros::Rate loop_rate(1); //image_tutorial::image型のオブジェクトを定義 //image.msgで定義したflameID,imgはメンバ変数としてアクセスできる image_tutorial::image msg; // 変数imgはsensor_msgs/Image型である // この型はもともとメンバ変数を持った型なので以下のような使い方でアクセスできる msg.img.height = 480; msg.img.width = 640; msg.img.encoding = "rgb8"; msg.img.step = msg.img.width; // sensor_msgs/Image型のデータ部にデータをプッシュバックしている // 実際はOpenCVなどで画像のRGB情報を得たあと,データを格納する for(int i = 0; i < msg.img.height; i++){ for (int j = 0; j < msg.img.width; j++){ msg.img.data.push_back(0xFF); } } int frameid = 0; //ノードが実行中は基本的にros::ok() = 1 // Ctrl + Cなどのインタラプトが起こるとros::ok() = 0となる while (ros::ok()) { msg.frameID = frameid; // Publishする関数 pub.publish(msg); cout << "published !" << endl; ros::spinOnce(); frameid++; loop_rate.sleep(); } return 0; }
Subscriberをつくる
以下に示すコードがSubscriberとなります。 なお、コード中のAPIの説明などはコメントによって記し、Publisherと同じ部分のコメントは省いています。
subscriber.cpp
#include "ros/ros.h" #include <stdio.h> #include "image_tutorial/image.h" #include <iostream> using namespace std; // Subscribeする対象のトピックが更新されたら呼び出されるコールバック関数 // 引数にはトピックにPublishされるメッセージの型と同じ型を定義する void chatterCallback(const image_tutorial::image msg) { cout << "height = " << msg.img.height << " width = " << msg.img.width << " frameID = " << msg.frameID << endl; } int main(int argc, char **argv) { ros::init(argc, argv, "subscriber"); ros::NodeHandle n; // Subscriberとしてimage_dataというトピックに対してSubscribeし、トピックが更新されたときは // chatterCallbackという名前のコールバック関数を実行する ros::Subscriber sub = n.subscribe("image_data", 1000, chatterCallback); // トピック更新の待ちうけを行う関数 ros::spin(); return 0; }
2つのファイルの記述が終わったら、パッケージをビルドするために CMakeLists.txtを編集します。 以下のコマンドでCMakeLists.txtを編集してください。
cd ~/ros_ws/src/image_tutorial emacs CMakeLists.txt
CMakeLists.txt
#ファイルの末尾に追加 + add_executable(publisher src/publisher.cpp) + target_link_libraries(publisher ${catkin_LIBRARIES}) + add_executable(subscriber src/subscriber.cpp) + target_link_libraries(subscriber ${catkin_LIBRARIES})
編集し終わったらビルドしましょう。
cd ~/ros_ws/ catkin_make
ROSで作ったノードを実行してみる
ビルドが成功したら、さっそく実行してみましょう。 現在開いているコンソールnほかにもう2つのコンソールを開き以下のコマンドを上からそれぞれ入力してください。
1つ目のコンソール
ROSではroscoreというコマンドを始めに起動することでさまざまなソフトウェアをスタートすることができます。
具体的にはroscoreはネームサービスなどを行います。
cd ~/ros_ws source devel/setup.bash roscore
2つ目のコンソール
ROSでは各ノードの実行はrosrunというコマンドによって実行されます。
ROSにおいて単体のノード実行は基本的にrosrunで行います。
subscriberを起動します。
cd ~/ros_ws source devel/setup.bash rosrun image_tutorial subscriber
3つ目のコンソール
publisherを起動します。
cd ~/ros_ws source devel/setup.bash rosrun image_tutorial publisher
起動に成功したら以下のような結果が得られます。
実行を止めたいときはCtrl + Cで止まります。
root@localhost:~/ros_ws# rosrun image_tutorial para_in published ! published ! published ! published ! published ! published ! root@localhost:~/ros_ws# rosrun image_tutorial adder height = 480 width = 640 flameID = 1 height = 480 width = 640 flameID = 2 height = 480 width = 640 flameID = 3 height = 480 width = 640 flameID = 4 height = 480 width = 640 flameID = 5 height = 480 width = 640 flameID = 6
コマンドまとめ
- catkin_make
- ワークスペース内のパッケージを一括ビルドするコマンド
- catkin_create_pkg
- ROSにおけるパッケージの雛形を作るコマンド
- roscore
- ROSのネームサービス、マスタ
- rosrun
- ROSの単体ノードを起動する際に使用するコマンド
課題
以下の仕様のパッケージadder_tutorialを作りましょう。
パッケージ内に含まれるノードの数は2つとします。
作るもの | 機能 | ファイル名 |
---|---|---|
Subscriber | input_dataというTopicに対してSubscribeする 入力値を2つ受け取り,足し算をして標準出力する ノード名:adder |
adder.cpp |
Publisher | input_dataというTopicに対してinput_value型のメッセージを1秒に1回Publishする ノード名:para_in |
para_in.cpp |
message | int32型の変数を2つ持つ input_valueと称す。 |
input_value.msg |
ヒント
- パッケージを作るときは
~/ros_ws/src
内でcatkin_create_pkg adder_tutorial roscpp std_msgs
としましょう。 - わからない点がある場合は積極的に質問してください。