基于ubuntu系统安装使用Fast DDS
什么是Fast DDS
Fast DDS是一个高性能的”分布式通信中间库”,用来在不同进程、不同设备之间传输数据的。Fast DDS是用C++写的一个DDS实现,在同一台机器的多个进程、或者多态机器之间进行通信。可以类似于ROS2的发布和订阅主题,可以自动发现对端(不用手写IP列表),支持可靠/不可靠、实时性、历史缓存、持久化等各种Qos。
FastDDS与Socket、gRPC有什么区别?
- socket:自己管连接、重连、协议、序列化、发现等。
- gRPC:偏请求、响应,RPC的风格。
- FastDDS:原生支持多对多发布订阅,内置自动发现(谁上来就能发现谁),有非常丰富的Qos做实时系统的调优。
安装FastDDS
基于ubuntu 系统源码式编译安装FastDDS环境。
~/FastDDS_ws/src$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 24.04.3 LTS
Release: 24.04
Codename: noble
为了保持系统的干净,我们统一将fastdds相关的库和依赖安装到指定的目录。
export FAST_DDS_INSTALL_DIR=$HOME/fastdds_install
# 创建安装目录和源码构建目录
mkdir -p $FAST_DDS_INSTALL_DIR
安装基础工具
sudo apt update
sudo apt install -y \
build-essential \
cmake \
git \
libssl-dev \
libasio-dev \
libtinyxml2-dev \
wget \
zip
FastDDS依赖C++编译环境、Cmake以及网络和XML解析库,因此需要安装以上的工具。
安装核心依赖
FastDDS主要依赖Foonathan memory和Fast CDR,前面是因为FastDDS使用foonathan_memory进行高效的内存管理(这对于实时性至关重要)。后者是FastDDS会把结构体编程二进制流,用于处理数据的序列化。
(1)安装Foonathan Memory
# 创建一个工作目录
mkdir -p ~/FastDDS_ws/src
cd ~/FastDDS_ws/src
# 克隆仓库
git clone https://github.com/eProsima/foonathan_memory_vendor.git
# 编译并安装
cd foonathan_memory_vendor
mkdir build && cd build
# 关键参数:-DCMAKE_INSTALL_PREFIX 指定安装位置
cmake .. -DCMAKE_INSTALL_PREFIX=$FAST_DDS_INSTALL_DIR -DBUILD_SHARED_LIBS=ON
cmake --build . -j$(nproc)
cmake --build . --target install
(2)安装Fast CDR(序列化)
cd ~/FastDDS_ws/src
git clone https://github.com/eProsima/Fast-CDR.git
cd Fast-CDR
mkdir build && cd build
# 关键参数:除了安装路径,还需要通过 -DCMAKE_PREFIX_PATH 告诉它去哪找 foonathan_memory (如果有依赖)
cmake .. -DCMAKE_INSTALL_PREFIX=$FAST_DDS_INSTALL_DIR \
-DCMAKE_PREFIX_PATH=$FAST_DDS_INSTALL_DIR
cmake --build . -j$(nproc)
cmake --build . --target install
编译安装Fast DDS核心库
FastDDS需要依赖上面两个库。
cd ~/FastDDS_ws/src
git clone https://github.com/eProsima/Fast-DDS.git
cd Fast-DDS
mkdir build && cd build
# 关键参数解释:
# -DCMAKE_INSTALL_PREFIX: 安装到我们自定义目录
# -DCMAKE_PREFIX_PATH: 告诉 CMake 去自定义目录里找 FastCDR 和 Foonathan
cmake .. -DCMAKE_INSTALL_PREFIX=$FAST_DDS_INSTALL_DIR \
-DCMAKE_PREFIX_PATH=$FAST_DDS_INSTALL_DIR \
-DSECURITY=ON \
-DCOMPILE_EXAMPLES=ON
cmake --build . -j$(nproc)
cmake --build . --target install
配置代码生成器Fast-DDS-Gen
这个工具是永远将
# 1. 回到源码目录
cd ~/FastDDS_ws/src
# 2. 克隆仓库 (获取最新版,通常是 v4.x)
git clone --recursive https://github.com/eProsima/Fast-DDS-Gen.git fastddsgen
# 3. 进入目录
cd Fast-DDS-Gen
# 4. 编译 (会自动下载 Gradle 并构建,需要保持网络畅通)
./gradlew assemble
环境加载
因为我们没有安装到系统默认路径,需要设置一下环境变量。
vim $FAST_DDS_INSTALL_DIR/setup.bash
#!/bin/bash
# 获取脚本所在的目录 (即 ~/fastdds_install)
INSTALL_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# 1. 告诉系统去哪里找 .so 动态库 (运行时必须)
export LD_LIBRARY_PATH=$INSTALL_ROOT/lib:$LD_LIBRARY_PATH
# 2. 告诉 CMake 去哪里找 find_package(FastDDS) (编译时必须)
export CMAKE_PREFIX_PATH=$INSTALL_ROOT:$CMAKE_PREFIX_PATH
# 3. 将 bin 加入 PATH,这样你可以直接输 fastddsgen (工具使用)
export PATH=$INSTALL_ROOT/bin:$PATH
echo "Fast DDS environment sourced from: $INSTALL_ROOT"
验证与使用
source ~/fastdds_install/setup.bash
mkdir -p ~/FastDDS_ws/src/SimpleChat
cd ~/FastDDS_ws/src/SimpleChat
(1)定义数据结构
// Chat.idl
struct ChatMessage
{
unsigned long user_id;
string message;
};
(2)生成胶水代码
fastddsgen Chat.idl
(3)编写发布者
创建 ChatPublisher.cpp。这个程序负责发送消息。
// ChatPublisher.cpp
#include "ChatPubSubTypes.hpp"
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <thread>
#include <iostream>
using namespace eprosima::fastdds::dds;
int main() {
// 1. 创建 Participant (参与者)
DomainParticipantQos participant_qos = PARTICIPANT_QOS_DEFAULT;
DomainParticipant* participant = DomainParticipantFactory::get_instance()->create_participant(0, participant_qos);
if (!participant) return 1;
// 2. 注册数据类型
TypeSupport type(new ChatMessagePubSubType());
type.register_type(participant);
// 3. 创建 Topic
Topic* topic = participant->create_topic("ChatTopic", type.get_type_name(), TOPIC_QOS_DEFAULT);
// 4. 创建 Publisher
Publisher* publisher = participant->create_publisher(PUBLISHER_QOS_DEFAULT);
// 5. 创建 DataWriter
DataWriter* writer = publisher->create_datawriter(topic, DATAWRITER_QOS_DEFAULT);
// 6. 循环发送数据
ChatMessage data;
data.user_id(101); // 假设我是用户 101
int count = 0;
while (true) {
data.message("Hello FastDDS " + std::to_string(count++));
writer->write(&data);
std::cout << "[Sent] " << data.message() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// 清理资源 (实际代码中通常在析构或信号处理中做)
// participant->delete_contained_entities();
// DomainParticipantFactory::get_instance()->delete_participant(participant);
return 0;
}
(4)编写订阅者
创建 ChatSubscriber.cpp。这个程序负责监听并打印消息。
// ChatSubscriber.cpp
#include "ChatPubSubTypes.hpp"
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/DataReaderListener.hpp>
#include <fastdds/dds/subscriber/SampleInfo.hpp>
#include <fastdds/dds/core/ReturnCode.hpp>
#include <thread>
#include <chrono>
#include <iostream>
using namespace eprosima::fastdds::dds;
// 监听器类:回调函数在这里触发
class MyListener : public DataReaderListener {
public:
void on_data_available(DataReader* reader) override {
ChatMessage data;
SampleInfo info;
// 取出数据
while (reader->take_next_sample(&data, &info) == eprosima::fastdds::dds::RETCODE_OK) {
if (info.valid_data) {
std::cout << "[Received] User " << data.user_id()
<< ": " << data.message() << std::endl;
}
}
}
void on_subscription_matched(DataReader* reader, const SubscriptionMatchedStatus& info) override {
if (info.current_count_change == 1) {
std::cout << ">> Match Found: Publisher connected!" << std::endl;
} else if (info.current_count_change == -1) {
std::cout << ">> Match Lost: Publisher disconnected." << std::endl;
}
}
};
int main() {
// 1. 创建 Participant
DomainParticipant* participant = DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT);
// 2. 注册类型
TypeSupport type(new ChatMessagePubSubType());
type.register_type(participant);
// 3. 创建 Topic
Topic* topic = participant->create_topic("ChatTopic", type.get_type_name(), TOPIC_QOS_DEFAULT);
// 4. 创建 Subscriber
Subscriber* subscriber = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT);
// 5. 创建 DataReader 并绑定 Listener
MyListener listener;
// 注意:这里需要传入 Listener 指针,并掩码设置我们要监听所有状态
DataReader* reader = subscriber->create_datareader(topic, DATAREADER_QOS_DEFAULT, &listener);
std::cout << "Waiting for messages... (Press Ctrl+C to stop)" << std::endl;
// 阻塞主线程,否则 main 结束程序就退出了
while(true) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return 0;
}
(5)编写CmakeList
cmake_minimum_required(VERSION 3.10)
project(SimpleChat)
set(CMAKE_CXX_STANDARD 11)
find_package(FastDDS REQUIRED)
# --- 关键修改:匹配你 tree 里的文件 ---
# Chat.hpp 是头文件,不需要加入 source list
# 加入 ChatPubSubTypes.cxx 和 ChatTypeObjectSupport.cxx
set(GENERATED_SOURCES
ChatPubSubTypes.cxx
ChatTypeObjectSupport.cxx
)
add_executable(ChatPublisher ChatPublisher.cpp ${GENERATED_SOURCES})
target_link_libraries(ChatPublisher fastdds)
add_executable(ChatSubscriber ChatSubscriber.cpp ${GENERATED_SOURCES})
target_link_libraries(ChatSubscriber fastdds)
(6)编译运行
cd build
cmake ..
make -j4
./ChatSubscriber &
./ChatPublisher