以前、Raspberry Pi 3 B + に Ubuntu MATE(16.04) を入れ、ROSでhelloworldしました。 基本的な機能を確認します。
catkin_makeは重いかも。ラズパイちゃんが固まるので注意。
目次
- ROSとは
- Topic
- Topicのサンプルコード
- 動作
- 雑記
- 参考
ROSとは
ロボット開発を促進するミドルウェアです。ROSではすべてモジュールとモジュール間通信の形式で構成されています。誰かが作ったプログラムを、詳細を知らないままに使用できます。1つ1つのモジュールをノードと呼びます。
ROSではすべての処理が「ノード(Node)とノードがメッセージ(Message)をやりとりする」という構造になっています。それらの集合がパッケージ(Package)と呼ばれます。ノードとノードの通信を行うのがマスタ(Master)で、roscoreコマンドで起動します。
ビルドはすべてパッケージフォルダ内のCMakeLists.txtに記述され、catkinで行われます。(Hydroバージョン以降)
- Topic
- Service
- Parameter
この投稿では参考の本のtopicついてさわりのみ扱います。
topic

濃い青が実際に作るプログラム、薄い青がプログラムによって作られたもの、矢印がデータの流れを表しています。
Topic はノード間でやり取りされるデータの入れ物(話題)です。データを送る動作をpublish、データを受け取る動作をSubscribeといいます。
Publisherはノードの起動時にTopic名をMasterに登録し、Messageで定めた形式で他のノードに送信します。 Subscriverは指定されたTopicを発振しているPublisherを問い合わせます。
名前と型などが設定されていて、Topicの名前を指定すればどのノードからでもデータを読み取ることができます。また一度接続されればメッセージ送受信が継続されます (非同期通信)ので、高頻度の通信を行うセンサ信号の送受信などに使用されます。
topicのサンプルコード
$cw
$catkin_create_pkg tutorial_topic roscpp std_msgs
まず上のようにパッケージを作成します。catkin_create_pkg パッケージ名 依存パッケージ のように書きます。
publisher.cpp
#include <ros/ros.h>
#include <std_msgs/Int32.h>
int main(int argc, char **argv)
{
ros::init(argc, argv, "publisher");
ros::NodeHandle nh;
ros::Publisher pub = nh.advertise<std_msgs::Int32>("number", 10);
ros::Rate loop_rate(1);
std_msgs::Int32 cnt;
cnt.data = 0;
while(ros::ok())
{
ROS_INFO("Count : %d", cnt.data);
pub.publish(cnt);
cnt.data++;
loop_rate.sleep();
}
return 0;
}
- ros::init(argc, argv, ノード名)はノードの初期化を行う関数です。
- ros::NodeHandle はROSシステムにアクセスし、他のノードと通信を行うクラスです。
- ros::NodeHandle::.advertise(topicの名前,バッファ容量)でtopicの登録を行っています。
- loop_rate(1)で1Hzのウェイトをかけてループしています。
- std_msgs::Int32は32bit符号付整数のラッパークラスです。ラッパークラスとは、別のプログラム(群)を包んで隠蔽し、使いやすくしたクラスです。今はデータはcnt.dataに格納されています。
- ros::ok()は基本的にtrue、CNTR+Cで停止したときにfalseを返します。
subscriber.cpp
#include <ros/ros.h>
#include <std_msgs/Int32.h>
void onNumberSubscribed(const std_msgs::Int32 &msg)
{
ROS_INFO("I heard: [%d]", msg.data);
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "subscriber");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe("number", 10, onNumberSubscribed);
ros::spin();
return 0;
}
- ros::NodeHandle::subscribe(topic名, バッファ容量, コールバック関数名)でSubscribeするTopicを設定しています。今は"number"というTopicを受信するたびにonNumberSubscribedが呼び出されます。
- ros::spin()は無限ループで、CNTR+Cで中断されると、以降の処理は行われません。
CMakeLists.txt
cmake_minimum_required(VERSION 2.8.3)
project(tutorial_topic)
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
)
catkin_package()
include_directories(${catkin_INCLUDE_DIRS})
add_executable(publisher src/publisher.cpp)
target_link_libraries(publisher ${catkin_LIBRARIES})
add_executable(subscriber src/subscriber.cpp)
target_link_libraries(subscriber ${catkin_LIBRARIES})
CMakeLists.txtはファイルを作成する指示書みたいなものです。自動で作成されるものを編集します。
- cmake_minimum_requiredにはバージョン、prjectにはproject名、find_packageには依存パッケージが入っていると思います。
- catkin_packageはcatkinビルドのオプションを指定します。
- include_directories()はインクルードフォルダを指定します。今は各パッケージのincludeフォルダのヘッダーファイルを指定しています。
add_executable(実行ファイル名 src/実行ファイル名.cpp)以下をそれぞれ書き足します。add_executableはビルド後に実行するファイルを、target_link_librariesには生成時に必要なファイルを指定します。以下で ビルドします。
$cd ~/catkin_ws
$caikin_make -DCATKIN_WHITELIST_PACKAGES="パッケージ名"
$roscore
$rosrun tutorial_topic subscriber
$rosrun tutorial_topic publisher
指定したパッケージのみビルドし、それぞれ実行します。rosrunなどは起動したままにするので、端末は複数開いてください。
topicの動作

出力はこんな感じ。下のコマンドでノードとデータの移動をグラフとして表示できます。
$rosrun rqt_graph rqt_graph

publisherノードとsubscriberノードがnumberというtopicを受け渡ししているのがわかります。
topicはプリミティブ型は使えないので、ラッパーされたメッセージ型を使わなければなりません。int型の代わりにstd_msgsパッケージのInt32というメッセージ型を使っています。
その他
参考にした本には以下についてのサンプルコードが載っています。
- 独自型の定義
- spinOnce
- AsyncSpinner
- 同期
ROSでは独自型を定義する事ができます。ただし、ROSの「他の人のコードも利用できる」という性質上、独自型を作成するのは最低限にすべきでしょう。 また、上のサンプルコードにはspin関数を使っているので複数のTopicをSubscribeする事はできません。ノンブロッキングなspinOnceなどを使う必要があります。
また、Lidarの情報を扱う際には同期が活躍します。
雑記
モーターとセンサを取り付けたい。こいつ単体で何かできるようにしてやりたい。