4. DDS多主题通信
任务目标
在同一个 DomainParticipant 下创建三个 Topic(车速 VehicleSpeed、胎压 TirePressure、温度 Temperature),由一个发布者(Publisher)通过三个 DataWriter 分别发布数据,并用三个 DataReader 订阅验证。
环境与依赖
- RTI Connext DDS 6.x(或兼容的 DDS 实现),已配置
NDDSHOME与LD_LIBRARY_PATH。 - IDL 编译器
rtiddsgen用于生成类型支持代码。 - C++11 编译器(g++ 或 clang++)。
实现思路
三个主题共用同一 Participant 和 Publisher,各自持有独立 DataWriter;订阅端在同一 Participant 下创建三个 DataReader,通过监听器回调打印收到的数据。IDL 中定义三个结构体,rtiddsgen 会为每个类型生成独立的注册与序列化代码。
IDL 定义
// sensor.idl
struct VehicleSpeed {
long id;
double speed_kmh;
};
struct TirePressure {
long id;
double pressure_kpa;
};
struct Temperature {
long id;
double celsius;
};
生成类型支持代码
rtiddsgen -language C++11 -example x64Linux4gcc7.3.0 sensor.idl
生成 sensor.hpp、sensorPlugin.hpp 等文件,包含三个类型的 TypeSupport。
核心代码:单发布者发布多主题
#include <dds/dds.hpp>
#include "sensor.hpp"
#include <thread>
#include <chrono>
using namespace dds::domain;
using namespace dds::topic;
using namespace dds::pub;
using namespace dds::sub;
using namespace dds::core::policy;
int main() {
DomainParticipant participant(0);
// 三个主题
Topic<VehicleSpeed> speedTopic(participant, "VehicleSpeed");
Topic<TirePressure> tireTopic(participant, "TirePressure");
Topic<Temperature> tempTopic(participant, "Temperature");
// 单发布者
Publisher publisher(participant);
DataWriter<VehicleSpeed> speedWriter(publisher, speedTopic);
DataWriter<TirePressure> tireWriter(publisher, tireTopic);
DataWriter<Temperature> tempWriter(publisher, tempTopic);
// 订阅端:三个 DataReader + 监听器
Subscriber subscriber(participant);
DataReader<VehicleSpeed> speedReader(subscriber, speedTopic);
DataReader<TirePressure> tireReader(subscriber, tireTopic);
DataReader<Temperature> tempReader(subscriber, tempTopic);
// 等待匹配
std::this_thread::sleep_for(std::chrono::seconds(2));
// 发布者循环发布三个主题
for (int i = 0; i < 5; ++i) {
VehicleSpeed s; s.id = i; s.speed_kmh = 60.0 + i * 5;
TirePressure t; t.id = i; t.pressure_kpa = 220.0 + i;
Temperature c; c.id = i; c.celsius = 25.0 + i * 0.5;
speedWriter.write(s);
tireWriter.write(t);
tempWriter.write(c);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
// 读取验证
auto readAll = [](auto& reader, const char* name) {
auto samples = reader.read();
for (auto& s : samples)
std::cout << name << " id=" << s.id() << "\n";
};
readAll(speedReader, "Speed");
readAll(tireReader, "Tire");
readAll(tempReader, "Temp");
return 0;
}
编译与运行
g++ -std=c++11 main.cpp sensor.cxx sensorPlugin.cxx \
-I$NDDSHOME/include -I$NDDSHOME/include/ndds \
-L$NDDSHOME/lib/x64Linux4gcc7.3.0 \
-lnddscpp2 -lnddsc -lnddscore -lpthread -o multi_topic
export LD_LIBRARY_PATH=$NDDSHOME/lib/x64Linux4gcc7.3.0:$LD_LIBRARY_PATH
./multi_topic
验证方法与通过条件
- 程序正常退出,无 DDS 异常抛出。
- 标准输出中依次出现
Speed id=0..4、Tire id=0..4、Temp id=0..4,说明三个主题的数据均被对应 DataReader 收到。 - 若某主题无输出,检查 Topic 名称是否与 IDL 类型注册一致,以及
sleep_for是否足够等待端点匹配。
常见问题
- 匹配超时:同一进程内发布订阅通常秒级匹配,跨主机需确认 Domain ID 与网络配置一致。
- 类型未注册:
rtiddsgen生成的register_type需在创建 Topic 前调用,示例中由 Topic 构造函数自动完成。 - read() 返回空:确认发布端已
write且订阅端在发布前已创建,否则历史数据可能未被缓存。