개요
사용하여 통신 할 때 데이터 요청을 보내는 노드를 클라이언트 노드라고 하고 요청에 응답하는 노드를 서비스 노드라고 합니다. srv
요청 및 응답의 구조는 파일에 의해 결정됩니다.
여기에 사용된 예제는 간단한 정수 추가 시스템입니다. 한 노드는 두 정수의 합을 요청하고 다른 노드는 결과로 응답합니다.
실습
1. 패키지 생성
ros2
명령이 작동하도록 새 터미널을 열고
에서 만든 디렉터리 ros2_ws
로 이동합니다 .
패키지는 작업 영역의 루트가 아닌 src
디렉터리 에 만들어야 한다는 점을 기억하십시오 . 새 패키지 로 이동하여 생성합니다.
ros2 pkg create --build-type ament_python py_srvcli --dependencies rclpy example_interfaces
터미널은 패키지 py_srvcli
와 필요한 모든 파일 및 폴더 생성을 확인하는 메시지를 반환합니다.
인수 --dependencies
는 필요한 종속성 줄을 package.xml
에 자동으로 추가합니다. 요청 및 응답을 구조화하는 데 필요한example_interfaces
가포함된 패키지입니다 .
int64 a
int64 b
---
int64 sum
처음 두 줄은 요청의 매개변수이고 대시 아래는 응답의입매개변수니입다.
1.1 package.xml
업데이트
패키지 생성 중에 --dependencies
이 옵션을 사용했기 때문에 수동으로 package.xml
에 종속성을 추가할 필요가 없습니다.
하지만 항상 그렇듯이 설명, 관리자 이메일, 이름, 라이선스 정보를 package.xml
에 추가해야 합니다 .
<description>Python client server tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
1.2 setup.py 업데이트
setup.py
파일에 동일한 정보를 추가 합니다
maintainer='Your Name',
maintainer_email='you@email.com',
description='Python client server tutorial',
license='Apache License 2.0',
2. 서비스 노드 쓰기
ros2_ws/src/py_srvcli/py_srvcli
디렉터리 내에서 service_member_function.py
라는 새 파일을 만들고 다음 코드를 붙여넣습니다.
from example_interfaces.srv import AddTwoInts
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)
def add_two_ints_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))
return response
def main(args=None):
rclpy.init(args=args)
minimal_service = MinimalService()
rclpy.spin(minimal_service)
rclpy.shutdown()
if __name__ == '__main__':
main()
2.1 코드 검토
첫 번째 명령문import
은 example_interfaces 패키지 에서 AddTwoInts
서비스 유형을 가져옵니다 . 다음 명령문import
은 ROS2 Python 클라이언트 라이브러리, 특히 Node
클래스를 가져옵니다.
from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node
클래스 MinimalService
생성자는 minimal_service
노드를 초기화합니다. 그런 다음 서비스를 생성하고 유형, 이름 및 콜백을 정의합니다.
def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
서비스 콜백의 정의는 요청 데이터를 수신하고 합산하여 응답으로 반환합니다.
def add_two_ints_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))
return response
마지막으로 메인 클래스는 ROS2 Python 클라이언트 라이브러리를 초기화하고 MinimalService
클래스를 인스턴스화하여 서비스 노드를 생성하고 노드를 spin 시켜 콜백을 처리합니다.
2.2 진입점 추가
ros2 run
명령이 노드를 실행하도록 허용하려면 진입점을 setup.py
(ros2_ws/src/py_srvcli
디렉토리에 있음 )에 추가해야 합니다.
'console_scripts'
대괄호 사이에 다음 줄을 추가합니다 :
.
'service = py_srvcli.service_member_function:main',
3. 클라이언트 노드 작성하기
ros2_ws/src/py_srvcli/py_srvcli
디렉터리 내에서 client_member_function.py
라는 새 파일을 만들고 다음 코드를 붙여넣습니다.
import sys
from example_interfaces.srv import AddTwoInts
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')
while not self.cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting again...')
self.req = AddTwoInts.Request()
def send_request(self, a, b):
self.req.a = a
self.req.b = b
self.future = self.cli.call_async(self.req)
rclpy.spin_until_future_complete(self, self.future)
return self.future.result()
def main(args=None):
rclpy.init(args=args)
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()
3.1 코드 검토
생성자 정의는 서비스 노드와 동일한 유형 및 이름으로 클라이언트를 생성합니다. 유형과 이름은 클라이언트와 서비스가 통신할 수 있도록 일치해야 합니다.
생성자의 루프 while
는 클라이언트의 유형 및 이름과 일치하는 서비스가 1초에 한 번씩 사용 가능한지 확인합니다.
생성자 아래에는 요청 정의와 main
.
클라이언트의 main 함수의
유일한 중요한 차이점은 while
루프 입니다. 루프는 시스템이 실행되는 동안 서비스의 응답이 있는지 future 변수를
확인합니다. 서비스에서 응답을 보낸 경우 결과는 로그 메시지에 기록됩니다.
3.2 진입점 추가
서비스 노드와 마찬가지로 클라이언트 노드를 실행할 수 있도록 진입점도 추가해야 합니다.
setup.py
파일의 entry_points
필드는 다음 과 같아야 합니다.
entry_points={
'console_scripts': [
'service = py_srvcli.service_member_function:main',
'client = py_srvcli.client_member_function:main',
],
},
4. 빌드 및 실행
작업 공간의 루트( ros2_ws
)에서 rosdep를
실행하여 빌드하기 전에 누락된 종속성을 확인하는 것이 좋습니다 .
rosdep install -i --from-path src --rosdistro humble -y
작업 공간의 루트ros2_ws
로 돌아가서 새 패키지를 빌드합니다.
colcon build --packages-select py_srvcli
새 터미널을 열고 로 이동하여 ros2_ws
설정 파일을 찾습니다.
source install/setup.bash
이제 서비스 노드를 실행합니다.
ros2 run py_srvcli service
노드는 클라이언트의 요청을 기다립니다.
다른 터미널을 열고 ros2_ws
내부에서 설정 파일을 다시 소싱합니다. 클라이언트 노드를 시작하고 그 뒤에 공백으로 구분된 두 개의 정수를 입력합니다.
ros2 run py_srvcli client 2 3
클라이언트는 다음과 같은 응답을 받습니다.
[INFO] [minimal_client_async]: Result of add_two_ints: for 2 + 3 = 5
서비스 노드가 실행 중인 터미널로 돌아갑니다. 요청을 받았을 때 로그 메시지를 게시한 것을 볼 수 있습니다.
[INFO] [minimal_service]: Incoming request
a: 2 b: 3
노드가 더이상하 spin 하지 않도록 서버 터미널에 Ctrl+C
입력하십시오 .