사용자 정의 msg 및 srv 파일 생성

저작권: 쿼드(QUAD) 드론연구소 https://smartstore.naver.com/maponarooo

개요

이전 자습서에서는 메시지 및 서비스 인터페이스를 활용하여 주제 , 서비스 , 간단한 게시자/구독자( C++ / Python ) 및 서비스/클라이언트( C++ / Python ) 노드 에 대해 배웠습니다 . 이러한 경우에 사용한 인터페이스는 미리 정의되어 있습니다.

미리 정의된 인터페이스 정의를 사용하는 것이 좋지만 때로는 자체 메시지와 서비스를 정의해야 할 수도 있습니다. 이 자습서에서는 사용자 지정 인터페이스 정의를 만드는 가장 간단한 방법을 소개합니다.

이 자습서는 게시자/구독자( C++Python ) 및 서비스/클라이언트( C++Python ) 자습서에서 생성된 패키지를 사용하여 새 사용자 지정 메시지를 시험해 봅니다.

실습

1. 새 패키지 만들기

이 자습서에서는 자체 패키지에 사용자 지정 .msg.srv 파일을 만든 다음 별도의 패키지에서 활용합니다. 두 패키지는 동일한 작업 공간에 있어야 합니다.

이전 자습서에서 만든 pub/sub 및 service/client 패키지를 사용하므로 해당 패키지( ros2_ws/src)와 동일한 작업 공간에 있는지 확인한 후 다음 명령을 실행하여 새 패키지를 만듭니다.

ros2 pkg create --build-type ament_cmake tutorial_interfaces

tutorial_interfaces는새 패키지의 이름입니다. 이는 CMake 패키지이며 패키지일 수 있지만 메시지 및 서비스를 사용할 수 있는 패키지 유형을 제한하지는 않습니다. CMake 패키지에서 고유한 사용자 지정 인터페이스를 만든 다음 마지막 섹션에서 다룰 C++ 또는 Python 노드에서 사용할 수 있습니다.

.msg.srv파일 은 각각 및 msg srv라는 디렉토리에 배치해야 합니다 . 다음 위치에 디렉토리를 생성합니다 .ros2_ws/src/tutorial_interfaces

mkdir msg srv

2. 사용자 지정 정의 만들기

2.1 메시지 정의

방금 만든 tutorial_interfaces/msg디렉터리 에서 데이터 구조를 선언하는 한 줄의 코드로 호출되는 새 파일Num.msg을 만듭니다.

int64 num

이것은 num이라는 단일 64비트 정수를 전송하는 사용자 지정 메시지입니다.

또한 방금 만든 tutorial_interfaces/msg디렉토리 에서 다음 내용으로 호출되는 새 파일Sphere.msg을 만듭니다.

geometry_msgs/Point center
float64 radius

이 사용자 지정 메시지는 다른 메시지 패키지( 이 경우geometry_msgs/Point) 의 메시지를 사용합니다 .

2.2 서비스 정의

방금 만든 tutorial_interfaces/srv디렉터리로 돌아가서 다음 요청 및 응답 구조로 호출되는 새 파일AddThreeInts.srv을 만듭니다.

int64 a
int64 b
int64 c
---
int64 sum

이것은a ,b,c및 라는 세 개의 정수를 요청하고, sum정수로 응답하는 사용자 지정 서비스입니다.

3. CMakeLists.txt

정의한 인터페이스를 해당 언어에서 사용할 수 있도록 언어별 코드(예: C++ 및 Python)로 변환하려면 다음 행을 CMakeLists.txt에 추가하십시오.

find_package(geometry_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/Num.msg"
  "msg/Sphere.msg"
  "srv/AddThreeInts.srv"
  DEPENDENCIES geometry_msgs # Add packages that above messages depend on, in this case geometry_msgs for Sphere.msg
)

참고

rosidl_generate_interfaces의 첫 번째 인수(라이브러리 이름)는 ${PROJECT_NAME}과 일치해야 합니다( https://github.com/ros2/rosidl/issues/441#issuecomment-591025515 참조 ).

4. package.xml

인터페이스는 언어별 코드를 생성하는데 의존하므로 이에 대한 빌드 도구 종속성을 선언해야 합니다. rosidl_default_generators rosidl_default_runtime은나중에 인터페이스를 사용하는 데 필요한 런타임 또는 실행 단계 종속성입니다. rosidl_interface_packages<member_of_group>태그를 사용하여 선언된 tutorial_interfaces패키지가 연결되어야 하는 종속성 그룹의 이름입니다 .

package.xml<package>요소 내에 다음 줄을 추가합니다 .

<depend>geometry_msgs</depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

5. tutorial_interfaces 패키지 빌드

이제 사용자 정의 인터페이스 패키지의 모든 부분이 준비되었으므로 패키지를 빌드할 수 있습니다. 작업 영역(~/ros2_ws)의 루트에서 다음 명령을 실행합니다.

colcon build --packages-select tutorial_interfaces

이제 다른 ROS2 패키지에서 인터페이스를 검색할 수 있습니다.

6. msg 및 srv 생성 확인

새 터미널에서 작업 공간(ros2_ws) 내에서 다음 명령을 실행하여 소싱합니다.

source install/setup.bash

이제 다음 명령ros2 interface show을 사용하여 인터페이스 생성이 작동했는지 확인할 수 있습니다 .

ros2 interface show tutorial_interfaces/msg/Num

다음을 반환해야 합니다.

int64 num

그리고

ros2 interface show tutorial_interfaces/msg/Sphere

다음을 반환해야 합니다.

geometry_msgs/Point center
        float64 x
        float64 y
        float64 z
float64 radius

그리고

ros2 interface show tutorial_interfaces/srv/AddThreeInts

다음을 반환해야 합니다.

int64 a
int64 b
int64 c
---
int64 sum

7. 새 인터페이스 테스트

이 단계에서는 이전 자습서에서 만든 패키지를 사용할 수 있습니다. CMakeLists.txtpackage.xml파일에 대한 몇 가지 간단한 수정으로 새 인터페이스를 사용할 수 있습니다.

7.1 pub/sub를 사용한 Num.msg테스트

이전 자습서( C++ 또는 Python ) 에서 만든 게시자/구독자 패키지를 약간 수정하면 Num.msg실제로 작동하는 것을 볼 수 있습니다 . 표준 문자열 msg를 숫자로 변경하기 때문에 출력이 약간 다를 것입니다.

발행자(Publisher)

import rclpy
from rclpy.node import Node

from tutorial_interfaces.msg import Num    # CHANGE


class MinimalPublisher(Node):

    def __init__(self):
        super().__init__('minimal_publisher')
        self.publisher_ = self.create_publisher(Num, 'topic', 10)     # CHANGE
        timer_period = 0.5
        self.timer = self.create_timer(timer_period, self.timer_callback)
        self.i = 0

    def timer_callback(self):
        msg = Num()                                           # CHANGE
        msg.num = self.i                                      # CHANGE
        self.publisher_.publish(msg)
        self.get_logger().info('Publishing: "%d"' % msg.num)  # CHANGE
        self.i += 1


def main(args=None):
    rclpy.init(args=args)

    minimal_publisher = MinimalPublisher()

    rclpy.spin(minimal_publisher)

    minimal_publisher.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

구독자(Subscriber)

import rclpy
from rclpy.node import Node

from tutorial_interfaces.msg import Num        # CHANGE


class MinimalSubscriber(Node):

    def __init__(self):
        super().__init__('minimal_subscriber')
        self.subscription = self.create_subscription(
            Num,                                              # CHANGE
            'topic',
            self.listener_callback,
            10)
        self.subscription

    def listener_callback(self, msg):
            self.get_logger().info('I heard: "%d"' % msg.num) # CHANGE


def main(args=None):
    rclpy.init(args=args)

    minimal_subscriber = MinimalSubscriber()

    rclpy.spin(minimal_subscriber)

    minimal_subscriber.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

CMakeLists.txt

다음 줄을 추가합니다(C++만 해당).

#...

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tutorial_interfaces REQUIRED)                         # CHANGE

add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp tutorial_interfaces)         # CHANGE

add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp tutorial_interfaces)     # CHANGE

install(TARGETS
  talker
  listener
  DESTINATION lib/${PROJECT_NAME})

ament_package()

package.xml

<exec_depend>tutorial_interfaces</exec_depend>

위의 편집을 수행하고 모든 변경 사항을 저장한 후 패키지를 빌드합니다.

colcon build --packages-select py_pubsub

그런 다음 두 개의 새 터미널을 열고 각각 source 를 입력하고 다음을 실행합니다.

ros2 run py_pubsub talker
ros2 run py_pubsub listener

정수만 릴레이 하므로 talker는 이전에 게시한 문자열과 달리 정수 Num.msg값만 게시해야 합니다.

[INFO] [minimal_publisher]: Publishing: '0'
[INFO] [minimal_publisher]: Publishing: '1'
[INFO] [minimal_publisher]: Publishing: '2'

7.2 서비스/클라이언트 AddThreeInts.srv테스트

이전 자습서( C++ 또는 Python ) 에서 만든 서비스/클라이언트 AddThreeInts.srv패키지를 약간 수정하면 실제로 작동하는 것을 볼 수 있습니다 . 원래의 2개 정수 요청 srv를 3개 정수 요청 srv로 변경할 것이므로 출력이 약간 다를 것입니다.

서비스

from tutorial_interfaces.srv import AddThreeInts     # CHANGE

import rclpy
from rclpy.node import Node


class MinimalService(Node):

    def __init__(self):
        super().__init__('minimal_service')
        self.srv = self.create_service(AddThreeInts, 'add_three_ints', self.add_three_ints_callback)        # CHANGE

    def add_three_ints_callback(self, request, response):
        response.sum = request.a + request.b + request.c                                                  # CHANGE
        self.get_logger().info('Incoming request\na: %d b: %d c: %d' % (request.a, request.b, request.c)) # CHANGE

        return response

def main(args=None):
    rclpy.init(args=args)

    minimal_service = MinimalService()

    rclpy.spin(minimal_service)

    rclpy.shutdown()

if __name__ == '__main__':
    main()

클라이언트

from tutorial_interfaces.srv import AddThreeInts       # CHANGE
import sys
import rclpy
from rclpy.node import Node


class MinimalClientAsync(Node):

    def __init__(self):
        super().__init__('minimal_client_async')
        self.cli = self.create_client(AddThreeInts, 'add_three_ints')       # CHANGE
        while not self.cli.wait_for_service(timeout_sec=1.0):
            self.get_logger().info('service not available, waiting again...')
        self.req = AddThreeInts.Request()                                   # CHANGE

    def send_request(self):
        self.req.a = int(sys.argv[1])
        self.req.b = int(sys.argv[2])
        self.req.c = int(sys.argv[3])                  # CHANGE
        self.future = self.cli.call_async(self.req)


def main(args=None):
    rclpy.init(args=args)

    minimal_client = MinimalClientAsync()
    minimal_client.send_request()

    while rclpy.ok():
        rclpy.spin_once(minimal_client)
        if minimal_client.future.done():
            try:
                response = minimal_client.future.result()
            except Exception as e:
                minimal_client.get_logger().info(
                    'Service call failed %r' % (e,))
            else:
                minimal_client.get_logger().info(
                    'Result of add_three_ints: for %d + %d + %d = %d' %                               # CHANGE
                    (minimal_client.req.a, minimal_client.req.b, minimal_client.req.c, response.sum)) # CHANGE
            break

    minimal_client.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    mai

package.xml

다음 줄을 추가합니다.

<exec_depend>tutorial_interfaces</exec_depend>

위의 편집을 수행하고 모든 변경 사항을 저장한 후 패키지를 빌드합니다.

colcon build --packages-select py_srvcli

그런 다음 두 개의 새 터미널을 열고 각각 source ros2_ws를 입력하고 다음을 실행합니다.

ros2 run py_srvcli service
ros2 run py_srvcli client 2 3 1

Last updated

Was this helpful?