ROS 2 : Pythonでパッケージ作成するときのチップス
海洋ロボコンをやってた人です。
今回は久しぶりにPythonでROS 2 パッケージ ping_sonar_ros を作成したので、その備忘録として記載していきます。
なぜまとめるかは以下です。
- C++で慣れるとPython ROS 2の書き方忘れるため
- setup.pyとCMakeLists.txtで(インポートなどの)各依存関係の書き方が違うため(ここ大切)
また、本記事に対するコメントも積極的に募集しますので、よろしくお願いいたします。
※2023/10/25 追記:コメントいただいたパス設定箇所を修正しました。
1: ROS 2 Pythonパッケージ作成のチップス
ROS 2 Pythonの基本は以下です。
- パッケージの作成
cd ~/dev_ws/src
ros2 pkg create ping_sonar_ros --build-type ament_python
cd ping_sonar_ros/
mkdir launch
- ros2 run用に、エントリーポイントを登録
ノード名 = パッケージ名.pythonファイル名:main
entry_points={
'console_scripts': [
'range_node = ping_sonar_ros.range_pub_test:main',
],
},
Humbleで試したときに出たエラーハンドリング
以下をインストールで解決
pip install setuptools==58.2.0
1.1: launchファイルの追加、パス設定
いろいろ書き方はありますが、後から沢山ファイルを追加できるため、こちらの書き方が気に入ってます。
+import os
+from glob import glob
from setuptools import setup
package_name = 'ping_sonar_ros'
# build a list of the data files
+data_files = []
+data_files.append(("share/ament_index/resource_index/packages", ["resource/" ++ package_name]))
+data_files.append(("share/" + package_name, ["package.xml"]))
+def package_files(directory, data_files):
+ for (path, directories, filenames) in os.walk(directory):
+ for filename in filenames:
+ data_files.append(("share/" + package_name + "/" + path, glob(path + "/**/*.*", recursive=True)))
+ return data_files
+data_files = package_files('launch/', data_files)
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
- data_files=[
- ('share/ament_index/resource_index/packages',
- ['resource/' + package_name]),
- ('share/' + package_name, ['package.xml']),
- ],
+ data_files=data_files,
install_requires=['setuptools'],
zip_safe=True,
maintainer='xxx',
maintainer_email='xxx@xxx.com',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'range_node = ping_sonar_ros.range_pub:main',
],
},
)
さらにフォルダを追加したい場合は,以下のように記述すればOK
data_files = package_files('meshes/', data_files)
data_files = package_files('urdf/', data_files)
data_files = package_files('rviz/', data_files)
data_files = package_files('models/', data_files)
data_files = package_files('worlds/', data_files)
1.2: Submoduleの追加、パス設定
他のPythonファイルをサブモジュールとして追加、利用するためのチップス
- 自作のPythonモジュールを追加する場合
以下にまとめているので参考にしてください。
- GitHub等から既存のパッケージを追加する場合
git submodule addで追加し、サブモジュール化のsetup.py設定は上記と同様に設定します。
submodule addで追加してくことで、パッケージの依存関係がわかりやすいのでこちらを利用することにしてます。
git submodule add -b master https://github.com/bluerobotics/ping-python.git
- サブモジュールのフォルダや実行pythonファイルにハイフンが含まれる場合
Blue Roboticsのパッケージはなぜか、フォルダ化名にハイフンが多く、インポートができなくて困ったので記載します。
pythonでハイフンを含むフォルダをインポートするとシンタックスエラーとなるので、以下でインポートします。
import importlib
module_name = "ping_sonar_ros.ping-python.brping.ping1d"
module = importlib.import_module(module_name)
class Ping1dComponent(Node):
def __init__(self):
super().__init__("ping1d_node")
# Make a new Ping
self.ping = module.Ping1D()
1.3: python版 ROS 2 パラメータの宣言、取得
ROS 2 でパッケージを作成するからには、ros2 paramでパラメータを変更できると非常に便利です。
以下に「declare_parameter」でパラメータを宣言し、「get_parameter」でパラメータの値を取得します。
またパラメータの変更は、About parameters in ROS 2 にも記載のある「add_on_set_parameters_callback」を使用して取得します。
from rcl_interfaces.msg import SetParametersResult
class Ping1dComponent(Node):
def __init__(self):
self.declare_parameter('interval_num', 100)
self.interval_num_:float = self.get_parameter('interval_num').value
self.param_handler_ptr_ = self.add_on_set_parameters_callback(self.set_param_callback)
def set_param_callback(self, params):
result = SetParametersResult(successful=True)
for param in params:
if param.name == 'interval_num':
self.interval_num_ = param.value
self.get_logger().info('Updated my_param value: %f' % self.interval_num_)
self.ping.set_ping_interval(self.interval_num_)
return result
2: Error Handling
前提、ビルドがそもそもできないときは下記がインストールされていることを確認する。
(初回でインストールしているはずだが念の為)
sudo apt update
sudo apt install python3-colcon-common-extensions python3-setuptools python3-pip
pip3 install -U setuptools
2.1: colcon build でsetup.py系統のエラー
File "/home/ubuntu/.local/lib/python3.10/site-packages/sphinx/setup_command.py", line 20, in <module>
from sphinx.application import Sphinx
File "/home/ubuntu/.local/lib/python3.10/site-packages/sphinx/application.py", line 32, in <module>
from sphinx.config import Config
File "/home/ubuntu/.local/lib/python3.10/site-packages/sphinx/config.py", line 23, in <module>
from sphinx.util import logging
File "/home/ubuntu/.local/lib/python3.10/site-packages/sphinx/util/__init__.py", line 35, in <module>
from sphinx.util import smartypants # noqa
File "/home/ubuntu/.local/lib/python3.10/site-packages/sphinx/util/smartypants.py", line 33, in <module>
from sphinx.util.docutils import __version_info__ as docutils_version
File "/home/ubuntu/.local/lib/python3.10/site-packages/sphinx/util/docutils.py", line 31, in <module>
from sphinx.util.typing import RoleFunction
File "/home/ubuntu/.local/lib/python3.10/site-packages/sphinx/util/typing.py", line 34, in <module>
from types import Union as types_Union
ImportError: cannot import name 'Union' from 'types'
Sphinx
のバージョンを更新することで解決した
pip install --upgrade sphinx setuptools
以上、ROS 2 Pythonでパッケージを作成するときのチップスでした。
皆様のお役に立てれば幸いです。
Discussion
ROS 2初学者でファイルまでのパスを取得するのに大変助かりました!
globの前に+が入ってたため,うまくビルドが通りませんでした.\
そちらの環境では,うまくいっていますか?
K.iso様
こちらこそ、お役に立てて幸いです。
ご指摘いただいた件、+は誤記になりますので修正しました。
コメントいただきありがとうございます。