View on GitHub

introduction-to-ros

ROS (Robot Operating System) 入門

トピック通信実践

前のページ

目次

概要

実際に手を動かしながらROSで非常に重要な概念であるトピック通信について理解を深めます.

ROS Wiki公式のチュートリアル

ROSパッケージの作成

cd ~/catkin_ws/src
catkin_create_pkg my_ros_tutorial std_msgs roscpp rospy

各行の説明

ROSノードの実装

ここではC++で実装します.余談にPythonでの実装例も載せています.

これから作るもの

my_simple_publisherという名前のpublisher,my_simple_subscriberという名前のsubscriberを実装します.

my_simple_publisherはmy_topicという名前のトピックをpublish,my_simple_subscriberはmy_topicという名前のトピックをsubscribeするようにします.

my_topicの型はstd_msgs::Stringとして,”hello world!”という文字列を格納するという設計にします.

実装

先程catkin_create_pkgで作成したmy_ros_tutorialパッケージにはsrcディレクトリが自動生成されています. srcディレクトリの中に以下のファイルを作成します.

参考

http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29

my_simple_publisher.cpp

#include <ros/ros.h>
#include <std_msgs/String.h>

int main(int argc, char **argv)
{
    ros::init(argc, argv, "my_simple_publisher");
    ros::NodeHandle nh;
    ros::Publisher simple_pub = nh.advertise<std_msgs::String>("my_topic", 1);
    ros::Rate loop_rate(10);

    while (ros::ok())
    {
        std_msgs::String msg;
        msg.data = "hello world!";
        ROS_INFO("publish: %s", msg.data.c_str());
        simple_pub.publish(msg);

        ros::spinOnce();
        loop_rate.sleep();
    }
    return 0;
}

各行の意味

#include <ros/ros.h>
#include <std_msgs/String.h>

コードを書くのに必要なライブラリを導入

int main(int argc, char **argv)
{
    ros::init(argc, argv, "my_simple_publisher");
    ros::NodeHandle nh;
    ros::Publisher simple_pub = nh.advertise<std_msgs::String>("my_topic", 1);
    ros::Rate loop_rate(10);

メイン関数冒頭

    while (ros::ok())
    {
        std_msgs::String msg;
        msg.data = "hello world!";
        ROS_INFO("publish: %s", msg.data.c_str());
        simple_pub.publish(msg);

        ros::spinOnce();
        loop_rate.sleep();
    }
    return 0;

メイン関数後半

my_simple_subscriber.cpp

#include <ros/ros.h>
#include <std_msgs/String.h>

void mytopicCallback(const std_msgs::String &msg)
{
    ROS_INFO("subscribe: %s", msg.data.c_str());
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "my_simple_subscriber");
    ros::NodeHandle nh;
    ros::Subscriber sub = nh.subscribe("my_topic", 1, mytopicCallback);

    ros::spin();
    return 0;
}

各行の意味

main関数のノードハンドラまではpublisherと同様なので割愛.

    ros::Subscriber sub = nh.subscribe("my_topic", 1, mytopicCallback);

    ros::spin();
    return 0;
void mytopicCallback(const std_msgs::String &msg)
{
    ROS_INFO("subscribe: %s", msg.data.c_str());
}

コールバック関数です.

ビルドの設定

my_ros_tutorialパッケージに自動生成されているCMakeLists.txtを編集して作成したcppファイルをビルド対象にします. VS CodeでCMakeの拡張機能を入れると色がついて見やすいです.

cmake_minimum_required(VERSION 3.0.2)
project(my_ros_tutorial)

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
)

catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES my_ros_tutorial
#  CATKIN_DEPENDS roscpp rospy std_msgs
#  DEPENDS system_lib
)

include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)

add_executable(my_simple_publisher src/my_simple_publisher.cpp)
target_link_libraries(my_simple_publisher ${catkin_LIBRARIES})

add_executable(my_simple_subscriber src/my_simple_subscriber.cpp)
target_link_libraries(my_simple_subscriber ${catkin_LIBRARIES})

最後の4行を追記しています.publisherとsubscriberで名前が違うだけでやっていることは一緒なのでsubscriberのところを例に意味を説明します.

add_executable(my_simple_subscriber src/my_simple_subscriber.cpp)

my_simple_subscriberという実行ファイルをsrc/my_simple_subscriber.cppから生成します.

target_link_libraries(my_simple_subscriber ${catkin_LIBRARIES})

target_link_librariesで,my_simple_subscriberを作成する際に使用するライブラリをリンクさせることができますが,ROSでは${catkin_LIBRARIES}という環境変数を指定すれば基本的にはokです.

ビルド

catkin build

ビルドすることで初めて実行ファイルが生成されます. コードやCMakeLists.txtがミスっているとここでエラーが出るので,エラーメッセージを見ながら修正しましょう.

動作確認

複数のターミナルを用います.

一つ目のターミナル

roscore

ros masterを起動します.

二つ目のターミナル

rosrun my_ros_tutorial my_simple_publisher

rosrunコマンドでrosノードを起動します. my_ros_tutorialのmy_simple_publisherを実行します.my_simple_publisherは,先程CMakeLists.txtで指定した,生成される実行ファイルの名前です.

三つ目のターミナル

rosrun my_ros_tutorial my_simple_subscriber

subscriberを実行します.

以下のようにpublisherのROS_INFO,subscriberのコールバック関数内のROS_INFOによってhelo world!が10Hzで表示されればokです.

概形確認

rqt_graph

rqt_graphというコマンドを打つと,ROS nodeとtopicの関係を以下のように可視化することができます.

演習問題

1. “hello world!”ではなく,”introduction-to-ros!”というデータをpublish, subscribeしてみよう

解答例

リンク

次のページ

目次


余談

package.xml

catkin_create_pkgすると自動生成されるファイルの一つです.ROS packageに関する情報が記載されており,ROS側はpackage.xmlが配置されているディレクトリをROSパッケージだと認識します.

パッケージ製作者の名前やメールアドレスといった事務的な情報から,このROSパッケージが依存している他のROSパッケージやライブラリの情報等が記載されており,特に後者は重要です. 興味のある方は以下のリンクを読んでみてください.

ROS講座97 CMakeList.txtとpackage.xmlの書き方

野良packageへ依存したROS packageのビルドを簡単にする

ROS Wiki

PythonでのROSノード実装例

my_simple_publisher.py

#!/usr/bin/env python
import rospy
from std_msgs.msg import String

def publisher():
    rospy.init_node('my_simple_publisher')
    simple_pub = rospy.Publisher('my_topic', String, queue_size=1)

    r = rospy.Rate(10)
    while not rospy.is_shutdown():
        str = "hello world!"
        rospy.loginfo("publish: %s", str)
        pub.publish(str)
        r.sleep()

if __name__ == '__main__':
    try:
        publisher()
    except rospy.ROSInterruptException: pass

※roscppと異なり,rospyではspinOnceを記述しなくても勝手にcallbackを呼び出してくれます.(今回は関係ありませんが)

my_simple_subscriber.py

#!/usr/bin/env python
import rospy
from std_msgs.msg import String

def mytopicCallback(msg):
    rospy.loginfo("subscribe: %s", msg.data)

def subscriber():
    rospy.init_node('my_simple_subscriber')
    rospy.Subscriber("my_topic", String, mytopicCallback)
    rospy.spin()

if __name__ == '__main__':
    subscriber()

C++のコードと比較すれば各行の意味は察せると思います.

NodeHandle等もないし,やっぱりPythonのほうが書くのは圧倒的に楽です.

また,ビルドが必要ないのでCMakeLists.txtを編集したりcatkin buildする必要もないです.

今回は逆にCMakeLists.txtやcatkin buildについても説明するためにまずはC++で説明しました.

C++ vs Python

じゃあ全部Pythonでいいじゃんと思えてきますが,Pythonは実行時間が遅いという欠点があります.

私は作成するROSノードの仕様によって言語を選択していて,例えばロボットのモータを回す制御量決定や経路計画のコードみたいな比較的リアルタイム性が求められるROSコードにはC++を用い,この行動が終わったら次はこの行動に移ってといったタスク管理のような高レイヤのROSコードはPythonで書いたりしています.

とはいえ割と気分で,高速で回したいときかつちゃんと書きたいときはC++,適当にサクッと実装したいときはPythonのような感じになってしまっているかもしれません.

あと,画像処理等Pythonのライブラリが非常に強力な分野のコードはPythonで書いてしまうことが私は多いです.