🐍

ROS 2 : Pythonでパッケージ作成するときのチップス

2023/05/07に公開
2

海洋ロボコンをやってた人です。

今回は久しぶりにPythonでROS 2 パッケージ ping_sonar_ros を作成したので、その備忘録として記載していきます。

なぜまとめるかは以下です。

  • C++で慣れるとPython ROS 2の書き方忘れるため
  • setup.pyとCMakeLists.txtで(インポートなどの)各依存関係の書き方が違うため(ここ大切)

また、本記事に対するコメントも積極的に募集しますので、よろしくお願いいたします。


※2023/10/25 追記:コメントいただいたパス設定箇所を修正しました。

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

setup.py
    entry_points={
        'console_scripts': [
            'range_node = ping_sonar_ros.range_pub_test:main',
        ],
    },

Humbleで試したときに出たエラーハンドリング

以下をインストールで解決

pip install setuptools==58.2.0

launchファイルの追加、パス設定

いろいろ書き方はありますが、後から沢山ファイルを追加できるため、こちらの書き方が気に入ってます。

setup.py
+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

setup.py
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)

Submoduleの追加、パス設定

他のPythonファイルをサブモジュールとして追加、利用するためのチップス

  • 自作のPythonモジュールを追加する場合

以下にまとめているので参考にしてください。

https://zenn.dev/tasada038/articles/5d8ba66aa34b85


  • 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()

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

以上、ROS 2 Pythonでパッケージを作成するときのチップスでした。

皆様のお役に立てれば幸いです。

Discussion

K.isoK.iso

ROS 2初学者でファイルまでのパスを取得するのに大変助かりました!
globの前に+が入ってたため,うまくビルドが通りませんでした.\

data_files.append(("share/" + package_name + "/" + path, +glob(path + "/**/*.*", recursive=True)))

そちらの環境では,うまくいっていますか?

Takumi AsadaTakumi Asada

K.iso様
こちらこそ、お役に立てて幸いです。
ご指摘いただいた件、+は誤記になりますので修正しました。
コメントいただきありがとうございます。