Skip to content
Back to skills

Add Ros Publisher

ASecurity

Adds a new topic to CARLA's native ROS 2 interface — a publisher (BasePublisher + PublisherImpl, topic suffix, QoS, registration in ROS2::GetOrCreateSensor, a ProcessDataFrom* entry point and its call site in the UE4 plugin) or a subscriber that lets ROS drive an actor. Also covers adding a middleware (RMW) behind IPublisherMiddleware. Use when the user asks to "publish X to ROS from CARLA", "add a ROS topic/publisher/subscriber", "make the obstacle/lane-invasion sensor publish", "add odometr...

  • 14,461 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 6, 2026
ai-agentsgobash

Works with

  • cli

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned October 6, 2026

npx -y skills add carla-simulator/carla --skill add-ros-publisher --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Add Ros Publisher?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Add Ros Publisher
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/carla-simulator-add-ros-publisher-carla/badge)](https://www.skillsdirectory.com/skills/carla-simulator-add-ros-publisher-carla)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: add-ros-publisher
description: Adds a new topic to CARLA's native ROS 2 interface — a publisher (BasePublisher + PublisherImpl, topic suffix, QoS, registration in ROS2::GetOrCreateSensor, a ProcessDataFrom* entry point and its call site in the UE4 plugin) or a subscriber that lets ROS drive an actor. Also covers adding a middleware (RMW) behind IPublisherMiddleware. Use when the user asks to "publish X to ROS from CARLA", "add a ROS topic/publisher/subscriber", "make the obstacle/lane-invasion sensor publish", "add odometry to ROS", or "add another RMW".
license: MIT
compatibility: A CARLA checkout whose LibCarla/source/carla/ros2 exists (native ROS 2 branch) and the toolchain to build LibCarla with --ros2 plus the UE4 plugin. Editing only. Proving a new topic on the wire needs a ROS 2 consumer (visualize-ros-rviz) and a server built with ROS 2.
metadata:
  group: ros2
  requires: add-ros-message-type
  prerequisites: scripts/check_env.sh
  reference: references/layers.md
---

# Add a ROS 2 publisher or subscriber

> **Paths.** `scripts/…` and `references/…` below are relative to the
> directory holding this SKILL.md. Your working directory is the user's
> project, not that directory, so prefix them with its absolute path or the
> command is not found.

A topic exists only when **four** things line up: a message type, a publisher
object, a registration so something creates it, and a call site that feeds it
data. Miss the last one and everything compiles while the topic never appears.

```
UE4 sensor/actor  --ProcessDataFrom*-->  ROS2 (dispatch, ROS2.cpp)
                                          |  GetOrCreateSensor / RegisterVehicle
                                          v
                            Carla<X>Publisher  (topic suffix + QoS)
                                          v
                            PublisherImpl<Traits>  -> MiddlewareFactory
                                          v
                     FastDDS | CycloneDDS | Zenoh middleware
```

Message types are [[add-ros-message-type]]; this skill is everything below the
type.

## On CARLA 0.10.0 (the UE5 line: 5.5 and 5.8)

The publisher layer was refactored on 0.10.0, so the file you copy from differs.
The right-hand column is UE 5.8; **UE 5.5 has the same `BasicPublisher` refactor
but no `middleware/` directory**, so its QoS type is the pre-`QosProfile.h` one
and it has none of the Autoware rows ([[check-ue5-limitations]]):

| | 0.9.x | 0.10.0 |
|---|---|---|
| base class to inherit | `CarlaPublisher` | `BasicPublisher` (and `BasicSubscriber` / `BasicListener`) |
| QoS type | `PublisherQos.h` | `QosProfile.h` |
| middleware | `IPublisherMiddleware` impls | same, plus `ActiveMiddleware.{h,cpp}` |
| map publisher | `CarlaMapPublisher` | **removed** — do not model a new publisher on it |
| DVS camera | `CarlaDVSPublisher` | `CarlaDVSCameraPublisher` |
| Autoware | — | `AutowareGNSSPublisher`, `AutowareVehicleStatusPublisher`, `AutowareControlSubscriber`, `AutowareSteeringCompensation.h` |

0.10.0 also factors the per-sensor maths out of the publishers into reusable
headers you should use rather than re-derive: `CameraIntrinsics`, `ImuMath`,
`DvsEventEncoding`, `OpticalFlowEncoding`, `PointCloudFieldsLayout`,
`RadarPolarToCartesian`, `TransformQuaternion`, and `AckermannControlConversion`
on the subscriber side. A new `listeners/` directory holds `BasicListener`.

The registration point, the `ProcessDataFrom*` entry points and the UE plugin call
sites work the same way on both.

## Instructions

```
Progress:
- [ ] Step 1: Check prerequisites (bash scripts/check_env.sh), clear FAILs
- [ ] Step 2: Confirm the message type exists in types/msg (else add-ros-message-type)
- [ ] Step 3: Write publishers/Carla<X>Publisher.h (+ .cpp for the Write logic)
- [ ] Step 4: Register it — GetOrCreateSensor case, or RegisterVehicle/Actor
- [ ] Step 5: Add the ProcessDataFrom<X> entry point in ROS2.h/.cpp
- [ ] Step 6: Call it from the UE4 side, inside `#if defined(WITH_ROS2)`
- [ ] Step 7: bash scripts/verify.sh, then rebuild the plugin (build-carla-ue4 ROS2=1)
- [ ] Step 8: Prove the topic — visualize-ros-rviz topics / hz
```

### Step 3: the publisher

Copy the shape of the simplest existing one (`CarlaGNSSPublisher`): derive from
`BasePublisher`, hold a `PublisherImpl<Traits>`, `Init` the topic in the
constructor, implement `Write(...)` and `Publish()`.

```cpp
class CarlaFooPublisher : public BasePublisher {
  public:
    struct FooMsgTraits { using msg_type = msg::Foo; };   // the only type binding

    CarlaFooPublisher(std::string base_topic_name, std::string frame_id):
      BasePublisher(base_topic_name, frame_id),
      _impl(std::make_shared<PublisherImpl<FooMsgTraits>>()) {
        // Suffix decides the topic; NO suffix means "publish on the base name",
        // which is what IMU/GNSS/collision do. Pass PublisherQos::SensorData()
        // for high-rate streams so a slow subscriber cannot stall the sim.
        if (!_impl->Init(GetBaseTopicName() + "/foo", PublisherQos::SensorData())) {
          log_warning("CarlaFooPublisher: Init failed for topic: ", GetBaseTopicName(), "/foo");
        }
    }
    bool Publish() { return _impl->Publish(); }
    bool Write(int32_t seconds, uint32_t nanoseconds, /* payload */);
  private:
    std::shared_ptr<PublisherImpl<FooMsgTraits>> _impl;
};
```

Header in `publishers/`, `Write` body in the matching `.cpp` if it is more than a
few lines. **No CMake edit is needed** — `LibCarla/cmake/ros2/CMakeLists.txt`
globs `publishers/*.cpp`, `subscribers/*.cpp`, `types/*.cpp`,
`middleware/*/*.cpp`. A brand-new *subdirectory* does need a glob entry.

Init failure is a **warning, not an error**: a typo'd topic leaves a live
publisher object that publishes nowhere. Watch for that line in the server log.

### Steps 4-6: wire it up

| For | Register in | Feed from |
|---|---|---|
| a sensor | a `case ESensors::<X>:` in `ROS2::GetOrCreateSensor` | `ProcessDataFrom<X>` called in that sensor's UE4 class |
| a vehicle/actor stream | `ROS2::RegisterVehicle` / `RegisterActor` | the actor's tick or an RPC path |
| a subscriber (ROS drives CARLA) | `RegisterVehicle`'s subscriber list | `ProcessMessages(callback)` → `ActorROS2Handler` |

The dispatch function is the seam: it takes the raw CARLA data, casts the base
publisher to the concrete type, `Write`s, `Publish`es, and (for sensors) also
updates the TF publisher. Copy `ProcessDataFromGNSS` — it is the shortest
complete example.

On the UE4 side the call must sit inside `#if defined(WITH_ROS2)` and be guarded
by `ROS2->IsEnabled()`; the transform passed is **relative to the attach parent**
when there is one:

```cpp
#if defined(WITH_ROS2)
auto ROS2 = carla::ros2::ROS2::GetInstance();
if (ROS2->IsEnabled()) {
  AActor* ParentActor = GetAttachParentActor();
  auto Transform = ParentActor ? GetActorTransform().GetRelativeTransform(ParentActor->GetActorTransform())
                               : GetActorTransform();
  ROS2->ProcessDataFromFoo(Stream.GetSensorType(), Transform, Data, this);
}
#endif
```

### Steps 7-8: verify

```bash
bash scripts/verify.sh                          # LibCarla + its tests, with --ros2
ROS2=1 bash ../../ue4/build-carla-ue4/scripts/06_build_editor.sh   # the plugin call site
# then, against a running ROS 2 server:
bash ../visualize-ros-rviz/scripts/ros_view.sh topics
bash ../visualize-ros-rviz/scripts/ros_view.sh hz /carla/<actor>/foo
```

`verify.sh` proves it compiles and the middleware layer still passes its tests.
**Only `hz`/`echo` proves the topic.** A publisher can build, register, and still
be silent: the most common reasons are the sensor not being enabled for ROS
([[create-sensor]] `--ros`), a UE4 call site that never runs, and a sensor whose
`Tick` is skipped because nothing listens.

## Sensors with no publisher yet

`ROS2::GetOrCreateSensor` deliberately returns `nullptr` for these, so they are
the natural targets — and each already has a `ProcessDataFrom*` or needs one:

| Sensor | Note |
|---|---|
| `other.lane_invasion` | `CarlaLineInvasion` message type already exists |
| `other.obstacle` | `ProcessDataFromObstacleDetection` exists in `ROS2.h` |
| `other.rss` | no message type yet |
| world observer | high volume; think before publishing |

## Adding a middleware (RMW)

Same layer, one level down: implement `IPublisherMiddleware` /
`ISubscriberMiddleware`, add the include + `case` in `MiddlewareFactory`, a
`Middleware` enum value and its `MiddlewareFromString` mapping, and a
`CARLA_ROS2_MIDDLEWARE_<NAME>` compile definition in
`LibCarla/cmake/ros2/CMakeLists.txt` (all three are defined at once, so
`IsMiddlewareAvailable` answers correctly at run time). Extend
`test_ros2_middleware.cpp`. Per-RMW quirks — Zenoh's router and wire format,
CycloneDDS's sertype, FastDDS's shared participant — are in
[`references/layers.md`](references/layers.md).

## Examples

**Example 1: publish the obstacle sensor**

User says: "make the obstacle detection sensor publish to ROS"

`CarlaLineInvasion`/a suitable type exists → write `CarlaObstaclePublisher` →
replace the `nullptr` case for `ESensors::ObstacleDetectionSensor` → fill in
`ProcessDataFromObstacleDetection` (declared already) → the call site in
`ObstacleDetectionSensor.cpp` already exists → verify → `hz`.

**Example 2: let ROS steer via a new command topic**

User says: "add a ROS topic to set the ego's target speed"

Type first ([[add-ros-message-type]]) → subscriber modelled on
`AckermannControlSubscriber` → register it in `RegisterVehicle` → extend
`ROS2CallbackData` and `ActorROS2Handler` to apply it → verify → `ros2 topic pub`.

**Example 3: a fourth RMW**

User says: "add support for another DDS implementation"

Follow "Adding a middleware": interface pair, factory case, enum + string, CMake
definition, test. No publisher changes at all — that is the point of the layer.

## Troubleshooting

**Problem: it all compiles but the topic never appears**
Cause: usually the call site, not the publisher — the `#if defined(WITH_ROS2)`
block is missing, unreachable, or the plugin was rebuilt without `ROS2=1`.
Solution: confirm `Ros2 ON` in `Config/OptionalModules.ini`, rebuild step 06 with
`ROS2=1`, then check the sensor is `enable_for_ros`-ed.

**Problem: `PublisherImpl: Failed to create middleware publisher`**
Cause: `MiddlewareFactory` has no case for the active middleware, or the RMW is
not compiled in.
Solution: check `IsMiddlewareAvailable` and the `CARLA_ROS2_MIDDLEWARE_*`
definitions in `LibCarla/cmake/ros2/CMakeLists.txt`.

**Problem: `Init failed for topic: …` in the server log, everything else fine**
Cause: an invalid topic name (empty `ros_name`, double slash) — Init only warns.
Solution: fix the name; remember unnamed actors become `actor<id>`.

**Problem: subscriber never receives, publisher works**
Cause: QoS mismatch (the peer is reliable+transient_local against a volatile
publisher), or the topic string differs by a suffix.
Solution: `visualize-ros-rviz info <topic>` shows endpoint counts and QoS on both
sides.

**Problem: a new `.cpp` is not compiled**
Cause: it is in a new subdirectory; the CMake glob lists directories explicitly.
Solution: add the glob line in `LibCarla/cmake/ros2/CMakeLists.txt`.

## Outputs

Edited sources in the checkout, a green `check.LibCarla`, and — after the plugin
rebuild — a new topic visible from ROS 2.

Layer-by-layer detail (publisher/subscriber contracts, QoS profiles, dispatch,
the middleware interfaces and their quirks) in
[`references/layers.md`](references/layers.md).

Files in this skill

  • SKILL.md11.4 KB
  • references/layers.md6.7 KB
  • scripts/check_env.sh3.3 KB
  • scripts/env.sh2.2 KB
  • scripts/verify.sh1.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…