Setup
- CARLA version: 0.9.16 (Python client and server)
- Platform: Linux x86_64; packaged CARLA in an Ubuntu 20.04 Docker container on an Ubuntu 24.04.3 LTS host
- Python version: 3.10.18
- GPU: NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation Edition
- GPU Drivers: 590.48.01
The reproducer has only been runtime-tested with CARLA 0.9.16. The relevant fallback code also
appears to be present in the current master and ue5-dev branches, but I have not runtime-tested
those branches.
Describe the bug
When an OpenDRIVE dynamic traffic-light signal is placed on a non-junction road and its controller
is not referenced by a junction, CARLA creates a carla.TrafficLight actor and accepts
set_state(). The rendered bulb changes to the requested state, but the client-side actor snapshot
is not populated correctly:
traffic_light.get_state() always returns TrafficLightState.Red, including after setting Green
or Yellow and waiting for a newer world snapshot.
traffic_light.get_opendrive_id() returns an empty string instead of the OpenDRIVE signal ID.
get_green_time(), get_yellow_time(), and get_red_time() all return 0.0.
In a larger custom map, a vehicle overlapping the signal trigger observed the requested state via
vehicle.get_traffic_light_state(). This and the visible bulb change indicate that the server-side
traffic-light component is updated, while the traffic-light actor snapshot exposed to the Python
client remains at its default values.
Steps to reproduce
The attached reproducer contains one 80 m straight road, one driving lane, one dynamic signal with
OpenDRIVE ID 10, and one controller that controls signal 10. It intentionally contains no
junction elements.
-
Start a CARLA 0.9.16 server.
-
Extract traffic_light_outside_junction_get_state_reproducer.zip.
-
Run the following from the extracted directory:
python3 reproduce.py --host 127.0.0.1 --port 2000
The script loads outside_junction_traffic_light.xodr using
client.generate_opendrive_world(), finds the single traffic-light actor, calls set_state() with
Green, Yellow, and Red, and waits for a new world snapshot after every call.
Expected behavior
After setting a traffic-light state and receiving a newer world snapshot, get_state() should
return the state that was set. The actor should also expose the OpenDRIVE signal ID:
set_state(Green) -> get_state() == Green
set_state(Yellow) -> get_state() == Yellow
set_state(Red) -> get_state() == Red
get_opendrive_id() == "10"
Logs
Client output:
CARLA client version: 0.9.16
CARLA server version: 0.9.16
Traffic light actor id: 25
OpenDRIVE signal id reported by CARLA: ''
Snapshot timings: green=0.0, yellow=0.0, red=0.0, frozen=False
set_state(Green) -> get_state() == Red
set_state(Yellow) -> get_state() == Red
set_state(Red) -> get_state() == Red
RESULT: BUG REPRODUCED
Green and Yellow were requested, but the client snapshot remained Red.
Relevant server output from the packaged UE4 server:
4.26.2-0+++UE4+Release-4.26 522 0
Disabling core dumps.
WARNING: cannot parse georeference: ''. Using default values.
Loading '/workspace/CarlaUE4/Content/Carla/Maps/Nav/OpenDriveMap.obj'...
Tiles 2 x 1
Max Tiles 2
Max Polys 2097152
Building...
TileRow 0/1: 2 tiles added (total 2)
Total tiles added: (2 / 2 max)
Saving...
Saving 2 tiles
The georeference warning is expected for this intentionally minimal XODR and does not prevent the
world or traffic-light actor from being created. No server-side error related to the set_state()
calls was emitted.
Scripts
traffic_light_outside_junction_get_state_reproducer.zip
It contains:
outside_junction_traffic_light.xodr: minimal OpenDRIVE 1.4 map
reproduce.py: standalone Python reproducer
README.md: run instructions and the expected/observed results
Additional investigation / suspected cause
The issue appears to come from the fallback path used for traffic-light signals that do not resolve
to a junction controller.
The normal path in TrafficLightManager.cpp calls:
TrafficLightGroup->AddController(NewTrafficLightController);
The fallback path instead directly appends to the controller array:
TrafficLightGroup->GetControllers().Add(NewTrafficLightController);
ATrafficLightGroup::AddController() also calls Controller->SetGroup(this). The direct array
append skips that call. As a result, TrafficLightComponent->GetController() is non-null while
TrafficLightComponent->GetGroup() is null.
WorldObserver.cpp serializes the traffic-light sign_id, state, timings, and frozen flag only when
both the controller and group are available. Otherwise, the zero-initialized snapshot is sent. Since
the enum value of TrafficLightState.Red is zero, get_state() always returns Red.
The same direct array append is visible in:
A possible minimal fix appears to be:
- TrafficLightGroup->GetControllers().Add(NewTrafficLightController);
+ TrafficLightGroup->AddController(NewTrafficLightController);
I have not rebuilt CARLA to validate this source change.
This may be related to
Discussion #6957: CARLA Traffic Light Data Missing,
which describes missing traffic-light snapshot data when a light has no group or controller. This
report adds a two-file minimal reproducer, demonstrates the set_state() versus get_state()
mismatch, and identifies the fallback path that leaves the group unset.
Setup
The reproducer has only been runtime-tested with CARLA 0.9.16. The relevant fallback code also
appears to be present in the current
masterandue5-devbranches, but I have not runtime-testedthose branches.
Describe the bug
When an OpenDRIVE dynamic traffic-light signal is placed on a non-junction road and its controller
is not referenced by a junction, CARLA creates a
carla.TrafficLightactor and acceptsset_state(). The rendered bulb changes to the requested state, but the client-side actor snapshotis not populated correctly:
traffic_light.get_state()always returnsTrafficLightState.Red, including after setting Greenor Yellow and waiting for a newer world snapshot.
traffic_light.get_opendrive_id()returns an empty string instead of the OpenDRIVE signal ID.get_green_time(),get_yellow_time(), andget_red_time()all return0.0.In a larger custom map, a vehicle overlapping the signal trigger observed the requested state via
vehicle.get_traffic_light_state(). This and the visible bulb change indicate that the server-sidetraffic-light component is updated, while the traffic-light actor snapshot exposed to the Python
client remains at its default values.
Steps to reproduce
The attached reproducer contains one 80 m straight road, one driving lane, one dynamic signal with
OpenDRIVE ID
10, and one controller that controls signal10. It intentionally contains nojunction elements.
Start a CARLA 0.9.16 server.
Extract
traffic_light_outside_junction_get_state_reproducer.zip.Run the following from the extracted directory:
The script loads
outside_junction_traffic_light.xodrusingclient.generate_opendrive_world(), finds the single traffic-light actor, callsset_state()withGreen, Yellow, and Red, and waits for a new world snapshot after every call.
Expected behavior
After setting a traffic-light state and receiving a newer world snapshot,
get_state()shouldreturn the state that was set. The actor should also expose the OpenDRIVE signal ID:
Logs
Client output:
Relevant server output from the packaged UE4 server:
The georeference warning is expected for this intentionally minimal XODR and does not prevent the
world or traffic-light actor from being created. No server-side error related to the
set_state()calls was emitted.
Scripts
traffic_light_outside_junction_get_state_reproducer.zip
It contains:
outside_junction_traffic_light.xodr: minimal OpenDRIVE 1.4 mapreproduce.py: standalone Python reproducerREADME.md: run instructions and the expected/observed resultsAdditional investigation / suspected cause
The issue appears to come from the fallback path used for traffic-light signals that do not resolve
to a junction controller.
The normal path in
TrafficLightManager.cppcalls:TrafficLightGroup->AddController(NewTrafficLightController);The fallback path instead directly appends to the controller array:
TrafficLightGroup->GetControllers().Add(NewTrafficLightController);ATrafficLightGroup::AddController()also callsController->SetGroup(this). The direct arrayappend skips that call. As a result,
TrafficLightComponent->GetController()is non-null whileTrafficLightComponent->GetGroup()is null.WorldObserver.cppserializes the traffic-lightsign_id, state, timings, and frozen flag only whenboth the controller and group are available. Otherwise, the zero-initialized snapshot is sent. Since
the enum value of
TrafficLightState.Redis zero,get_state()always returns Red.The same direct array append is visible in:
masterTrafficLightManager.cppue5-devTrafficLightManager.cppA possible minimal fix appears to be:
I have not rebuilt CARLA to validate this source change.
This may be related to
Discussion #6957: CARLA Traffic Light Data Missing,
which describes missing traffic-light snapshot data when a light has no group or controller. This
report adds a two-file minimal reproducer, demonstrates the
set_state()versusget_state()mismatch, and identifies the fallback path that leaves the group unset.