4、多车协同仿真基础:多车生成与管理、车辆控制接口、协同感知基础

好,咱们进入第四章。说实话,多车协同仿真这块,是我个人觉得整个课程里最有意思的部分。你想想看,单辆车跑得再溜,那也只是个“孤勇者”。真正的自动驾驶落地,必须面对车与车之间的交互。这一章,咱们就聊聊怎么在 Carla 里把多辆车“变”出来,怎么管好它们,以及怎么让它们“看见”彼此。

4.1 多车生成与管理:别让车海战术搞崩你的仿真

我第一次做多车仿真时,上来就 spawn 了 50 辆车。结果呢?Carla 直接卡死,CPU 飙到 100%。嗯,这里要注意,多车生成不是简单的 for 循环堆叠,你得讲究策略。

4.1.1 批量生成的艺术

我个人习惯用蓝图库(Blueprint Library)配合随机点生成。核心思路是:先规划好生成位置,再批量创建车辆。别边生成边找位置,那样效率低。

import carla
import random

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

# 选一批车辆蓝图,我一般混着来,轿车、SUV、卡车都来点
vehicle_blueprints = blueprint_library.filter('vehicle.*')
spawn_points = world.get_map().get_spawn_points()

# 生成 20 辆车
vehicles_list = []
for i in range(20):
    bp = random.choice(vehicle_blueprints)
    # 避坑指南:我曾经没加这个判断,结果 spawn 到不可用点,直接报错
    if spawn_points:
        spawn_point = random.choice(spawn_points)
        vehicle = world.spawn_actor(bp, spawn_point)
        vehicles_list.append(vehicle)
        spawn_points.remove(spawn_point)  # 避免重复占用

print(f'成功生成 {len(vehicles_list)} 辆车')

这里有个小技巧:spawn_points.remove(spawn_point) 这行很重要。如果不移除已用的点,两辆车可能生成在同一位置,直接“叠罗汉”。

4.1.2 车辆生命周期管理

车多了,管理就成问题。我建议用列表或字典维护所有车辆的引用。比如,你想让某辆车“退役”,直接 vehicle.destroy() 就行。但要注意,销毁前最好让车先停下来,否则仿真里会出现“幽灵车”卡在原地。

我的习惯: 用一个全局字典 vehicle_dict = {} 来管理,key 是车辆 ID,value 是 actor 对象。这样查找、删除都方便。

4.2 车辆控制接口:让车动起来,而且动得聪明

生成车只是第一步。怎么控制它们?Carla 提供了两种控制方式:自动驾驶模式手动控制模式。说白了,一个省心,一个灵活。

4.2.1 自动驾驶模式:开箱即用

如果你只是想看车流跑起来,用 set_autopilot(True) 最省事。Carla 内置的交通管理器(Traffic Manager)会帮你处理跟车、变道、红绿灯。

for vehicle in vehicles_list:
    vehicle.set_autopilot(True)  # 一键开启自动驾驶

但说实话,这个模式有点“傻”。我记得有一次仿真,所有车都规规矩矩地排队,完全不像真实路况。所以,如果你需要更复杂的交互,得用手动控制。

4.2.2 手动控制:精细到油门和方向盘

手动控制的核心是 carla.VehicleControl 这个类。你可以控制油门(0-1)、刹车(0-1)、转向(-1到1)等。

# 创建一个控制指令
control = carla.VehicleControl()
control.throttle = 0.5   # 50% 油门
control.steer = -0.3     # 左转 30%
control.brake = 0.0      # 不刹车

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

你想想看,如果每帧都手动计算这些值,那得多累?所以实际项目中,我们会封装一个控制类。比如,写一个 follow_vehicle() 函数,让后车自动跟随前车。

关键点: 手动控制模式下,你需要自己处理 PID 控制器或模型预测控制(MPC)。别怕,后面章节我会给代码模板。

4.3 协同感知基础:让车与车“对话”

协同感知,说白了就是让车之间共享信息。比如,前车看到了一个行人,它能不能告诉后车?这在 Carla 里是可以实现的。

4.3.1 传感器数据共享

每辆车都可以挂载传感器,比如摄像头、激光雷达。但默认情况下,这些数据只属于那辆车。要实现共享,你得手动“搬运”数据。

我常用的方法是:在仿真循环里,读取前车的传感器数据,然后通过 Python 变量传递给后车。举个例子:

# 假设 vehicle_1 有摄像头,vehicle_2 需要它的图像
sensor_data = None

def on_image(data):
    global sensor_data
    sensor_data = data  # 保存到全局变量

# 为 vehicle_1 添加摄像头传感器
camera_bp = blueprint_library.find('sensor.camera.rgb')
camera = world.spawn_actor(camera_bp, carla.Transform(), attach_to=vehicle_1)
camera.listen(on_image)

# 仿真循环中,vehicle_2 可以直接读取 sensor_data
while True:
    if sensor_data is not None:
        # vehicle_2 使用 sensor_data 做决策
        pass
注意: 这种全局变量的方式只适合教学演示。真实项目中,你会用消息队列或共享内存。否则,数据量大时,Python 的 GIL 会卡死你。

4.3.2 协同感知的典型场景

我遇到过最典型的场景是“前车遮挡”。一辆大卡车挡住了后面轿车的视线,轿车看不到前方的红绿灯。这时候,如果卡车能把自己的摄像头数据共享给轿车,轿车就能提前减速。

实现思路很简单:

  • 卡车检测到红绿灯状态
  • 通过某种通信机制(比如 UDP 或共享变量)发送给轿车
  • 轿车根据接收到的信息调整速度

说白了,这就是 V2V(车对车)通信的雏形。在 Carla 里,你不需要真的搭建网络,直接用 Python 变量传递就行。

4.3.3 数据融合的坑

嗯,这里要泼点冷水。协同感知听起来很美,但实际做起来有很多坑。比如:

  • 时间同步问题: 前车的数据是 0.1 秒前的,后车拿到时已经过时了。我建议给每个数据包打时间戳。
  • 坐标系转换: 前车的“前方 10 米”和后车的“前方 10 米”不是同一个方向。你需要做坐标变换。
  • 数据量爆炸: 如果每辆车都广播自己的激光雷达点云,网络带宽瞬间爆掉。实际中只传目标级别的信息(比如“前方 50 米有行人”)。
我的建议: 刚开始做协同感知时,先从“目标级共享”开始。别一上来就传原始点云。先传“障碍物列表”,等系统稳定了再升级。

小结

这一章咱们聊了多车生成、控制接口和协同感知。说白了,多车仿真的核心就是“生成-控制-通信”这三个环节。下一章,我们会深入具体的协同算法,比如编队行驶和交叉口协商。到时候,我会拿一个实际项目案例来拆解。

记住:别贪多,先把 10 辆车跑顺了,再挑战 50 辆。我曾经一口气生成 100 辆,结果仿真器直接崩了,教训啊。