Skip to content

feat(LibCarla/ros2): add configurable ROS 2 domain id via --ros-domain-id - #9778

Merged
Blyron merged 4 commits into
carla-simulator:ue4-devfrom
JArmandoAnaya:feature/ros2-configurable-domain-id
Jun 17, 2026
Merged

feat(LibCarla/ros2): add configurable ROS 2 domain id via --ros-domain-id#9778
Blyron merged 4 commits into
carla-simulator:ue4-devfrom
JArmandoAnaya:feature/ros2-configurable-domain-id

Conversation

@JArmandoAnaya

@JArmandoAnaya JArmandoAnaya commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Description

CARLA's native ROS 2 integration always used a fixed ROS 2 domain. The domain participant was created with a hardcoded value (domain 0 for FastDDS, the DDS default for CycloneDDS), so there was no way to run CARLA on a custom domain or to keep several ROS 2 systems isolated on the same network.

This PR adds a new server option, --ros-domain-id=<N>, that sets the ROS 2 domain id at startup. It follows the same pattern as the existing --rmw option:

./CarlaUE4.sh --ros2 --ros-domain-id=42      # explicit option
ROS_DOMAIN_ID=42 ./CarlaUE4.sh --ros2        # via the standard env var
make launch ARGS="--ros2 --ros-domain-id=42"

The domain id is not tied to any specific DDS vendor. It is stored in the ROS 2 middleware abstraction layer (a small MiddlewareConfig helper) and read by each middleware when it creates its transport context. FastDDS and CycloneDDS both honor it today, and any middleware added later (for example, a Zenoh backend) picks it up the same way. The requirement is documented on the publisher and subscriber middleware interfaces.

When the option is not provided, the server falls back to the standard ROS_DOMAIN_ID environment variable, and then to the default domain 0. The full resolution order is --ros-domain-id, then ROS_DOMAIN_ID, then 0. This matches how other ROS 2 tools select a domain and keeps the behavior backward-compatible, since domain 0 is still used when neither source is set. Values outside the valid range (0 to 232), from either source, are ignored and the next source is used.

The rviz Docker example (PythonAPI/examples/ros2/run_rviz.sh) now includes a matching --ros-domain-id=<N> option, allowing it to be run end-to-end on a custom domain. Both the example README and the command-line options documentation were updated.

This addresses the request in #8853, which asks for a way to change the ROS_DOMAIN_ID in 0.10.0. The change is implemented here first against ue4-dev (0.9.x), where the ROS 2 middleware abstraction layer already lives, and can be ported to ue5-dev (0.10.0) afterward. The reference is kept as "Related to" rather than "Closes" so the issue stays open until the 0.10.0 port lands.

Related to #8853

Where has this been tested?

  • Platform(s): Ubuntu 22.04
  • Python version(s): 3.12
  • Unreal Engine version(s): 4.26

Validation performed:

  • make check.LibCarla ARGS="--ros2" passes (debug and release), including new unit tests for the domain id configuration, string parsing, the ROS_DOMAIN_ID fallback precedence, and range validation.
  • Full ROS 2 package built successfully, confirming the UE4 plugin compiles.
  • Live end to end against a running simulator with a FastDDS server and a CycloneDDS rviz container:
    • --ros-domain-id=42: all CARLA topics and live data appear on domain 42 and nothing appears on domain 0.
    • ROS_DOMAIN_ID=42 with no option: topics appear on domain 42, confirming the environment-variable fallback.
    • no option and no env var: topics appear on the default domain 0 and nothing on domain 42.
    • make launch ARGS="--ros2 --ros-domain-id=42": same result, confirming the flag is forwarded to the editor.

Possible Drawbacks

A single domain id applies to the whole server process, which matches the current model of one shared transport context per middleware. There is no per-sensor or per-topic domain selection, and this is not expected to be needed.


This change is Reviewable

Copilot AI review requested due to automatic review settings June 7, 2026 18:10
@JArmandoAnaya
JArmandoAnaya requested a review from a team as a code owner June 7, 2026 18:10

Copilot AI left a comment

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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds support for selecting a ROS 2 domain id for CARLA’s native ROS 2 connector, wiring the value from CLI/settings into the middleware layer so FastDDS/CycloneDDS (and future middlewares) consistently honor it.

Changes:

  • Introduces --ros-domain-id=<N> end-to-end: build/launch script → UE settings parsing → ROS2 enable → middleware participants.
  • Adds a middleware-agnostic MiddlewareConfig plus validation utilities and unit tests for domain-id handling.
  • Updates ROS2 RViz helper script and documentation (quickstart, example README, changelog) to describe and use the new option.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Util/BuildTools/BuildCarlaUE4.sh Adds CLI option parsing and forwards --ros-domain-id into editor/server flags.
Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Settings/CarlaSettings.h Adds a persisted UE setting for ROS 2 domain id with default sentinel.
Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Settings/CarlaSettings.cpp Parses domain id from command line into settings at startup.
Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEngine.cpp Validates domain id and passes it into ROS2 enablement.
PythonAPI/examples/ros2/run_rviz.sh Accepts --ros-domain-id, validates it, and forwards ROS_DOMAIN_ID into the container.
PythonAPI/examples/ros2/README.md Documents how to use a custom ROS 2 domain id with CARLA and RViz.
LibCarla/source/test/server/test_ros2_middleware.cpp Adds unit tests covering domain-id validity and MiddlewareConfig behavior.
LibCarla/source/carla/ros2/middleware/fastdds/FastDDSSharedParticipant.cpp Creates FastDDS participant using configured domain id.
LibCarla/source/carla/ros2/middleware/cyclonedds/CycloneDDSSertype.cpp Creates CycloneDDS participant using configured domain id (or defaults).
LibCarla/source/carla/ros2/middleware/MiddlewareConfig.h New shared config header holding domain id + helper constants/functions.
LibCarla/source/carla/ros2/middleware/ISubscriberMiddleware.h Documents requirement for middlewares to honor MiddlewareConfig domain id.
LibCarla/source/carla/ros2/middleware/IPublisherMiddleware.h Documents requirement for middlewares to honor MiddlewareConfig domain id.
LibCarla/source/carla/ros2/ROS2.h Extends ROS2::Enable API to accept domain_id.
LibCarla/source/carla/ros2/ROS2.cpp Stores domain id into MiddlewareConfig and logs chosen domain.
Docs/ext_quickstart.md Documents the new server CLI option and its constraints.
CHANGELOG.md Notes the newly added --ros-domain-id option.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread LibCarla/source/test/server/test_ros2_middleware.cpp
Comment thread LibCarla/source/test/server/test_ros2_middleware.cpp
Comment thread Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Settings/CarlaSettings.h
Comment thread LibCarla/source/carla/ros2/ROS2.cpp Outdated
@JArmandoAnaya
JArmandoAnaya marked this pull request as draft June 7, 2026 18:43
@JArmandoAnaya
JArmandoAnaya marked this pull request as ready for review June 7, 2026 20:56
@LuisPovedaCano LuisPovedaCano self-assigned this Jun 10, 2026
…n-id

Add a --ros-domain-id=<N> server option to select the ROS 2 domain id at
startup, mirroring the existing --rmw flow. The domain id (0 to 232) lives in
the middleware abstraction layer (new MiddlewareConfig) and is read by every
middleware when it creates its transport context, so FastDDS, CycloneDDS, and
future middlewares all honor it. The interface contract is documented on the
publisher/subscriber middleware interfaces.

When the option is omitted, each middleware keeps its previous default domain,
so existing behavior is unchanged. Out-of-range values log an error and fall
back to the default.

The flag is parsed by CarlaSettings, validated and forwarded by CarlaEngine,
and forwarded into the editor command line by BuildCarlaUE4.sh so that
"make launch ARGS=\"--ros2 --ros-domain-id=N\"" works. The rviz Docker example
(run_rviz.sh) gains a matching --ros-domain-id option that sets ROS_DOMAIN_ID.
…play name

Make the test group numbering in test_ros2_middleware.cpp contiguous after the
middleware_config group was inserted (the new group kept number 5 and the rest
were left unchanged, producing two Group 5 headers). The groups are now numbered
1 through 12 in order.

Use "ID" instead of "Id" in the CarlaSettings ROS2 domain id display name so
the editor label matches the wording used in the CLI option and documentation.
…n id

Resolve the effective ROS 2 domain id in the middleware abstraction layer in
priority order: the --ros-domain-id command line value, then the ROS_DOMAIN_ID
environment variable, then the default domain 0. This lets the server pick up
the standard ROS 2 environment variable when the option is not given, matching
how other ROS 2 tools select a domain, while the option still takes precedence.

MiddlewareConfig gains TryParseDomainId and ResolveDomainId (pure, unit-tested)
plus GetEffectiveDomainId/ResolveEffective. FastDDS and CycloneDDS now use the
resolved value, so both middlewares select the domain identically instead of
CycloneDDS relying on DDS_DOMAIN_DEFAULT. The startup log reports the effective
domain id and its source.

Also rename the launch scripts' internal --ros-domain-id variable to
ROS_DOMAIN_ID_ARG. The previous name shadowed the inherited ROS_DOMAIN_ID
environment variable and reset it to empty for the launched editor, so an
exported ROS_DOMAIN_ID was lost when starting CARLA through make launch.

Add unit tests for parsing, precedence and the environment wrapper, and document
the resolution order.
@JArmandoAnaya
JArmandoAnaya force-pushed the feature/ros2-configurable-domain-id branch from c592c67 to ca0ab5c Compare June 10, 2026 09:22
@JArmandoAnaya
JArmandoAnaya marked this pull request as draft June 10, 2026 09:40
…leware

zenoh_ros_domain_id() now uses the effective domain id resolved by
MiddlewareConfig (--ros-domain-id, then ROS_DOMAIN_ID, then 0) instead
of reading the environment variable directly, so the Zenoh keyexprs
honor the same resolution order as the FastDDS and CycloneDDS
participants. Adds keyexpr and resolution-order unit tests.
@JArmandoAnaya

JArmandoAnaya commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

This PR is now ready for review.

@habby1012 I integrated the new ROS 2 domain ID management into your Zenoh implementation, zenoh_ros_domain_id() now resolves the ID through the middleware abstraction layer, so the Zenoh keyexprs honor --ros-domain-id, then ROS_DOMAIN_ID, then the default domain 0. With this, the PR proposes a generic approach to managing the domain ID across all ROS 2 middlewares (FastDDS, CycloneDDS, and Zenoh). Validated end to end with rmw_zenoh.

The CARLA topics are visible on the configured domain and isolated from the other domains.

@JArmandoAnaya
JArmandoAnaya marked this pull request as ready for review June 10, 2026 10:12
@habby1012

Copy link
Copy Markdown

@JArmandoAnaya, It looks good, thanks!

@Blyron
Blyron merged commit e78db15 into carla-simulator:ue4-dev Jun 17, 2026
2 checks passed
@JArmandoAnaya
JArmandoAnaya deleted the feature/ros2-configurable-domain-id branch June 17, 2026 06:54
@JArmandoAnaya

Copy link
Copy Markdown
Contributor Author

Thank you for merging @Blyron.

@habby1012, now it is possible to change the ROS Domain ID in CARLA UE4. If you have time to share your feedback on how Zenoh is working with this new feature, it would be great! Thank you.

@habby1012

Copy link
Copy Markdown

@JArmandoAnaya, Sorry for the late reply, I was off for the Dragon Boat Festival!
Tested on CARLA UE4 with the native Zenoh middleware — launching with --rmw=zenoh --ros-domain-id=42, all Zenoh keyexprs are correctly prefixed with the domain id (e.g. 42/clock/...), and the ROS 2 side only discovers the topics when ROS_DOMAIN_ID matches. Works great, thanks!

@JArmandoAnaya

Copy link
Copy Markdown
Contributor Author

@habby1012 nice to hear it! Thank you for your feedback.

germanros1987 pushed a commit that referenced this pull request Aug 19, 2026
…ain-id

Add a --ros-domain-id=<N> server option to select the ROS 2 domain id at
startup, alongside the existing --rmw flow. The domain id (0 to 232) lives in
the middleware abstraction layer (new MiddlewareConfig) and is read by FastDDS,
CycloneDDS, and Zenoh when they create their transport context. Resolution
order is --ros-domain-id, then the ROS_DOMAIN_ID environment variable, then the
default domain 0. Out-of-range values are ignored and the next source is used;
omitting the option keeps the previous behavior (domain 0).

ue5 adaptation: ROS2.cpp compiles into carla-server while the middlewares
compile into libcarla-ros2-native.so, so MiddlewareConfig's process-wide
storage would not be shared across that boundary. The domain id therefore
crosses it through a new DDS-free SetActiveDomainId export on the
ActiveMiddleware bridge (it stores the value in the shared library's
MiddlewareConfig and returns the resolved id for the startup log), replacing
the direct MiddlewareConfig::SetDomainId call used on the monolithic ue4 build.
The ue4 BuildCarlaUE4.sh editor-flag forwarding is not ported (ue5 reads
FCommandLine directly), and the doc note goes into Docs/ros2_native.md
(Docs/ext_quickstart.md does not exist on ue5).

Upstream commit (ue4-dev e78db15):

  feat(LibCarla/ros2): add configurable ROS 2 domain id via --ros-domain-id (#9778)

  * feat(LibCarla/ros2): add configurable ROS 2 domain id via --ros-domain-id

  Add a --ros-domain-id=<N> server option to select the ROS 2 domain id at
  startup, mirroring the existing --rmw flow. The domain id (0 to 232) lives in
  the middleware abstraction layer (new MiddlewareConfig) and is read by every
  middleware when it creates its transport context, so FastDDS, CycloneDDS, and
  future middlewares all honor it. The interface contract is documented on the
  publisher/subscriber middleware interfaces.

  When the option is omitted, each middleware keeps its previous default domain,
  so existing behavior is unchanged. Out-of-range values log an error and fall
  back to the default.

  The flag is parsed by CarlaSettings, validated and forwarded by CarlaEngine,
  and forwarded into the editor command line by BuildCarlaUE4.sh so that
  "make launch ARGS=\"--ros2 --ros-domain-id=N\"" works. The rviz Docker example
  (run_rviz.sh) gains a matching --ros-domain-id option that sets ROS_DOMAIN_ID.

  * refactor(LibCarla/ros2): renumber test groups and align domain id display name

  Make the test group numbering in test_ros2_middleware.cpp contiguous after the
  middleware_config group was inserted (the new group kept number 5 and the rest
  were left unchanged, producing two Group 5 headers). The groups are now numbered
  1 through 12 in order.

  Use "ID" instead of "Id" in the CarlaSettings ROS2 domain id display name so
  the editor label matches the wording used in the CLI option and documentation.

  * feat(LibCarla/ros2): fall back to ROS_DOMAIN_ID env var for the domain id

  Resolve the effective ROS 2 domain id in the middleware abstraction layer in
  priority order: the --ros-domain-id command line value, then the ROS_DOMAIN_ID
  environment variable, then the default domain 0. This lets the server pick up
  the standard ROS 2 environment variable when the option is not given, matching
  how other ROS 2 tools select a domain, while the option still takes precedence.

  MiddlewareConfig gains TryParseDomainId and ResolveDomainId (pure, unit-tested)
  plus GetEffectiveDomainId/ResolveEffective. FastDDS and CycloneDDS now use the
  resolved value, so both middlewares select the domain identically instead of
  CycloneDDS relying on DDS_DOMAIN_DEFAULT. The startup log reports the effective
  domain id and its source.

  Also rename the launch scripts' internal --ros-domain-id variable to
  ROS_DOMAIN_ID_ARG. The previous name shadowed the inherited ROS_DOMAIN_ID
  environment variable and reset it to empty for the launched editor, so an
  exported ROS_DOMAIN_ID was lost when starting CARLA through make launch.

  Add unit tests for parsing, precedence and the environment wrapper, and document
  the resolution order.

  * feat(LibCarla/ros2): apply the configured domain id to the Zenoh middleware

  zenoh_ros_domain_id() now uses the effective domain id resolved by
  MiddlewareConfig (--ros-domain-id, then ROS_DOMAIN_ID, then 0) instead
  of reading the environment variable directly, so the Zenoh keyexprs
  honor the same resolution order as the FastDDS and CycloneDDS
  participants. Adds keyexpr and resolution-order unit tests.

(adapted from ue4-dev e78db15)

(cherry picked from commit 0e6969a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants