1. DDS(Data Distribution Service) 정의
OMG 1에서 표준화한 middle ware2 서비스이다. 즉, middle ware(프로그램) + 프로토콜(통신규약, API)이 DDS이다. ROS2에서는 DDS의 RTPS (Realtime Publish Subscribe) 프로토콜을 사용한다. Open source로는 Fast DDS와 Cyclone DDS(Eclipse Foundation)가 있고 사설 DDS를 활용하면 고성능 네트워킹이 가능하다. 국내 자체 개발 Gurum DDS(GurumNetwork)도 존재한다. ROS2에서 노드들은 DDS(Data Distribution Service)에 참여하고 publisher subscriber 1개당 1개의 QOS profile을 갖는다.
1.1 DDS 4가지 특징
- IDL(Interface Description Language)를 활용하여 메세지 정의 및 직렬화3를 더 쉽게 가능하다.
- DCPS(Data centric publish subscribe) DLRL(Data Local Reconstruction Layer)를 추가해 DDSI-RTPS embedded protocol을 만들 수 있다.
즉, 데이터 중심적 설계를 한다. 데이터가 어떤 형식이고, 어느 시간 동안 저장되고 처리 돼야 하는지에 관해 QoS에 서술한다. - Dynamic Discovery(동적검색)로 DDS 미들웨어를 통해 노드 간 검색이 가능하다.
토픽에 이름(domain)을 부여해 누가 발신하고 수신 하는지 분리 가능하다. 어떤 노드가 이를 발신하고 수신하는지 알 필요가 없다. 즉, ROS1의 master node가 필요 없어졌다. 노드들의 IP 및 포트를 미리 입력할 필요 없다. - Qos(Quality of Security) 항목 22가지 성능과 안정성의 사이에서 절충안을 찾는다. 그중 ROS에선 8가지의 QOS를 세팅할 수 있다. Publish와 Subscribe에 사용가능하다.
1.2 QOS(Quality of Security) : 8가지 ROS의 QOS 세팅
| QoS 정책 | 기본값 (Default) | 의미 |
| History [HistoryPolicy::KeepLast] [HistoryPolicy::KeepAll] |
Keep Last | 최신 데이터만 저장할까? 전체 데이터를 저장할까? [Keep All]로 설정시 데이터 전체를 저장한다. |
| Depth [int, size_t] |
10 | 얼마나 저장할까? History가 [Keep Last] 일 때 에만 의미가 있다. [Keep All]로 설정시 Depth는 무시된다. [Keep All]로 설정할 때에도 ResourceLimits을 설정 가능하다. |
| Reliability (신뢰성) [ReliabilityPolicy::Reliable] [ReliabilityPolicy::BestEffort] |
Reliable | 데이터 도착 보장 할 것인가? (4계층을 무엇으로 쓸 것인가?) (RELIABLE[TCP] vs BEST_EFFORT[UDP]): TCP는 재전송 흐름제어 혼잡제어를 담당한다. UDP는 port를 기준으로 process간 통신을 맡는다. |
| Durability(내구성) [DurabilityPolicy::Volatile] [DurabilityPolicy::TransientLocal] |
Volatile (휘발성) |
신규 구독자를 위한 초기 데이터를 주냐? Volatile은 초기 데이터 안 주고, TransientLocal는 history의 depth만큼 데이터를 주고 받고 통신을 시작한다. |
| Deadline [std::chrono::duration] [sec+ nsec] |
Infinite (무한) 0초 == 무한 |
받고 보내는 FPS를 체크. [경고만 한다] Pub이 데드라인 내에 보내지 않으면 -> Offered Deadline Missed Sub이 데드라인 내에 받지 못하면 -> Requested Deadline Missed 송수신 버퍼에서 처리 속도 |
| Lifespan (수명) [std::chrono::duration] [sec+ nsec] |
Infinite (무한) 0초 == 무한 |
데이터의 수명 [데이터가 실제로 사라짐] 데이터는 만들어진 뒤 몇 초 뒤면 없어진다. |
| Liveliness (활기) [LivelinessPolicy::Automatic] [LivelinessPolicy::ManualByTopic] |
Automatic 0초 == 무한 |
데이터를 안 보내더라도, 나 살아있으니 연결 끊지 마 Automatic: 데이터 보내면 산 것으로 간주 - 주기적으로 데이터를 쏘는 센서 (보낼 때 마다 Automatic 타이머 리셋) ManualByTopic: 특정 함수를 통해 살아 있음을 알림 데이터가 드물게 발생하는 이벤트성 노드(화재 경보기, 살아 있어요!) |
| Lease Duration (임대기간, 생존기간) [std::chrono::duration] [sec+ nsec] |
Infinite (무한) | 이 시간 동안 연락 없으면 죽은 걸로 칠게 생존 시간 체크 안 함 |
- 서로 종속적인 파라미터가 아닌지 의심된다.
가령 History와 Depth는 종속적이다. Liveliness와 Lease Duration도 종속적이다.
가령, Depth가 -1일 경우 keep all로 대체 하면 안되나? 답변은 LLM의 추론이다.
2000년대에 만들어진 DDS는 C언어 기반으로 설계돼, place holder 개념으로 메모리를 먹고 있어야 한다.
또한 Magic Number로 -1로 할 경우, 시간 기반 로깅으로 확장이 어려워질 수 있다.
- Durability(내구성)의 어원
내구성이랑, 데이터 처음 연결시 어디부터 받아야 하는지 설정하는 것이랑 무슨 관계인지 서술한다. 데이터의 관점에서 네트워크상에 얼마나 잘 살아남느냐에 대한 내용이다. 원래는 DDS에는 데이터 생존의 4가지 설정이 있다. Volatile, Transient Local, Transient, Persistent e
데이터가 생성된 직후에 바로 사라지는가, 아니면 Publisher가 죽을 때까지 버티는가, 아니면 컴퓨터가 꺼져도 버티는가 등등으로 나눈다. 그래서 ROS에선 Volatile, Transient Local 두가지 설정으로 pub과 sub이 어디부터 데이터 송수신을 할지 결정한다.
- Lease Duration (임대 기간)의 어원
DHCP같은 시스템에서 계약 갱신(Renewal) 같은 용어를 사용했기에 임대기간이 살아있는 시간이다. 예시는 아래와 같다.
qos.liveliness(rclcpp::LivelinessPolicy::ManualByTopic);
qos.lease_duration(std::chrono::seconds(1)); // 1초 안에 신고 안 하면 죽음
auto pub = this->create_publisher<std_msgs::msg::String>("topic", qos);
void timer_callback() {
if (has_new_data) {
pub->publish(msg); // 데이터 보내면 자동 갱신
} else {
// ★ 데이터가 없어도 이 함수를 호출해서 "나 살아있어"라고 갱신!
pub->assert_liveliness();
}
}
1.3 코딩
1.3.1 RMW_ENVIRONMENT 환경변수를 통해 DDS의 종류를 정할 수 있다.
export RMW_IMPLEMENTATION =
rmw_connext_cpp
rmw_cyclonedds_cpp
rmw_fastrtps_cpp
rmw_gurumdds_cpp
//talker listerner 두개 env 달라도 가능하다.
1.3.2 Domain으로 원하는 Node끼리 통신 가능하다.
UDP multicast이기 때문에 도메인 내에 모든 네트워크 연결 된다. DDS domain이나 ros namespace를 변경한다.
export ROS_DOMIAN_ID= int[0,101]
//같은 domain끼리만 통신 가능하다.

1.3.3 QOS를 세팅하는 3가지 방법
첫번째 방법이 가장 좋은 것 같다.
1.3.3.1 Default QOS
rclcpp (ros client library c++)에선 Default 로 QOS setting을 주고 있다.

auto qos_profile = rclcpp::SensorDataQoS();
pub_ = this->create_publisher<sensor_msgs::msg::Image>(
"camera/image",
qos_profile
);
| Sensor Data | Service | |
| Reliability | BEST_EFFORT | RELIABLE |
| History | KEEP_LAST | KEEP_LAST |
| Depth(History Depth) | 5 | 10 |
| Durability | VOLATILE | VOLATILE |
1.3.3.2 런타임에 yaml를 load하기
# 노드 이름 (네임스페이스가 있다면 /ns/node_name)
/camera_driver_node:
ros__parameters:
qos_overrides:
# 설정할 토픽 이름
/camera/image_raw:
publisher:
reliability: best_effort # reliable 또는 best_effort
durability: volatile # volatile 또는 transient_local
history: keep_last # keep_last 또는 keep_all
depth: 5 # 정수값
deadline: 0.1 # 초 단위 (100ms)
ros2 run my_package my_node --ros-args --params-file qos_params.yaml
1.3.3.3 코드에 임베딩 하기
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include <chrono>
using namespace std::chrono_literals; // ms, s 단위 사용
class ControlNode : public rclcpp::Node {
public:
ControlNode() : Node("control_node") {
// 1. 기본 Depth 설정 (생성자 인자)
rclcpp::QoS custom_qos_profile(10);
// 2. 세부 정책 설정 (체이닝 방식)
custom_qos_profile
.reliability(rclcpp::ReliabilityPolicy::Reliable) // 데이터 도착 보장
.durability(rclcpp::DurabilityPolicy::TransientLocal) // 늦게 온 구독자에게도 전송
.deadline(500ms) // 0.5초마다 데이터 안 오면 경고
.lifespan(1s); // 1초 지나면 데이터 폐기
// 3. Publisher에 적용
pub_ = this->create_publisher<std_msgs::msg::String>(
"robot_command",
custom_qos_profile
);
// 4. Subscriber에도 동일한 QoS 적용 (Transiet Local 등은 맞춰야 함)
sub_ = this->create_subscription<std_msgs::msg::String>(
"robot_command",
custom_qos_profile,
[](const std_msgs::msg::String::SharedPtr msg) {
// 콜백 함수 내용
}
);
}
private:
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_;
};
ROS에서 쓰지 않는 DDS QOS
| 분류 | ROS 2 미지원(숨김) DDS 정책 예시 | 역할 |
| 리소스 제한 | ResourceLimits | 메모리 할당의 최대치를 물리적으로 제한 (메모리 릭 방지) |
| 네트워크 우선순위 | TransportPriority | 네트워크 패킷에 태그를 달아 라우터에서 우선 처리되게 함 |
| 소유권 관리 | Ownership | 같은 토픽을 여러 명이 보낼 때, 누구의 데이터가 '진짜'인지 결정 (Strength 기반) |
| 데이터 분할 | Partition | 같은 도메인 ID 안에서도 논리적 그룹을 나누어 서로 못 보게 함 |
| 메타데이터 | UserData / TopicData | 통신 데이터 외에 부가 정보(ID, 보안 토큰 등)를 헤더에 실어 보냄 |
| 지연 허용 | LatencyBudget | "이 정도 지연은 참을 수 있다"는 힌트를 미들웨어에 줌 (전송 묶음 처리 등 최적화용) |
DDS 벤더(FastDDS, CycloneDDS)의 XML 설정에서 얼마나 빨리 복구할지 같은 것도 설정할 수 있다.
ref.
https://www.dds-foundation.org/what-is-dds-3/
https://en.wikipedia.org/wiki/Data_Distribution_Service
ROS2로 시작하는 로봇 프로그래밍
https://docs.ros.org/en/rolling/Concepts/Intermediate/About-Quality-of-Service-Settings.html
https://www.omg.org/spec/DDS/1.4/PDF
https://docs.ros2.org/dashing/api/rclcpp/classrclcpp_1_1QoS.html
- Object Management Group (객체 관리 그룹)은 UML, CORBA(Common Object Request Broker Architecture) 등도 표준화했다. [본문으로]
- Middle ware란 OS와 application 사이에서 통신을 시켜주는 프로그램이다. 많은 미들웨어가 네트워크 프로그래밍으로 구현된다. 서로 다른 OS에서 다양한 기기들이 다양한 언어로 프로그래밍 해 통신 해주는 프로그램이다. 대표적으로 DB, web server, DDS 등이 있다. [본문으로]
- 직렬화 Serialization은 RAM에 있는 Object 들을 Stream으로 Storage에 저장하는 것을 의미한다. [본문으로]
'Engineering > ROS' 카테고리의 다른 글
| ROS2 Node 스케줄링, 구조, 인터페이스 파라미터 (3) | 2025.01.14 |
|---|---|
| ROS2 표준 시간, 좌표계 (1) | 2025.01.12 |
| ROS2 참고 사이트 (2) | 2025.01.10 |
| ROS2 빌드, 파일 시스템 (4) | 2025.01.08 |
| Docker로 ROS를 사용하면 안 되는 이유 (3) | 2025.01.06 |