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() 같은 함수 사용 불가
- ament_cmake는 메세지/서비스/액션 파일을 빌드할 수 있게 해주는 rosidl_generate_interfaces() 라이브러리가 있다.
- 이유? -> 인터페이스 패키지는 실행하는 파일이 아니라 타입을 정의하는 것이 목적이기 때문
- 개발 언어가 python이라도 build-type을 ament_cmake로 설정이 필요
이제 인터페이스 패키지를 만들어보자
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) |
*하나의 노드는 서비스, 액션, 토픽 발행과 구독 등의 여러 개의 통신을 노드 내부의 객체들을 통해서 수행할 수 있다.