note01
ROS2和ROS最大的区别中间件
中间件:
- 介于某两个或者多个节点中间的组件;
- 提供多个节点中间通信;
ROS1:中间件是ROS组织自己基于TCP机制建立的,随着现在传感器的升级,数据量越来越大,原先的数据拷贝机制就不再适了
ROS2:
- 将进程间通信intra-process自己来做
- 其他部分的通信就使用来DDS实现
- ROS2将DDS服务接口进行了一层抽象,保证了上层应用层调用接口的统一性
- ROS2去掉mater节点
DDS
Data Distribution Service:一种用于分布式系统的通信中间件标准
key points:
- 发布-订阅模式:
- 数据生产者(Publisher)发布数据到主题(Topic)
- 数据消费者(Subscriber)订阅感兴趣的主题
- 发布者和订阅者之间是解耦的,不需要知道彼此的存在
- 数据为中心:
- DDS以数据为中心,而不是以节点为中心
- 数据通过主题进行标识,主题是数据的逻辑分类
- 实时性:
- DDS设计用于实时系统,支持低延迟和高吞吐量的数据传输
Domain
定义:一个逻辑隔离的通信空间,只有在同一个域中的节点才能互相通信;每个域有一个唯一的 Domain ID,用于标识不同的域
作用:
- 隔离通信:不同域中的节点无法互相通信,即使它们在同一个网络中
- 灵活性:可以通过 Domain ID 将系统划分为多个独立的通信空间
Fast DDS中的Domain 通过 DomainParticipant
来管理的
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
int main() {
// 创建 DomainParticipant
eprosima::fastdds::dds::DomainParticipant* participant =
eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->create_participant(0);
// 检查是否创建成功
if (participant == nullptr) {
std::cerr << "Failed to create DomainParticipant!" << std::endl;
return -1;
}
std::cout << "DomainParticipant created successfully!" << std::endl;
// 清理资源
eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->delete_participant(participant);
return 0;
}
DomainParticipantFactory
:- 用于创建和管理
DomainParticipant
- 通过
get_instance()
获取单例对象
- 用于创建和管理
create_participant(domain_id)
:- 创建一个
DomainParticipant
,参数domain_id
指定域的 ID - 返回一个指向
DomainParticipant
的指针
- 创建一个
delete_participant(participant)
:- 删除
DomainParticipant
,释放资源
- 删除
Topic
Topic是数据的逻辑分离,每个 Topic 有一个名称和一个数据类型;
发布者(Publisher)将数据发布到 Topic,订阅者(Subscriber)从 Topic 中订阅数据
作用:
- 数据分类:通过 Topic 将数据分类,例如“温度数据”或“位置数据”
- 解耦:发布者和订阅者不需要知道彼此的存在,只需要知道 Topic 的名称和数据类型
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/topic/Topic.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
// 自定义数据类型
class HelloWorld {
public:
std::string message;
};
int main() {
// 创建 DomainParticipant
eprosima::fastdds::dds::DomainParticipant* participant =
eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->create_participant(0);
// 注册数据类型
eprosima::fastdds::dds::TypeSupport type(new HelloWorldPubSubType());
type.register_type(participant);
// 创建 Topic
eprosima::fastdds::dds::Topic* topic = participant->create_topic(
"HelloWorldTopic", // Topic 名称
type.get_type_name(), // 数据类型名称
eprosima::fastdds::dds::TOPIC_QOS_DEFAULT // QoS 配置
);
if (topic == nullptr) {
std::cerr << "Failed to create Topic!" << std::endl;
return -1;
}
std::cout << "Topic created successfully!" << std::endl;
// 清理资源
participant->delete_topic(topic);
eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->delete_participant(participant);
return 0;
}
- TypeSupport:
- 用于注册和管理数据类型
HelloWorldPubSubType
是自动生成的数据类型支持
create_topic(name, type_name, qos)
:- 创建一个 Topic,参数包括 Topic 名称、数据类型名称和 QoS 配置
- 返回一个指向
Topic
的指针
delete_topic(topic)
:- 删除 Topic,释放资源
Publisher 和 Subscriber
- Publisher:负责将数据发布到 Topic
- Subscriber:负责从 Topic 订阅数据
Topic实现:
- 观察者模式(Observer Pattern):
- Topic 是数据的逻辑分类,发布者(Publisher)和订阅者(Subscriber)通过 Topic 进行解耦
- 发布者将数据发布到 Topic,Topic 负责将数据分发给所有订阅了该 Topic 的订阅者
- 中介者模式(Mediator Pattern):
- Topic 充当发布者和订阅者之间的中介者,负责管理数据的传递
- 发布者和订阅者不需要直接交互,而是通过 Topic 进行通信
Publisher实现:
- Publisher 通过
DomainParticipant
的工厂方法(如create_publisher
)创建 - 代理模式(Proxy Pattern):
- Publisher 是数据发布的代理,负责将数据写入 DDS 中间件
- 实际的网络通信和数据传输由 DDS 中间件处理,Publisher 只是一个接口
- Publisher 内部维护了一个或多个
DataWriter
,用于将数据写入 Topic
DataWriter
DataWriter是publisher一部分,负责将数据写入topic
- 数据写入:将应用程序生成的数据发布到 DDS 中间件
- QoS 管理:根据配置的 QoS 策略(如可靠性、持久性等)处理数据
- 生命周期管理:管理数据的生命周期,例如数据的创建、更新和删除
ROS2的DDS
DDS抽象:
- 节点:
- ROS 2 引入了 Node 的概念,封装了 DDS 中的
DomainParticipant
- 只需要创建和管理 Node,而不需要直接操作
DomainParticipant
- ROS 2 引入了 Node 的概念,封装了 DDS 中的
- Publisher和Subscriber
- ROS 2 的
Publisher
和Subscriber
是对 DDS 中DataWriter
和DataReader
的封装 - 只需要指定 Topic 名称和数据类型,ROS 2 会自动处理底层的 DDS 对象
- ROS 2 的
- 消息Message
- ROS 2 使用 消息 作为数据传输的基本单位,消息类型通过
.msg
文件定义
- ROS 2 使用 消息 作为数据传输的基本单位,消息类型通过
- 统一的API
- ROS 2 通过 RMW(ROS Middleware Interface) 抽象了底层 DDS 实现的差异
- 每种 DDS 实现(如 Fast DDS、Cyclone DDS)都有一个对应的 RMW 实现
- 通过环境变量选择使用的 DDS 实现