본문으로 건너뛰기

ROS 2 — 메시지 타입

토픽·서비스·액션이 운반하는 '타입'의 정체. struct처럼 생긴 데이터 모양 정의가 .msg 파일이다.

토픽·서비스·액션을 다룰 때 자꾸 “이름 + 타입” 이라는 표현이 나왔다. 토픽 이름은 /camera/image, 타입은 sensor_msgs/msg/Image 같이. 그런데 “타입” 이 정확히 뭔지는 짚지 않았다.

이번 글에서 그 정체를 본다. 한 줄로 — 메시지가 운반할 데이터의 모양 정의 다.

1. 메시지 = 구조화된 데이터의 모양

자율주행 로봇이 모터에게 “앞으로 1m/s, 오른쪽으로 0.5rad/s로 돌면서 가라” 라는 명령을 보낸다고 해보자. 이 명령에 필요한 정보는:

  • 직선 속도: x, y, z 방향
  • 회전 속도: x, y, z 축

이 6개 숫자를 한 묶음으로 묶은 게 메시지다. ROS에는 이걸 위해 정해진 표준 메시지가 있다 — geometry_msgs/Twist.

Twist 메시지의 타입 정의(.msg 파일) 는 이렇게 생겼다:

geometry_msgs/Vector3 linear
geometry_msgs/Vector3 angular

해석:

  • linear 라는 필드가 있고, 타입은 Vector3 (벡터 — x/y/z 세 숫자)
  • angular 라는 필드도 같은 Vector3 타입

Vector3 자체도 메시지 타입이라서, 그 정의를 또 보면:

float64 x
float64 y
float64 z

float64(64비트 부동소수점 — 소수점 있는 숫자) 세 개. 즉 Twist 메시지는 실제로 6개의 숫자 묶음이다.

이게 “타입” 의 정체다. 구조화된 데이터의 모양 정의 — 어떤 필드가 있고 각 필드가 어떤 자료형인지를 명시한 schema. (C 언어 struct, Python dataclass와 비슷한 개념.)

2. 메시지 안에 메시지가 들어간다 (중첩)

위에서 봤듯이 Twist 안에 Vector3 가 들어가 있다. 이렇게 메시지가 다른 메시지를 포함할 수 있다.

그림으로 보면:

geometry_msgs/Twist linear: Vector3 x: float64 y: float64 z: float64 angular: Vector3 x: float64 y: float64 z: float64

큰 cream 박스(Twist) 안에 두 개의 lavender 박스(linear, angular)가 들어 있고, 각 lavender 박스 안에 float64 필드 3개씩.

자주 만나는 다른 중첩 예시:

  • Pose = Point(위치 x, y, z) + Quaternion(방향 x, y, z, w)
  • PoseStamped = Header(시간·좌표계 정보) + Pose
  • Image = Header + 해상도(height, width) + 픽셀 배열

복잡한 데이터도 작은 메시지의 조합으로 깔끔하게 표현된다.

3. 표준 메시지 패키지

ROS에는 대부분의 로봇이 공통으로 쓰는 데이터를 위한 표준 메시지 패키지가 있다. 직접 정의할 필요 없이 import해서 쓰면 된다.

패키지다루는 데이터대표 타입
std_msgs기본 자료형String, Int32, Float64, Bool
geometry_msgs기하 (위치·방향·속도)Twist, Pose, PoseStamped, Point
sensor_msgs센서Image, LaserScan, Imu, NavSatFix(GPS)
nav_msgs자율주행OccupancyGrid(지도), Path, Odometry(주행거리)
visualization_msgs시각화Marker, MarkerArray (RViz 표시용)

자율주행 로봇이 평소 쓰는 것:

  • 카메라 → sensor_msgs/Image
  • 라이다 → sensor_msgs/LaserScan
  • GPS → sensor_msgs/NavSatFix
  • 모터 명령 → geometry_msgs/Twist
  • 현재 위치 추정 → nav_msgs/Odometry

표준 타입은 다른 ROS 시스템과 말이 통하는 공용어다. 새 카메라 노드를 가져와도 sensor_msgs/Image 로 발행하기만 하면 알고리즘 노드는 그대로 받을 수 있다.

4. 메시지 타입 이름의 구조

자세히 보면 geometry_msgs/Twist, sensor_msgs/msg/Image 같은 형식이 보인다. 이건:

[패키지 이름] / msg / [메시지 이름]
  • 패키지 이름 — 어느 패키지에 속하는지 (geometry_msgs)
  • msg — 이게 메시지 타입이라는 표시 (서비스는 srv, 액션은 action)
  • 메시지 이름 — 패키지 안에서의 이름 (Twist)

가끔 줄여서 geometry_msgs/Twist 라고 쓰는데, 정식은 geometry_msgs/msg/Twist.

5. 커스텀 메시지

표준 패키지에 없는 데이터를 다루려면 직접 정의한다. 자율주행 로봇만의 “주행 모드” 정보를 표현하고 싶다면 — 패키지 my_robot_msgs/msg/DrivingMode.msg:

string mode_name           # "주행", "주차", "비상정지"
float64 max_speed_mps
bool obstacle_detected

.msg 파일을 만들고 빌드하면, 다른 노드들이 my_robot_msgs/msg/DrivingMode 타입으로 토픽·서비스·액션을 만들 수 있다.

원칙: 표준에 있으면 표준 쓰기, 없을 때만 직접 정의. 표준 타입은 다른 도구·라이브러리(RViz, rosbag 등)가 자동으로 인식하기 때문.

6. .msg / .srv / .action 관계

세 종류의 파일이 있었는데, 셋의 관계를 그림으로 정리하면:

토픽 .msg 1개 메시지 서비스 .msg 2개 Request Response 액션 .msg 3개 Goal Feedback Result
  • .msg — 메시지 1개의 모양 정의
  • .srv — 메시지 2개를 묶음 (Request + Response)
  • .action — 메시지 3개를 묶음 (Goal + Feedback + Result)

.srv 파일 형식:

# Request (요청에 들어가는 데이터)
string mode_name
---
# Response (응답에 들어가는 데이터)
bool success
string message

위/아래를 --- 로 가른다.

.action 파일 형식:

# Goal (목표)
geometry_msgs/Pose target_pose
---
# Result (최종 결과)
bool success
float64 total_distance
---
# Feedback (진행 알림)
geometry_msgs/Pose current_position
float64 distance_remaining

세 부분을 --- 두 줄로 가른다.

결국 타입 시스템의 단위는 .msg — 토픽, 서비스, 액션 모두 .msg 위에서 조합되는 셈.

7. 직접 보기 — ros2 interface 명령

타입 정의를 명령어로 직접 볼 수 있다.

$ ros2 interface show geometry_msgs/msg/Twist
# Twist 메시지 정의를 그대로 출력함

$ ros2 interface list
# 시스템에 설치된 모든 메시지 타입 목록

$ ros2 topic type /cmd_vel
geometry_msgs/msg/Twist
# /cmd_vel 토픽이 운반하는 메시지 타입

한 줄로 박아둘 것

  1. “타입”의 정체 — 메시지가 운반할 데이터의 모양 정의 (struct 같은 schema)
  2. 메시지는 중첩될 수 있다Twist 안에 Vector3, Pose 안에 PointQuaternion이 들어가는 식
  3. 표준 메시지 패키지std_msgs / geometry_msgs / sensor_msgs / nav_msgs 등. 가급적 표준을 쓴다
  4. 타입 이름은 패키지/msg/이름 형식 — 예: geometry_msgs/msg/Twist
  5. .msg가 단위, .srv/.action은 조합 — 서비스 = msg 2개 묶음, 액션 = msg 3개 묶음