Skip to main content

FourierDDS

1. Overview

FourierDDS is the DDS communication SDK used by Fourier robot software. It provides C++ and Python APIs on top of Fast DDS for creating participants, publishing topics, subscribing to topics, and implementing request/response services.

This page is written for application developers using an installed SDK. You do not need the FourierDDS source code to use these APIs.

ElementValue
C++ namespacefourier_dds
Python packagefourierdds_py
DDS backendFast DDS
Default install prefix/opt/fftai
C++ standardC++17

2. Communication Checklist

All participants that communicate with each other must use compatible settings.

SettingRequirement
domain_idMust match on both sides.
Topic or service nameMust match after namespace and ROS-compatible name mapping.
Message typeThe generated message and PubSubType must match.
NamespaceMust match, or be disabled on both sides.
ROS compatibilityMust match when communicating with ROS 2-compatible DDS names.
Discovery ServerIf enabled, all processes must use the same server address list.
QoSPublisher and subscriber QoS must be compatible.

For local single-machine tests, use an explicit domain and disable namespace and Discovery Server.

3. Verify the Installed 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*

If CMake cannot find fourier_dds, set:

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

If the executable cannot find shared libraries at runtime, set:

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

If this check fails, confirm that fourierdds-py, fourierdds_core, Fast DDS, fastcdr, and generated message packages were installed as one compatible release set.

4. C++ SDK

4.1 CMake Setup

Replace MotorCfgState with the message library delivered with your target message package.

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 Core Classes

ClassPurpose
fourier_dds::DdsNodeCreates and owns the DDS participant configuration.
fourier_dds::DdsPublisher<MsgPubSubType>Publishes typed messages.
fourier_dds::DdsSubscription<MsgPubSubType>Subscribes to typed messages and invokes callbacks.
fourier_dds::DdsClient<ReqPubSubType, RepPubSubType>Sends service requests and waits for replies.
fourier_dds::DdsServer<ReqPubSubType, RepPubSubType>Receives service requests and sends replies.

4.3 Create a 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
);

For robot-side applications using the installed system configuration, the short form is usually enough:

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

4.4 Publish a 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 Subscribe to a 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 Publishing

For plain and bounded message types, a publisher can borrow a message sample before publishing. If the type cannot be loaned, the API falls back to a local message object.

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));

Use pub->can_loan() to check whether the current message type is loan-capable.

5. Python SDK

5.1 Import and Create a 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 Message Type Rule

Use the generated PubSubType class when creating publishers and subscriptions. Use the generated message class when publishing or reading callback data.

Python
from fourier_msgs_pod.msg.ActuatorState import ActuatorState, ActuatorStatePubSubType

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

5.3 Publish and Subscribe

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()

Callbacks run in the DDS callback path. Keep callbacks short and move long-running work to an application queue or worker thread.

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 exposes common QoS presets:

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

pub_qos = PublisherQosProfile.reliable()
sub_qos = SubscriberQosProfile.reliable()
FieldDescription
reliabilitybest_effort or reliable.
durabilityvolatile or transient_local.
history_kindkeep_last or keep_all.
history_depthDDS history depth.
deadline_sec / deadline_nanosecDDS deadline. The default is infinite deadline.
max_samples_per_callbackMaximum samples drained by one subscription callback.

When C++ and Python communicate, make sure their QoS settings are compatible.

7. Namespace, ROS Compatibility, and Discovery Server

7.1 Namespace

FourierDDS can automatically prefix topic and service names with a robot namespace. The namespace is usually read from robot configuration or BMS.

ScenarioRecommendation
Local testSet use_namespace=false in C++ or use_namespace=False in Python.
Robot runtimeUse the default namespace behavior unless instructed otherwise.
Multi-robot deploymentMake sure both sides intentionally use the same namespace or different namespaces for isolation.

7.2 ROS Compatibility

ROS-compatible mode maps DDS names to ROS 2-compatible DDS names.

EntityROS-compatible DDS name
Topicrt/<topic>
Service requestrq/<service>Request
Service responserr/<service>Reply

Enable it explicitly when communicating with ROS 2-compatible DDS participants:

Bash
export FOURIERDDS_ROS_COMPATIBLE=1

7.3 Discovery Server

If the deployment uses Fast DDS Discovery Server, all processes must use the same address list.

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

To connect ROS 2 CLI tools or PlotJuggler to the same DDS network, source the installed environment 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. Troubleshooting

8.1 CMake Cannot Find fourier_dds

Set the install prefix before running CMake:

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

8.2 Runtime Shared Library Error

Set the runtime library path:

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

8.3 Python _core Import Error

Confirm that fourierdds-py, fourierdds_core, Fast DDS, fastcdr, and generated message packages are from the same compatible release set. If the dynamic linker cannot find shared libraries, set LD_LIBRARY_PATH as shown above.

8.4 No Topic Data Received

Check these items first:

CheckDescription
domain_idMust match.
Topic nameMust match after namespace and ROS-compatible mapping.
Message typePubSubType must match.
NamespaceConfirm whether a robot namespace was added.
ROS compatibilityMust match on both sides.
Discovery ServerMust use the same server configuration if enabled.
QoSReliability, durability, and history must be compatible.

8.5 Service Client Timeout

Confirm that the server is already running and that domain_id, service name, namespace, ROS compatibility, Discovery Server, and request/response types match. For Python, increase wait_for_server_ms if discovery is slow.

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

8.6 ROS 2 CLI Cannot See Topics

Use the installed wrapper to generate a ROS 2 SUPER_CLIENT profile. Setting only ROS_DISCOVERY_SERVER is usually not enough.

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

Status: Stable
Last updated: 2026-06-23