はじめに
今回はサービスノードを作ってみる.このチュートリアルでは整数を加算するサービスを実装する.
Writing a simple service and client (Python) — ROS 2 Documentation: Iron documentation
Request/Response してみる
service node を作る
いつもどおり,アンダーレイをソースして,パッケージのベースを作る.
$ source ~/ros2_iron/install/setup.bash
$ mkdir -p ros-ws/src
$ cd ros-ws/src
$ ros2 pkg create --build-type ament_python --license Apache-2.0 py_srvcli --dependencies rclpy example_interfaces
--dependencies
オプションをつけることで,package.xml に依存関係を追記してくれる.description,maintainer email,name,license の項目はお好みでどうぞ.
<?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>py_srvcli</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="*****@todo.todo">*****</maintainer>
<license>Apache-2.0</license>
<depend>rclpy</depend> # オプションで指定した依存関係
<depend>example_interfaces</depend> # オプションで指定した依存関係
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
特に example_interfaces はサービスのリクエストとレスポンスのデータ構造の定義が含まれるパッケージで,中身はこんな感じ.最初の2つはリクエスト,最後の1つはレスポンスのデータ構造を表す.なお srv ファイルの実体はアンダーレイにおいてあるので探してみるといいかも.
int64 a
int64 b
---
int64 sum
サービスノードを実装する.
from example_interfaces.srv import AddTwoInts # Request/Response のデータ構造をインポート
import rclpy
from rclpy.node import Node
class MinimalService(Node):
def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback) # Req/Res のデータタイプ,サービス名,コールバックを指定してサービスを生成
def add_two_ints_callback(self, request, response):
response.sum = request.a + request.b # Req データから Res データとなる合計値を計算
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))
return response # Res を送信
def main():
rclpy.init()
minimal_service = MinimalService()
rclpy.spin(minimal_service)
rclpy.shutdown()
if __name__ == '__main__':
main()
setup.py にエントリーポイントを追記する.
from setuptools import find_packages, setup
package_name = 'py_srvcli'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='*****',
maintainer_email='*****@todo.todo',
description='TODO: Package description',
license='Apache-2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'service = py_srvcli.service_member_function:main', # エントリーポイントを追記
],
},
)
client node を作る
先ほどと同様に,クライアントを実装する.リクエストとして渡す2つの値はメイン関数の引数から設定する.
import sys
from example_interfaces.srv import AddTwoInts # Request/Response のデータ構造をインポート
import rclpy
from rclpy.node import Node
class MinimalClientAsync(Node):
def __init__(self):
super().__init__('minimal_client_async')
self.cli = self.create_client(AddTwoInts, 'add_two_ints') # Req/Res のデータタイプ,サービス名を指定してサービスを生成
while not self.cli.wait_for_service(timeout_sec=1.0): # サービスが有効になるまでループ待ち
self.get_logger().info('service not available, waiting again...')
self.req = AddTwoInts.Request() # Req オブジェクトを生成
def send_request(self, a, b):
self.req.a = a
self.req.b = b
self.future = self.cli.call_async(self.req) # Req 送信
rclpy.spin_until_future_complete(self, self.future) # Res 待ち
return self.future.result()
def main():
rclpy.init()
minimal_client = MinimalClientAsync()
response = minimal_client.send_request(int(sys.argv[1]), int(sys.argv[2]))
minimal_client.get_logger().info(
'Result of add_two_ints: for %d + %d = %d' %
(int(sys.argv[1]), int(sys.argv[2]), response.sum))
minimal_client.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
setup.py にエントリーポイントを追記する.
from setuptools import find_packages, setup
package_name = 'py_srvcli'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='*****',
maintainer_email='*****@todo.todo',
description='TODO: Package description',
license='Apache-2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'service = py_srvcli.service_member_function:main',
'client = py_srvcli.client_member_function:main', # エントリーポイントを追記
],
},
)
ビルドして実行してみる
ワークスペースのルートに戻って依存関係を確認してから,colcon でビルドする.エラーメッセージ出てるけど無視しておく.
$ rosdep install -i --from-path src --rosdistro iron -y
#All required rosdeps installed successfully
$ colcon build --packages-select py_srvcli --symlink-install
Starting >>> py_srvcli
--- stderr: py_srvcli
/usr/lib/python3.12/site-packages/setuptools/command/develop.py:40: EasyInstallDeprecationWarning: easy_install command is deprecated.
!!
********************************************************************************
Please avoid running ``setup.py`` and ``easy_install``.
Instead, use pypa/build, pypa/installer, pypa/build or
other standards-based tools.
See https://github.com/pypa/setuptools/issues/917 for details.
********************************************************************************
!!
easy_install.initialize_options(self)
/usr/lib/python3.12/site-packages/setuptools/_distutils/cmd.py:66: SetuptoolsDeprecationWarning: setup.py install is deprecated.
!!
********************************************************************************
Please avoid running ``setup.py`` directly.
Instead, use pypa/build, pypa/installer, pypa/build or
other standards-based tools.
See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details.
********************************************************************************
!!
self.initialize_options()
---
Finished <<< py_srvcli [4.10s]
Summary: 1 package finished [6.82s]
1 package had stderr output: py_srvcli
オーバーレイをソースして,サービスノードを起動.
$ source install/setup.bash
$ ros2 run py_srvcli service
[INFO] [minimal_service]: Incoming request
a: 2 b: 3
もう一つターミナル開いて,オーバーレイをソースして,クライアントノードを起動.
$ source install/setup.bash
$ ros2 run py_srvcli client 2 3
[INFO] [minimal_client_async]: Result of add_two_ints: for 2 + 3 = 5
サービスノードが合計値を返してくれれば成功.
おわりに
チュートリアルに沿ってシンプルなサービスを実装した.
Action のように途中経過(Feedback)は要らないけど,任意のタイミングでなにかの状態確認したり,なにかの機能を ON/OFF したり,使い道はたくさんありそう.
コメント