本章深入探讨 ROS2 的核心架构设计,重点解析其如何通过 DDS 中间件实现分布式通信、如何满足实时系统需求,以及如何通过 QoS 策略提供灵活的通信保证。通过对比 ROS1,我们将理解 ROS2 在架构层面的根本性改进,这些改进使其能够满足工业级机器人系统的严苛要求。
数据分发服务(Data Distribution Service, DDS)是对象管理组织(OMG)制定的中间件协议和 API 标准,专为分布式实时系统设计。ROS2 采用 DDS 作为其通信基础设施,从根本上改变了机器人系统的通信架构。
DDS 的核心概念模型:
┌─────────────────────────────────────────────────────┐
│ DDS Domain │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Domain │ │ Domain │ │
│ │ Participant │◄────────────►│ Participant │ │
│ │ │ RTPS │ │ │
│ │ ┌──────────┐ │ │ ┌──────────┐ │ │
│ │ │Publisher │ │ │ │Subscriber│ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │┌────────┐│ │ │ │┌────────┐│ │ │
│ │ ││DataWriter│─────Topic────►│ ││DataReader│ │ │
│ │ │└────────┘│ │ │ │└────────┘│ │ │
│ │ └──────────┘ │ │ └──────────┘ │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────┘
DDS 采用以数据为中心的发布-订阅(DCPS)模型,具有以下关键特性:
实时发布订阅协议(Real-Time Publish-Subscribe, RTPS)是 DDS 的有线协议,定义了数据在网络上的传输格式。RTPS 2.2 规范包含以下核心组件:
RTPS 实体模型:
{prefix, entityId}发现协议(Discovery Protocol):
RTPS 使用两阶段发现机制:
239.255.0.1:7400 (IPv4)可靠性机制:
RTPS 提供多种可靠性保证:
心跳机制(Heartbeat):
Writer ──HB(seq_min, seq_max)──► Reader
◄──────ACK/NACK───────────
────────DATA──────────────►
其中:
- HB: 心跳消息,包含序列号范围
- ACK: 确认接收
- NACK: 请求重传
ROS2 支持多种 DDS 实现,各有特点:
| DDS 实现 | 开发商 | 许可证 | 特点 | 适用场景 |
|---|---|---|---|---|
| Fast DDS | eProsima | Apache 2.0 | ROS2 默认,功能全面 | 通用开发 |
| Cyclone DDS | Eclipse | EPL/EDL | 轻量级,低延迟 | 嵌入式系统 |
| RTI Connext | RTI | 商业 | 工业级,高可靠性 | 关键任务系统 |
| CoreDX | Twin Oaks | 商业 | 极小内存占用 | 资源受限设备 |
| GurumDDS | Gurum Networks | 商业 | 韩国航天认证 | 航空航天 |
性能基准测试(基于 1KB 消息,1000Hz 发布频率):
延迟对比(microseconds):
Cyclone DDS: ████ 45μs
Fast DDS: ███████ 78μs
RTI Connext: █████ 52μs
CoreDX: ███ 38μs
吞吐量对比(messages/sec):
Fast DDS: ████████████ 120K
Cyclone DDS: ██████████ 100K
RTI Connext: ███████████████ 150K
CoreDX: █████████ 90K
ROS2 通过 RMW(ROS MiddleWare)接口抽象不同的 DDS 实现:
// RMW 接口示例
typedef struct rmw_node_t {
const char * name;
const char * namespace_;
rmw_context_t * context;
rmw_node_impl_t * impl;
} rmw_node_t;
// 创建节点的统一接口
rmw_node_t * rmw_create_node(
rmw_context_t * context,
const char * name,
const char * namespace_);
RMW 层次结构:
┌─────────────┐
│ ROS2 API │ <- rclcpp/rclpy
├─────────────┤
│ RCL │ <- ROS Client Library
├─────────────┤
│ RMW │ <- 抽象接口层
├─────────────┤
│ DDS Impl │ <- Fast DDS/Cyclone等
├─────────────┤
│ Network │ <- UDP/共享内存
└─────────────┘
实时系统的核心要求是 确定性(Determinism) 和 时间约束(Timing Constraints)。ROS2 从设计之初就考虑了实时性需求:
实时性分类:
ROS2 实时性保证的数学模型:
设系统响应时间为 $R$,包含:
则:$R = T_{sensor} + T_{comm} + T_{proc} + T_{actuator}$
对于硬实时系统,必须满足:$R \leq D_{deadline}$
实时系统必须避免动态内存分配导致的不确定性。ROS2 提供多种内存管理策略:
1. 预分配内存池:
// 使用自定义分配器避免运行时分配
template<typename T>
class PoolAllocator {
private:
std::array<T, POOL_SIZE> pool_;
std::bitset<POOL_SIZE> used_;
public:
T* allocate(size_t n) {
// O(1) 时间复杂度的分配
for(size_t i = 0; i < POOL_SIZE; ++i) {
if(!used_[i]) {
used_[i] = true;
return &pool_[i];
}
}
throw std::bad_alloc();
}
};
2. 零拷贝通信:
ROS2 支持通过共享内存实现零拷贝:
进程 A 共享内存 进程 B
┌──────┐ ┌─────────┐ ┌──────┐
│Writer│───write ptr────►│ Message │◄───read ptr────│Reader│
└──────┘ └─────────┘ └──────┘
无需复制,直接访问同一内存区域
ROS2 执行器支持多种调度策略:
1. 单线程执行器(SingleThreadedExecutor):
2. 多线程执行器(MultiThreadedExecutor):
3. 静态单线程执行器(StaticSingleThreadedExecutor):
实时调度示例:
// 设置实时优先级
struct sched_param param;
param.sched_priority = 95; // RT优先级 1-99
pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
// CPU 亲和性设置
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(2, &cpuset); // 绑定到 CPU 2
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
时间抖动(Jitter)是实时系统的关键指标:
理想周期执行:
t0───┬───t1───┬───t2───┬───t3
│100ms │100ms │100ms
实际执行(含抖动):
t0───┬────t1'───┬──t2'────┬───t3'
│102ms │98ms │101ms
抖动 = max(|ti' - ti|) = 2ms
ROS2 提供工具测量和优化抖动:
# 使用 ros2 trace 分析时间特性
ros2 trace --session-name realtime_analysis
ros2 run my_pkg realtime_node
# 分析结果
babeltrace ~/tracing/realtime_analysis | grep callback_duration
ROS1 和 ROS2 在架构上存在根本性差异:
ROS1 架构(中心化):
┌─────────┐
│ Master │ <- 单点故障
└────┬────┘
┌────────┼────────┐
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐
│Node A│ │Node B│ │Node C│
└──────┘ └──────┘ └──────┘
└────────┬────────┘
P2P 通信
ROS2 架构(去中心化):
┌──────┐ ┌──────┐ ┌──────┐
│Node A│◄───►│Node B│◄───►│Node C│
└──────┘ └──────┘ └──────┘
▲ ▲ ▲
└────────────┼────────────┘
DDS Discovery
(自动发现)
| 特性 | ROS1 | ROS2 |
|---|---|---|
| 协议 | TCPROS/UDPROS | DDS-RTPS |
| 发现机制 | Master 注册 | 自动发现 |
| QoS | 基础(队列大小) | 完整 QoS 策略 |
| 安全性 | 无内置支持 | SROS2 安全框架 |
| 传输层 | TCP/UDP | UDP/共享内存 |
| 类型协商 | MD5 哈希 | 类型描述 |
ROS2 引入了标准化的节点生命周期:
┌─────────────┐
│Unconfigured │
└──────┬──────┘
│ configure()
┌──────▼──────┐
│ Inactive │
└──────┬──────┘
│ activate()
┌──────▼──────┐
│ Active │
└──────┬──────┘
│ deactivate()
┌──────▼──────┐
│ Inactive │
└──────┬──────┘
│ cleanup()
┌──────▼──────┐
│Unconfigured │
└─────────────┘
生命周期节点实现:
class MyLifecycleNode : public rclcpp_lifecycle::LifecycleNode {
public:
CallbackReturn on_configure(const State &) {
// 分配资源,读取参数
publisher_ = create_publisher<std_msgs::msg::String>("topic", 10);
return CallbackReturn::SUCCESS;
}
CallbackReturn on_activate(const State &) {
// 开始处理
publisher_->on_activate();
return CallbackReturn::SUCCESS;
}
};
| 方面 | ROS1 (catkin) | ROS2 (colcon/ament) |
|---|---|---|
| 构建工具 | catkin_make | colcon build |
| 包格式 | package.xml v1 | package.xml v3 |
| CMake | catkin macros | ament_cmake |
| Python | setup.py | setup.py + setup.cfg |
| 并行构建 | 有限支持 | 原生支持 |
| 增量构建 | 需要 catkin tools | 默认支持 |
QoS 策略定义了数据传输的服务质量特性。ROS2 继承了 DDS 的完整 QoS 模型,提供细粒度的通信控制:
QoS 策略维度:
BEST_EFFORT:尽力传输,允许丢失RELIABLE:保证传输,自动重传VOLATILE:不保存历史数据TRANSIENT_LOCAL:保存历史数据给后来的订阅者KEEP_LAST(n):保留最新 n 条消息KEEP_ALL:保留所有消息ROS2 预定义了常用的 QoS 配置文件:
// 传感器数据配置(高频,允许丢失)
rmw_qos_profile_sensor_data = {
.history = KEEP_LAST,
.depth = 5,
.reliability = BEST_EFFORT,
.durability = VOLATILE,
.deadline = {0, 0},
.lifespan = {0, 0},
.liveliness = AUTOMATIC,
.liveliness_lease_duration = {0, 0}
};
// 服务配置(可靠传输)
rmw_qos_profile_services = {
.history = KEEP_LAST,
.depth = 10,
.reliability = RELIABLE,
.durability = VOLATILE
};
发布者和订阅者的 QoS 必须兼容才能通信:
兼容性矩阵:
| Publisher → | Subscriber ↓ | 结果 |
|---|---|---|
| RELIABLE | RELIABLE | ✓ 兼容 |
| RELIABLE | BEST_EFFORT | ✓ 兼容(降级) |
| BEST_EFFORT | RELIABLE | ✗ 不兼容 |
| BEST_EFFORT | BEST_EFFORT | ✓ 兼容 |
QoS 事件通知:
// 监听 QoS 不兼容事件
auto incompatible_qos_callback = [](QOSOfferedIncompatibleQoSInfo & event) {
RCLCPP_WARN(get_logger(),
"QoS incompatible: last_policy_kind=%d, total_count=%d",
event.last_policy_kind, event.total_count);
};
publisher->set_on_incompatible_qos_callback(incompatible_qos_callback);
ROS2 支持动态调整 QoS 以适应网络条件:
class AdaptiveQoSNode : public rclcpp::Node {
private:
size_t lost_messages_ = 0;
rclcpp::QoS current_qos_;
public:
void adjust_qos() {
if (lost_messages_ > THRESHOLD) {
// 降级为可靠传输
current_qos_.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
recreate_publisher();
}
}
void recreate_publisher() {
publisher_ = create_publisher<std_msgs::msg::String>(
"adaptive_topic", current_qos_);
}
};
Robonaut 2(R2)是 NASA 与通用汽车联合开发的人形机器人,设计用于国际空间站(ISS)和地面危险环境作业。2019 年,NASA 将 R2 控制系统从专有架构迁移到 ROS2,成为 ROS2 在航天领域的标志性应用。
技术挑战:
为何选择 ROS2:
DDS 实现选择:
┌─────────────────── ISS ──────────────────┐
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ R2 Robot │◄──────►│ Control │ │
│ │ Hardware │ CAN │ Computer │ │
│ └──────────┘ └────┬─────┘ │
│ │ ROS2 │
│ ┌────▼─────┐ │
│ │ Gateway │ │
│ │ Node │ │
│ └────┬─────┘ │
└───────────────────────────┼──────────────┘
│ Ku-band
┌─────▼─────┐
│ Ground │
│ Station │
└─────┬─────┘
│
┌─────▼─────┐
│ Mission │
│ Control │
└───────────┘
关键性能指标:
| 指标 | 需求 | 实现 |
|---|---|---|
| 控制循环频率 | 1000 Hz | 1000 Hz ± 0.1% |
| 末端执行器精度 | < 1mm | 0.8mm |
| 通信延迟(本地) | < 1ms | 0.45ms |
| 故障恢复时间 | < 100ms | 67ms |
| CPU 使用率 | < 50% | 38% |
优化技术:
# 内核参数调优
echo 0 > /proc/sys/kernel/sched_rt_runtime_us
echo 1000000 > /proc/sys/kernel/sched_rt_period_us
3. **内存池预分配**:
```cpp
// 预分配 10MB 内存池
rmw_init_options_t options = rmw_get_zero_initialized_init_options();
options.enclave = "/r2/control";
options.domain_id = 42;
options.allocator.allocate = custom_allocate;
options.allocator.deallocate = custom_deallocate;
问题 1:Discovery 风暴
**问题 2:共享内存权限**
- 现象:不同用户的进程无法通信
- 原因:共享内存文件权限问题
- 解决:配置统一的共享内存段
```cpp
setenv("CYCLONEDDS_URI",
"file:///opt/r2/cyclone_config.xml", 1);
问题 3:时钟同步漂移
class SpaceTimeSync : public rclcpp::Node {
void sync_callback() {
auto ground_time = request_ground_time();
auto offset = ground_time - this->now();
apply_time_correction(offset);
}
};
DDS Security 规范提供端到端的安全保护,包括认证、授权和加密。ROS2 通过 SROS2 集成这些功能。
安全架构层次:
┌─────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────┤
│ SROS2 Security Layer │
├─────────────────────────────────┤
│ DDS Security Plugins │
│ ┌──────┬──────┬──────────┐ │
│ │Auth │Access│Crypto │ │
│ │Plugin│Ctrl │Plugin │ │
│ └──────┴──────┴──────────┘ │
├─────────────────────────────────┤
│ RTPS Protocol │
└─────────────────────────────────┘
安全插件功能:
配置示例:
<!-- governance.xml -->
<dds>
<domain_access_rules>
<domain_rule>
<domains>
<id>0</id>
</domains>
<allow_unauthenticated_participants>false</allow_unauthenticated_participants>
<enable_join_access_control>true</enable_join_access_control>
<discovery_protection_kind>ENCRYPT</discovery_protection_kind>
<liveliness_protection_kind>ENCRYPT</liveliness_protection_kind>
<rtps_protection_kind>SIGN</rtps_protection_kind>
<topic_access_rules>
<topic_rule>
<topic_expression>/robot/cmd_vel</topic_expression>
<enable_discovery_protection>true</enable_discovery_protection>
<enable_liveliness_protection>true</enable_liveliness_protection>
<enable_read_access_control>true</enable_read_access_control>
<enable_write_access_control>true</enable_write_access_control>
<metadata_protection_kind>ENCRYPT</metadata_protection_kind>
<data_protection_kind>ENCRYPT</data_protection_kind>
</topic_rule>
</topic_access_rules>
</domain_rule>
</domain_access_rules>
</dds>
TSN 是 IEEE 802.1 标准集,提供确定性以太网通信。ROS2 与 TSN 结合可实现微秒级确定性。
TSN 核心机制:
ROS2 + TSN 配置:
// TSN 配置类
class TSNConfig {
public:
void configure_stream(const std::string& topic) {
// 配置 VLAN 优先级
set_vlan_priority(topic, 7); // 最高优先级
// 配置时间窗口
TimeWindow window;
window.start = microseconds(0);
window.duration = microseconds(100);
window.period = microseconds(1000); // 1ms 周期
schedule_transmission_window(topic, window);
}
void enable_frame_preemption() {
// 启用帧抢占
system("ethtool -K eth0 frame-preemption on");
system("tc qdisc add dev eth0 parent root handle 100 taprio \\
num_tc 8 \\
map 0 1 2 3 4 5 6 7 \\
queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 \\
base-time 0 \\
sched-entry S 0xff 100000 \\
clockid CLOCK_TAI");
}
};
1. CPU 亲和性与 NUMA 优化:
void optimize_numa_placement() {
// 绑定到 NUMA 节点 0
numa_set_preferred(0);
// 分配本地内存
void* buffer = numa_alloc_onnode(SIZE, 0);
// 设置内存策略
numa_set_membind(numa_parse_nodestring("0"));
}
2. 零拷贝 LoanedMessage API:
// 使用借用消息避免拷贝
auto loaned_msg = publisher_->borrow_loaned_message();
auto& msg = loaned_msg.get();
// 直接操作消息内存
msg.data.resize(1000000);
std::fill(msg.data.begin(), msg.data.end(), 0);
// 发布时无需拷贝
publisher_->publish(std::move(loaned_msg));
3. 批量处理优化:
class BatchProcessor : public rclcpp::Node {
private:
std::vector<sensor_msgs::msg::PointCloud2> batch_;
const size_t BATCH_SIZE = 10;
public:
void callback(const sensor_msgs::msg::PointCloud2::SharedPtr msg) {
batch_.push_back(*msg);
if (batch_.size() >= BATCH_SIZE) {
// SIMD 并行处理
#pragma omp parallel for simd
for (size_t i = 0; i < batch_.size(); ++i) {
process_pointcloud(batch_[i]);
}
batch_.clear();
}
}
};
1. 确定性 DDS (Deterministic DDS):
2. 量子安全加密:
3. 边缘-云协同架构:
必读论文:
本章深入探讨了 ROS2 的核心架构设计,重点理解了以下关键概念:
DDS 中间件架构:ROS2 采用 DDS 作为通信基础,实现了去中心化、自动发现和工业级可靠性。RTPS 协议提供了实时发布-订阅机制,支持多种 QoS 策略。
实时性设计:通过操作系统层(RT 内核)、中间件层(DDS QoS)和应用层(内存管理)的协同优化,ROS2 可以满足软实时到硬实时的不同需求。
与 ROS1 的本质区别:从中心化到去中心化架构的转变,引入生命周期管理、强类型参数系统等现代化特性。
QoS 策略系统:提供细粒度的通信质量控制,包括可靠性、持久性、截止时间等多个维度,支持不同应用场景的需求。
实时系统响应时间: \(R = T_{sensor} + T_{comm} + T_{proc} + T_{actuator} \leq D_{deadline}\)
时间抖动计算: \(Jitter = \max_{i}|t_i' - t_i|\)
DDS 发现时间: \(T_{discovery} = T_{SPDP} + T_{SEDP} + T_{handshake}\)
选择 ROS2 架构时应考虑:
练习 4.1:DDS Domain 概念理解
某机器人系统有 3 个节点:传感器节点、处理节点和控制节点。
如果传感器节点在 Domain 0,处理节点在 Domain 1,控制节点在 Domain 0,
请问哪些节点可以直接通信?为什么?
练习 4.2:QoS 兼容性判断
发布者配置:Reliability=RELIABLE, Durability=TRANSIENT_LOCAL
订阅者配置:Reliability=BEST_EFFORT, Durability=VOLATILE
这两个端点能否建立通信?如果不能,如何修改?
练习 4.3:实时性分析
某控制系统要求 10ms 的控制周期,测量得到:
- 传感器采样:1ms
- DDS 通信:2ms(平均),4ms(最坏情况)
- 数据处理:3ms
- 执行器响应:2ms
该系统能否满足硬实时要求?软实时要求?
练习 4.4:DDS 发现机制优化
某大规模机器人系统有 100 个节点,启动时出现"发现风暴"导致网络拥塞。
请设计一个分阶段启动策略,并给出 DDS 配置建议。
提示:考虑 SPDP/SEDP 的周期、多播配置、启动延迟
练习 4.5:零拷贝通信设计
设计一个点云处理管道,从激光雷达接收数据(10Hz, 每帧 2MB),
经过滤波、分割、特征提取三个处理步骤,最终发送给规划模块。
如何设计才能实现零拷贝?
提示:考虑共享内存、LoanedMessage、内存池
练习 4.6:跨域通信安全设计
设计一个工厂机器人系统,包含:
- 生产线机器人(Domain 10,高安全要求)
- 监控系统(Domain 20,中等安全)
- 访客演示系统(Domain 30,低安全)
如何设计 DDS Security 配置,实现分级安全访问?
提示:考虑 Domain Bridge、访问控制、加密级别
练习 4.7:实时性能分析与优化
分析以下 ROS2 节点代码的实时性问题,并提出优化方案:
class DataProcessor : public rclcpp::Node {
void callback(const sensor_msgs::msg::PointCloud2::SharedPtr msg) {
// 问题代码
auto result = std::make_shared<ProcessedData>();
result->points.reserve(msg->data.size() / 32);
for(size_t i = 0; i < msg->data.size(); i += 32) {
Point p = extract_point(msg->data, i);
if(p.z > threshold_) {
result->points.push_back(p);
}
}
std::sort(result->points.begin(), result->points.end());
publisher_->publish(result);
}
};
练习 4.8:分布式系统时钟同步
设计一个分布式机器人系统的时钟同步方案,要求:
- 5 个计算节点,通过千兆以太网连接
- 同步精度 < 100μs
- 支持节点动态加入/退出
- 容忍单点故障