이번 실습에서는 ROS 2에서 Service 통신을 Python으로 구현하는 방법을 실습했다.
operator 노드가 Service Client로 연산자를 요청하고, calculator 노드가 Service Server로 요청을 받아 계산 결과를 반환하는 과정을 직접 확인했다.
ROS 2의 Service는 서비스 요청(Request)과 서비스 응답(Response)을 주고받는 방식으로 통신한다.
이번 실습에서는 다음과 같은 구조로 구성되어 있다.
operator → Service Clientcalculator → Service Serverarithmetic_operatorArithmeticOperatoroperator 노드는 +, -, *, / 중 하나의 연산자를 요청값으로 보내고, calculator 노드는 Topic을 통해 저장하고 있던 a, b 값을 이용하여 계산한 뒤 결과를 응답값으로 반환한다.
operator
(Service Client)
│
│ Request
▼
arithmetic_operator
│
▼
calculator
(Service Server)
│
│ Response
▼
operator
먼저 calculator.py에서 Service Server 부분을 확인했다.
sed -n '70,130p' ~/robot_ws/src/ros2-seminar-examples/topic_service_action_rclpy_example/topic_service_action_rclpy_example/calculator/calculator.py
get_arithmetic_operator() 함수에서는 서비스 요청으로 전달받은 연산자를 가져온다.
def get_arithmetic_operator(self, request, response):
self.argument_operator = request.arithmetic_operator
self.argument_result = self.calculate_given_formula(
self.argument_a,
self.argument_b,
self.argument_operator)
response.arithmetic_result = self.argument_result
request에는 서비스 요청값이 들어오고, response에는 서비스 응답값을 저장한다.
요청받은 연산자와 기존에 저장된 argument_a, argument_b를 이용하여 계산한 후 결과를 response.arithmetic_result에 저장한다.
Service Server는 create_service()를 이용하여 생성되어 있다.
grep -n -A6 "create_service" ~/robot_ws/src/ros2-seminar-examples/topic_service_action_rclpy_example/topic_service_action_rclpy_example/calculator/calculator.py
확인한 코드는 다음과 같다.
self.arithmetic_service_server = self.create_service(
ArithmeticOperator,
'arithmetic_operator',
self.get_arithmetic_operator,
callback_group=self.callback_group)
각 항목은 다음과 같다.
ArithmeticOperator → 서비스 타입arithmetic_operator → 서비스 이름get_arithmetic_operator → 서비스 요청 시 실행되는 콜백 함수callback_group → 콜백 그룹 설정즉, operator에서 서비스 요청이 들어오면 get_arithmetic_operator() 함수가 실행된다.
calculate_given_formula() 함수에서는 전달받은 연산자에 따라 계산을 수행한다.
if operator == ArithmeticOperator.Request.PLUS:
self.argument_result = a + b
elif operator == ArithmeticOperator.Request.MINUS:
self.argument_result = a - b
elif operator == ArithmeticOperator.Request.MULTIPLY:
self.argument_result = a * b
elif operator == ArithmeticOperator.Request.DIVISION:
self.argument_result = a / b
따라서 서비스 요청값에 따라 다음과 같이 계산된다.
PLUS → +
MINUS → -
MULTIPLY → *
DIVISION → /
나눗셈 과정에서 0으로 나누는 경우에는 ZeroDivisionError를 처리하도록 구성되어 있다.
다음으로 operator.py에서 Service Client 코드를 확인했다.
sed -n '1,80p' ~/robot_ws/src/ros2-seminar-examples/topic_service_action_rclpy_example/topic_service_action_rclpy_example/arithmetic/operator.py
Operator 클래스는 Node를 상속하고 operator라는 이름의 노드로 생성된다.
class Operator(Node):
def __init__(self):
super().__init__('operator')
Service Client는 다음과 같이 생성되어 있다.
self.arithmetic_service_client = self.create_client(
ArithmeticOperator,
'arithmetic_operator')
Service Server와 동일하게 ArithmeticOperator 타입과 arithmetic_operator라는 서비스 이름을 사용한다.
Service Client에서는 서버가 실행되어 있는지 확인하기 위해 wait_for_service()를 사용한다.
while not self.arithmetic_service_client.wait_for_service(timeout_sec=0.1):
self.get_logger().warning(
'The arithmetic_operator service not available.')
서비스 서버가 실행될 때까지 0.1초 간격으로 확인한다.
send_request() 함수에서는 실제 서비스 요청을 생성한다.
def send_request(self):
service_request = ArithmeticOperator.Request()
service_request.arithmetic_operator = random.randint(1, 4)
futures = self.arithmetic_service_client.call_async(
service_request)
return futures
먼저 ArithmeticOperator.Request()를 이용해 요청 객체를 생성한다.
그 다음 random.randint(1, 4)를 이용하여 1~4 중 하나를 선택하고 arithmetic_operator에 저장한다.
마지막으로 call_async()를 이용하여 Service Server에 요청을 보낸다.
이제 실제 Service 통신을 확인하기 위해 calculator 노드를 실행했다.
source ~/robot_ws/install/setup.zsh
ros2 run topic_service_action_rclpy_example calculator
calculator 노드는 기존 Topic Subscriber와 함께 Service Server 역할도 수행한다.
새로운 터미널에서 operator 노드를 실행했다.
source ~/robot_ws/install/setup.zsh
ros2 run topic_service_action_rclpy_example operator
실행하면 operator에서 랜덤하게 연산자를 선택하여 calculator에 Service Request를 보낸다.
calculator는 저장된 argument_a, argument_b와 전달받은 연산자를 이용해 계산한 후 결과를 반환한다.
operator 터미널에서 계산 결과가 출력되는 것을 확인했다.
[INFO]: Result: ...
또한 operator에서 Enter를 입력하면 다음 Service Request가 실행되어 새로운 계산 결과가 계속 출력되는 것을 확인했다.
즉, 한 번의 요청으로 끝나는 것이 아니라 다음과 같이 반복적으로 Service 통신이 이루어졌다.
Request
↓
calculator 계산
↓
Response
↓
Result 출력
↓
Enter
↓
다음 Request
PPT에서도 operator가 서비스 요청을 보내고 calculator가 계산한 결과를 반환하는 과정을 동일하게 설명하고 있다.
마지막으로 ROS 2에 등록된 Service를 확인했다.
source ~/robot_ws/install/setup.zsh
ros2 service list
실행 결과에서 다음 Service를 확인했다.
/arithmetic_operator
이를 통해 arithmetic_operator Service가 ROS 2에 정상적으로 등록되어 있는 것을 확인했다.
이번 실습에서는 ROS 2의 Service Client와 Service Server 통신을 Python으로 구현하고 실제로 실행해 보았다.
operator
(Service Client)
│
│ 연산자 Request
▼
/arithmetic_operator
│
▼
calculator
(Service Server)
│
│ 계산 결과 Response
▼
operator
operator 노드가 랜덤하게 연산자를 선택하여 Service Request를 보내고, calculator 노드가 a, b와 연산자를 이용하여 계산한 후 결과를 반환하는 과정을 확인했다.
또한 ros2 service list 명령어를 통해 /arithmetic_operator Service가 정상적으로 등록된 것도 확인했다.