1. DDS发布订阅基础通信
任务目标
用 DDS 建立一个最小可运行的发布/订阅链路:发布者周期发布车载传感器数据(车速、转速),订阅者接收并打印。本案例使用 Eclipse Cyclone DDS 的 C API,单进程内同时启动发布者与订阅者,便于直接验证。
环境与依赖
- Linux(或 WSL),已安装 CMake ≥ 3.10 与 GCC。
- 安装 Cyclone DDS:
sudo apt install cyclonedds-dev(或从源码构建后source install/setup.sh)。 - 确认头文件与库存在:
pkg-config --cflags --libs cyclonedds能输出编译参数。
数据模型与通信结构
传感器数据用固定结构体 SensorData 表示,包含时间戳、车速、转速。发布者写入 VehicleSensor 主题,订阅者监听同一主题。DDS 通过主题名 + 类型名匹配,因此两端必须使用完全一致的类型定义与主题名。
核心代码
单文件 dds_demo.c:先创建参与者、主题、发布者与订阅者,订阅者用监听器回调接收数据,主线程每 500ms 发布一次,共 5 次后退出。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "dds/dds.h"
typedef struct {
int64_t timestamp;
float speed;
float rpm;
} SensorData;
static volatile int g_received = 0;
/* 订阅者回调:每收到一条数据打印一次 */
static void on_data(dds_entity_t reader, void *arg)
{
(void)arg;
void *samples[1] = {0};
dds_sample_info_t infos[1];
while (dds_take(reader, samples, infos, 1, 1) > 0) {
if (infos[0].valid_data) {
SensorData *s = (SensorData *)samples[0];
printf("[SUB] t=%lld speed=%.1f rpm=%.0f\n",
(long long)s->timestamp, s->speed, s->rpm);
g_received++;
}
dds_return_loan(reader, samples, 1);
}
}
int main(void)
{
dds_entity_t participant = dds_create_participant(DDS_DOMAIN_DEFAULT, NULL, NULL);
if (participant < 0) { fprintf(stderr, "create participant failed\n"); return 1; }
dds_entity_t topic = dds_create_topic(participant, &SensorData_desc, "VehicleSensor", NULL, NULL);
if (topic < 0) { fprintf(stderr, "create topic failed\n"); return 1; }
dds_entity_t writer = dds_create_writer(participant, topic, NULL, NULL);
dds_entity_t reader = dds_create_reader(participant, topic, NULL, NULL);
if (writer < 0 || reader < 0) { fprintf(stderr, "create writer/reader failed\n"); return 1; }
dds_listener_t *listener = dds_create_listener(NULL);
dds_lset_data_available(listener, on_data);
dds_set_listener(reader, listener);
for (int i = 0; i < 5; i++) {
SensorData s = { .timestamp = (int64_t)time(NULL),
.speed = 40.0f + i * 5.0f,
.rpm = 1500.0f + i * 200.0f };
dds_write(writer, &s);
printf("[PUB] sent speed=%.1f rpm=%.0f\n", s.speed, s.rpm);
usleep(500000);
}
sleep(1); /* 等待订阅者处理完最后一条 */
printf("total received = %d\n", g_received);
dds_delete_listener(listener);
dds_delete(participant);
return 0;
}
编译与运行
gcc dds_demo.c -o dds_demo $(pkg-config --cflags --libs cyclonedds)
./dds_demo
验证方法与通过条件
- 运行后终端应交替出现
[PUB] sent ...与[SUB] t=... speed=... rpm=...行。 - 发布 5 条后,最后一行
total received = 5,说明订阅者完整收到全部数据。 - 若只看到
[PUB]而无[SUB],检查主题名与类型是否一致、DDS 域是否相同(默认域 0)。
常见问题
- 链接报未定义符号:确认
pkg-config能找到 cyclonedds,或手动加-lddsc。 - 回调不触发:
dds_take需在回调内循环读取,且必须调用dds_return_loan归还样本。 - 多进程验证:把发布与订阅拆成两个进程运行,主题名与类型保持一致即可互通。