跳到主要内容

FourierDDS

1. 概述

FourierDDS 是 Fourier 机器人软件使用的 DDS 通信 SDK。它基于 Fast DDS 提供 C++ 和 Python API,用于创建 participant、发布 topic、订阅 topic,以及实现 request/response service。

本文面向已经安装 SDK 的应用开发者。使用这些 API 不需要 FourierDDS 源码。

元素
C++ namespacefourier_dds
Python packagefourierdds_py
DDS backendFast DDS
默认安装前缀/opt/fftai
C++ 标准C++17

2. 通信检查清单

所有需要互相通信的 participant 都必须使用兼容配置。

配置项要求
domain_id两端必须一致。
Topic 或 service 名称namespace 和 ROS 兼容命名映射后必须一致。
消息类型生成消息和 PubSubType 必须匹配。
Namespace两端必须一致,或两端都关闭。
ROS 兼容与 ROS 2 兼容 DDS 名称通信时必须一致。
Discovery Server启用时所有进程必须使用同一组 server 地址。
QoSPublisher 和 subscriber 的 QoS 必须兼容。

本机单机测试时,建议显式设置 domain,并关闭 namespace 和 Discovery Server。

3. 确认 SDK 已安装

3.1 C++ SDK

Bash
ls /opt/fftai/lib/cmake/fourier_dds/fourier_ddsConfig.cmake
ls /opt/fftai/fourier_dds_msgs/lib/libfourierdds_core.so*

如果 CMake 找不到 fourier_dds,设置:

Bash
export CMAKE_PREFIX_PATH=/opt/fftai:${CMAKE_PREFIX_PATH}

如果可执行程序运行时找不到共享库,设置:

Bash
export LD_LIBRARY_PATH=/opt/fftai/fourier_dds_msgs/lib:${LD_LIBRARY_PATH}

3.2 Python SDK

Bash
python - <<'PY'
import fourierdds_py
import fourierdds_py._core
import fastdds
from fourier_msgs_pod.msg.ActuatorState import ActuatorStatePubSubType

print("fourierdds_py:", fourierdds_py.__file__)
print("core binding: OK")
print("message package: OK")
PY

如果检查失败,请确认 fourierdds-pyfourierdds_core、Fast DDS、fastcdr 和生成消息包来自同一批兼容版本。

4. C++ SDK

4.1 CMake 接入

MotorCfgState 替换成目标消息包交付的消息库名称。

CMake
cmake_minimum_required(VERSION 3.13)
project(my_fourierdds_app CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(fourier_dds REQUIRED)
find_package(fastdds 3 REQUIRED)

add_executable(my_app main.cpp)

target_include_directories(my_app PRIVATE ${fourier_dds_INCLUDE_DIRS})
target_link_directories(my_app PRIVATE ${fourier_dds_LIBRARY_DIRS})
target_link_libraries(my_app PRIVATE
fastdds
${fourier_dds_LIBRARIES}
MotorCfgState
)

4.2 核心类

用途
fourier_dds::DdsNode创建并持有 DDS participant 配置。
fourier_dds::DdsPublisher<MsgPubSubType>发布强类型消息。
fourier_dds::DdsSubscription<MsgPubSubType>订阅强类型消息并触发 callback。
fourier_dds::DdsClient<ReqPubSubType, RepPubSubType>发送 service request 并等待 reply。
fourier_dds::DdsServer<ReqPubSubType, RepPubSubType>接收 service request 并发送 reply。

4.3 创建 Node

C++
auto node = std::make_shared<fourier_dds::DdsNode>(
42, // domain_id
0, // transport mode
false, // is_ros_compatible
false, // use_namespace
false // use_ds
);

对于使用已安装系统配置的机器人侧应用,通常可以使用简写:

C++
auto node = std::make_shared<fourier_dds::DdsNode>(0);

4.4 发布 Topic

C++
#include <chrono>
#include <memory>
#include <thread>

#include <MotorCfgState/MotorCfgStatePubSubTypes.hpp>
#include "fourier_dds/dds_node.hpp"
#include "fourier_dds/dds_publisher.hpp"

int main() {
auto node = std::make_shared<fourier_dds::DdsNode>(42, 0, false, false, false);

auto pub = std::make_shared<
fourier_dds::DdsPublisher<fourier_msgs::msg::MotorCfgGroupStatePubSubType>>(
node,
"hello"
);

fourier_msgs::msg::MotorCfgGroupState msg;
msg.group_name("demo");
msg.pos_kp({0.1, 0.2});

while (true) {
pub->publish(msg);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}

4.5 订阅 Topic

C++
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>

#include <MotorCfgState/MotorCfgStatePubSubTypes.hpp>
#include "fourier_dds/dds_node.hpp"
#include "fourier_dds/dds_subscription.hpp"

int main() {
auto node = std::make_shared<fourier_dds::DdsNode>(42, 0, false, false, false);

auto sub = std::make_shared<
fourier_dds::DdsSubscription<fourier_msgs::msg::MotorCfgGroupStatePubSubType>>(
node,
"hello",
[](const fourier_msgs::msg::MotorCfgGroupState& msg) {
std::cout << "group=" << msg.group_name() << std::endl;
}
);

while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}

4.6 Service Server

C++
#include <chrono>
#include <memory>
#include <thread>

#include <Calculator/Calculator.hpp>
#include <Calculator/CalculatorPubSubTypes.hpp>
#include "fourier_dds/dds_node.hpp"
#include "fourier_dds/dds_server.hpp"

int main() {
auto node = std::make_shared<fourier_dds::DdsNode>(42, 0, false, false, false);

auto server = std::make_shared<
fourier_dds::DdsServer<fourier_msgs::srv::Calculator_RequestPubSubType,
fourier_msgs::srv::Calculator_ResponsePubSubType>>(
node,
"calculator",
[](const fourier_msgs::srv::Calculator_Request& req,
fourier_msgs::srv::Calculator_Response& rep) {
rep.result() = req.x() + req.y();
}
);

server->run();

while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}

4.7 Service Client

C++
#include <iostream>
#include <memory>

#include <Calculator/Calculator.hpp>
#include <Calculator/CalculatorPubSubTypes.hpp>
#include "fourier_dds/dds_client.hpp"
#include "fourier_dds/dds_node.hpp"

int main() {
auto node = std::make_shared<fourier_dds::DdsNode>(42, 0, false, false, false);

auto client = std::make_shared<
fourier_dds::DdsClient<fourier_msgs::srv::Calculator_RequestPubSubType,
fourier_msgs::srv::Calculator_ResponsePubSubType>>(
node,
"calculator"
);

fourier_msgs::srv::Calculator_Request req;
fourier_msgs::srv::Calculator_Response rep;
req.x() = 1;
req.y() = 2;

bool ok = client->send_request(req, rep, 2000);
if (ok) {
std::cout << "result=" << rep.result() << std::endl;
} else {
std::cout << "timeout" << std::endl;
}
}

4.8 Loaned Message 发布

对于 plain 且 bounded 的消息类型,publisher 可以先 borrow 一个 message sample 再发布。如果类型不支持 loan,接口会自动回退为本地消息对象。

C++
auto msg = pub->borrow_message();
msg->angular_velocity().x(1.0);
msg->linear_acceleration().z(9.81);

bool ok = pub->publish(std::move(msg));

使用 pub->can_loan() 可以检查当前消息类型是否支持 loan。

5. Python SDK

5.1 导入并创建 Node

Python
from fourierdds_py import DDSInterface
from fourierdds_py import PublisherQosProfile, SubscriberQosProfile

dds = DDSInterface(
domain_id=42,
use_namespace=False,
use_ds=False,
is_ros_compatible=False,
)

5.2 消息类型规则

创建 publisher 和 subscription 时使用生成的 PubSubType 类。发布或读取 callback 数据时使用对应的生成消息类。

Python
from fourier_msgs_pod.msg.ActuatorState import ActuatorState, ActuatorStatePubSubType

msg = ActuatorState()
msg.state_size(3)
value = msg.state_size()

5.3 发布与订阅

Python
import time

from fourierdds_py import DDSInterface
from fourierdds_py import PublisherQosProfile, SubscriberQosProfile
from fourier_msgs_pod.msg.ActuatorState import ActuatorState, ActuatorStatePubSubType

dds = DDSInterface(domain_id=42, use_namespace=False, use_ds=False)

def on_msg(msg):
print("received state_size:", msg.state_size())

sub = dds.create_subscription(
ActuatorStatePubSubType,
"actuator_state",
on_msg,
qos_profile=SubscriberQosProfile.best_effort(),
)

pub = dds.create_publisher(
ActuatorStatePubSubType,
"actuator_state",
qos_profile=PublisherQosProfile.best_effort(),
)

time.sleep(1.0)

msg = ActuatorState()
msg.state_size(1)
pub.publish(msg)

time.sleep(1.0)
dds.close()

Callback 运行在 DDS 回调路径中。请保持 callback 简短,将耗时工作放到应用队列或工作线程中。

5.4 Service Server

Python
import time

from fourierdds_py import DDSInterface
from std_srvs.srv import Trigger

dds = DDSInterface(domain_id=42, use_namespace=False, use_ds=False)

def handle_request(request, response):
response.success(True)
response.message("ok")

server = dds.create_service(
Trigger.Trigger_RequestPubSubType,
Trigger.Trigger_ResponsePubSubType,
"demo_service",
handle_request,
)

while True:
time.sleep(1)

5.5 Service Client

Python
from fourierdds_py import DDSInterface
from std_srvs.srv import Trigger

dds = DDSInterface(domain_id=42, use_namespace=False, use_ds=False)

client = dds.create_client(
Trigger.Trigger_RequestPubSubType,
Trigger.Trigger_ResponsePubSubType,
"demo_service",
)

request = Trigger.Trigger_Request()
response = client.send_request(
request,
wait_ms=2000,
wait_for_server_ms=1000,
)

if response is None:
print("timeout")
else:
print("success:", response.success())
print("message:", response.message())

dds.close()

6. QoS Profiles

Python 提供常用 QoS 预设:

Python
pub_qos = PublisherQosProfile.best_effort()
sub_qos = SubscriberQosProfile.best_effort()

pub_qos = PublisherQosProfile.reliable()
sub_qos = SubscriberQosProfile.reliable()
字段说明
reliabilitybest_effortreliable
durabilityvolatiletransient_local
history_kindkeep_lastkeep_all
history_depthDDS history depth。
deadline_sec / deadline_nanosecDDS deadline。默认是无限 deadline。
max_samples_per_callback单次 subscription callback 最多处理的样本数。

C++ 和 Python 互通时,也要确保两端 QoS 兼容。

7. Namespace、ROS 兼容与 Discovery Server

7.1 Namespace

FourierDDS 可以自动给 topic 和 service 名称添加机器人 namespace。namespace 通常来自机器人配置或 BMS。

场景建议
本机测试C++ 设置 use_namespace=false,Python 设置 use_namespace=False
机器人运行时除非另有说明,使用默认 namespace 行为。
多机器人部署确认双方有意使用相同 namespace,或使用不同 namespace 进行隔离。

7.2 ROS 兼容

ROS 兼容模式会将 DDS 名称映射成 ROS 2 兼容 DDS 名称。

实体ROS 兼容 DDS 名称
Topicrt/<topic>
Service requestrq/<service>Request
Service responserr/<service>Reply

与 ROS 2 兼容 DDS participant 通信时,显式启用:

Bash
export FOURIERDDS_ROS_COMPATIBLE=1

7.3 Discovery Server

如果部署环境使用 Fast DDS Discovery Server,所有进程必须使用同一组地址。

Bash
export FOURIERDDS_USE_DISCOVERY_SERVER=1
export FOURIERDDS_DISCOVERY_SERVER="192.168.1.10:11811;192.168.1.11:11812"

如需让 ROS 2 CLI 工具或 PlotJuggler 接入同一个 DDS 网络,source 已安装的环境 wrapper:

Bash
source /opt/fftai/fourierdds/shells/discovery_env.sh --ros2
ros2 topic list
Bash
source /opt/fftai/fourierdds/shells/discovery_env.sh --plotjuggler
plotjuggler

8. 故障排查

8.1 CMake 找不到 fourier_dds

运行 CMake 前设置安装前缀:

Bash
export CMAKE_PREFIX_PATH=/opt/fftai:${CMAKE_PREFIX_PATH}

8.2 运行时报共享库错误

设置运行时库路径:

Bash
export LD_LIBRARY_PATH=/opt/fftai/fourier_dds_msgs/lib:${LD_LIBRARY_PATH}

8.3 Python _core 导入错误

确认 fourierdds-pyfourierdds_core、Fast DDS、fastcdr 和生成消息包来自同一批兼容版本。如果动态链接器找不到共享库,按上方方式设置 LD_LIBRARY_PATH

8.4 收不到 Topic 数据

优先检查这些项:

检查项说明
domain_id必须一致。
Topic 名称namespace 和 ROS 兼容映射后必须一致。
消息类型PubSubType 必须匹配。
Namespace确认是否自动添加了机器人 namespace。
ROS 兼容两端必须一致。
Discovery Server启用时必须使用同一组 server 配置。
QoSReliability、durability、history 必须兼容。

8.5 Service Client 超时

确认 server 已启动,并且 domain_id、service 名称、namespace、ROS 兼容、Discovery Server、request/response 类型都一致。Python 中如果 discovery 较慢,可以调大 wait_for_server_ms

Python
response = client.send_request(request, wait_ms=3000, wait_for_server_ms=3000)

8.6 ROS 2 CLI 看不到 Topic

使用已安装 wrapper 生成 ROS 2 SUPER_CLIENT profile。通常只设置 ROS_DISCOVERY_SERVER 不够。

Bash
source /opt/fftai/fourierdds/shells/discovery_env.sh --ros2
ros2 topic list

状态:Stable
最后更新:2026-06-23