Environment
- OS Version: Ubuntu 24.04.4 LTS
- Source or binary build?
- Source build
- Branch: main
- gz-transport version: latest commit
- ROS 2 Version: Rolling
- Gazebo Sim Version: v11.0.0~pre1
Description
-
Expected behavior:
gz::transport::Node::TopicList() should return all available Gazebo Transport topics after the simulation has started, including topics that only have subscribers.
-
Actual behavior:
gz::transport::Node::TopicList() always returns an incomplete topic list.
Steps to reproduce
Setup
A minimal reproduction package is provided here: https://github.com/azeey/gsoc2026_multirobot/tree/main/bridge/topiclist_test
The package contains a simple ROS 2 node (topiclist_test_node) that:
- Creates a Gazebo Transport node.
- Periodically calls
gz::transport::Node::TopicList().
- Compares the returned topic list with a predefined list of expected Gazebo topics.
- Reports missing topics in the console output.
The node supports the following launch parameters:
use_composition: whether to load the node as a composable node.
freq: the frequency (Hz) at which TopicList() is called.
Steps
- Build & Source
colcon build --symlink-install
source install/setup.bash
- Launch
ros2 launch topiclist_test topiclist_test.launch.py use_composition:=True freq:=1.0
- Observe the output
The node periodically prints the number of expected topics, the number of topics returned by TopicList(), and any missing topics.
Output
The test was performed with different TopicList() query frequencies (0.1 Hz, 1 Hz, and 2 Hz) and with both composable and non-composable node modes.
In all cases, the same incomplete topic list was consistently returned. The node was running for more than 2 minutes, but the missing topics did not appear in the result.
[INFO] [topiclist_test_node]: Expected Gazebo topics: 25, discovered by TopicList(): 21
[INFO] [topiclist_test_node]: Missing topics:
/model/vehicle/cmd_vel
/model/vehicle/enable
/world/test_world/light_config
/world/test_world/material_color
Additional information
1. Possible cause: asynchronous discovery
The issue appears to be related to the asynchronous nature of Gazebo Transport discovery.
From the current implementation, TopicList() requests subscriber information and then immediately reads the cached discovery information. Since discovery responses are processed asynchronously, the cache may not contain all remote subscriber information when the topic list is generated.
2. Comparison with gz topic --list
Interestingly, the command line tool gz topic --list does not show this issue. Under the same simulation setup, it returns the complete topic list, including the topics missing from TopicList().
This suggests that the underlying discovery mechanism can eventually obtain the complete topic information, but TopicList() may return before all discovery responses have been received.
3. Experiment: waiting for discovery responses
Adding a small delay inside TopicList() before reading the discovery cache improves the completeness of the result. For example, adding a sleep after requesting subscriber information in [Discovery::TopicList()] (
|
public: void TopicList(std::vector<std::string> &_topics) |
|
{ |
|
if (!this->useZenoh) |
|
{ |
|
std::lock_guard<std::mutex> lock(this->mutex); |
|
this->remoteSubscribers.Clear(); |
|
} |
|
|
|
// Request the list of subscribers. |
|
Publisher pub("", "", this->pUuid, "", AdvertiseOptions()); |
|
this->SendMsg( |
|
DestinationType::ALL, msgs::Discovery::SUBSCRIBERS_REQ, pub); |
|
|
|
this->WaitForInit(); |
|
std::lock_guard<std::mutex> lock(this->mutex); |
|
this->info.TopicList(_topics); |
|
|
|
std::vector<std::string> remoteSubs; |
|
this->remoteSubscribers.TopicList(remoteSubs); |
|
|
|
// Add the remote subscribers |
|
for (auto const &t : remoteSubs) |
|
{ |
|
if (std::find(_topics.begin(), _topics.end(), t) == _topics.end()) |
|
{ |
|
_topics.push_back(t); |
|
} |
|
} |
|
} |
):
public: void TopicList(std::vector<std::string> &_topics)
{
...
// Request the list of subscribers.
Publisher pub("", "", this->pUuid, "", AdvertiseOptions());
this->SendMsg(
DestinationType::ALL, msgs::Discovery::SUBSCRIBERS_REQ, pub);
this->WaitForInit();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
std::lock_guard<std::mutex> lock(this->mutex);
this->info.TopicList(_topics);
...
}
With this modification, TopicList() returns the complete topic list in the current reproduction case.
However, the required waiting time depends on the number of topics and the discovery state. In a scenario with a larger number of topics, a longer delay is required before TopicList() can return the complete topic list.
4. Local process discovery limitation
Additionally, another issue was observed when using use_composition:=True. The discovery logic ignores information from local processes, so topics subscribed by nodes running in the same process are not included in the result. Therefore, when the test node is loaded as a composable node in the same process, TopicList() cannot discover topics from that local process.
|
private: void DispatchDiscoveryMsg(const std::string &_fromIp, |
|
char *_msg, uint16_t _len) |
|
{ |
|
gz::msgs::Discovery msg; |
|
|
|
// Parse the message, and return if parsing failed. Parsing could |
|
// fail when another discovery node is publishing messages using an |
|
// older (or newer) format. |
|
if (!msg.ParseFromArray(_msg, _len)) |
|
return; |
|
|
|
// Discard the message if the wire protocol is different than mine. |
|
if (this->Version() != msg.version()) |
|
return; |
|
|
|
std::string recvPUuid = msg.process_uuid(); |
|
|
|
// Discard our own discovery messages. |
|
if (recvPUuid == this->pUuid) |
|
return; |
Environment
Description
Expected behavior:
gz::transport::Node::TopicList()should return all available Gazebo Transport topics after the simulation has started, including topics that only have subscribers.Actual behavior:
gz::transport::Node::TopicList()always returns an incomplete topic list.Steps to reproduce
Setup
A minimal reproduction package is provided here: https://github.com/azeey/gsoc2026_multirobot/tree/main/bridge/topiclist_test
The package contains a simple ROS 2 node (
topiclist_test_node) that:gz::transport::Node::TopicList().The node supports the following launch parameters:
use_composition: whether to load the node as a composable node.Truefreq: the frequency (Hz) at whichTopicList()is called.1.0Steps
colcon build --symlink-install source install/setup.bashThe node periodically prints the number of expected topics, the number of topics returned by
TopicList(), and any missing topics.Output
The test was performed with different
TopicList()query frequencies (0.1 Hz, 1 Hz, and 2 Hz) and with both composable and non-composable node modes.In all cases, the same incomplete topic list was consistently returned. The node was running for more than 2 minutes, but the missing topics did not appear in the result.
[INFO] [topiclist_test_node]: Expected Gazebo topics: 25, discovered by TopicList(): 21 [INFO] [topiclist_test_node]: Missing topics: /model/vehicle/cmd_vel /model/vehicle/enable /world/test_world/light_config /world/test_world/material_colorAdditional information
1. Possible cause: asynchronous discovery
The issue appears to be related to the asynchronous nature of Gazebo Transport discovery.
From the current implementation,
TopicList()requests subscriber information and then immediately reads the cached discovery information. Since discovery responses are processed asynchronously, the cache may not contain all remote subscriber information when the topic list is generated.2. Comparison with
gz topic --listInterestingly, the command line tool
gz topic --listdoes not show this issue. Under the same simulation setup, it returns the complete topic list, including the topics missing fromTopicList().This suggests that the underlying discovery mechanism can eventually obtain the complete topic information, but
TopicList()may return before all discovery responses have been received.3. Experiment: waiting for discovery responses
Adding a small delay inside
TopicList()before reading the discovery cache improves the completeness of the result. For example, adding a sleep after requesting subscriber information in [Discovery::TopicList()] (gz-transport/src/Discovery.hh
Lines 818 to 846 in 0fdc2df
With this modification,
TopicList()returns the complete topic list in the current reproduction case.However, the required waiting time depends on the number of topics and the discovery state. In a scenario with a larger number of topics, a longer delay is required before
TopicList()can return the complete topic list.4. Local process discovery limitation
Additionally, another issue was observed when using
use_composition:=True. The discovery logic ignores information from local processes, so topics subscribed by nodes running in the same process are not included in the result. Therefore, when the test node is loaded as a composable node in the same process, TopicList() cannot discover topics from that local process.gz-transport/src/Discovery.hh
Lines 1123 to 1142 in 0fdc2df