3. 车辆与行人控制:让仿真世界动起来

好,我们继续往下走。上一章我们把地图和传感器搭好了,但一个空荡荡的仿真环境,说实话跟个鬼城似的。这一章,咱们就来给它注入灵魂——让车跑起来,让人走起来。

我个人习惯把车辆和行人控制分成两大块:生成控制。生成是让对象出现在场景里,控制是让它们按照我们的意图动。这两步缺一不可。

3.1 生成车辆:从蓝图到实体

在Carla里,每一辆车都对应一个蓝图(Blueprint)。你可以把蓝图理解成汽车的「设计图纸」。我刚开始接触时,总以为直接给个车型名字就行,结果发现得先查蓝图库。

来看看怎么生成一辆车:

import carla

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

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

# 挑选一辆车,比如奥迪A2
vehicle_bp = blueprint_library.find('vehicle.audi.a2')

# 找个生成点
spawn_points = world.get_map().get_spawn_points()
spawn_point = spawn_points[0]

# 生成车辆
vehicle = world.spawn_actor(vehicle_bp, spawn_point)

嗯,这里要注意:spawn_points 是地图预定义的停车位或起始位置。如果你随便给个坐标,车可能会卡在地底下。我曾经犯过这个错,调试了半天才发现是坐标没落在路面上。

小技巧: 如果你想要随机车型,可以用 random.choice(blueprint_library.filter('vehicle.*'))。但记得过滤掉自行车和摩托车,它们的行为逻辑跟汽车不太一样。

3.2 控制车辆运动:让车听你的话

车生成了,但它不会自己动。你需要给它指令。Carla提供了两种控制方式:手动控制自动驾驶

3.2.1 手动控制:油门、刹车、方向盘

说白了,就是模拟真实驾驶操作。通过 carla.VehicleControl 这个类,你可以设置油门、刹车、转向等参数。

# 创建一个控制指令
control = carla.VehicleControl()
control.throttle = 0.5   # 油门踩一半
control.steer = 0.3      # 方向盘右转30%
control.brake = 0.0      # 不刹车

# 应用到车辆
vehicle.apply_control(control)

你想想看,如果只给一次指令,车只会动一瞬间。所以通常我们会把它放在一个循环里,每帧更新一次控制参数。我在项目中遇到过一个问题:油门给太大,车直接漂移撞墙了。后来我加了个速度限制,超过30km/h就自动收油。

3.2.2 自动驾驶模式:交给Carla自己开

如果你不想手写控制逻辑,Carla内置了自动驾驶。用起来很简单:

vehicle.set_autopilot(True)

就这么一行。它会自动遵守红绿灯、避让行人、沿着车道走。但说实话,这个自动驾驶比较「死板」,有时候会在路口犹豫不决。我一般只在测试传感器时用它,真要验证算法,还是得自己写控制。

核心区别: 手动控制适合做极端场景测试(比如急刹车、蛇形走位),自动驾驶适合做常规交通流模拟。

3.3 生成行人:让街道有人气

行人的生成逻辑跟车辆类似,但有个关键区别:行人没有预定义的生成点。你得自己指定坐标。

# 获取行人蓝图
walker_bp = blueprint_library.find('walker.pedestrian.0001')

# 指定生成位置(注意:要在人行道上)
walker_transform = carla.Transform(
    carla.Location(x=100, y=50, z=0.5),
    carla.Rotation(yaw=0)
)

# 生成行人
walker = world.spawn_actor(walker_bp, walker_transform)

为什么 z=0.5?因为行人的原点在脚底,如果不抬高一点,它会陷进地面。我记得第一次生成行人时,只看到一个脑袋在地面上飘,吓我一跳。

注意: 行人蓝图的名字可能因版本而异。建议先用 blueprint_library.filter('walker.*') 打印出所有行人蓝图,再挑选。

3.4 控制行人行走:让走路更自然

行人控制比车辆复杂一些。车辆只需要油门刹车转向,但行人需要控制走路方向、速度,甚至动画状态。

3.4.1 使用WalkerControl

Carla提供了 carla.WalkerControl 类,可以控制行人的移动方向和速度。

# 创建行人控制指令
walker_control = carla.WalkerControl()
walker_control.direction = carla.Vector3D(x=1, y=0, z=0)  # 朝X轴正方向走
walker_control.speed = 1.5  # 速度1.5m/s,约等于正常步行速度

# 应用控制
walker.apply_control(walker_control)

这里有个坑:direction 必须是单位向量,否则速度会异常。我曾经传了个长度为2的向量,结果行人跑得跟博尔特一样快。

3.4.2 让行人自动行走

跟车辆一样,行人也可以开启自动模式。但行人的自动模式需要额外启动一个「行人控制器」:

# 开启行人自动驾驶
walker.set_autopilot(True)

不过说实话,Carla自带的行人自动驾驶效果一般。它们只会沿着人行道随机走,不会过马路,也不会避让车辆。如果你需要更真实的行人行为,我建议自己写路径规划。

我的经验: 在测试自动驾驶算法时,我会生成20-30个行人,让它们随机走动。这样既能模拟真实街景,又不会太消耗性能。行人数量超过50个时,仿真帧率会明显下降。

3.5 批量生成:让场景更丰富

单个车辆或行人太孤单了。实际项目中,我们往往需要生成一个车队或一群人。来看看怎么批量操作:

import random

# 批量生成车辆
vehicle_list = []
for i in range(10):
    bp = random.choice(blueprint_library.filter('vehicle.*'))
    spawn_point = random.choice(spawn_points)
    v = world.spawn_actor(bp, spawn_point)
    v.set_autopilot(True)
    vehicle_list.append(v)

# 批量生成行人
walker_list = []
for i in range(20):
    bp = random.choice(blueprint_library.filter('walker.*'))
    location = carla.Location(
        x=random.uniform(50, 150),
        y=random.uniform(30, 80),
        z=0.5
    )
    transform = carla.Transform(location)
    w = world.spawn_actor(bp, transform)
    w.set_autopilot(True)
    walker_list.append(w)

你想想看,如果每次生成都手动指定位置,那得累死。用随机生成配合自动驾驶,几行代码就能造出一个热闹的街道。

避坑指南: 批量生成时,一定要检查 spawn_actor 的返回值。如果返回 None,说明生成失败(通常是位置冲突)。我曾经没做检查,结果列表里一堆空对象,后面调用控制方法时直接报错。

3.6 清理场景:别让仿真环境变垃圾场

仿真结束后,记得清理生成的车辆和行人。否则下次启动时,你会发现满大街都是上一轮留下的「幽灵车」。

# 销毁所有车辆
for v in vehicle_list:
    v.destroy()

# 销毁所有行人
for w in walker_list:
    w.destroy()

嗯,这个习惯一定要养成。我在早期开发时,经常忘记清理,结果仿真越来越卡,最后客户端直接崩溃。后来我写了个 cleanup() 函数,每次仿真结束自动调用。

3.7 本章小结

这一章我们覆盖了车辆和行人的生成与控制。说白了,核心就三件事:选蓝图、定位置、发指令。手动控制给你精细度,自动驾驶给你便利性。根据场景需求,灵活切换就好。

下一章,我们会把传感器数据和控制逻辑结合起来,做一个完整的感知-决策-控制闭环。到时候你会发现,前面这些基础操作,都是为那个闭环服务的。