コンテンツへスキップ

以前、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の情報を扱う際には同期が活躍します。

雑記

モーターとセンサを取り付けたい。こいつ単体で何かできるようにしてやりたい。

参考