4、场景构建基础:静态场景搭建、动态场景搭建、场景参数化、场景随机化

好,咱们进入正题。场景构建,说白了就是给自动驾驶的车子搭一个「考场」。这个考场要能模拟真实世界的各种情况。我做了这么多年仿真,最深的体会就是:场景建得好不好,直接决定了你的算法是「真牛」还是「假牛」。

今天咱们就聊聊场景构建的四个核心维度。嗯,这也是我平时工作中最常用的套路。

4.1 静态场景搭建:打好地基

静态场景,就是那些不会动的东西。比如道路、建筑、交通标志、路灯、树木。这些是场景的「骨架」。

核心思路: 用 CARLA 的 Python API 把地图元素加载出来,然后固定住。

我个人习惯先把地图加载好,再往上面「贴」道具。举个例子:

import carla

# 连接客户端
client = carla.Client('localhost', 2000)
world = client.get_world()

# 获取蓝图库
blueprint_library = world.get_blueprint_library()

# 放置一个路灯(静态物体)
light_bp = blueprint_library.find('static.prop.streetlight')
light_transform = carla.Transform(carla.Location(x=100, y=50, z=0.5))
world.spawn_actor(light_bp, light_transform)

这里要注意一点:静态物体一旦放置,就不会自己动。但你可以通过 set_transform() 来调整它的位置。我在项目中遇到过一个问题——路灯放歪了,结果激光雷达点云一直报错。排查了半天才发现是路灯杆的位置偏了 5 厘米。

避坑指南: 我曾经在放置大量静态物体时,忘记设置 set_simulate_physics(False)。结果车子一撞,路灯直接飞了。静态场景一定要关闭物理模拟,否则会出幺蛾子。

4.2 动态场景搭建:让世界活起来

静态场景只是背景。真正考验算法的,是那些会动的东西——车辆、行人、自行车。动态场景的核心是「行为控制」。

CARLA 提供了两种方式:

  • Autopilot 模式: 让车辆自己跑,适合做随机交通流。
  • 手动控制模式: 你写代码控制每一帧的速度和转向,适合做精确的切入、切出场景。

我建议新手先用 Autopilot。代码很简单:

# 生成一辆车并开启自动驾驶
vehicle_bp = blueprint_library.filter('vehicle.*')[0]
spawn_point = random.choice(world.get_map().get_spawn_points())
vehicle = world.spawn_actor(vehicle_bp, spawn_point)
vehicle.set_autopilot(True)

但 Autopilot 有个问题——它太「规矩」了。真实世界里,有人会突然变道、有人会急刹车。这时候就需要手动控制。说白了,就是你在每一帧里告诉车子:「往左打 10 度,油门踩 30%」。

举个例子,让一辆车在 3 秒内完成变道:

import time

start_time = time.time()
while time.time() - start_time < 3.0:
    # 计算横向偏移量
    lateral_offset = (time.time() - start_time) / 3.0 * 3.5  # 3.5米变道距离
    control = carla.VehicleControl()
    control.steer = lateral_offset * 0.1  # 简单映射
    control.throttle = 0.5
    vehicle.apply_control(control)
    world.tick()

嗯,这里要注意:手动控制时一定要用 world.tick() 推进仿真。不然你的控制指令发不出去。

4.3 场景参数化:让场景可配置

你想想看,如果每次改场景都要改代码,那得多累?参数化就是把这些「可变的东西」抽出来,变成配置文件。

我一般用 YAML 文件来管理参数。比如:

# scene_config.yaml
static_objects:
  - type: streetlight
    count: 10
    distribution: uniform
  - type: traffic_sign
    count: 5
    types: [stop, yield, speed_limit]

dynamic_objects:
  - type: vehicle
    count: 20
    speed_range: [5, 15]  # m/s
    behavior: random
  - type: pedestrian
    count: 10
    speed_range: [1, 2]
    jaywalking_probability: 0.3

然后在 Python 里读取:

import yaml

with open('scene_config.yaml', 'r') as f:
    config = yaml.safe_load(f)

# 根据配置生成场景
for obj in config['static_objects']:
    for _ in range(obj['count']):
        # 生成物体...
        pass

这样做的好处是:测试人员不需要懂代码,改个 YAML 文件就能换场景。我在项目里就是这么干的,测试团队反馈特别好。

小技巧: 参数化时,把「速度范围」「数量」「行为模式」都做成可配置的。这样同一个场景模板,可以衍生出几十种变体。

4.4 场景随机化:对抗过拟合

为什么要随机化?说白了,就是防止你的算法「背题」。如果每次测试都是同一个场景,算法很容易过拟合。真实世界里,没有两个完全一样的场景。

随机化可以从这几个维度入手:

  • 位置随机: 车辆、行人的初始位置随机分布。
  • 速度随机: 每辆车的速度在一定范围内随机。
  • 行为随机: 有的车直行,有的车变道,有的车刹车。
  • 天气随机: 晴天、雨天、雾天、夜晚,随机切换。

代码实现其实不复杂:

import random

# 随机生成车辆位置
spawn_points = world.get_map().get_spawn_points()
random.shuffle(spawn_points)

# 随机生成速度
for vehicle in vehicles:
    target_speed = random.uniform(5.0, 15.0)  # 5-15 m/s
    # 设置车辆的目标速度...
    pass

# 随机切换天气
weather_presets = [
    carla.WeatherParameters.ClearNoon,
    carla.WeatherParameters.RainNoon,
    carla.WeatherParameters.FoggyNoon,
    carla.WeatherParameters.Night,
]
world.set_weather(random.choice(weather_presets))

这里有个坑:随机化不是「瞎随机」。我曾经犯过一个错误——把车辆随机生成在路肩上,结果车子直接卡住了。后来我加了一个校验:生成位置必须在可行驶区域内。

核心原则: 随机化要保证「合理」。车辆必须在车道内,行人必须在人行道上,速度不能超过物理极限。否则你测试出来的结果没有意义。

4.5 实战组合拳:把四个维度串起来

好了,咱们把静态、动态、参数化、随机化串起来,做一个完整的场景生成流程:

  1. 加载地图: 选择 Town03 或 Town05。
  2. 放置静态物体: 根据 YAML 配置,放置路灯、标志牌。
  3. 生成动态物体: 随机生成 20 辆车、10 个行人。
  4. 设置行为: 80% 车辆用 Autopilot,20% 车辆手动控制做切入。
  5. 随机化: 随机切换天气,随机调整车辆速度。
  6. 开始测试: 让被测车辆进入场景,记录数据。

这个流程我用了好几年,效果很稳定。你想想看,一个场景模板就能生成几百种不同的测试用例,效率提升不是一点半点。

最后说一句:场景构建没有「完美」一说。关键是找到你算法最薄弱的环节,然后针对性地构建场景。别想着一次覆盖所有情况,那不现实。