Webots City Night 世界深度解析:夜间自动驾驶仿真的光照构建与鲁棒性测试
Webots City Night 世界深度解析:夜间自动驾驶仿真的光照构建与鲁棒性测试
本文以 Webots 机器人模拟器自带的 City Night 示例世界(docs/automobile/city-night.md)为核心,逐层剖析其夜间场景的构建方式——从移除太阳光、暗色天空到路灯阵列与故障路灯控制器,再到车辆前大灯(SpotLight)与 autonomous_vehicle 自动驾驶控制器的配合。读完本文,你将掌握如何在 Webots 中搭建一套可复现的夜间自动驾驶测试环境,并理解灯光系统(LED、SpotLight、驾驶库灯光 API)在仿真中的实际实现原理。
一、世界定位:City 的夜间变体
City Night 世界在 Webots 的"自动驾驶(Automobile)"示例世界中与 City 世界 成对出现。按照官方文档的定义,它与 city 世界共享同一套城市布局——包括高层建筑群、交通信号灯、有信号灯控制的交叉路口,以及用于辅助车道保持的黄色中央车道线(道路中央线为黄色,便于基于相机视觉的控制算法识别车道)。
两者的本质区别在于光照条件:
- City 世界模拟白天的日照环境,场景中存在太阳光(DirectionalLight);
- City Night 世界移除了太阳光,将背景天空设置为接近全黑的暗色,并额外布置了一批路灯(StreetLight),其中一盏是"故障闪烁"的受控路灯(ControlledStreetLight);
- 同时,世界中唯一的自动驾驶车辆(BMW X5)启用了车头聚光灯(front SpotLights)。
官方对该世界的定位是:"This world is useful to test the robustness of driving control algorithms by night."——即专门用于检验驾驶控制算法在夜间低照度条件下的鲁棒性。世界文件本体位于 projects/vehicles/worlds/city_night.wbt,可直接在 Webots 中打开运行。
二、夜间氛围的构建:从天空到泛光
1. 暗色天空背景
打开 city_night.wbt,首先看到的是与世界其他部分截然不同的 Background 节点:
Background {
skyColor [
0.00784313725490196 0.00784313725490196 0.00784313725490196
]
}
skyColor 的三个分量都被设置为 0.0078 左右,也就是 RGB 各分量约为 2/255,天空几乎完全漆黑。这正是"夜间"最直接的视觉信号:没有太阳光(世界中不存在 DirectionalLight 节点),唯一的环境照明来自路灯、车辆灯光与建筑物上的广告牌背光。
2. 视点与泛光(Bloom)效果
Viewpoint 节点中除了设置相机初始位置(position -47.3 15.1 10.7)和朝向(orientation),还包含两个与夜间渲染直接相关的配置:
Viewpoint {
orientation 0.2138569286807612 -0.17298355770714735 -0.9614270137759858 4.710610973056149
position -47.31614945279596 15.092670844033632 10.712041538536933
near 1
follow "vehicle"
bloomThreshold -1
}
follow "vehicle"使视点跟随名为vehicle的自动驾驶车辆,方便直接观察车辆的行驶与灯光效果;bloomThreshold -1表示开启泛光(Bloom)后处理且无强度阈值,让高亮光源(路灯、车灯)在渲染时产生光晕,更贴近真实夜间灯光的主观观感。这与白天世界(通常无 bloom 或阈值更高)形成对比,是夜间场景视觉真实感的关键一环。
3. 地面材质的低光照适配
地面使用 PBRAppearance 贴图(webots://projects/vehicles/worlds/textures/ground.jpg),roughness 0.5、metalness 0,纹理被缩放 500 倍铺满 2000×2000 的平面。在夜间只有局部光源的情况下,粗糙度适中的地面能正确反射路灯与车灯光斑,避免过亮或全黑的极端表现。
三、路灯阵列:StreetLight PROTO 与 SpotLight 参数
1. 24 盏常规路灯的布置
世界中共放置了 24 盏 StreetLight(从 street light(1) 到 street light(24)),分布在道路交叉口、街道两侧与建筑周边,位置各异(如 -57.1336 56.913 0、91.1728 12.3071 0 等),并通过 rotation 调整灯头朝向。它们的模型定义在 projects/objects/traffic/protos/StreetLight.proto。
StreetLight.proto 的核心是一盏可定制的 SpotLight:
SpotLight {
attenuation 0 0 1
beamWidth IS beamWidth # 默认 1.1(弧度)
color IS color # 默认 1 1 1(白光)
cutOffAngle IS cutOffAngle # 默认 1.4(弧度)
direction IS direction # 默认 0.1 0 -1
location 3.1 0 7.2 # 灯头位置(相对路灯根部)
on IS on # 默认 TRUE
radius IS radius # 默认 1000
castShadows IS castShadows # 默认 FALSE
intensity IS intensity # 默认 30
}
结合 docs/reference/spotlight.md 中关于 SpotLight 节点的定义,可以这样理解这套参数的作用:
| 参数 | 默认值(路灯场景) | 语义 |
|---|---|---|
beamWidth |
1.1 | 内锥角,在此角度内以满强度发射光线 |
cutOffAngle |
1.4 | 外锥角,光束的最外边界(超出后不发光) |
intensity |
30 | 光强(文档通用定义为 [0,1],此处按 PROTO 自定义取值更大以在夜间可见) |
radius |
1000 | 光的最大照射半径 |
attenuation 0 0 1 |
— | 二次衰减系数为 1,光强随距离平方衰减 |
SpotLight 的强度衰减遵循公式 intensity(angle) = intensity * (angle - cutOffAngle) / (beamWidth - cutOffAngle):在 beamWidth 内全强度输出,在 beamWidth 到 cutOffAngle 之间线性衰减。路灯正是利用这种"近亮远暗、锥形照射"的特性,把光斑准确地投射到车道与人行道上。
2. 故障闪烁路灯:ControlledStreetLight 与 defective_street_light 控制器
官方文档特别提到的"defective blinking one"(一盏故障闪烁的路灯),在源码中由 projects/objects/traffic/protos/ControlledStreetLight.proto 实现。它在世界文件中位于坐标 -117.541 -60.0694:
ControlledStreetLight {
translation -117.541 -60.0694 0
rotation 0 0 -1 6.28318
}
该 PROTO 的巧妙之处在于:它本质是一个带 LED 的 Robot 节点,SpotLight 被嵌套在 LED 内:
Robot {
children [
LED {
children [
SpotLight {
beamWidth IS beamWidth
attenuation 0 0 1
color 0 0 0 # 灯本身的颜色由 LED 的 color 决定
cutOffAngle IS cutOffAngle
direction IS direction
location 3.1 0 7.2
radius IS radius
castShadows IS castShadows
intensity 30
}
]
color IS color # 默认 [ 1 0.9 0.8 ],暖黄光
gradual TRUE
}
StreetLight {
on FALSE # 静态灯体不带光,发光完全交给 LED 控制
}
]
controller "defective_street_light" # 默认控制器,驱动 LED 闪烁
}
关键设计点:
- LED 的
gradual TRUE:允许以 0–255 的强度渐变控制 LED,而不是简单的开关; color 0 0 0的 SpotLight:灯的发光颜色不再由 SpotLight 自身决定,而是由 LED 的color字段(暖黄光1 0.9 0.8)驱动,实现"LED 亮度即灯光亮度"的联动;- 默认控制器
defective_street_light:实现了模拟"灯管老化故障"的闪烁状态机。
defective_street_light 控制器的完整实现在 projects/objects/traffic/controllers/defective_street_light/defective_street_light.c,核心逻辑如下(TIME_STEP 为 50 ms):
while (wb_robot_step(TIME_STEP) != -1) {
if (step_to_perform <= 0) {
int led_state = wb_led_get(led);
if (led_state == 255) {
// 灯全亮:关闭 20 步(约 1 秒)
step_to_perform = 20;
wb_led_set(led, 0);
} else if (led_state == 170) {
// 灯处于 2/3 亮度:继续全亮 60 步(约 3 秒)
step_to_perform = 60;
wb_led_set(led, 255);
} else if (led_state == 85) {
// 灯处于 1/3 亮度:升到 2/3 亮度,持续 2 步(约 0.1 秒)
step_to_perform = 2;
wb_led_set(led, 170);
} else {
// 灯全灭:先以 1/3 亮度尝试启动,2 步(约 0.1 秒)
step_to_perform = 2;
wb_led_set(led, 85);
}
} else
step_to_perform--;
}
这段状态机模拟了典型的"坏路灯"行为:路灯并非稳定熄灭,而是反复尝试启动(1/3 亮度 → 2/3 亮度 → 全亮)后又突然熄灭,形成不规则的闪烁节奏。这种动态光照会给基于视觉的车道线识别带来间歇性的局部过曝/欠曝干扰,恰好可以用来检验控制算法对光照突变的容忍度。
四、车辆前大灯:frontSpotLights 字段的底层实现
1. 世界中的车辆配置
city_night.wbt 中只有一辆车——BmwX5,其关键配置如下:
BmwX5 {
translation -45 45.88 0.4
rotation 0 0 1 3.14159
controller "autonomous_vehicle"
frontSpotLights TRUE
sensorsSlotTop [
Camera {
translation 0.72 0 -0.05
rotation 0 -1 0 -0.15
fieldOfView 1
width 128
}
]
sensorsSlotCenter [
GPS {
}
Gyro {
}
]
}
controller "autonomous_vehicle"指向 projects/vehicles/controllers/autonomous_vehicle/autonomous_vehicle.c;frontSpotLights TRUE开启车头聚光灯——这是夜间世界与city世界车辆配置最显著的不同点;- 车顶搭载一台 128×128、视场角 1 弧度的前视相机(车道线检测用),车身中央挂载 GPS 与陀螺仪。
2. 大灯的 PROTO 级实现
frontSpotLights 是车辆 PROTO 的一个标准字段(默认 FALSE,注释为 "Defines whether the car should have front spot lights")。在 projects/vehicles/protos/bmw/BmwX5.proto 中,当该字段为 TRUE 时,模板代码会在 VehicleLights 的 front 槽位内实例化两盏对称的 SpotLight:
%< if (frontSpotLights) { >%
SpotLight {
attenuation 0 0 1
intensity 20
beamWidth 0.7
cutOffAngle 0.8
direction 1 -0.2 -1
location 3.95 0.65 0.45
radius 20
castShadows TRUE
}
SpotLight {
attenuation 0 0 1
intensity 20
beamWidth 0.7
cutOffAngle 0.8
direction 1 0.2 -1
location 3.95 -0.65 0.45
radius 20
castShadows TRUE
}
%< } >%
这两盏聚光灯的几何位置(location 3.95 ±0.65 0.45)对应车头两侧大灯位置,方向(direction 1 ∓0.2 -1)略微向外侧发散形成近光照射面。与路灯不同,车灯开启了 castShadows TRUE,能在夜间产生真实的投影。同一字段在 projects/vehicles/protos/toyota/ToyotaPrius.proto、projects/vehicles/protos/citroen/CitroenCZero.proto 等其它车辆 PROTO 中同样存在,说明这是 Webots 车辆系列的统一灯光能力。
值得注意:车辆 PROTO 中的这两盏聚光灯是由场景直接渲染的静态光;而 autonomous_vehicle 控制器运行时还会通过驾驶库 API 进一步开启近光灯、危险警示灯、雾灯等可编程车灯(见下节),两者共同构成完整的夜间车灯体系。
五、道路网络与交通设施
1. 道路结构
City Night 沿用了 City 的道路骨架,由 StraightRoadSegment、CurvedRoadSegment、RoadIntersection 与 Crossroad 四种道路 PROTO 拼合而成,形成多路口环路:
- 多段直线路段(
StraightRoadSegment),长度 30–69 米不等,宽 21.5 米,numberOfLanes 4、numberOfForwardLanes 2(双向 4 车道); - 弯曲路段(
CurvedRoadSegment)曲率半径 40.5 米; - 两个
RoadIntersection(信号灯控制的十字路口)与 12 个Crossroad(通过id和connectedRoadIDs组成道路网络拓扑)。
车道标线使用 RoadLine,其中中央分隔线是黄色双实线:
RoadLine {
color 0.85 0.75 0.3 # 黄色
type "double"
}
这条黄色中线的颜色(RGB 0.85, 0.75, 0.3)与 autonomous_vehicle 控制器中相机识别的参考色(BGR 95, 187, 203)完全对应——它正是车道保持算法的视觉锚点。在夜间,路灯与车灯照射下黄色标线的反光特性会成为算法的重要输入。
2. 交通信号与标志
世界中部署了完整的交通设施:
GenericTrafficLight:位于-34.665 55.2732,配置startGreen FALSE、greenTime 10、redTime 10、初始状态state "red",按 10 秒绿/红循环切换;CrossRoadsTrafficLight:位于交叉路口45 -45;- 交通标志:
StopSign(停车)、YieldSign(让行)、SpeedLimitSign(限速 55/65 mph)、OrderSign(禁右转、禁止行人通行)、CautionSign(左转、人行横道、颠簸、交叉口警告)等,均挂载在对应的StopPanel、OrderPanel、SpeedLimitPanel、CautionPanel面板上; PedestrianCrossing:两处人行横道斑马线;- 城市家具:
CommercialBuilding、UBuilding、HollowBuilding、Hotel、TheThreeTowers、CyberboticsTower、BigGlassTower、Auditorium、Museum、ResidentialBuilding、BuildingUnderConstruction、FastFoodRestaurant(subway 品牌)等建筑 PROTO,以及AdvertisingBoard(广告牌,displayBackLight TRUE开启背光,在夜间提供额外光源)、多棵Oak/Pine/Cypress/Sassafras树木。
AdvertisingBoard 与 FastFoodRestaurant 的背光/亮牌细节表明,夜间世界不仅"删除了太阳",还特意加入了人工光源来模拟真实的城市夜间光污染环境——这对基于相机的算法而言是一种额外的干扰源。
3. 物理仿真参数
WorldInfo 节点中:
WorldInfo {
info [ ... ]
title "Autonomous Vehicle"
ERP 0.6
basicTimeStep 10
lineScale 1
contactProperties [
ContactProperties {
coulombFriction [ 8 ]
softCFM 1e-05
bumpSound ""
rollSound ""
slideSound ""
}
...
]
}
basicTimeStep 10(ms)为物理与传感器采样基准步长;ERP 0.6控制接触约束的误差修正比例(取值 0.1–0.8 为推荐区间);ContactProperties定义了路面摩擦(coulombFriction 8)与柔软度(softCFM 1e-05),并针对各车型轮胎材质(CitroenCZeroWheels、ToyotaPriusWheels、LincolnMKZWheels、RangeRoverSportSVRWheels、TruckWheels)建立了不同的接触属性——这些参数与 projects/vehicles/protos/bmw/BmwX5.proto 中time0To100 7、engineFunctionCoefficients、brakeCoefficient 1800等动力学参数共同决定了车辆在夜间路面上的真实操控特性。
六、autonomous_vehicle 控制器:夜间行车的关键实践
1. 驾驶库灯光与巡航控制
autonomous_vehicle 控制器使用 Webots 驾驶库(<webots/vehicle/driver.h>,参见 docs/automobile/driver-library.md)与标准设备 API 协同工作。初始化后,控制器立即为夜间行驶做好了灯光准备:
wbu_driver_set_hazard_flashers(true); // 危险警示灯
wbu_driver_set_dipped_beams(true); // 近光灯
wbu_driver_set_antifog_lights(true); // 雾灯
wbu_driver_set_wiper_mode(SLOW); // 慢速雨刮
wbu_driver_set_dipped_beams(true) 会通过驾驶库点亮车辆的近光灯(与 PROTO 中 frontSpotLights TRUE 渲染的聚光灯相互配合);同时控制器调用 wbu_driver_set_cruising_speed(50.0) 将巡航速度设为 50 km/h,并通过 wbu_driver_set_steering_angle 控制转向。
2. 车道保持:基于黄色中线的 PID 控制
控制器以 50 ms(TIME_STEP)为周期读取相机图像,识别黄色车道线:
const unsigned char REF[3] = {95, 187, 203}; // 道路黄线(BGR 格式)
...
if (color_diff(pixel, REF) < 30) { // 颜色差小于 30 视为黄线像素
sumx += x % camera_width;
pixel_count++;
}
// 返回黄线相对于画面中心的角度
return ((double)sumx / pixel_count / camera_width - 0.5) * camera_fov;
随后通过一个简单的 PID 控制器(KP 0.25、KI 0.006、KD 2)把黄线角度转换为转向角,并带积分抗饱和(当误差符号翻转时清空积分项、积分限幅 ±30)与转向速率限制(每步变化不超过 0.1 rad)。此外,filter_angle 对连续 3 帧的黄线角度取平均,抑制夜间光照抖动带来的单帧噪声。
3. 避障:SICK 激光雷达
控制器会自动探测名为 Sick LMS 291 的设备并启用避障模式:在激光雷达中央 40° 区域内检测 20 米以内的障碍物,计算其方位角与距离;当障碍物与车道保持的转向指令方向一致时,取更急的转向角优先避障,同时把制动强度清零加速通过;若同时丢失黄线与障碍物信息,则施加 0.4 的制动力减速,等待重新找到车道线。
4. GPS 与仪表盘
若世界中挂载了 GPS 与 Display 设备,控制器还会实时读取经纬度坐标与车速(wb_gps_get_speed 换算为 km/h),并在仪表盘上绘制速度表指针与 GPS 文本——夜间测试时可以直观监控车辆状态。
七、运行与实验建议
1. 如何运行
- 打开 Webots,通过菜单 File → Open World 选择 projects/vehicles/worlds/city_night.wbt(也可直接用
webots projects/vehicles/worlds/city_night.wbt启动); - 世界启动后,
BmwX5会自动运行autonomous_vehicle控制器进入自动驾驶状态,以 50 km/h 沿黄色中线行驶并躲避障碍; - 点击 3D 窗口后,可用方向键接管车辆:
↑/↓加速/减速,←/→转向,按A键回到自动驾驶。
2. 可验证的实验场景
- 对比实验:在 City 世界(白天)与 City Night 世界(夜间)中运行同一控制器,观察黄线检测的像素数量与 PID 转向指令的抖动幅度差异,即可量化夜间低照度对视觉车道保持的影响;
- 鲁棒性测试:观察
defective_street_light闪烁路灯(位于-117.541 -60.0694)扫过路面时,控制器是否出现转向抖动或短暂丢线——这正是"检测夜间鲁棒性"的典型场景; - 灯光调试:将
BmwX5的frontSpotLights改为FALSE、或删除某盏StreetLight,对比不同灯光配置下相机图像中黄色标线的可识别性; - 参数研究:调整
StreetLight的beamWidth/cutOffAngle/intensity,或修改defective_street_light.c中的状态机步数(如把"关闭 20 步"改为更长/更短),观察灯光动态变化对视觉算法的影响。
3. 扩展自制夜间世界
若要打造自己的夜间测试场景,可复用本文拆解的三大组件:
- 环境光:
Background.skyColor设置为低值(如0.01 0.01 0.01)并移除DirectionalLight,Viewpoint.bloomThreshold设为负值开启泛光; - 静态光源:放置 StreetLight.proto,按需调整 SpotLight 的
beamWidth(如 1.1)、cutOffAngle(如 1.4)、radius(如 1000)与intensity; - 动态光源:使用 ControlledStreetLight.proto(默认带
defective_street_light控制器)模拟闪烁故障灯,或仿照其结构把任意SpotLight嵌入LED节点实现可编程亮度控制。
八、总结
City Night 是 Webots 自动驾驶示例世界中一个小而精的"夜间专项"场景:它通过移除太阳光 + 暗色天空 + 泛光渲染建立夜间基调,用24 盏常规路灯 + 1 盏故障闪烁灯构建城市照明网络,用车辆 PROTO 的 frontSpotLights 字段点亮车头聚光灯,再交由 autonomous_vehicle 控制器(近光灯/雾灯/危险警示灯 + PID 车道保持 + 激光雷达避障)在低照度、动态光照干扰下完成巡航。无论是用于算法鲁棒性验证、视觉感知研究,还是作为自制夜间世界的搭建范本,它的每一层实现(world 文件、路灯 PROTO、故障灯控制器、车辆大灯模板、自动驾驶控制器)都值得开发者直接对照阅读与二次开发。