简介:毕业设计聚焦多服务器环境下CARLA仿真系统的搭建与调度,适合自动驾驶、智能网联方向的学生及需要完成期末大作业或毕设项目的开发者。资源共251个文件,涵盖Python核心脚本、txt说明文档、地图与路网相关的sumocfg/xodr配置文件、XML/YAML场景配置、可视化结果图及README笔记等,可用于理解多机协同仿真、传感器配置与数据交互流程。压缩包约4.66MB,结构清晰,方便对照源码进行二次开发或快速复现实验。已有196人浏览学习,适合正在规划毕设技术路线、希望快速获取可运行工程模板的学习者。
1. 多服务器CARLA仿真到底在解决谁的什么问题
做自动驾驶方向的毕业设计,最容易卡住人的不是模型而是数据。单机跑CARLA,在Town03放8辆车、挂上相机和激光雷达,帧率直接掉到十几,传感器回调的数据乱成一团——很多时间不是花在调算法,而是花在“等仿真跑完”。多服务器CARLA仿真的思路很直接:一台机器扛不住,就把渲染和逻辑负载拆到多台服务器上,让每个仿真服务器各自管一个场景,客户端按需取数。这篇文章会按毕业设计能落地的路线,把架构选型、安装启动、调度脚本和避坑经验一次讲清。适合用CARLA做数据集采集、多场景并行验证或算法对比的同学,新手能照着搭,熟手可以直接抄参数。
2. 先搞懂CARLA的Server-Client边界:多服务器方案的前提是单机能跑顺
2.1 CARLA的进程模型:为什么它天生适合多服务器
CARLA不是一个单机游戏,它是严格的服务器-客户端架构。你运行CarlaUE4.sh之后,实际已经启动了一个仿真服务器进程,它负责虚幻引擎的渲染、物理计算、车辆动力学和地图管理,对外暴露一个TCP端口,默认是2000。Python端通过carla.Client连接这个端口,之后的加载地图、生成车辆、配置传感器、读取交通信号,全是走RPC调用发到服务器端执行。
这个架构有一个关键收益:一个服务器可以同时被多个客户端连接。Traffic Manager是一个独立客户端,它专门负责控制交通流;传感器数据回调又是另一批客户端。也正因此,多服务器方案不需要改CARLA源码,只需要把它当多实例来调度——多开几个端口、多跑几份服务器进程,就得到了互相隔离的仿真世界。这也是为什么市面上的多机仿真项目大多以“多实例”为底座,而不是去改内核。
理解了这个边界,再看“多服务器”就知道实际操作对象是什么了:你管理的不是一台物理机里的抽象资源,而是多个监听在不同端口上的CarlaServer进程。任何能在单端口上做的事情——加载地图、设置天气、生成车辆、配置传感器——在多个端口上可以原样重复。唯一要额外处理的是“这些服务器听谁的”,这就是后面调度层要做的事。
2.2 两种多服务器拓扑:多实例并行与多机分布式
常见做法有两种,毕设选型时先看清区别。第一种是单机多实例:一台工作站配一张或两张显卡,通过-carla-port参数拉起多个Server进程,每个进程监听不同端口,Python端分别连接。这种方案的好处是代码改动最小、部署成本几乎为零,适合把数据采集的并行度翻倍;坏处是吃显存和CPU,一个实例在Low画质下也要吃掉4到6GB显存,两张卡跑四个实例基本是极限。
第二种是多机分布式:每台机器跑一个CARLA Server,监听端口暴露给局域网,由一台控制机集中调度和汇聚数据。这种方案适合多车协同、远端路测数据回传、或者需要同时跑大量不同场景的进阶场景,但你要额外处理网络延迟、时钟同步和带宽问题,复杂度比单机多实例高一个量级,调试一次的时间够单机方案跑完三组实验。
我的建议很直接:毕业设计先做单机多实例,把“多服务器”流程跑通、把数据收上来、把实验做完,再考虑多机分布式。多机的问题不是连不上,而是连上之后数据怎么对齐、怎么不丢,这在第5章会展开讲。单机多实例踩坑少,但论文里能讲清楚架构和参数设计,工作量和创新点都够。
2.3 系统与硬件选型:Ubuntu 24.04下装CARLA的兼容性陷阱
CARLA官方发布包主要面向Ubuntu,Windows虽然能跑,但多实例部署的灵活性差不少。如果你是Ubuntu 24.04,要特别注意:官方支持最好的是22.04及更早版本,24.04不在默认支持列表里,最少要手工补几个运行库,包括libomp、libtiff、libpng和libjpeg。缺库的典型表现是不报错,但启动后黑屏或者卡在Logo界面,日志里看不到任何明确的错误信息。
显存方面,官方最低要求是8GB,但那是单实例的标准。多实例并行部署时,每个实例至少单独预留6GB显存才稳定,两张8GB显卡跑两个实例是安全配置,硬塞四个实例反而会因为显存交换让帧率跌到不可用。内存同理,16GB内存能稳定跑两个实例,想跑四个至少32GB。
多机场景下网卡比CPU更重要。千兆网是底线,批量回传激光雷达点云时千兆带宽很快就会被占满,表现是客户端回调越来越慢,最后卡死。有条件就上万兆或25G网卡,普通毕设环境没有这个条件就退回单机多实例,别硬上多机。
3. 搭出第一套多服务器环境:CARLA安装、端口化启动与多客户端验证
3.1 从0到1:Ubuntu上CARLA的安装与启动
拿到CARLA压缩包之后解压到工作目录,然后在终端里先补依赖。如果你在Ubuntu 24.04上,下面这组命令可以解决大部分缺库问题:
sudo apt update sudo apt install -y libomp5 libtiff5 libpng16-16 libjpeg8 cd ~/carla chmod +x CarlaUE4.sh ./CarlaUE4.sh -quality-level=Low -carla-port=2000这里的-quality-level有三个常用档位:Low适合做纯物理仿真和车辆动力学测试,渲染开销最小;High是默认档位,相机图像质量可以用于视觉感知;Epic画质最接近真实光影,但帧率压力最大,多实例环境下不建议一上来就开Epic。内存16GB的机器,建议统一用Low起步,先验证流程通不通,再去调画质。
启动之后你会看到虚幻引擎的控制台窗口,日志里出现Server listening on port 2000就说明服务器已经就绪。这时候再用python3 -c "import carla"确认Python API可用,如果提示找不到carla模块,一般是忘了把~/carla/PythonAPI/carla/dist下的.egg文件或.whl安装到当前Python环境。这一步是大量新手翻车的地方:服务器启动成功、Python客户端连不上,绝大多数不是网络问题,是Python包里carla模块没装。
3.2 一台机器跑多个服务器实例:拉起四个端口的启动脚本
确认单个服务器能跑起来之后,就可以写多实例启动脚本。脚本要做的事情只有三件:设置每个实例的CUDA显卡、指定端口、指定画质。
#!/bin/bash export CARLA_ROOT=$HOME/carla pkill -f CarlaUE4.sh sleep 2 CUDA_VISIBLE_DEVICES=0 $CARLA_ROOT/CarlaUE4.sh \ -quality-level=Low -carla-port=2000 & sleep 5 CUDA_VISIBLE_DEVICES=1 $CARLA_ROOT/CarlaUE4.sh \ -quality-level=Low -carla-port=2001 & sleep 5 CUDA_VISIBLE_DEVICES=0 $CARLA_ROOT/CarlaUE4.sh \ -quality-level=Low -carla-port=2002 -render-off & sleep 5 CUDA_VISIBLE_DEVICES=1 $CARLA_ROOT/CarlaUE4.sh \ -quality-level=Low -carla-port=2003 -render-off &CUDA_VISIBLE_DEVICES=0的含义是让这个CARLA进程只能看到第0张显卡,显存被它独占,不会和另一个实例抢显存。这里的-render-off参数非常关键:它关闭渲染管线,服务器不再生成画面,大幅降低CPU和GPU消耗。但要注意,-render-off模式下相机传感器无法工作,面向视觉感知的数据采集必须关掉这个参数;如果这个实例只跑车辆控制、路径规划或激光雷达点云逻辑,那-render-off能省下大量资源。
脚本里每启动一个实例就sleep 5,目的是等虚幻引擎完成资源加载,避免多个实例同时抢CPU和磁盘IO导致启动失败。pkill -f CarlaUE4.sh放在最前面是为了清理上一次实验残留的僵尸进程,在调试阶段它比手动关窗口省事得多。启动后用nvidia-smi看每个进程的显存占用,用lsof -i:2000确认端口已被监听,这两个命令是排查“为什么连不上”的第一现场。
3.3 多客户端验证:两个Client分别连接两个端口
服务器脚本跑起来之后,用下面的Python脚本验证两个端口都能正常响应:
import carla CLIENT_TIMEOUT = 15.0 for port in (2000, 2001): client = carla.Client('127.0.0.1', port) client.set_timeout(CLIENT_TIMEOUT) world = client.get_world() tmap = world.get_map() actors = world.get_actors() print(f'[{port}] map: {tmap.name}, actors: {len(actors)}')set_timeout(15.0)这个参数一定要给够,默认只有几秒,CARLA服务器首次加载地图时响应很慢,超时设置太短会直接抛出连接异常,让你误以为端口没起来。get_world()如果返回正常,说明RPC通道已通;tmap.name能告诉你当前服务器跑的是哪个Town,方便后面按地图分配任务。
初次验证时一次连两个端口就够,不要一次连五个。每个客户端建立连接后CARLA都会往服务器推一份世界状态,连接数量越多服务器负担越重。验证的目的只是确认多实例部署成功,不是压测,后面第6章再单独说压测该怎么设计。
3.4 关键参数怎么定:渲染质量、帧同步与GPU分配
参数设置是这套方案里最不能拍脑袋的部分。先看一张常用参数表,后面再解释每个参数在什么场景下怎么调:
| 参数 | 推荐值 | 使用场景 |
|---|---|---|
| quality-level | Low | 多实例、纯控制/规划实验 |
| quality-level | High | 视觉感知数据采集 |
| render-off | 开 | 不需要相机图像,只跑点云或控制 |
| synchronous_mode | True | 需要逐帧同步数据的训练场景 |
| fixed_delta_seconds | 0.05 | 等效20FPS的固定步长 |
| CUDA_VISIBLE_DEVICES | 按实例分配 | 多GPU避免显存竞争 |
fixed_delta_seconds=0.05意味着仿真时间每步前进0.05秒,服务器不再按真实时间跑,而是按你设定的步长推演。这在多服务器场景下极其重要:两个服务器的时钟可以分别推进,但只要步长一致,数据在时间轴上就是对齐的。synchronous_mode=True则是让客户端调用world.tick()之后仿真才前进一步,服务器不会自己往下跑。这样做的代价是帧率完全由客户端决定,如果客户端处理太慢,仿真就卡住,但对于数据采集来说这是最稳的模式。
GPU分配不是玄学。显存小的卡先分配给了-render-off的实例,因为它不需要渲染缓冲,显存占用低;带相机采样的实例单独占一张卡。这个细节能直接影响多实例并发时的稳定性。
4. 把多服务器串成一套仿真系统:任务调度、时间同步与ROS对接
4.1 调度层的角色与任务分法
多服务器搭好只是第一步,真正让它在毕业设计里有价值的是调度层。调度层要回答三个问题:哪个服务器跑哪张地图、哪个服务器跑什么天气、哪个服务器负责输出什么数据。手工一个个在客户端里写死也可以,但改一个场景要改代码重跑,效率太低。
我更建议把调度设计成一个独立的配置层:用一份任务表描述“每个端口要做什么”,调度脚本读表然后分发给对应的服务器。这样改实验条件只需要改配置,不需要动仿真代码。这也是后面做多组对比实验时的后悔药——跑完一组换天气和交通流,改一行配置就能重新开始。
4.2 场景任务怎么拆:按Town、天气、交通流分配
场景任务拆分的常见做法是按数据用途拆。比如你的毕业设计同时需要感知训练数据、规划测试数据和极端场景回归数据,那就让不同的服务器管不同的部分:
| 端口 | 地图 | 天气 | 车辆数 | 数据用途 |
|---|---|---|---|---|
| 2000 | Town03 | RainyNoon | 10 | 感知模型训练 |
| 2001 | Town05 | ClearNoon | 5 | 规划算法测试 |
| 2002 | Town01 | CloudyNoon | 20 | 密集交通流回归 |
这样分配的核心原则是“资源隔离”:一个服务器跑深度学习推理导致帧率下降,不会影响另一个服务器正在采集的训练数据。很多人在单机上用多线程模拟多环境,一旦一个线程卡住,所有数据全废。多服务器方案天然规避了这个问题。
天气不要选太多极端天气,我通常每个服务器固定一种天气,避免在实验中频繁调用set_weather。频繁切换天气会让服务器重新编译部分渲染资源,连续切换十几次之后虚幻引擎会积累大量内存碎片,表现就是帧率明显降低,重启服务器才能恢复。
4.3 一个最小可用的任务调度脚本
调度脚本的核心结构很简单,看代码就能理解:
import carla TASKS = { 2000: {'map': 'Town03', 'weather': carla.WeatherParameters.RainyNoon, 'vehicles': 10}, 2001: {'map': 'Town05', 'weather': carla.WeatherParameters.ClearNoon, 'vehicles': 5}, 2002: {'map': 'Town01', 'weather': carla.WeatherParameters.CloudyNoon, 'vehicles': 20}, } def load_map(port, task): client = carla.Client('127.0.0.1', port) client.set_timeout(30.0) world = client.load_world(task['map']) world.set_weather(task['weather']) return world def spawn_vehicles(world, count): bp_lib = world.get_blueprint_library() vehicle_bps = bp_lib.filter('vehicle.*') spawn_points = world.get_map().get_spawn_points() for index in range(min(count, len(spawn_points))): bp = vehicle_bps[index % len(vehicle_bps)] world.try_spawn_actor(bp, spawn_points[index])load_world是耗时操作,一个Town的加载需要20到60秒不等,所以set_timeout(30.0)只是底线,慢的时候直接抛超时异常。try_spawn_actor比spawn_actor更安全,它会在生成位置冲突时返回None而不是抛异常中断整个脚本。车辆蓝图按index % len(vehicle_bps)轮询,是为了避免所有服务器都生成同一车型,导致后续数据同质化。
这个脚本只是一个骨架,实际使用时每个端口要有一个独立的“数据消费者”线程,持续读取传感器数据并落盘。调度脚本只负责初始化场景,之后每个服务器各跑各的,调度层不参与实时数据流。这样设计的好处是调度脚本挂掉不会影响已经生成的车辆和数据采集。
4.4 与ROS小车自主导航仿真对接:多服务器数据怎么流向ROS话题
如果你的毕业设计涉及ROS,多服务器这套结构可以直接和导航仿真对接。CARLA官方和社区都有现成的ROS桥接方案,核心思路是在每个CARLA服务器旁边挂一个桥接节点,把服务器里所有actor的状态、传感器数据转换成ROS话题。
多服务器环境下的关键操作是话题隔离。一个桥接节点对应一个命名空间,比如2000端口的服务器话题发布在/ego_0/下,2001端口发布在/ego_1/下。这样做的直接收益是:导航规划节点可以同时订阅两个命名空间的话题,一台虚拟机的多车协同数据互不干扰。
我在实际使用中遇到最多的对接问题不是连不上,而是缓存积压。ROS话题默认队列长度设置太大会让桥接节点内存暴涨,尤其是激光雷达点云这种高频大体积数据。建议点云话题队列长度控制在5以内,图像控制在10以内,控制指令话题可以放宽到50。队列变长的代价是延迟变大,而导航仿真吃的是新鲜数据不是历史数据,宁可丢帧不可滞后。
5. 多服务器仿真避坑:五条能救毕设的血泪经验
5.1 第一个Client刚连上,服务器FPS崩了一半
先描述现象:单实例跑得好好的,帧率稳定在25FPS,Python客户端一连上并生成几个车辆,帧率瞬间掉到12FPS,传感器数据全是延迟。
原因有两个:一是生成大量车辆本身就会增加物理计算负载,二是客户端连接后服务器默认会启动一个面向客户端的同步推送通道,客户端读数据太慢,服务器的数据堆积就把帧率拖垮了。
解决方式分两步。第一步,降低画质档位,把High降到Low,减少渲染开销;如果是纯传感器数据采集,直接加-render-off。第二步,在客户端里限流,不要每帧都去拉传感器数据,改成按固定步长批量取数,也就是把服务器设为同步模式,每处理完一帧统一回传一次。这样服务器不会被客户端的慢读卡住渲染循环。
5.2 两个Server的时钟对不上,传感器时间戳错位
现象:两个服务器采集的数据各自看起来很正常,但合并训练时发现时间戳错位,同一时刻的数据相互对不上,模型训练loss高得离谱。
原因:默认异步模式下,每个服务器按自己的真实运行节奏推进仿真,负载不同步导致时间轴漂移。服务器A已经跑完500帧,服务器B才跑完460帧,时间戳自然对不上。
解决:必须在所有服务器的客户端里统一开启同步模式并设置一致的固定步长。我一般统一用fixed_delta_seconds=0.05,然后在每个数据样本里记录完整的时间标签:服务器端口、帧序号、仿真时间。对齐时以帧序号为基准,仿真时间只作为辅助校验。这个改动的成本不高,但对后期数据使用体验提升极大。
5.3 多机回传激光雷达数据,网卡跑满直接丢包
现象:多机部署之后,控制机回传数据一开始正常,过了半小时回调越来越慢,最后报错RPC timeout,数据出现大量空洞。
原因:32线激光雷达单帧点云数据量大,多个传感器的数据同时回传,千兆网卡带宽被占满,数据包在网卡缓冲区排队,超过RPC超时时间后连接被判定为失效。
解决:第一,降低传感器采集频率,把激光雷达的采集频率从20Hz降到10Hz,视觉数据正常,这个调整对很多导航任务影响很小,但带宽占用直接减半。第二,开启网卡的巨帧支持,让单个数据包承载更多数据,降低包处理开销。第三,做数据分级回传:关键传感器数据实时回传,日志和可视化数据延时批量回传,错峰传输。我见过有人在多机场景把相机图像从RGB换成灰度图,带宽降到三分之一,对某些任务完全够用。
5.4 用load_world切换地图时,服务端直接挂掉
现象:客户端调用world.load_world('Town05')之后,服务器进程直接消失,终端里没有任何错误日志,其他端口也受到影响。
原因:load_world本质上是让虚幻引擎卸载当前地图再加载新地图,这个过程涉及大量资源释放和重新编译着色器。多实例环境下,一个实例做地图切换时占满CPU,其他实例的资源请求被饿死,最终一起崩溃。
解决:切换地图前先断开对应端口的客户端连接,让服务器进入空闲状态;然后调用load_world并等待返回;确认地图加载完成后再重新连接客户端。我一般在切换地图前后加10到20秒的延时,并且用world.get_map().name确认地图真的切换成功而不是只收到一个成功信号。另外,尽量安排两台服务器在不同时间切地图,避免两个实例同时触发资源重载。
5.5 Ubuntu 24.04启动出现黑屏或卡在Logo
现象:Ubuntu 24.04上运行CarlaUE4.sh,窗口弹出后黑屏,或者一直停在虚幻引擎Logo画面,终端没有任何Error输出。
原因:24.04的图形依赖库与CARLA运行时不一致,最常见的是libomp和libtiff版本不兼容,导致渲染初始化阶段未能完成,但引擎没有把错误打到标准输出。
解决:先按3.1节把依赖库手工装齐,尤其libomp5,然后确认显卡驱动版本新于535并支持Vulkan。还有一个被很多人忽略的原因是集成显卡被设为默认GPU,在终端里用__NV_PRIME_RENDER_OFFLOAD=1强制独显运行。如果这些都不行,最省事的方案是换Ubuntu 22.04,或者用官方Docker镜像跑,宿主机的图形环境问题就绕过去了。
6. 用三个指标验证多服务器方案是否真的值:帧率、吞吐与稳定性
6.1 三个指标怎么测才算数
多服务器方案值不值得,不要凭“感觉变快了”来判断,要看三个可量化指标。第一个是各服务器的帧率,在同一实验条件下,多实例部署的帧率和单实例对比,能看出资源分配是否合理。第二个是吞吐量,单位时间内客户端从所有服务器拿到的传感器数据量,这是衡量数据采集能力的核心数字。第三个是稳定性,连续运行半小时或一小时后,各服务器的帧率是否还在可接受区间,有没有内存持续增长和RPC超时。
这三个指标分别对应三个层面:帧率代表单机性能有没有被多实例拖垮,吞吐代表系统整体产出,稳定性代表能不能支撑毕业设计里的批量实验。
6.2 一个能直接跑的压测脚本
下面的脚本会连接两个服务器,模拟传感器数据读取并统计帧间隔与带宽:
import time import carla def pressure_test(ports, duration=60): clients = {} for port in ports: client = carla.Client('127.0.0.1', port) client.set_timeout(10.0) clients[port] = client.get_world() print(f'connected to {port}') start = time.time() tick_counts = {port: 0 for port in ports} last_time = None while time.time() - start < duration: for port, world in clients.items(): if hasattr(world, 'tick'): world.tick() tick_counts[port] += 1 now = time.time() if last_time: elapsed = now - last_time print(f'step_interval={elapsed * 1000:.1f}ms') last_time = now for port, count in tick_counts.items(): print(f'port {port}: {count} ticks in {duration}s') pressure_test([2000, 2001], duration=30)脚本原理是给每个服务器发tick请求,记录完成一轮tick需要的时间。world.tick()的返回耗时直接反映了服务器的物理计算和渲染负载,耗时越长说明服务器越接近瓶颈。我建议测试时长至少30秒,短于这个时间服务器还没达到稳态负载,测出来的数据偏差很大。
6.3 一个验证习惯:把实验配置写成JSON存档
最后分享一个我一直在用的习惯:每套实验的配置都写成一个JSON文件,和采集到的数据放在同一个目录里。配置包含端口列表、地图、天气、交通流数量、渲染档位、同步模式、固定步长和各传感器的参数。这样做有两个好处:一是数据出问题时有据可查,能快速定位是服务器配置问题还是采集代码问题;二是跑对比实验时直接改JSON,不用改代码,能减少很多因为手滑造成的不公平对比。
我现在每次开始新实验,第一件事就是写好JSON配置再启动服务器;数据采集完,先核对配置和数据帧数再决定要不要重跑。这个习惯救了我不止一次——有一次实验跑了一整晚,第二天发现帧率异常,靠配置文件里的同步模式参数定位到是上次调试时没改回来。希望帮到你。
本文还有配套的精品资源,点击获取