概要
ROSで自作のパッケージ内に作成したPythonプログラムを、ほかのプログラムで継承して少し改変して使いたい場合や、頻繁につかう関数などをまとめた自作モジュールをimportしたいときの解決策です。
sys.path.appendなどの方法もありますが、まるでROSのデフォルトのパッケージからimportしているような書き心地にできる設定に成功したので共有します。
環境
OS : Ubuntu 18.04(WSL)
ROS dist : melodic
状況
「test_module」と「test_user」という2つの自作パッケージで考えます。
test_moduleがimportされるPythonコードを有するパッケージで、test_userがtest_moduleからPythonコードをimportして、ROSノードを立ち上げるプログラムを有します。
test_module
test_module内のmodule.pyが今回importしたいプログラムとします。module.pyは以下のようなプログラムとします。
#!/usr/bin/env python2
# coding: utf-8
class Module():
def __init__(self):
print("Module imported")
def call(self):
print("module used")
if __name__ == '__main__':
app = Module()
app.call()
以下、手順を説明します。
- まずtest_module内にscriptsフォルダを作成します。
- 次にtest_module/scripts内にtest_moduleというパッケージと同じ名前のフォルダを作成します。
- test_module/scripts/test_module/ の中にmodule.pyと__init__.pyを作成します。
・ __init__.pyには何も記入しません。 - 次に、パッケージのtest_module直下にsetup.pyを作成し、以下のように書きます。
from distutils.core import setup
from catkin_pkg.python_setup import generate_distutils_setup
## DO NOT run this code manually !!!
setup_args = generate_distutils_setup(
packages=['test_module'],
package_dir={'': 'scripts'},
)
setup(**setup_args)
- CMakeLists.txtのcatkin_python_setup()のコメントアウトを外します。
最終的なディレクトリ構成は以下のようになります。
test_module/
├ setup.py
├ scripts/test_module
├ __init__.py
├ module.py
test_user
続いてユーザ側のパッケージについてですが、こちらは特に設定をする必要がありません。
このパッケージ内でmodule.pyをimportするプログラム(user.py)を以下のように書きます。
#!/usr/bin/env python2
# coding: utf-8
import rospy
from test_module.module import Module
if __name__ == '__main__':
app = Module()
app.call()
実行してみる
catkin buildをします。すると、~/catkin_ws/devel/lib/python2.7/dist_packages/以下にtest_moduleというフォルダが作られています。C++のコードを含むパッケージをビルドした場合も作成されますが、通常C++コードを含まずPythonコードのみで構成されたパッケージをビルドしてもここにはそのパッケージ名のフォルダは作成されません。
次にroscoreを立てて、通常通りuser.pyをrosrunします。
すると、importエラーが出ずに実行に成功します。
$ rosrun test_user user.py
Module imported
module used
メリット
user.py内で、from test_module.module import Module
でimportできるのは、from geometry_msgs.msg import Twist
と表記するのと同じようにかけるので、コードに一貫性が出てとても気持ち良いです。
sys.path.appendなどよりもPythonコード自体に書くべきものを減らせるので、ミスも少なくなると思います。
参考
[ROS Q&A] How to import python modules from different ROS packages - The Construct
[ROS] How To Import a Python Module From Another Package - The Robotics Back-End