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