初めに
前回はros2でpythonのサンプルを実行しました1.
今回は自分で作成したプログラムを動かす方法とポイントをまとめます.
ターミナルを立ち上げたままだと環境情報が残っている場合があるので再度立ち上げてから行ってください.
ワークスペースとパッケージの作成
まずはワークスペースを作ります.
mkdir -p ~/ros2_my_python_ws/src
cd ~/ros2_my_python_ws/src
自分でpython用のROS2パッケージを作成して実行していきます2.
ros2 pkg create [パッケージ名]
コマンドでros2のパッケージを作成します.
ros1でのcatkin_create_packageに当たります.
ros2 pkg create my_python
これで必要なファイルが生成されました.ディレクトリ構造は以下のようになります.
ros2_my_python_ws
└── src
└── my_python
├── CMakeLists.txt
├── include
│ └── my_python
├── package.xml
└── src
pythonプログラムの作成
先程作成したmy_pythonパッケージ
の中に移動してください.
pythonプログラムを配置するフォルダを作ります.
フォルダ名はなんでもいいです.
mkdir python_programs
この中にlistenerとtalkerのサンプルプログラムを入れます.
listener.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Listener(Node):
def __init__(self):
super().__init__('listener')
self.sub = self.create_subscription(String, 'chatter', self.chatter_callback, 10)
def chatter_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
def main(args=None):
rclpy.init(args=args)
try:
listener = Listener()
rclpy.spin(listener)
finally:
listener.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
talker.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Talker(Node):
def __init__(self):
super().__init__('talker')
self.i = 0
self.pub = self.create_publisher(String, 'chatter', 10)
self.timer = self.create_timer(1.0, self.timer_callback)
def timer_callback(self):
msg = String()
msg.data = 'Hello World: {0}'.format(self.i)
self.i += 1
self.get_logger().info('Publishing: "{0}"'.format(msg.data))
self.pub.publish(msg)
def main(args=None):
rclpy.init(args=args)
try:
talker = Talker()
rclpy.spin(talker)
finally:
talker.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
最後にポイントとして,python_programs
フォルダに作成したpythonファイルが認識されるように__init__.py
を追加します.
touch __init__.py
作成したROSパッケージのディレクトリ構造は以下のようになります.
./my_python
├── CMakeLists.txt
├── include
│ └── my_python
├── package.xml
├── python_programs
│ ├── __init__.py
│ ├── talker.py
│ └── listener.py
└── src
package.xmlの編集
まずデフォルトでは以下のようになります.
package.xml
<?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>my_python</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="hoge@gmail.com">root</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
<build_type>
がcpp用になっていたりするのでpython用に書き換えます.
package.xml
<?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>my_python</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="hoge@gmail.com">root</maintainer>
<license>TODO: License declaration</license>
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
CMakeLists.txtの削除とpython用ファイルの追加
現時点でのros2 create pkg
コマンドはpython用ではなく,c++用のファイル(CMakeLists.txt
)を生成します.
今回はpythonだけを動かしたいのでc++のコンパイル対象にならないようにCMakeLists.txt
は削除します.
rm CMakeLists.txt
setup.pyの追加
pythonをros2で実行するためにsetup.py
を追加します.
ros1の頃はこのようなファイルは必要なかったのですが,ros2から導入されたようです.
自分用に書き換えるのはpackage_name,py_modules,entry_points
あたりでしょうか.
他はメタデータだったりするので適当で.
setup.py
from setuptools import setup
package_name = 'my_python'
setup(
name=package_name,
version='0.0.0',
packages=[],
py_modules=[
'python_programs.talker',
'python_programs.listener',
],
install_requires=['setuptools'],
zip_safe=True,
author='user',
author_email="user@todo.todo",
maintainer='user',
maintainer_email="user@todo.todo",
keywords=['ROS', 'ROS2'],
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Topic :: Software Development',
],
description='TODO: Package description.',
license='Apache License, Version 2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'my_talker= python_programs.talker:main',
'my_listener= python_programs.listener:main',
],
},
)
ポイント
いくつかポイントがあります.
py_modules
について
py_modules=["foo","bar.hoge"]
これは
foo.py
はsetup.py
と同じディレクトリにあるということです.
bar.hoge
はsetup.py
と同じディレクトリにbar
というフォルダが有り,その中にhoge.py
があるということです.
以下のような構造になります.
.
├── bar
│ └── hoge.py
├── foo.py
└── setup.py
フォルダ構造を自分で作るとハマリポイントになります.
entry_points
について
entry_pointsは実行するファイルと関数を指定します.
'my_talker= python_programs.talker:main',
これはmy_talkerを実行すると,python_programsフォルダの中のtalker.pyの中のmain関数を呼び出すということです.
setup.cfgの追加
次にsetup.cfg
を追加します.自分で作成したros2のパッケージ名をしっかりと指定しましょう.
[develop]
script-dir=$base/lib/my_python
[install]
install-scripts=$base/lib/my_python
ここでしっかりと指定しないと
$ ros2 run my_python my_talker
$ No executable found
などのエラーが発生します.
以上を行ったディレクトリ構造は以下のようになります.
./my_python
├── include
│ └── my_python
├── package.xml
├── python_programs
│ ├── __init__.py
│ ├── listener.py
│ └── talker.py
├── setup.cfg
├── setup.py
└── src
コンパイルと実行
コンパイルします.
cd ~/ros2_my_python_ws
colcon build
コンパイルの出力先フォルダにパスを通します.
source install/setup.bash && source install/local_setup.bash
talkerを実行します.
ros2 run my_python my_talker
ターミナルを新しく開いて同様にsourceをしてパスを通してからlistenerを起動します.
ros2 run my_python my_listener
これでtalkerからpublishされたメッセージをlistenerがsubscribeします.
おわりに
ros2で自分で作成したpythonプログラムを動かす方法をまとめました.
ros2 pkg create
コマンドがpython用に働かないため,自分でディレクトリ構成を整えるのが大変だと思いました.
pythonのモジュールの読み込み方を知らない人などは__init__.py
などのハマりポイントがあります.
飽きてなかったら次はpythonとC++を共生させる方法について書きます.