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:
- Same topic name - Exact string match
- Compatible type - Type name and structure match
- 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
- Use meaningful names:
robot/sensors/lidarnottopic1 - Match types exactly: Same IDL definition on both sides
- Set topic QoS: Provides sensible defaults for endpoints
- Use keys for instances: When tracking multiple entities
- 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
- Publishers and Subscribers - Data distribution
- Key Instance Example - Working with instances
- QoS Overview - Quality of Service