Skip to main content

Topics

A Topic is a named channel for exchanging data of a specific type between publishers and subscribers.

Overview

Topics define:

  • Name: A string identifier (e.g., "SensorData", "RobotStatus")
  • Type: The data structure being exchanged
  • QoS: Quality of Service policies for the topic
use hdds::prelude::*;

let participant = DomainParticipant::new(0)?;

// Create a topic for SensorData type
let topic = participant.create_topic::<SensorData>("SensorTopic")?;

Topic Naming

Topic names are strings that identify data channels:

// Simple names
let sensor_topic = participant.create_topic::<SensorData>("sensors")?;

// Hierarchical naming (convention, not enforced)
let lidar_topic = participant.create_topic::<PointCloud>("/robot/sensors/lidar")?;
let cmd_topic = participant.create_topic::<Command>("/robot/control/commands")?;

// Namespaced (common in ROS2)
let ros_topic = participant.create_topic::<Image>("rt/camera/image_raw")?;

Naming Rules

  • Case-sensitive: SensorData != sensordata
  • No length limit (but keep reasonable)
  • Characters: alphanumeric, /, _, -
  • Avoid: spaces, special characters

Type Registration

Before creating a topic, the type must be known:

// Types generated from IDL are auto-registered
// when you use create_topic::<T>()

let topic = participant.create_topic::<SensorData>("SensorTopic")?;

// Or register manually
participant.register_type::<SensorData>("sensors::SensorData")?;

Type Names

The type name is used for matching:

SensorData.idl
module sensors {
@topic
struct SensorData {
uint32 sensor_id;
float value;
};
};

Type name: sensors::SensorData

Topic Matching

Writers and readers match when:

  1. Same topic name - Exact string match
  2. Compatible type - Type name and structure match
  3. Compatible QoS - QoS policies are compatible
Publisher (Domain 0)              Subscriber (Domain 0)
┌─────────────────────┐ ┌─────────────────────┐
│ Topic: "SensorData" │ MATCH │ Topic: "SensorData" │
│ Type: SensorData │ <─────> │ Type: SensorData │
│ QoS: Reliable │ │ QoS: Reliable │
└─────────────────────┘ └─────────────────────┘

Publisher (Domain 0) Subscriber (Domain 0)
┌─────────────────────┐ ┌─────────────────────┐
│ Topic: "SensorData" │ NO MATCH │ Topic: "OtherTopic" │
│ Type: SensorData │ ✗ │ Type: SensorData │
└─────────────────────┘ └─────────────────────┘

Creating Topics

Basic Creation

// Default QoS
let topic = participant.create_topic::<SensorData>("SensorTopic")?;

With QoS

let topic_qos = TopicQos::default()
.reliability(Reliability::Reliable {
max_blocking_time: Duration::from_secs(1),
})
.durability(Durability::TransientLocal);

let topic = participant.create_topic_with_qos::<SensorData>(
"SensorTopic",
topic_qos
)?;

Topic Description

let topic = participant.create_topic::<SensorData>("SensorTopic")?;

println!("Name: {}", topic.name()); // "SensorTopic"
println!("Type: {}", topic.type_name()); // "sensors::SensorData"

Keyed Topics

Topics with @key fields support multiple instances:

Robot.idl
@topic
struct RobotStatus {
@key uint32 robot_id; // Key field
float battery;
float position_x;
float position_y;
};

Each unique robot_id creates a separate instance:

// Multiple robots on same topic
writer.write(&RobotStatus { robot_id: 1, battery: 95.0, ... })?;
writer.write(&RobotStatus { robot_id: 2, battery: 87.0, ... })?;

// Each robot has independent history

See Key Instance Example for details.

Topic QoS

Topic QoS provides defaults for writers and readers:

let topic_qos = TopicQos::default()
// Data distribution
.reliability(Reliability::Reliable { max_blocking_time: Duration::from_secs(1) })
.durability(Durability::TransientLocal)
.history(History::KeepLast { depth: 100 })

// Timing
.deadline(Deadline::new(Duration::from_millis(100)))
.lifespan(Lifespan::new(Duration::from_secs(60)))

// Metadata
.topic_data(b"sensor_v2".to_vec());

Writer/reader QoS can override topic defaults:

let topic = participant.create_topic_with_qos::<Data>("Topic", topic_qos)?;

// Reader uses stricter deadline
let reader_qos = DataReaderQos::default()
.deadline(Deadline::new(Duration::from_millis(50))); // Override

let reader = subscriber.create_reader_with_qos(&topic, reader_qos)?;

Finding Topics

Lookup Existing Topic

// Find topic created elsewhere
if let Some(topic) = participant.lookup_topic::<SensorData>("SensorTopic")? {
let reader = subscriber.create_reader(&topic)?;
}

Discovered Topics

// See topics from other participants
for topic_info in participant.discovered_topics() {
println!("Topic: {}, Type: {}", topic_info.name, topic_info.type_name);
}

Content Filtering

Filter data at the topic level:

// SQL-like filter expression
let filtered = participant.create_content_filtered_topic(
"HighTempSensors", // Filtered topic name
&sensor_topic, // Base topic
"temperature > 50.0", // Filter expression
vec![] // Parameters
)?;

// Reader only receives samples where temperature > 50
let reader = subscriber.create_reader(&filtered)?;

Filter Expressions

// Comparison
"value > 100"
"sensor_id = 42"
"status <> 'ERROR'"

// Logical
"temperature > 30 AND humidity < 80"
"priority = 1 OR priority = 2"

// Parameters
"sensor_id = %0 AND zone = %1" // Parameters: ["42", "A"]

Multi-Topic

Create topics with the same type but different names:

// Different rooms, same sensor type
let kitchen = participant.create_topic::<Temperature>("kitchen/temp")?;
let bedroom = participant.create_topic::<Temperature>("bedroom/temp")?;
let bathroom = participant.create_topic::<Temperature>("bathroom/temp")?;

// Or use partitions instead (more efficient)

Topic Data

Attach metadata to topics:

let qos = TopicQos::default()
.topic_data(b"format=json;version=2".to_vec());

// Other participants can read this
for topic_info in participant.discovered_topics() {
if let Some(data) = &topic_info.topic_data {
println!("Topic data: {}", String::from_utf8_lossy(data));
}
}

Topic Lifecycle

// Create
let topic = participant.create_topic::<SensorData>("SensorTopic")?;

// Use (create writers/readers)
let writer = publisher.create_writer(&topic)?;
let reader = subscriber.create_reader(&topic)?;

// Topic lives while any writer/reader exists
drop(writer);
drop(reader);

// Explicit delete (after all endpoints removed)
participant.delete_topic(&topic)?;

// Or automatic cleanup when participant drops
drop(participant);

Best Practices

  1. Use meaningful names: robot/sensors/lidar not topic1
  2. Match types exactly: Same IDL definition on both sides
  3. Set topic QoS: Provides sensible defaults for endpoints
  4. Use keys for instances: When tracking multiple entities
  5. Consider partitions: For topic-level filtering

Common Patterns

Topic per Sensor Type

let temperature = participant.create_topic::<TempReading>("temperature")?;
let humidity = participant.create_topic::<HumidReading>("humidity")?;
let pressure = participant.create_topic::<PressReading>("pressure")?;

Topic with Keyed Instances

// Single topic, multiple sensors via @key
let sensors = participant.create_topic::<SensorData>("all_sensors")?;
// sensor_id is @key, creates separate instances

Hierarchical Topics

// Navigation subsystem
let gps = participant.create_topic::<GPS>("/nav/gps")?;
let imu = participant.create_topic::<IMU>("/nav/imu")?;
let odom = participant.create_topic::<Odometry>("/nav/odom")?;

Next Steps