Skip to content

クラスでのパラメータの使用 (Python)

目標: Pythonを使用してROSパラメータを持つクラスを作成し実行する。

チュートリアルレベル: 初心者

所要時間: 20分

背景

独自のノードを作成する際、ローンチファイルから設定できるパラメータを追加する必要がある場合があります。

このチュートリアルでは、Pythonクラスでそれらのパラメータを作成する方法と、ローンチファイルでそれらを設定する方法を示します。

前提条件

これまでのチュートリアルで、ワークスペースの作成パッケージの作成の方法を学びました。 また、パラメータとROS 2システムでのその機能についても学びました。

タスク

1 パッケージの作成

新しいターミナルを開き、ROS 2インストレーションをソースしてros2コマンドが機能するようにします。

これらの手順に従って、ros2_wsという名前の新しいワークスペースを作成します。

パッケージはワークスペースのルートではなく、srcディレクトリに作成する必要があることを思い出してください。 ros2_ws/srcに移動し、新しいパッケージを作成します:

bash
ros2 pkg create --build-type ament_python --license Apache-2.0 python_parameters --dependencies rclpy

ターミナルには、パッケージpython_parametersとそのすべての必要なファイルとフォルダーの作成を確認するメッセージが返されます。

--dependencies引数により、package.xmlCMakeLists.txtに必要な依存関係行が自動的に追加されます。

1.1 package.xmlの更新

パッケージ作成時に--dependenciesオプションを使用したため、package.xmlCMakeLists.txtに手動で依存関係を追加する必要はありません。

ただし、いつものように、package.xmlに説明、メンテナーのメールアドレスと名前、ライセンス情報を追加してください。

xml
<description>Python parameter tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>

2 Pythonノードの記述

ros2_ws/src/python_parameters/python_parametersディレクトリ内で、python_parameters_node.pyという新しいファイルを作成し、以下のコードを貼り付けます:

python
import rclpy
import rclpy.node

class MinimalParam(rclpy.node.Node):
    def __init__(self):
        super().__init__('minimal_param_node')

        self.declare_parameter('my_parameter', 'world')

        self.timer = self.create_timer(1, self.timer_callback)

    def timer_callback(self):
        my_param = self.get_parameter('my_parameter').get_parameter_value().string_value

        self.get_logger().info('Hello %s!' % my_param)

        my_new_param = rclpy.parameter.Parameter(
            'my_parameter',
            rclpy.Parameter.Type.STRING,
            'world'
        )
        all_new_parameters = [my_new_param]
        self.set_parameters(all_new_parameters)

def main():
    rclpy.init()
    node = MinimalParam()
    rclpy.spin(node)

if __name__ == '__main__':
    main()

2.1 コードの説明

上部のimport文はパッケージの依存関係を読み込むために使用されます。

次のコード部分では、クラスとコンストラクタを作成します。 コンストラクタの行self.declare_parameter('my_parameter', 'world')では、my_parameterという名前のパラメータをデフォルト値worldで作成します。 パラメータタイプはデフォルト値から推定されるため、この場合は文字列タイプに設定されます。 次に、timerが周期1で初期化され、timer_callback関数が1秒に1回実行されます。

python
class MinimalParam(rclpy.node.Node):
    def __init__(self):
        super().__init__('minimal_param_node')

        self.declare_parameter('my_parameter', 'world')

        self.timer = self.create_timer(1, self.timer_callback)

timer_callback関数の最初の行では、ノードからパラメータmy_parameterを取得し、my_paramに格納します。 次に、get_logger関数がイベントをログに記録することを確実にします。 そしてset_parameters関数がパラメータmy_parameterをデフォルトの文字列値worldに戻します。 ユーザーが外部からパラメータを変更した場合、これは常に元の値にリセットされることを確実にします。

python
def timer_callback(self):
    my_param = self.get_parameter('my_parameter').get_parameter_value().string_value

    self.get_logger().info('Hello %s!' % my_param)

    my_new_param = rclpy.parameter.Parameter(
        'my_parameter',
        rclpy.Parameter.Type.STRING,
        'world'
    )
    all_new_parameters = [my_new_param]
    self.set_parameters(all_new_parameters)

timer_callbackの後に続くのはmain関数です。 ここでROS 2が初期化され、MinimalParamクラスのインスタンスが構築され、rclpy.spinがノードからのデータ処理を開始します。

python
def main():
    rclpy.init()
    node = MinimalParam()
    rclpy.spin(node)

if __name__ == '__main__':
    main()
2.1.1 (オプション)ParameterDescriptorの追加

オプションとして、パラメータの記述子を設定することができます。 記述子により、パラメータのテキスト説明とその制約(読み取り専用にする、範囲を指定するなど)を指定できます。 これを機能させるには、__init__コードを以下のように変更する必要があります:

python
# ...

class MinimalParam(rclpy.node.Node):
    def __init__(self):
        super().__init__('minimal_param_node')

        from rcl_interfaces.msg import ParameterDescriptor
        my_parameter_descriptor = ParameterDescriptor(description='This parameter is mine!')

        self.declare_parameter('my_parameter', 'world', my_parameter_descriptor)

        self.timer = self.create_timer(1, self.timer_callback)

rcl_interfacesをインポートしているため、将来の依存関係の問題を避けるためにpackage.xmlに依存関係を追加する必要があります:

xml
# ...
<depend>rclpy</depend>
<depend>rcl_interfaces</depend>

コードの残りの部分は変わりません。 ノードを実行すると、ros2 param describe /minimal_param_node my_parameterを実行してタイプと説明を確認できます。

2.2 エントリーポイントの追加

setup.pyファイルを開きます。 再度、package.xmlに合わせてmaintainermaintainer_emaildescriptionlicenseフィールドを更新します:

python
maintainer='YourName',
maintainer_email='you@email.com',
description='Python parameter tutorial',
license='Apache License 2.0',

entry_pointsフィールドのconsole_scriptsブラケット内に以下の行を追加します:

python
entry_points={
    'console_scripts': [
        'minimal_param_node = python_parameters.python_parameters_node:main',
    ],
},

保存することを忘れないでください。

3 ビルドと実行

ワークスペース(ros2_ws)のルートでrosdepを実行して、ビルド前に不足している依存関係をチェックするのが良い習慣です:

bash
rosdep install -i --from-path src --rosdistro humble -y

ワークスペースのルートros2_wsに戻り、新しいパッケージをビルドします:

bash
colcon build --packages-select python_parameters

新しいターミナルを開き、ros2_wsに移動し、セットアップファイルをソースします:

bash
source install/setup.bash

今度はノードを実行します。 ターミナルには毎秒Hello world!が返されるはずです:

bash
ros2 run python_parameters minimal_param_node
[INFO] [parameter_node]: Hello world!

これでパラメータのデフォルト値を確認できますが、自分で設定できるようにしたいでしょう。 これを実現する方法は2つあります。

3.1 コンソール経由での変更

この部分では、パラメータについてのチュートリアルで得た知識を使用し、作成したノードに適用します。

ノードが実行されていることを確認してください:

bash
ros2 run python_parameters minimal_param_node

別のターミナルを開き、ros2_ws内から再度セットアップファイルをソースし、以下の行を入力します:

bash
ros2 param list

そこにカスタムパラメータmy_parameterが表示されます。 変更するには、コンソールで以下の行を実行します:

bash
ros2 param set /minimal_param_node my_parameter earth

Set parameter successfulの出力が得られれば成功です。 他のターミナルを見ると、出力が[INFO] [minimal_param_node]: Hello earth!に変わっているのを確認できるはずです。

その後、ノードがパラメータをworldに戻すため、以降の出力では[INFO] [minimal_param_node]: Hello world!が表示されます。

3.2 ローンチファイル経由での変更

ローンチファイルでもパラメータを設定できますが、まずlaunchディレクトリを追加する必要があります。 ros2_ws/src/python_parameters/ディレクトリ内で、launchという新しいディレクトリを作成します。 そこにpython_parameters_launch.pyという新しいファイルを作成します:

python
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='python_parameters',
            executable='minimal_param_node',
            name='custom_minimal_param_node',
            output='screen',
            emulate_tty=True,
            parameters=[
                {'my_parameter': 'earth'}
            ]
        )
    ])

ここでは、ノードparameter_nodeを起動する際にmy_parameterearthに設定していることがわかります。 以下の2行を追加することで、出力がコンソールに印刷されることを確実にします。

python
output="screen",
emulate_tty=True,

次にsetup.pyファイルを開きます。 ファイルの上部にimport文を追加し、すべてのローンチファイルを含むためにdata_filesパラメータに他の新しい文を追加します:

python
import os
from glob import glob
# ...

setup(
  # ...
  data_files=[
      # ...
      (os.path.join('share', package_name, 'launch'), glob('launch/*')),
    ]
  )

コンソールを開き、ワークスペースのルートros2_wsに移動し、新しいパッケージをビルドします:

bash
colcon build --packages-select python_parameters

次に新しいターミナルでセットアップファイルをソースします:

bash
source install/setup.bash

次に、作成したローンチファイルを使用してノードを実行します。 ターミナルには最初に以下のメッセージが返されるはずです:

bash
ros2 launch python_parameters python_parameters_launch.py
[INFO] [custom_minimal_param_node]: Hello earth!

以降の出力では毎秒[INFO] [minimal_param_node]: Hello world!が表示されるはずです。

まとめ

ローンチファイルまたはコマンドラインから設定できるカスタムパラメータを持つノードを作成しました。 依存関係、実行可能ファイル、ローンチファイルをパッケージ設定ファイルに追加して、それらをビルドして実行し、パラメータの動作を確認できるようにしました。

次のステップ

今度は独自のパッケージとROS 2システムがいくつかあるので、次のチュートリアルでは、問題が発生した場合に環境とシステムの問題を調査する方法を示します。

Released under the MIT License.