OpenNH

日常のひとこま(自分用のメモとかあれこれ)

Python3でROS2を初める(Ubuntu18.04LTS)

目次



はじめに


ROS2にはPython3用のクライアントライブラリ「rclpy」が提供されています。 そこでこの記事では、rclpyを使った基本的なpublisher/subscriberのROS2パッケージ作成方法について簡単に紹介します。
書き方が正しくない可能性がありますので、ご指摘はコメント欄にお願いします。

環境構成

colcon_ws
 ├── buld
 ├── install
 ├── log
 └── src
     └── <package1>
     └── <package2>
      ...


ROS2パッケージの生成


ros2 pkg createコマンドでROS2パッケージの雛形を生成します。今回はrclpyを利用するので、--build-typeでament_pythonを指定します。 なお、C++のROS2パッケージを生成する場合は、ament_cmakeとします。

$ cd ~/colcon_ws/src
$ ros2 pkg create --build-type ament_python py_hello_ros2

ここで、「py_hello_ros2」はパッケージ名であり任意に設定してください。

上記コマンドでパッケージを生成すると、以下のような構成となっているはずです。 ROS2のPython3パッケージではCMakeLists.txtは必要なく、package.xmlとsetup.pyだけでよくなりました。

$ cd py_hello_ros2
$ tree
.
├── package.xml
├── py_hello_ros2
│   └── __init__.py
├── resource
│   └── py_hello_ros2
├── setup.cfg
├── setup.py
└── test
    ├── test_copyright.py
    ├── test_flake8.py
    └── test_pep257.py


package.xmlの記述


まず、package.xmlを記述していきます。C++で記述するときと書き方は同様です。

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>py_hello_ros2</name>
  <version>0.1.0</version>
  <description>Sample of publishers/subscribers using rclpy.</description>
  <maintainer email="yourname@mail.domain">yourname</maintainer>
  <license>Apache License, Version 2.0</license>

  <exec_depend>rclpy</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  <exec_depend>launch_ros</exec_depend>

  <test_depend>ament_copyright</test_depend>
  <test_depend>ament_flake8</test_depend>
  <test_depend>ament_pep257</test_depend>
  <test_depend>python3-pytest</test_depend>

  <export>
    <build_type>ament_python</build_type>
  </export>
</package>


XMLタグ 説明
<exec_depend> パッケージを実行するために必要な依存パッケージ
<test_depend> パッケージをテストするために必要な依存パッケージ


setup.pyの記述


次に、setup.pyを記述していきます。ROS2のための特殊な書き方は特になく、詳しくはPython公式ドキュメント(日本語)を参照してください。具体的な書き方についてはsampleproject/setup.py at master · pypa/sampleprojectが注釈付きで記載されておりわかりやすいかと思います。

from setuptools import setup

package_name = 'py_hello_ros2'

setup(
    name=package_name,
    version='0.1.0',
    packages=[package_name], # Directory of source code.
    data_files=[ # File other than souece code
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'], # Dependent python3 modules
    zip_safe=True,
    maintainer='yourname',
    maintainer_email='yourname@mail.domain',
    keywords=['ROS2'],
    classifiers=[
        'Intended Audience :: Developers',
        'License :: OSI Approved :: Apache Software License',
        'Programming Language :: Python',
        'Topic :: Software Development',
    ],
    description='Sample of publishers/subscribers using rclpy.',
    license='Apache License, Version 2.0',
    tests_require=['pytest'], # Test frame name
    entry_points={ # Execution command name and its call destination
        'console_scripts': [
            'publisher = '+ package_name + '.publisher:main',
            'subscriber = '+ package_name + '.subscriber:main',
        ],
    },
)

記載内容の簡単な説明

  • name : パッケージ名
  • version : パッケージのバージョン
  • packages : ソースが格納されるディレクトリ名
  • data_files :ソース以外のファイル
  • install_requires : このパッケージが依存するpython3モジュールを列挙
  • zip_safe : "True"にすると, 全てのファイルを単一の.egg(zipファイル)にまとめる (デフォルト zip_safe=True)
  • maintainer : 管理者の名前
  • maintainer_email: 管理者のコンタクト先
  • keywords : パッケージを検索するとき用のキーワード
  • classifiers:そパッケージを検索するとき用のタグ名。タグ一覧はコチラを参照。
  • description : パッケージについての簡潔な概要説明 (200文字以内の一文)。長い説明を記載したい場合は、long_descriptionを利用する。
  • license : パッケージのライセンス
  • tests_require : テスト実行時に必要なモジュールのリスト。詳細はコチラを参照。
  • entry_points : 実行コマンド名とその呼び出し先の指定。'console_scripts'で指定されている文字列が、ros2 runコマンドで使用される。
    例) $ ros2 run py_hello_ros2 publisher

公式の説明はコチラを参照してくだい。


トピックの実装


ROSのチュートリアルでよく利用される、chatterトピックを送受信する”Publisher"と"Subscriber”を実装していきます。
ソースコードの格納先は以下のようになります。

py_hello_ros2
├── package.xml
├── py_hello_ros2
│   └── __init__.py
│   └── publisher.py #<--ここ
│   └── subscriber.py #<--ここ
├── resource
├── setup.cfg
├── setup.py
└── test

publisher.py

import rclpy
from rclpy.node import Node

from std_msgs.msg import String

class Publisher(Node):
    def __init__(self):
        super().__init__('publisher')
        self.count = 0
        self.publisher = self.create_publisher(String, 'chatter')
        self.timer = self.create_timer(0.5, self.timer_callback)

    def timer_callback(self):
        self.count += 1
        msg = String()
        msg.data = "Hello Publisher! [{0}]".format(self.count)
        self.publisher.publish(msg)
        self.get_logger().info(msg.data)

def main():
    rclpy.init()
    publisher_node = Publisher()
    rclpy.spin(publisher_node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

subscriber.py

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Subscriber(Node):
    def __init__(self):
        super().__init__('subscriber')
        self.subscription = self.create_subscription(
            String(), '/chatter', self.listener_callback)
    
    def listener_callback(self, msg):
        self.get_logger().info(msg.data)

def main():
    rclpy.init()
    subscriber_node = Subscriber()
    rclpy.spin(subscriber_node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()


ビルドと実行


colcon buildコマンドを用いてビルドしていきます。

colcon build

$ cd ~/colcon_ws
$ colcon build --base-path src/py_hello_ros2
$ source install/setup.bash

実行

Terminal #1

$ ros2 run py_hello_ros2 publisher
[INFO] [publisher]: Hello Publisher! [1]
[INFO] [publisher]: Hello Publisher! [2]
[INFO] [publisher]: Hello Publisher! [3]
...

Terminal #2

$ ros2 run py_hello_ros2 subscriber
[INFO] [subscriber]: Hello Publisher! [1]
[INFO] [subscriber]: Hello Publisher! [2]
[INFO] [subscriber]: Hello Publisher! [3]
...

参考