ROS2 이론 정리

사용자 정의 Interface 생성 및 활용

dawon-project 2025. 4. 18. 02:04

인터페이스(Interface)  : ROS2에서 노드 사이에 데이터를 전송시 사용되는 토픽(Topic), 서비스(Service), 액션(Action)에서 사용되는 데이터 타입

  • 인터페이스 내부 그룹
    • Messages : .msg 파일에서 정의되며 ROS 메세지에 대해 기술 -> Topic에서 사용
    • Services : .srv 파일에서 정의되며, request, response 두 부분으로 구성 -> Service에서 사용
    • Actions : .action 파일에서 정의되며, goal, result, feedback 세 부분으로 구성 -> Action에서 사용
ros2 interface list  # interface 들을 전부 보여주는 명령어
#결과 
Messages:
     action_msgs/msg/GoalInfo
     action_msgs/msg/GoalStatus
     action_msgs/msg/GoalStatusArray
     actionlib_msgs/msg/GoalID
     actionlib_msgs/msg/GoalStatus
      ...
Services:
      action_msgs/srv/CancelGoal
      composition_interfaces/srv/ListNodes
      composition_interfaces/srv/LoadNode
      ...
Actions:
      action_tutorials_interfaces/action/Fibonacci
      control_msgs/action/FollowJointTrajectory
      control_msgs/action/GripperCommand
      control_msgs/action/JointTrajectory
       ...
ros2 interface show  <interface_name> # 특정 인터페이스에 대한 정보를 얻는데 사용되는 명령어

ros2 interface proto <interface_name> # 해당 인터페이스의 프로토타입을 보여주는 명령어(기본값)

  • 일반적으로 std_msgs나 geometry_msgs와 같은 미리 선언된 인터페이스를 바로 사용 가능하나 필요에 따라 커스텀 인터페이스 생성가능
  • 여러 개의 패키지를 가지는 경우, 별도의 인터페이스 패키지를 생성하여 사용하는 것을 추천
    (이 경우 여러 패키지들이 만들어진 인터페이스 패키지를 공유하며 사용 가능)
  • 인터페이스 패키지 생성시 주의할 점
    • 개발 언어가 python이라도 build-type을 ament_cmake로 설정이 필요
      • 이유? -> 인터페이스 패키지는 실행하는 파일이 아니라 타입을 정의하는 것이 목적이기 때문
        • ament_cmake는 메세지/서비스/액션 파일을 빌드할 수 있게 해주는 rosidl_generate_interfaces() 라이브러리가 있다. 
          -> 즉, rosidl_generate_interfaces()는 .msg, .srv, .action 파일을 빌드시 자동으로 언어별 코드(Python, C++)로 생성되게 해준다.
        • ament_python은 빌드 기능이 없다
          • ament_python은 설치용(build가 목적X)목적으로 사용됨
          • ament_python 패키지에서는 rosidl_generate_interfaces() 같은 함수 사용 불가

 

이제 인터페이스 패키지를 만들어보자

1. 패키지 생성

ros2 pkg create --build-type ament_cmake ros_study_msgs # "ros_study_msgs" 명의 패키지 생성

 

  • 초기 패키지 생성시 파일 구조

 

2. action, msg, srv 폴더 생성

 

3. 각 통신 방식(Topic, service, action)에 맞는 인터페이스 형식으로 파일 생성

 

4. 생성한 파일에 형식에 맞게 코드 작성

4-1 Topic interface

  • Topic에서 사용하는 인터페이스 파일 형식인 .msg는 구분선(---)이 없다.
    -> 단순한 필드만 나열

4-2 sevice interface

  • Service에서 사용하는 인터페이스 파일 형식인 .srv는 구분선(---)이 1개이다.
    -> 요청(Request)과 응답(Response) 두 부분으로 구성

4-3 Action interface

  • Action에서 사용하는 인터페이스 파일 형식인  .action은 구분선(---)이 2개이다.
    ->goal, result, feedback 세 부분으로 구성
    • 구분선을 안 쓰면 빌드 시 오류나거나 잘못된 인터페이스로 처리된다.

 

* 패키지 생성후 타입명 정의할때 참고할 표

  • Type name의 이름들을 인터페이스 내에서 변수의 타입으로 정의해주면 C++, Python, DDS에서 자동으로 아래 표와같이 Type이 인식이 된다.

 

5. Package.xml 파일 수정 -> 형광박스 안의 4개의 태그를 선언해줘야한다.

<buildtool_depend>rosidl_default_generators</buildtool_depend>
#코드 생성기 즉, 인터페이스 파일(.msg, .srv, .action)을 자동으로 언어별 코드로 자동 생성해주는 빌드 도구
#  .msg, .srv, .action 파일을 읽어서 c++, python 등 언어 타입지원 코드를 생성시키고 colcon build 시에 msg 파일을 코드로#변환하는 역활을 하며 자동실행됨
# ex) .msg -> C++/Python 용 메세지 클래스를 생성해준다.
# 없으면 빌드 도중 .msg나 .srv 파일을 처리 못해서 오류 발생

<exec_depend>builtin_interfaces</exec_depend>
#ROS2에서 자주 쓰이는 기본 타입들을 정의하는 패키지
# 런타임에서 메시지 타입 사용 가능하게 함
# ex) std_msgs/Header, builtin_interfaces/Time, Duration 등

<exec_depend>rosidl_default_runtime</exec_depend>
# 런타임에서 메세지를 import하거나 사용할 수 있도록 하는 기능을 제공
# Python 노드에서 from my_pkg.msg import MyMsg 할떄 필요한 라이브러리
# 업으면 생성된 메세지 타입을 노드에서 import 할 수 없다.

<exec_pend>rosidl_interface_packages</exec_depend>
# 다른 ROS 인터페이스 패키지들에 대한 공통 의존성
# 즉, 인터페이스 전용 패키지를 만들때 쓰이는 태그
# 예를 들어 std_msgs, sensor_msgs, geometry_msgs 같은 패키지를 포함
#간접적인 종속성 관리를 용이하게 함
# 없으면 다른 인터페이스 패키지에서 정의한 타입을 사용할때 누락될 수 있다.

 

6. CMakeLists.txt 파일 수정

  • 아래의 태그들을 추가해줘야하는 이유
    • 인터페이스 파일은 자동 변환이 필요하기 때문
      • .msg, .srv, .action 파일은 그냥 텍스트일 뿐이라 실제로 쓰려면 Python/C++ 코드로 변환되어야 함
    • 빌드 시 어떤 파일을 코드로 만들 건지를 알려줘야하기 때문
      • set(msg_files ...), rosidl_default_generators(...) 이런 설정이 없으면 ROS는 어떤 인터페이스를 코드로 만들어야 하는지 모른다.
    • 의존하는 타입들도 같이 알려줘야하기 때문
      • 예를 들어 메시지 안에 builtin_interfaces/Time이 있으면, 이게 어디서 왔는지 알려줘야 코드 생성할 수 있음.
      • ROS2가 기본적으로 제공하는 인터페이스들을 사용하기 위함
find_package(builtin_interfaces REQUIRED)
# REQUIRED 키워드 : find_packages 에서 해당 패키지가 반드시 있어야 빌드를 성공하게 하겠다는 조건을 거는 키워드
# builtin_interfaces : 패키지를 찾는 명령어
# ros2 인터페이스 메시지에서 공통으로 사용되는 기본타입을 정의한 패키지
# Time, Duration, String, Bool, Int32, Float54 등등

find_package(rosidl_default_generators REQUIRED) # 인터페이스 파일을 코드로 자동 생성하는 도구 패키지를 찾음

# 사용자가 정의한 메세지 파일 리스트를 설정
set(msg_files
   "msg/MyMsg.msg"

set(srv_files
   "srv/MySrv.srv"
)
set(action_files
   "action/MyAction.action"
)

# 인터페이스 파일을 기반으로 C++/Python 코드 자동 생성
# rosidl_default_generators() 함수를 실행시켜 위에서 지정한 .msg, .srv, .action 파일들을 찾아서
# 각각의 언어에 맞는 메세지 타입 코드를 생성해준다.
rosidl_default_generators(${PROJECT_NAME}
    ${msg_files}
    ${srv_files}
    ${action_files}
   DEPENDENCIES builtin_interfaces # 메세지 정의에서 사용한 외부 타입들을 알려줘서 빌드시 처리할 수 있게함
)
#${} : CMake에서 변수를 참조할 때 쓰는 문법
#set(변수이름  파일명) 명령어를 통해 만든 변수는 ${변수이름}으로 불러올 수 있게 된다.
# 공백 또는 줄바꿈을 통해서 여러 값을 인식해서 리스트를 만든다

 

만약 두 개의 인터페이스 파일을 추가 하고 싶은 경우  아래와 같이 set 함수 안에 띄워쓰기 혹은 줄바꿈해서 파일 명을 추가해 주면 된다.

 

7. 빌드 후 확인

아래와 같이 명령어를 입력했을때 만든 인터페이스들이 떠야한다.

 

colcon build 하고 나면 install-> 해당 패키지 명 -> include 파일에 만든 인터페이스들이 생성된다. DDS 통신이 가능하도록 알아서 생성해준다.

 

이렇게 만든 인터페이스를 다른 패키지에서 불러와서 사용하는 것을 봐보자

my_msg_test.py에 아래와 같이 코드를 작성해서 토픽을 발행하는 노드를 생성할 수 있고, 이때 우리가 만든 인터페이스를 아래와 같이 불러와서 사용할 수 있다.

from ros_study_msgs.msg import MyMsg 

 

 

근데 이것을 실행 하기전에 만든 사용자 인터페이스 패키지에 대한 의존성을 추가하는 사전 작업이 필요하다.
-> 패키지명이 일치해야한다.

 

이제 터미널 창에서 publish했을때 topic echo로 값이 잘 들어오는 것을 볼 수 있다.

 

*추가로 알아야할 내용

 Message Topic Message, Service Message, Action Messaage 나뉜다.

이 중 Service Message 안에 있는 request와 response 두 부분으로 구성되어 있으며, 이 두 부분을 각 section value라고 한다.

  section value ros2 내부에 미리 정의된 형식을 따르며 메시지 파일(.srv)에서는 --- 구분선을 기준으로 위쪽이 request, 아래쪽이 response로 나뉘게 된다.
-> ROS2가 자동으로 인식!

그리고 section 안에 정의된 변수 arithmetic_operator, arithmetic_value field value라고 부른다.

즉, section이 메세지의 큰 덩어리 단위, field는 그 안에 포함된 구체적인 데이터 항목을 의미한다.

 

<타입> <이름> = <값> # 이와 같은 형식은 ROS2가 상수라고 인식하게 된다.
# 상수는 각 Section의 최상단에 선언하는 것이 규칙이다.

 

위의 Service interface를 예시로 들었을 때 ROS2는 구분선(---)을 기준으로 자동으로 아래의 그림과 같이 인식된다.

 

-> 각 field에 접근하기 위해서는 "." 연산자를 통해 접근한다.

<인터페이스명>.<Section명(Topic 처럼 없는 경우 생략)>.<Field value> # 각 필드에 접근하기 위한 방법
# ex) ArithmeticOperator.Request.section
  토픽(Topic)-> .msg 서비스(Service)-> .srv 액션(Action)-> .action
Section 있는가? X(단일 메세지 구조) O O
Section 개수
(---을 기준으로 순서대로 자동 할당)
X 2(Request, Response) 3(Goal, Result, Feedback)

 

*하나의 노드는 서비스, 액션,  토픽 발행과 구독 등의 여러 개의 통신을 노드 내부의 객체들을 통해서 수행할 수 있다.

 

ros_study_msgs.zip
0.01MB

 

 

ros_study_ros.zip
0.01MB