Skip to content

Dynamic ros gz bridges - #910

Open
Tuxliri wants to merge 4 commits into
gazebosim:ros2from
Tuxliri:dynamic-ros-gz-bridges
Open

Dynamic ros gz bridges#910
Tuxliri wants to merge 4 commits into
gazebosim:ros2from
Tuxliri:dynamic-ros-gz-bridges

Conversation

@Tuxliri

@Tuxliri Tuxliri commented Jun 27, 2026

Copy link
Copy Markdown

🎉 New feature

Closes #849

Summary

Adds opt-in dynamic discovery of Gazebo Transport topics to ros_gz_bridge.

When create_dynamic_bridges is enabled, the bridge periodically:

  • Discovers Gazebo topics using TopicList and TopicInfo.
  • Maps supported Gazebo message types using the existing generated mappings.
  • Creates bridges for topics that appear after startup.
  • Prevents duplicate bridge creation.
  • Ignores unsupported or ambiguous message types.

Dynamic bridges default to GZ_TO_ROS. The direction can be changed using
dynamic_bridge_direction, including BIDIRECTIONAL.

parameter_bridge can now run without positional topic arguments when dynamic
bridging is enabled.

Usage

ros2 run ros_gz_bridge parameter_bridge \
  --ros-args \
  -p create_dynamic_bridges:=true

Publish from Gazebo:

gz topic -t /hello_dynamic_bridge \
  -m gz.msgs.StringMsg \
  -p 'data: "hello from Gazebo"'

Observe from ROS:

ros2 topic echo /hello_dynamic_bridge std_msgs/msg/String

Tests

Added launch integration tests covering:

  • Discovery of topics created after bridge startup.
  • Gazebo-to-ROS message delivery.
  • Bidirectional ROS and Gazebo delivery.
  • Duplicate bridge prevention.
  • Unsupported message types.
  • A burst of 50 ordered messages without duplicates.
  • Isolation from other ROS and Gazebo processes.

The tests use separate GZ_PARTITION and ROS_DOMAIN_ID values.

source /opt/ros/rolling/setup.bash

colcon build --packages-select ros_gz_bridge \
  --cmake-args -DBUILD_TESTING=ON

source install/setup.bash
colcon test --packages-select ros_gz_bridge
colcon test-result --all --verbose

Local result:

100% tests passed, 0 tests failed out of 20

Backport Policy

  • This should not be backported

This is a Jetty roadmap feature introducing new parameters and behavior.

Checklist

  • Signed all commits for DCO
  • Screen capture not applicable; this feature has no UI
  • Added tests
  • Added example and/or tutorial
  • Updated documentation
  • Migration guide not needed; the feature is opt-in
  • Python binding changes are not applicable
  • codecheck passed
  • All tests passed
  • Bazel changes are not applicable to this package
  • Reviewed another open Gazebo pull request
  • GenAI was used and Generated-by trailers are included

Note to maintainers: Please use Squash-Merge and retain the
Signed-off-by and Generated-by trailers.

@azeey

azeey commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Hi @Tuxliri, thanks for the contribution. However, as mentioned in #849, the issue is actively being worked on by a GSoC student @C88-YQ. To avoid duplication of work, I recommend finding other Github issues to contribute to.

@Tuxliri

Tuxliri commented Jul 14, 2026

Copy link
Copy Markdown
Author

Hi @azeey, gotcha. If @C88-YQ hasn’t coded this in yet the PR is pretty much ready for review, I have tested it and is working fine on my side. Might take this task off his plate.

Davide Iafrate added 2 commits July 16, 2026 11:37
Signed-off-by: Davide Iafrate <dvde.iafrate98@gmail.com>
Generated-by: OpenAI Codex (GPT-5)
Signed-off-by: Davide Iafrate <dvde.iafrate98@gmail.com>
@Tuxliri
Tuxliri force-pushed the dynamic-ros-gz-bridges branch from 497e09e to 2be4169 Compare July 16, 2026 11:39
@Tuxliri
Tuxliri marked this pull request as ready for review July 16, 2026 11:53
@Tuxliri
Tuxliri requested a review from ahcorde as a code owner July 16, 2026 11:53
@azeey

azeey commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

@C88-YQ will be reviewing this

Comment on lines +155 to +161
ASSERT_TRUE(gzNode.Subscribe<gz::msgs::StringMsg>(
topic,
[&mutex, &gzMessages](const gz::msgs::StringMsg & _msg)
{
std::lock_guard<std::mutex> lock(mutex);
gzMessages.push_back(_msg.data());
}));

@C88-YQ C88-YQ Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ASSERT_TRUE(gzNode.Subscribe<gz::msgs::StringMsg>(
topic,
[&mutex, &gzMessages](const gz::msgs::StringMsg & _msg)
{
std::lock_guard<std::mutex> lock(mutex);
gzMessages.push_back(_msg.data());
}));
std::function<void(const gz::msgs::StringMsg &)> gzCallback =
[&mutex, &gzMessages](const gz::msgs::StringMsg & _msg)
{
std::lock_guard<std::mutex> lock(mutex);
gzMessages.push_back(_msg.data());
};
ASSERT_TRUE(gzNode.Subscribe(topic, gzCallback));

I hit a build failure with gz-transport16 here. The migration notes mention that Node::Subscribe changed to a forwarding wrapper in newer gz-transport versions: Migration.md.

Perhaps we can change it like this to make it compatible across different gz-transport versions

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I was mistakenly building against Jetty

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regression is tracked in gazebosim/gz-transport#880

Comment on lines +78 to +84
bool create_dynamic_bridges = false;
bridge_node->get_parameter("create_dynamic_bridges", create_dynamic_bridges);
if (filteredArgs.empty() && !create_dynamic_bridges) {
usage();
return -1;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems to break the existing config-file-only usage:

ros2 run ros_gz_bridge parameter_bridge --ros-args -p config_file:=$WORKSPACE/ros_gz/ros_gz_bridge/test/config/full.yaml

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a check for empty config file string.

Comment on lines +284 to +285
const auto direction_str = this->get_parameter("dynamic_bridge_direction").as_string();
const auto direction = parse_bridge_direction(direction_str);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should consider determining the bridge direction automatically instead. Since Gazebo and ROS topics may have different directions depending on the specific topic, relying on a single dynamic_bridge_direction for all topics might not be flexible enough.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could potentially infer the direction based on the publishers and subscribers of each topic. For example, if a topic only has a publisher on the Gazebo side, it could be bridged as GZ_TO_ROS; if it only has subscribers on the Gazebo side, it could be ROS_TO_GZ; and if both exist, we could fall back to BIDIRECTIONAL for now.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you suggest to automatically bridge all compatible ROS topics to Gazebo?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just supporting a mix of GZ_TO_ROS and ROS_TO_GZ based on the gazebo topic direction?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +301 to +307
std::set<std::string> gz_type_names;
for (const auto & publisher : publishers) {
const auto gz_type_name = publisher.MsgTypeName();
if (!gz_type_name.empty()) {
gz_type_names.insert(gz_type_name);
}
}

@C88-YQ C88-YQ Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the current implementation only supports automatically bridging topics published from Gazebo, while topics subscribed by Gazebo are not handled in the same way. This might be another case we need to consider.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could potentially apply the same logic to the subscribers information from TopicInfo and get the gz_type_name from there as well.

Comment on lines +281 to +282
bool lazy;
this->get_parameter("lazy", lazy);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the default lazy state for dynamically created bridges should be true. Otherwise, it may cause unnecessary overhead, especially when there are many topics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Inbox

Development

Successfully merging this pull request may close these issues.

Dynamically create ros_gz bridges

3 participants