ROS2 Tutorial (Basic)
  • 교육 안내
  • ROS2 개요
  • ROS2 환경 구성
  • turtlesim, ROS2 rqt
  • 노드(Node) 이해하기
  • 토픽(Topic) 이해하기
  • 서비스(Service) 이해하기
  • 파라메터(Parameter) 이해하기
  • 액션(Action) 이해하기
  • rqt_console 을 이용해 로그 보기
  • 노드 실행하기(launch)
  • 데이터 기록 및 재생
  • ROS2 프로그래밍 시작
  • 작업공간 만들기
  • 패키지 만들기
  • 간단한 게시자와 구독자 노드 만들기
  • 간단한 서비스 및 클라이언트 작성
  • 사용자 정의 msg 및 srv 파일 생성
  • 사용자 정의 인터페이스 구현
  • 파라메터 사용하기
  • 문제 식별에 ros2doctor 사용하기
Powered by GitBook
On this page
  • 개요
  • 실습
  • 1. 새 패키지 만들기
  • 2. 사용자 지정 정의 만들기
  • 3. CMakeLists.txt
  • 4. package.xml
  • 5. tutorial_interfaces 패키지 빌드
  • 6. msg 및 srv 생성 확인
  • 7. 새 인터페이스 테스트

Was this helpful?

Export as PDF

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

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

Previous간단한 서비스 및 클라이언트 작성Next사용자 정의 인터페이스 구현

Last updated 1 year ago

Was this helpful?

개요

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

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

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

실습

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
)

참고

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.txt와 package.xml파일에 대한 몇 가지 간단한 수정으로 새 인터페이스를 사용할 수 있습니다.

7.1 pub/sub를 사용한 Num.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테스트

서비스

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

rosidl_generate_interfaces의 첫 번째 인수(라이브러리 이름)는 ${PROJECT_NAME}과 일치해야 합니다( 참조 ).

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

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

이전 자습서에서는 메시지 및 서비스 인터페이스를 활용하여 주제
서비스
C++
Python
C++
Python
C++
Python
C++
Python
https://github.com/ros2/rosidl/issues/441#issuecomment-591025515
이전 자습서( C++
Python
이전 자습서( C++
Python