커스텀 인터페이스
Custom Topic 사용 실습은 ROS 2 Jazzy 공식 Docs의 자료를 사용합니다. 보다 자세한 내용은 아래 링크를 참고해 주세요.
Custom Topic 생성하기
지금까지는 미리 정의된 형식의 토픽을 사용했습니다. 하지만, 필요할 경우 자신만의 커스텀 메시지와 커스텀 서비스, 즉 인터페이스를 만들어 사용할 수 있습니다. 이번 시간에는 커스텀 메시지를 만들고 사용해 보겠습니다.
가장 먼저, 커스텀 메시지를 정의하기 위한 인터페이스 패키지를 생성합니다.
$ ros2 pkg create --build-type ament_cmake --license Apache-2.0 tutorial_interfaces
인터페이스 패키지는 cmake 패키지로만 생성할 수 있지만, 다른 패키지에서 사용할 때는 파이썬과 C++ 패키지에서 모두 사용할 수 있습니다.
커스텀 메시지는 msg 폴더에, 커스텀 서비스는 srv 폴더에 저장되어야 합니다. 커스텀 메시지를 정의하기 위해 msg 폴더를 만듭니다.
$ mkdir msg
msg 폴더 안으로 이동하여 Num.msg 파일을 생성합니다. 파일을 열고 아래 내용을 입력합니다.
int64 num
이렇게 num이라고 불리는 64비트 정수 형태의 커스텀 메시지를 완성했습니다. 인터페이스는 std_msgs나 geometry_msgs 등 다른 메세지 패키지를 타입으로 사용할 수도 있습니다. msg 폴더에 Sphere.msg 파일을 새로 생성하고 아래 내용을 입력합니다.
geometry_msgs/Point center
float64 radius
커스텀 서비스를 만들고 싶다면 srv 폴더를 생성한 후 .srv 형태의 파일을 통해 생성할 수 있습니다.
int64 a
int64 b
int64 c
---
int64 sum
위와 같이 서비스 파일을 생성한다면 a, b, c를 요청하고 sum을 응답하는 형태의 커스텀 서비스가 됩니다.
커스텀 메시지를 다른 노드 등에서 사용하기 위해선 CMakeLists.txt 파일을 열고 아래 내용을 추가합니다. 다른 메시지 패키지를 사용했다면 find_package()와 DEPENDENCIES에 기재합니다.
find_package(geometry_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Num.msg"
"msg/Sphere.msg"
DEPENDENCIES geometry_msgs
)
다음으로 package.xml 파일을 수정합니다. 특히, 인터페이스는 rosidl_default_generators를 통해 서로 다른 언어에서 사용되기 때문에 빌드 도구 의존성을 선언해야 합니다.
package.xml 안에 아래 내용을 추가합니다.
<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>
단축어 cb를 입력해 빌드를 진행합니다. 소싱을 위해 터미널 창을 닫고 다시 열어줍니다.
$ cb
ROS 2 내장 명령어를 통해 인터페이스를 확인해 보겠습니다.
$ ros2 interface show tutorial_interfaces/msg/Num
$ ros2 interface show tutorial_interfaces/msg/Sphere
입력했던 형태로 인터페이스가 생성된 것을 확인할 수 있습니다.
Custom Topic 사용하기
이렇게 생성한 인터페이스는 파이썬 패키지와 c++패키지 모두 사용할 수 있습니다. 예시로 파이썬 패키지에서 custom 메시지를 사용해 보겠습니다. 이전에 작성했던 패키지 코드에 들어가 아래와 같이 수정합니다.
publisher_member_function.py
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_member_function.py
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()
package.xml 파일에 아래 내용을 추가합니다.
<exec_depend>tutorial_interfaces</exec_depend>
터미널 창을 2개 열고 각각 publisher와 subscriber 노드를 실행해 보겠습니다.
ros2 run py_pubsub talker
ros2 run py_pubsub listener
인터페이스에 지정해 둔 것과 같이 문자열이 아닌, 숫자 형태로 메시지를 가져오는 것을 확인할 수 있습니다.