Skip to content

fix(nav): [8/8] guard against null traffic light in WalkerManager - #9758

Merged
Blyron merged 7 commits into
carla-simulator:ue5-devfrom
JArmandoAnaya:fix/walker-manager-traffic-light-guard
Jul 14, 2026
Merged

fix(nav): [8/8] guard against null traffic light in WalkerManager#9758
Blyron merged 7 commits into
carla-simulator:ue5-devfrom
JArmandoAnaya:fix/walker-manager-traffic-light-guard

Conversation

@JArmandoAnaya

@JArmandoAnaya JArmandoAnaya commented May 27, 2026

Copy link
Copy Markdown
Contributor

This is the final PR (8 of 8) of the ROS 2 enhancement port from ue4-dev. It is stacked on #9757 and opened against ue5-dev, so its diff includes the lower PRs in the chain until they merge. Its own changes are two small fixes plus the consolidated series CHANGELOG.

WalkerManager::GetAllTrafficLightWaypoints cast every candidate actor to TrafficLight and dereferenced it without a null check, so an actor that reported the traffic-light type but did not resolve to a TrafficLight could null-dereference. It now iterates World::GetActors(), filters on the type id, and dynamic_pointer_casts behind a null guard. Separately, World::ApplySettings no longer calls Tick() in asynchronous mode, where the server free-runs and only honors tick cues in synchronous mode; it waits for the next server frame instead.

Changes

  • Guard the traffic-light gather in WalkerManager against a null or non-TrafficLight actor, and drop a dead per-actor snapshot query whose result was unused.
  • Add a test_walker_navigation smoke test that drives a controller.ai.walker so the reworked gather and the AI routing path run end to end.
  • World::ApplySettings: tick only in synchronous mode, wait for the server otherwise; pair the sync smoke-test teardown.
  • Add the consolidated CHANGELOG entry for the whole series.

Closes #9627

Where has this been tested?

  • Platform/OS: Ubuntu 22.04, local cmake build, Development package.
  • Python version: 3.10. libcarla_test_client 154/154 and libcarla_test_server 140/140 pass; smoke.test_walker_navigation passes against the Development package.
  • Unreal Engine version: 5.5.

Possible Drawbacks

  • World.apply_settings in asynchronous mode now waits for a server frame rather than sending a tick cue during the settling loop. The observable result is unchanged, but the call path differs.
  • The traffic-light gather is still gated by a process-static one-shot, so it is not recomputed on map reload. This is pre-existing and left for a follow-up; the new smoke test therefore exercises the reworked gather only on the first AI walker registered in a server process.

Series status

PR Theme Status
1 IMU compass yaw + actor disposal order #9743
2 Template infrastructure (publisher/subscriber) + Ackermann subscriber #9745
3 Camera publisher unification #9746
4 Point-cloud + scalar publishers, listener cleanup #9748
5 geom types + pitch/roll fix #9751
6 ROS2TopicVisibility default-startup flag #9756
7 V2X sensor family #9757
8 WalkerManager null-guard + series CHANGELOG (this PR) this PR

Related: #9627, #9757


This change is Reviewable

JArmandoAnaya and others added 6 commits June 24, 2026 11:45
…X, V2I)

Port the V2X sensor family to ue5-dev: the CAM sensor (sensor.other.v2x)
with ETSI CAM generation via CaService, the custom-payload sensor
(sensor.other.v2x_custom) carrying a fixed binary blob, the PathLossModel
propagation engine, and owner-less V2I (infrastructure) sensors. Adds the
engine-agnostic LibCarla message model (LibITS ITS types, CAMData /
CustomV2XData containers, V2X serializers, CustomV2XBytes RPC type), wires
both sensors into the SensorRegistry, exposes the client send() RPC path,
and adds the actor-definition builders, Python bindings, and docs.

V2X is a client-streamed sensor: data flows sensor -> SensorRegistry
serializer -> stream -> Python listen() callback. No DDS publisher is
added here, matching upstream; the data model is the foundation a future
ROS 2 publisher would consume.

Plain C++ models (CaService, PathLossModel) are owned via std::unique_ptr
so teardown frees them after UE clears ownership; the static actor maps
are cleared in EndPlay so a destroyed sensor leaves no dangling AActor*
key. CustomV2XBytes is zero-initialized.

Coverage: GTest round-trip for the serializers and the binary blob;
manual smoke test for CAM generation, custom send/receive, and V2I.

Upstream subjects and bodies (verbatim):

v2x 2nd try

Extend ROS2 support Step 1: V2XSensor (carla-simulator#9435)

- V2XSensor allows selection of virtual communication channels via
  sensors 'channel_id ' attribute.
- V2XCustomSensor sends bytes instead of std::string to support
  binary data blobs
- V2XCustomSensor allows multiple messages to be sent at once
  supporting individual larger messages
- Allow V2X-sensors without an owner if these are placed within the
  infrastructure (V2I communication)
- Removed the extra height on the PathLossModel after switching the
  actor to the sensor's actor instance. Therefore, the considered
  position is always the position of the sensor itself which can
  be defined when spawning/attaching
- Updated V2X docu

(adapted from ue4-dev c7cfdad, 8f33324)

Co-Authored-By: Daniel Grimm <daniel.grimm@kit.edu>
Co-Authored-By: berndgassmann <bernd.gassmann@motor-ai.com>
…ypes

Address correctness and robustness issues in the V2X sensor family:

- CaService: initialize mGenCamLowDynamicsLimit (and its counter) so the
  CAM low-dynamics fallback rate is deterministic instead of reading
  uninitialized memory; seed PrevLocation/PrevDeltaTime in the vehicle
  init path so the first acceleration estimate does not read uninitialized
  FVector components or divide by a zero delta time (mirrors
  AInertialMeasurementUnit, driving the first reading to ~0).
- PathLossModel: make Frequency_GHz/Frequency/lambda per-instance fields.
  They were static, so two sensors configured with different frequencies
  clobbered each other globally and corrupted the path-loss math; clamp
  the propagation distance to the FSPL reference distance before any
  log10/division so co-located actors no longer feed -inf/NaN into the
  received-power computation.
- CAM wire type: store PathPoint::pathDeltaTime inline with an availability
  flag instead of a raw pointer. The message is copied byte-for-byte onto
  the sensor data stream, so a server-side pointer must never travel on the
  wire.
- V2X/CustomV2X sensors: skip senders with no queued message (front() on an
  empty list is undefined behaviour) and look up sender queues with
  find-or-skip instead of map::at, which would throw if a sender dropped out
  of the map between the simulate and retrieval passes.
- Python set_bytes: size the copy from the buffer's byte length rather than
  its element count, so a buffer with itemsize > 1 (e.g. a NumPy int32
  array) is copied in full.
- Fix two user-visible/comment typos.

Add unit coverage for the inline CAM path-delta-time wire type
(value-not-pointer, trivially copyable, serializer round-trip) and a smoke
test that exercises set_bytes with a multi-byte-itemsize buffer.
WalkerManager::GetAllTrafficLightWaypoints collected every traffic light
affecting a pedestrian route by static_pointer_cast-ing each candidate to
TrafficLight and dereferencing it without a null check, so an actor that
reported the traffic-light type but did not resolve to a TrafficLight caused
a null dereference. Iterate World::GetActors() (the same episode actor set),
filter on the type id, dynamic_pointer_cast to TrafficLight and guard the
result before reading its stop waypoints. Also drop the dead per-actor
GetActorSnapshot query whose result was never used.

Add a smoke test that drives a controller.ai.walker so the reworked gather
and the AI routing path run end to end without taking the server down.

From upstream "Extend ROS2 support Step 2: Serializer and RPC (carla-simulator#9449)":
  - nav/WalkerManager: prevent from using nullptr

(adapted from ue4-dev 1146277)
Co-Authored-By: berndgassmann <bernd.gassmann@motor-ai.com>
…s mode

World::ApplySettings drove the fixed_delta_seconds settling loop with an
unconditional Tick(), which sends a tick cue and synchronizes to that frame.
In asynchronous mode the server free-runs and only waits on a client tick cue
in synchronous mode (CarlaEngine's frame loop gates on bSynchronousMode), so
the client should wait for the next server-produced frame rather than drive
one. Call Tick() only in synchronous mode and WaitForTick() otherwise. Pair
the SyncSmokeTest teardown to tick only when the restored settings keep
synchronous mode active.

From upstream "Extend ROS2 support Step 2: Fine grained ServerSynchronization (carla-simulator#9450)":
  - Ensure tick calls are ignored if sync mode is not active.
  - Client needs to wait for next tick on non synchronous mode

(adapted from ue4-dev aa9c92b)
Co-Authored-By: berndgassmann <bernd.gassmann@motor-ai.com>
Add a consolidated CHANGELOG entry for the ROS 2 enhancement port series: the
publisher and subscriber template rework with an Ackermann control subscriber,
the new geom velocity, acceleration and quaternion types with the pitch and
roll rotation fix, the ROS2TopicVisibility startup flag, the V2X sensor
family, the IMU compass yaw and actor disposal-order fix, and the pedestrian
navigation null-dereference hardening.
Align the ue5-dev ROS 2 rviz launcher with ue4-dev. run_rviz.sh now
builds a versioned Docker image from a local Dockerfile and accepts a
--distro argument (humble or jazzy) instead of pulling a hardcoded
osrf/ros:humble-desktop image, and the Dockerfile it builds from is
added.

This is the FastDDS-only slice of the upstream change; the CycloneDDS
RMW selection (--rmw) and its cyclonedds.xml profile are deferred to a
later change. The example vehicle stays vehicle.lincoln.mkz, as the
ue4-dev mkz_2017 blueprint is not shipped here.

Upstream commit:

  Update ROS2 Example (carla-simulator#9672)

  * Added `--distro` and `--rmw` arguments to `run_rviz.sh` to be able to
  run the example with ROS2 distros `Humble` and `Jazzy` and RMW `FastDDS`
  and `CycloneDDS`

  * add udp transport to cyclonedds config file

(adapted from ue4-dev 7fd91eb)

Co-Authored-By: Luis Poveda Cano <luispovedacano@gmail.com>
@JArmandoAnaya
JArmandoAnaya force-pushed the fix/walker-manager-traffic-light-guard branch from e5fd8aa to 3d69aeb Compare July 7, 2026 05:44
@JArmandoAnaya
JArmandoAnaya marked this pull request as ready for review July 13, 2026 09:09
@JArmandoAnaya
JArmandoAnaya requested a review from a team as a code owner July 13, 2026 09:09
@JArmandoAnaya

Copy link
Copy Markdown
Contributor Author

Thank you for merging the previous PRs. Now this one is ready for review.

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

LGTM

@Blyron
Blyron merged commit 39c4fda into carla-simulator:ue5-dev Jul 14, 2026
1 check passed
@JArmandoAnaya
JArmandoAnaya deleted the fix/walker-manager-traffic-light-guard branch July 14, 2026 08:26
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.

Port ue4-dev ROS2 enhancements to ue5-dev (Category 2: ROS2)

3 participants