4、交通灯基础:交通灯Actor、交通灯状态(红/黄/绿)、交通灯组(TrafficLightGroup)
好,咱们开始进入交通灯的世界。说实话,在自动驾驶仿真里,交通灯是个看似简单、实则坑不少的东西。我最早做仿真时,以为交通灯就是个会变颜色的模型,结果第一次跑场景就翻车了——车在绿灯前停着不动,红灯反而冲了过去。后来我才明白,Carla里的交通灯远不止一个模型那么简单。
4.1 交通灯Actor:它到底是个啥?
在Carla里,交通灯是一个独立的Actor。它不是贴图,也不是动画,而是一个实实在在的仿真实体。你可以像操作车辆一样,获取它的位置、旋转、甚至给它发指令。
我个人习惯把交通灯Actor理解成一个「带状态的传感器」。它不发光,它只是告诉仿真世界:我现在是啥状态。至于灯亮不亮,那是渲染引擎的事。
核心要点:交通灯Actor继承自carla.Actor,拥有完整的Actor接口。你可以用world.get_actors().filter('traffic.traffic_light')来获取所有交通灯。
获取交通灯的代码很简单:
import carla
client = carla.Client('localhost', 2000)
world = client.get_world()
# 获取所有交通灯
traffic_lights = world.get_actors().filter('traffic.traffic_light')
for tl in traffic_lights:
print(f"交通灯ID: {tl.id}, 位置: {tl.get_location()}")
嗯,这里要注意:交通灯Actor的ID是固定的,但它的位置可能和路口绑定。我在项目中遇到过一个问题——同一个路口有多个交通灯,它们的ID不同,但逻辑上属于一组。这就引出了后面的「交通灯组」概念。
4.2 交通灯状态:红、黄、绿
Carla的交通灯状态其实比你想的要多。除了红黄绿,还有「未知」和「关闭」状态。不过日常仿真中,我们主要关心这三个:
| 状态 | 枚举值 | 说明 |
|---|---|---|
| 红灯 | carla.TrafficLightState.Red |
禁止通行,车辆必须停止 |
| 黄灯 | carla.TrafficLightState.Yellow |
警告,即将变红,建议减速 |
| 绿灯 | carla.TrafficLightState.Green |
允许通行 |
| 关闭 | carla.TrafficLightState.Off |
交通灯不工作,通常用于夜间或故障 |
| 未知 | carla.TrafficLightState.Unknown |
状态未定义,一般不会出现 |
获取当前状态的方法:
# 获取某个交通灯的当前状态
tl = traffic_lights[0]
state = tl.get_state()
print(f"当前状态: {state}")
# 判断状态
if state == carla.TrafficLightState.Red:
print("红灯,停车!")
elif state == carla.TrafficLightState.Green:
print("绿灯,可以走")
避坑指南:我曾经以为黄灯就是「马上变红」,结果发现Carla的黄灯持续时间是可以配置的。默认是3秒,但如果你不设置,它可能只有1秒。所以写逻辑时,别硬编码黄灯时长。
你还可以手动设置交通灯状态。这在测试场景里特别有用:
# 强制设为红灯
tl.set_state(carla.TrafficLightState.Red)
# 强制设为绿灯
tl.set_state(carla.TrafficLightState.Green)
为什么会需要手动设置?举个例子:你想测试车辆在红灯时是否真的会停车。如果等自然变化,你可能要等30秒。直接设成红灯,立刻就能验证。
4.3 交通灯组:TrafficLightGroup
这是新手最容易忽略的概念。一个路口通常有多个交通灯——左转灯、直行灯、右转灯。它们不是独立的,而是属于同一个组。
说白了,交通灯组就是一组逻辑上同步的交通灯。组内的灯共享同一个时序。比如:左转灯变绿时,直行灯一定是红灯。
Carla里用TrafficLightGroup来管理。不过要注意,Carla没有直接暴露这个类的API,它是通过TrafficLight的get_group_traffic_lights()方法来获取同组灯。
# 获取某个交通灯所属组的所有灯
tl = traffic_lights[0]
group_lights = tl.get_group_traffic_lights()
print(f"这个组共有 {len(group_lights)} 个交通灯")
for light in group_lights:
print(f" 组内灯ID: {light.id}, 当前状态: {light.get_state()}")
关键理解:交通灯组不是物理上的「一组灯」,而是逻辑上的「一组灯」。同一个物理灯杆上的两个灯,可能属于不同组(比如左转和直行)。反过来,不同路口的灯也可能属于同一组(比如双向同时放行)。
我在项目中遇到过这样一个坑:我写了一个脚本,想控制某个路口的全部交通灯。我遍历了所有交通灯,逐个设置状态。结果发现,有些灯设置了没反应。为什么?因为它们属于同一个组,组内状态是同步的,你改一个,组内其他灯也会跟着变。你再去改另一个,反而会冲突。
警告:不要同时修改同一组内的多个交通灯状态。正确的做法是:找到组内的任意一个灯,修改它,然后整个组都会同步变化。我曾经因为同时修改两个灯,导致仿真卡死——两个灯互相抢控制权。
那么,如何判断两个交通灯是否属于同一组?很简单:
# 判断两个灯是否同组
tl1 = traffic_lights[0]
tl2 = traffic_lights[1]
group1 = tl1.get_group_traffic_lights()
group2 = tl2.get_group_traffic_lights()
# 如果tl2在tl1的组里,说明它们同组
if tl2 in group1:
print("这两个灯属于同一组")
else:
print("不同组")
你想想看,这个判断在实际项目中有什么用?比如你要实现一个「绿波带」控制——让一条路上的交通灯依次变绿。如果你不知道哪些灯同组,可能会把一组灯拆开控制,导致逻辑混乱。
4.4 实战小技巧
最后分享几个我常用的技巧:
- 获取交通灯对应的路口:用
tl.get_landmark_id()可以获取关联的路口ID,但要注意,不是所有交通灯都有路口关联。 - 设置绿灯时长:用
tl.set_green_time(seconds),默认是30秒。我一般设成10秒,测试时不用等太久。 - 重置交通灯:用
tl.reset_group()可以把整个组恢复到初始时序。这个在场景重置时特别有用。
# 一个完整的交通灯控制示例
def control_traffic_light(tl, state, duration=10):
"""控制交通灯并设置持续时间"""
tl.set_state(state)
if state == carla.TrafficLightState.Green:
tl.set_green_time(duration)
elif state == carla.TrafficLightState.Red:
tl.set_red_time(duration)
print(f"已将交通灯 {tl.id} 设为 {state},持续 {duration}秒")
# 使用示例
tl = world.get_actors().filter('traffic.traffic_light')[0]
control_traffic_light(tl, carla.TrafficLightState.Green, 15)
嗯,这一章的内容就这些。交通灯的基础概念其实不复杂,但细节很多。下一章我们会讲如何用Python脚本控制交通灯的时序变化,到时候你会看到,这些基础概念有多重要。