Skip to content

feat(LibCarla/ros2): [2/7] add POD message types, FastDDS conversions, and TypeMap specializations - #9612

Merged
LuisPovedaCano merged 3 commits into
carla-simulator:ue4-devfrom
JArmandoAnaya:feature/ros2-pod-types-and-fastdds-conversions
Apr 8, 2026
Merged

feat(LibCarla/ros2): [2/7] add POD message types, FastDDS conversions, and TypeMap specializations#9612
LuisPovedaCano merged 3 commits into
carla-simulator:ue4-devfrom
JArmandoAnaya:feature/ros2-pod-types-and-fastdds-conversions

Conversation

@JArmandoAnaya

@JArmandoAnaya JArmandoAnaya commented Mar 28, 2026

Copy link
Copy Markdown
Contributor

Introduce backend-neutral POD message structs and to_fastdds/from_fastdds conversion functions, completing the type abstraction layer started in PR # 1 - without touching any concrete publisher or subscriber.

Note: This PR is submitted as a draft. It is blocked on #9608 (PR # 1 of this series) and will be marked ready for review once that PR is merged.

New files (LibCarla/source/carla/ros2/types/):

  • 31 plain C++ structs in types/msg/ - one per ROS2 message type, no DDS dependency
  • FastDDSConversions.h - inline to_fastdds/from_fastdds overloads for all 31 type pairs, with move overloads for Image and PointCloud2

New test file:

  • LibCarla/source/test/server/test_dds_middleware.cpp - 45 unit tests across 10 groups covering the middleware enum, factory, interfaces, PublisherImpl, and SubscriberImpl

Modified files:

  • FastDDSTypeMap.h: real specializations mapping msg::X to FastDDS types, alongside the existing identity specializations
  • DDSMiddleware.h: add ToROS2DDSTypeName() utility for use in PR CARLA takes forever to start when run from a network folder #3
  • DDSMiddlewareFactory.h: add && !defined(CARLA_ROS2_DDS_TESTING) guard to FastDDS include/create blocks so tests compile without real DDS headers
  • PublisherImpl.h / SubscriberImpl.h: #ifdef LIBCARLA_WITH_GTEST test seams for middleware injection
  • cmake/fast_dds/CMakeLists.txt: added glob for types/msg/*.h install

No behavior change - identity TypeMap specializations remain active; all concrete publishers and subscribers compile and work unchanged. Real POD specializations exist but are unused until PR #2b.

Description

This PR introduces the type abstraction layer that decouples ROS2 message data from FastDDS-generated types. It is the second in the PR series that incrementally implements the decoupled DDS middleware architecture originally prototyped in draft PR #9589.

The key step here is replacing FastDDS-generated structs (with their getter/setter API) with plain C++ structs (msg::X) that have no DDS dependency. The FastDDS middleware receives these POD values and converts them internally via to_fastdds/from_fastdds - a pattern that will work identically for CycloneDDS in PR #3.

These changes do not affect current behavior. The identity FastDDSTypeMap specializations remain active, so all concrete publishers and subscribers continue to use FastDDS types directly until PR #2b switches them over.

What's new

31 POD message structs (LibCarla/source/carla/ros2/types/msg/):

Plain C++ structs in namespace carla::ros2::msg, one per ROS2 message type. Each struct has no dependency on FastDDS, CycloneDDS, or any DDS library - only standard library headers. All members are value-initialized.

File ROS2 message Notes
Time.h builtin_interfaces/Time int32_t sec, uint32_t nanosec
Header.h std_msgs/Header Time stamp, std::string frame_id
Vector3.h geometry_msgs/Vector3 double x, y, z
Quaternion.h geometry_msgs/Quaternion double x, y, z, w
Point.h geometry_msgs/Point double x, y, z
Point32.h geometry_msgs/Point32 float x, y, z
Float32.h std_msgs/Float32 float data
String.h std_msgs/String std::string data
Pose.h geometry_msgs/Pose Point position, Quaternion orientation
PoseWithCovariance.h geometry_msgs/PoseWithCovariance Pose, std::array<double, 36>
Twist.h geometry_msgs/Twist Vector3 linear, angular
TwistWithCovariance.h geometry_msgs/TwistWithCovariance Twist, std::array<double, 36>
Transform.h geometry_msgs/Transform Vector3 translation, Quaternion rotation
TransformStamped.h geometry_msgs/TransformStamped Header, child_frame_id, Transform
Odometry.h nav_msgs/Odometry Header, PoseWithCovariance, TwistWithCovariance
RegionOfInterest.h sensor_msgs/RegionOfInterest offsets, dimensions, bool do_rectify
PointField.h sensor_msgs/PointField std::string name, offset, datatype, count
NavSatStatus.h sensor_msgs/NavSatStatus int8_t status, uint16_t service
NavSatFix.h sensor_msgs/NavSatFix Header, NavSatStatus, lat/lon/alt, covariance
Clock.h rosgraph_msgs/Clock Time clock
Imu.h sensor_msgs/Imu Header, orientation + angular velocity + linear acceleration with covariances
Image.h sensor_msgs/Image Header, dimensions, encoding, std::vector<uint8_t> data
CameraInfo.h sensor_msgs/CameraInfo Header, dimensions, distortion model, K/R/P matrices, RegionOfInterest roi
PointCloud2.h sensor_msgs/PointCloud2 Header, std::vector<PointField>, std::vector<uint8_t> data
TFMessage.h tf2_msgs/TFMessage std::vector<TransformStamped>
TF2Error.h tf2_msgs/TF2Error uint8_t error, std::string error_string
AckermannDrive.h ackermann_msgs/AckermannDrive steering angle/velocity, speed, acceleration, jerk
AckermannDriveStamped.h ackermann_msgs/AckermannDriveStamped Header, AckermannDrive drive
CarlaCollisionEvent.h carla_msgs/CarlaCollisionEvent Header, uint32_t other_actor_id, Vector3 normal_impulse
CarlaEgoVehicleControl.h carla_msgs/CarlaEgoVehicleControl Header, throttle/steer/brake/gear/bools
CarlaLineInvasion.h carla_msgs/LaneInvasionEvent Header, int32_t crossed_lane_markings

Conversion functions (types/FastDDSConversions.h):

Inline to_fastdds()/from_fastdds() overload pairs for all 31 type pairs, ordered bottom-up so composites can call primitives. Move overloads are provided for msg::Image and msg::PointCloud2 to avoid copying large std::vector<uint8_t> data buffers when the source is mutable.

Real FastDDSTypeMap specializations (dds/fastdds/FastDDSTypeMap.h):

26 new template specializations alongside the existing identity specializations. Both sets coexist because they key on different types - msg::Image vs sensor_msgs::msg::Image - so there is no ambiguity and no ODR violation.

ToROS2DDSTypeName() utility (dds/DDSMiddleware.h):

Mangles a C++ type name into the ROS2-compatible DDS format ("sensor_msgs::msg::Image""sensor_msgs::msg::dds_::Image_"). Added for use in PR #3 when PubSubTypes are regenerated with unmangled names. Not applied in Init() methods in this PR - current auto-generated PubSubTypes already return mangled names from getName().

Test seams (publishers/PublisherImpl.h, subscribers/SubscriberImpl.h):

#ifdef LIBCARLA_WITH_GTEST guards expose middleware injection for unit tests:

  • SetMiddlewareForTesting(std::unique_ptr<IDDSPublisherMiddleware>) - inject a mock publisher
  • SetMiddlewareForTesting(std::unique_ptr<IDDSSubscriberMiddleware>) - inject a mock subscriber
  • SimulateMessageReceiptForTesting(const msg_type&) - write directly to internal message storage

Added early so PR #2b's publisher/subscriber changes can be validated in unit tests without a running DDS network.

Unit tests (LibCarla/source/test/server/test_dds_middleware.cpp):

45 tests across 10 groups using GoogleTest and hand-written mocks. The test file defines CARLA_ROS2_DDS_TESTING to suppress real DDS includes (no DDS daemon required), while keeping CARLA_ROS2_DDS_FASTDDS defined so availability checks exercise their real compile-time logic.

Group Tests Coverage
dds_middleware_enum 2 Enum values, switch exhaustiveness
dds_middleware_to_string 3 String conversion
dds_middleware_from_string 5 String parsing, edge cases
dds_middleware_available 2 Compile-time availability
dds_middleware_type_name 4 ToROS2DDSTypeName() mangling
DDSMiddlewareFactoryFixture 5 Factory set/get/resolve
dds_publisher_interface 5 Mock against abstract interface
dds_subscriber_interface 5 Mock against abstract interface
publisher_impl 7 PublisherImpl delegation and data flow
subscriber_impl 7 SubscriberImpl delegation and flag behavior

Design decisions

  • Dual TypeMap coexistence - identity specializations (FastDDSTypeMap<sensor_msgs::msg::Image>) and real POD specializations (FastDDSTypeMap<msg::Image>) key on different types, so both sets compile and coexist with no conflict until identity specializations are removed in PR #2b
  • ToROS2DDSTypeName deferred - current auto-generated PubSubTypes already call setName() with the mangled format (e.g., "sensor_msgs::msg::dds_::NavSatFix_"); applying the function now would double-mangle. It will be used in PR CARLA takes forever to start when run from a network folder #3 when PubSubTypes are regenerated
  • Current include paths - FastDDSConversions.h uses the existing flat types/Image.h paths rather than a reorganized types/fastdds/ layout; reorganization is deferred to avoid scope creep
  • Test seams in PR #2a - added ahead of PR #2b so the publisher/subscriber switch can be unit-tested immediately after landing
  • CARLA_ROS2_DDS_TESTING guard in factory - DDSMiddlewareFactory.h includes/creates FastDDS middleware only when CARLA_ROS2_DDS_TESTING is not defined; availability checks (IsMiddlewareAvailable, GetAvailableMiddlewareString) are unaffected and reflect the real compile-time state

PR series

This PR is part of the DDS middleware decoupling series (#9294). Each PR in the chain inherits the commits of all prior PRs. The Commits column lists only the commits introduced by that PR, and the Files column counts only the files changed by those new commits.

# PR Title Branch Commits Files
1/7 #9608 DDS middleware abstraction layer feature/dds-middleware-abstraction-layer 0ab35ee6 10
2/7 #9612 POD message types, FastDDS conversions, and TypeMap specializations (this PR) feature/ros2-pod-types-and-fastdds-conversions e665d2af 39
3/7 #9619 Migrate publishers and subscribers to POD message types feature/ros2-publishers-pod-types-migration 98b6f7aa 27
4/7 #9620 CycloneDDS enum, factory, stubs, and tests feature/ros2-cyclonedds-enum-factory 2a4a8db3 5
5/7 #9643 Unified CDR serialization + GenericCdrPubSubType feature/ros2-cdr-serialization 5a9dd3b4, 0a9ca585 8
6/7 #9644 CycloneDDS CDR middleware, UE4 runtime selection, and correctness fixes feature/ros2-cyclonedds-cdr-middleware d3f2a45b, 2d80046b 26
7/7 #9645 Remove legacy FastDDS-generated type files feature/ros2-remove-fastdds-generated-types eea4e0a4 125

Related

Fixes #9294

Where has this been tested?

  • Platform(s): Linux Ubuntu 22.04
  • Python version(s): 3.10, 3.11, 3.12
  • Unreal Engine version(s): UE4

Possible Drawbacks

  • The new POD types and conversion layer add indirection between CARLA sensor data and the DDS wire format; this is intentional and enables the multi-backend support that is the goal of this PR series.
  • Until PR #2b lands, the real FastDDSTypeMap specializations exist but are unused - the identity specializations remain active. This is a deliberate transitional state.

Compilation steps

# 1. Setup all dependencies (FastDDS, CycloneDDS, Chrono, etc.)
make setup ARGS="--python-version=3.10,3.11,3.12 --ros2 --target-wheel-platform=manylinux_2_31_x86_64"

# 2. Build LibCarla (server + client, debug + release)
make LibCarla ARGS="--python-version=3.10,3.11,3.12 --ros2 --target-wheel-platform=manylinux_2_31_x86_64"

# 3. Build PythonAPI
make PythonAPI ARGS="--python-version=3.10,3.11,3.12 --ros2 --target-wheel-platform=manylinux_2_31_x86_64"

# 4. Build CarlaUE4Editor
make CarlaUE4Editor ARGS="--python-version=3.10,3.11,3.12 --ros2 --target-wheel-platform=manylinux_2_31_x86_64"

# 5. Build the final package
make package ARGS="--python-version=3.10,3.11,3.12 --ros2 --target-wheel-platform=manylinux_2_31_x86_64 --no-zip"

Test plan

  • Verify make LibCarla ARGS="--ros2" compiles successfully
  • Verify make check.LibCarla passes (90/90 tests: 45 existing + 45 new DDS middleware tests)
  • Verify new DDS middleware tests pass: --gtest_filter='*dds_middleware*:*DDSMiddleware*:*publisher_impl*:*subscriber_impl*:*publisher_interface*:*subscriber_interface*'
  • Verify no concrete publisher/subscriber .cpp files were modified

This change is Reviewable

…ackend support

Introduce a strategy-pattern abstraction that decouples PublisherImpl and
SubscriberImpl from the FastDDS API, enabling future integration of
additional DDS middleware implementations (e.g. CycloneDDS).

New abstraction layer (LibCarla/source/carla/ros2/dds/):
- DDSMiddleware enum and string conversion utilities
- IDDSPublisherMiddleware / IDDSSubscriberMiddleware pure virtual interfaces
- DDSMiddlewareFactory with thread-safe middleware selection and creation
- FastDDSPublisherMiddleware<T> / FastDDSSubscriberMiddleware<S> template
  implementations behind the new interfaces
- FastDDSTypeMap<T> identity-mapping type traits (to be replaced with real
  conversions when POD message types are introduced)

Modified files:
- PublisherImpl.h: replaced direct FastDDS inheritance and members with
  delegation to IDDSPublisherMiddleware via the factory (public API unchanged)
- SubscriberImpl.h: same pattern for subscriber side
- cmake/fast_dds/CMakeLists.txt: added CARLA_ROS2_DDS_FASTDDS compile
  definition, new header globs, and fixed missing include directories in
  the debug build target

No behavior change — FastDDS remains the only compiled middleware.
Concrete publishers and subscribers are unaffected.
…real TypeMap specializations

Introduce backend-neutral POD message structs and to_fastdds/from_fastdds
conversion functions, completing the type abstraction layer started in PR carla-simulator#1
without touching any concrete publisher or subscriber.

New files (LibCarla/source/carla/ros2/types/):
- 31 plain C++ structs in types/msg/ - one per ROS2 message type, no DDS
  dependency, standard library headers only, all members value-initialized
- FastDDSConversions.h: inline to_fastdds/from_fastdds overload pairs for all
  31 type pairs, ordered bottom-up so composites call primitives; move overloads
  for msg::Image and msg::PointCloud2 to avoid copying large data vectors

New test file (LibCarla/source/test/server/test_dds_middleware.cpp):
- 45 unit tests across 10 groups covering the middleware enum, factory,
  interfaces, PublisherImpl, and SubscriberImpl using hand-written mocks;
  no DDS daemon required (CARLA_ROS2_DDS_TESTING suppresses real includes)

Modified files:
- FastDDSTypeMap.h: 28 real specializations mapping msg::X to FastDDS types,
  coexisting with the existing identity specializations (different key types,
  no ODR conflict)
- DDSMiddleware.h: add ToROS2DDSTypeName() for use in PR carla-simulator#3
- DDSMiddlewareFactory.h: add CARLA_ROS2_DDS_TESTING guard to FastDDS
  include/create blocks so test binary compiles without real DDS headers
- PublisherImpl.h / SubscriberImpl.h: LIBCARLA_WITH_GTEST test seams for
  middleware injection and message simulation
- cmake/fast_dds/CMakeLists.txt: added glob for types/msg/*.h install

No behavior change - identity TypeMap specializations remain active; all
concrete publishers and subscribers compile and work unchanged.
@update-docs

update-docs Bot commented Mar 28, 2026

Copy link
Copy Markdown

Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would update our CHANGELOG.md based on your changes.

@LuisPovedaCano

Copy link
Copy Markdown
Contributor

I've been looking the changes, this looks good to me. Please resolve the conflicts.

@JArmandoAnaya
JArmandoAnaya marked this pull request as ready for review April 7, 2026 17:04
@JArmandoAnaya
JArmandoAnaya requested a review from a team as a code owner April 7, 2026 17:04
@JArmandoAnaya

Copy link
Copy Markdown
Contributor Author

@LuisPovedaCano, Done!

@LuisPovedaCano LuisPovedaCano 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.

Good Job 👍

@LuisPovedaCano
LuisPovedaCano merged commit 542959a into carla-simulator:ue4-dev Apr 8, 2026
2 checks passed
germanros1987 pushed a commit that referenced this pull request Aug 19, 2026
Introduce the middleware-neutral type layer of the ROS 2 middleware
decoupling series, ported from ue4-dev:

- types/msg/*.h: 31 plain C++ structs, one per ROS 2 message type, no
  DDS dependency, standard library headers only, all members
  value-initialized (upstream ue4-dev #9612).
- types/CdrSerialization.h: serialize_to_cdr(), deserialize_from_cdr()
  and cdr_serialized_size() for all msg::* types using Fast-CDR
  (classic CDR, little-endian, DDS encapsulation header). The buffers
  are wire-compatible with every ROS 2 distribution and can be handed
  directly to FastDDS write() paths or CycloneDDS dds_writecdr(),
  removing the need for per-vendor generated type files. A
  kMaxCdrSequenceElements cap rejects hostile sequence lengths during
  deserialization (upstream ue4-dev #9643).
- types/CdrTopicInfo.h: per-type type_name(), REP-2011 RIHS01 type
  hash and max_serialized_size(); the hashes let ROS 2 Iron and newer
  RMWs parse the type hash CARLA advertises via USER_DATA (upstream
  ue4-dev #9681).
- types/UserDataFormat.h: build_user_data() / build_user_data_for<T>()
  helpers producing the REP-2016 "typehash=RIHS01_<hex>;" key-value
  payload (upstream ue4-dev #9681).

UE5 adaptation: ue5-dev pins FastDDS 2.11.2 with bundled Fast-CDR 1.x,
so CdrSerialization.h keeps the Fast-CDR 1.x spellings
(eprosima::fastcdr::Cdr::DDS_CDR, getSerializedDataLength()) instead
of the Fast-CDR 2.x forms the ue4-dev tip carries since its Fast-DDS
2.14.6 upgrade (ue4-dev #9789). Five lines differ; the wire format is
identical either way and is pinned by the golden-bytes test added in
the follow-up test commit.

The FastDDSConversions.h / FastDDSTypeMap.h files from #9612 are
deliberately not ported; they were superseded by unified CDR upstream.

The new headers are not referenced by any build target yet; they start
compiling when the middleware abstraction lands in the next PR of the
series.

(adapted from ue4-dev 542959a)
(adapted from ue4-dev f53144c)
(adapted from ue4-dev c64e8f4)
(adapted from ue4-dev b865088)

(cherry picked from commit 5d0d578)
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.

2 participants