Webots City Night 世界深度解析:夜间自动驾驶仿真的光照构建与鲁棒性测试

原创2026-10-03 13:35:441,421 阅读
文章标签:科研自动驾驶物理引擎

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 中打开运行。

Webots City Night 世界运行画面

二、夜间氛围的构建:从天空到泛光

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. 如何运行

  1. 打开 Webots,通过菜单 File → Open World 选择 projects/vehicles/worlds/city_night.wbt(也可直接用 webots projects/vehicles/worlds/city_night.wbt 启动);
  2. 世界启动后,BmwX5 会自动运行 autonomous_vehicle 控制器进入自动驾驶状态,以 50 km/h 沿黄色中线行驶并躲避障碍;
  3. 点击 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. 扩展自制夜间世界

若要打造自己的夜间测试场景,可复用本文拆解的三大组件:

  1. 环境光:Background.skyColor 设置为低值(如 0.01 0.01 0.01)并移除 DirectionalLight,Viewpoint.bloomThreshold 设为负值开启泛光;
  2. 静态光源:放置 StreetLight.proto,按需调整 SpotLight 的 beamWidth(如 1.1)、cutOffAngle(如 1.4)、radius(如 1000)与 intensity;
  3. 动态光源:使用 ControlledStreetLight.proto(默认带 defective_street_light 控制器)模拟闪烁故障灯,或仿照其结构把任意 SpotLight 嵌入 LED 节点实现可编程亮度控制。

八、总结

City Night 是 Webots 自动驾驶示例世界中一个小而精的"夜间专项"场景:它通过移除太阳光 + 暗色天空 + 泛光渲染建立夜间基调,用24 盏常规路灯 + 1 盏故障闪烁灯构建城市照明网络,用车辆 PROTO 的 frontSpotLights 字段点亮车头聚光灯,再交由 autonomous_vehicle 控制器(近光灯/雾灯/危险警示灯 + PID 车道保持 + 激光雷达避障)在低照度、动态光照干扰下完成巡航。无论是用于算法鲁棒性验证、视觉感知研究,还是作为自制夜间世界的搭建范本,它的每一层实现(world 文件、路灯 PROTO、故障灯控制器、车辆大灯模板、自动驾驶控制器)都值得开发者直接对照阅读与二次开发。

登录后查看全文
webots